Snap for 7973711 from 3d206ca917
to sc-d2-release
Change-Id: Ia8f0f3cd3b4774d408de9920d15b1b2955f461bf
This commit is contained in:
commit
1124bd4a66
|
@ -13,11 +13,20 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
|
||||
</vector>
|
||||
android:pathData="M5.8334,1.666H8.3334V3.3327H5.8334V6.666H4.1667V3.3327C4.1667,2.4122 4.9129,1.666 5.8334,1.666Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M4.1667,13.3327V16.666C4.1667,17.5865 4.9129,18.3327 5.8334,18.3327H8.3334V16.666H5.8334V13.3327H4.1667Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M14.1667,13.3327V16.666H11.6667V18.3327H14.1667C15.0872,18.3327 15.8334,17.5865 15.8334,16.666V13.3327H14.1667Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M15.8334,6.666V3.3327C15.8334,2.4122 15.0872,1.666 14.1667,1.666H11.6667V3.3327H14.1667V6.666H15.8334Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
|
@ -38,13 +38,13 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@drawable/ic_screenshot"
|
||||
android:text="@string/action_screenshot"
|
||||
android:theme="@style/ThemeControlHighlightWorkspaceColor"
|
||||
android:visibility="gone" />
|
||||
android:theme="@style/ThemeControlHighlightWorkspaceColor" />
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/action_split_space"
|
||||
android:layout_width="@dimen/overview_actions_button_spacing"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/action_split"
|
||||
|
@ -56,11 +56,9 @@
|
|||
android:visibility="gone" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/action_split_space"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone" />
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/oav_three_button_space"
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
<!-- Overrideable in overlay that provides the Overview Actions. -->
|
||||
<dimen name="overview_actions_height">48dp</dimen>
|
||||
<dimen name="overview_actions_button_spacing">32dp</dimen>
|
||||
<dimen name="overview_actions_button_spacing_grid">36dp</dimen>
|
||||
<dimen name="overview_actions_margin_gesture">28dp</dimen>
|
||||
<dimen name="overview_actions_top_margin_gesture_grid_portrait">19.37dp</dimen>
|
||||
<dimen name="overview_actions_bottom_margin_gesture_grid_portrait">22dp</dimen>
|
||||
|
|
|
@ -218,10 +218,10 @@
|
|||
<string name="taskbar_edu_closed">Taskbar education closed</string>
|
||||
<!-- Text in dialog that lets a user know how they can use the taskbar to switch apps on their device.
|
||||
[CHAR_LIMIT=60] -->
|
||||
<string name="taskbar_edu_switch_apps" translatable="false">Use the taskbar to switch apps</string>
|
||||
<string name="taskbar_edu_switch_apps">Use the taskbar to switch apps</string>
|
||||
<!-- Text in dialog that lets a user know how they can use the taskbar to use multiple apps at once on their device.
|
||||
[CHAR_LIMIT=60] -->
|
||||
<string name="taskbar_edu_splitscreen" translatable="false">Drag to the side to use two apps at once</string>
|
||||
<string name="taskbar_edu_splitscreen">Drag to the side to use two apps at once</string>
|
||||
<!-- Text in dialog that lets a user know how they can hide the taskbar on their device.
|
||||
[CHAR_LIMIT=60] -->
|
||||
<string name="taskbar_edu_stashing">Touch & hold to hide the taskbar</string>
|
||||
|
|
|
@ -1214,14 +1214,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns view on the workspace that corresponds to the closing app in the list of app targets
|
||||
* Returns view on launcher that corresponds to the closing app in the list of app targets
|
||||
*/
|
||||
private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat[] appTargets) {
|
||||
private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) {
|
||||
for (RemoteAnimationTargetCompat appTarget : appTargets) {
|
||||
if (appTarget.mode == MODE_CLOSING) {
|
||||
View workspaceView = findWorkspaceView(appTarget);
|
||||
if (workspaceView != null) {
|
||||
return workspaceView;
|
||||
View launcherView = findLauncherView(appTarget);
|
||||
if (launcherView != null) {
|
||||
return launcherView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1229,9 +1229,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns view on the workspace that corresponds to the {@param runningTaskTarget}.
|
||||
* Returns view on launcher that corresponds to the {@param runningTaskTarget}.
|
||||
*/
|
||||
private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat runningTaskTarget) {
|
||||
private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) {
|
||||
if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1269,7 +1269,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
}
|
||||
}
|
||||
|
||||
return mLauncher.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
|
||||
return mLauncher.getFirstMatchForAppClose(launchCookieItemId,
|
||||
packageName, UserHandle.of(runningTaskTarget.taskInfo.userId));
|
||||
}
|
||||
|
||||
|
@ -1292,7 +1292,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
* Closing animator that animates the window into its final location on the workspace.
|
||||
*/
|
||||
private void getClosingWindowAnimators(AnimatorSet animation,
|
||||
RemoteAnimationTargetCompat[] targets, View workspaceView) {
|
||||
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) {
|
||||
FloatingIconView floatingIconView = null;
|
||||
FloatingWidgetView floatingWidget = null;
|
||||
RectF targetRect = new RectF();
|
||||
|
@ -1308,17 +1308,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
}
|
||||
|
||||
// Get floating view and target rect.
|
||||
if (workspaceView instanceof LauncherAppWidgetHostView) {
|
||||
if (launcherView instanceof LauncherAppWidgetHostView) {
|
||||
Size windowSize = new Size(mDeviceProfile.availableWidthPx,
|
||||
mDeviceProfile.availableHeightPx);
|
||||
int fallbackBackgroundColor =
|
||||
FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget);
|
||||
floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher,
|
||||
(LauncherAppWidgetHostView) workspaceView, targetRect, windowSize,
|
||||
(LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
|
||||
mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
|
||||
isTransluscent, fallbackBackgroundColor);
|
||||
} else if (workspaceView != null) {
|
||||
floatingIconView = getFloatingIconView(mLauncher, workspaceView,
|
||||
} else if (launcherView != null) {
|
||||
floatingIconView = getFloatingIconView(mLauncher, launcherView,
|
||||
true /* hideOriginal */, targetRect, false /* isOpening */);
|
||||
} else {
|
||||
targetRect.set(getDefaultWindowTargetRect());
|
||||
|
@ -1373,15 +1373,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
}
|
||||
|
||||
// Use a fixed velocity to start the animation.
|
||||
float velocityPxPerS = DynamicResource.provider(mLauncher)
|
||||
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
|
||||
PointF velocity = new PointF(0, -velocityPxPerS);
|
||||
animation.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
|
||||
true /* animateOverviewScrim */, workspaceView).getAnimators());
|
||||
animation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
anim.start(mLauncher, velocity);
|
||||
anim.start(mLauncher, velocityPxPerS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1556,22 +1551,30 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
if (anim == null) {
|
||||
anim = new AnimatorSet();
|
||||
|
||||
View workspaceView = findWorkspaceView(appTargets);
|
||||
boolean isWorkspaceViewVisible = workspaceView != null
|
||||
&& !mLauncher.isInState(LauncherState.ALL_APPS)
|
||||
&& !mLauncher.getWorkspace().isOverlayShown();
|
||||
boolean playFallBackAnimation = !isWorkspaceViewVisible
|
||||
&& (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|
||||
|| mLauncher.isForceInvisible());
|
||||
final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
|
||||
|| launcherIsATargetWithMode(appTargets, MODE_OPENING);
|
||||
|
||||
View launcherView = findLauncherView(appTargets);
|
||||
boolean playFallBackAnimation = (launcherView == null
|
||||
&& launcherIsForceInvisibleOrOpening)
|
||||
|| mLauncher.getWorkspace().isOverlayShown();
|
||||
|
||||
boolean playWorkspaceReveal = true;
|
||||
if (mFromUnlock) {
|
||||
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
|
||||
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
|
||||
&& !playFallBackAnimation) {
|
||||
getClosingWindowAnimators(anim, appTargets, workspaceView);
|
||||
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
|
||||
playWorkspaceReveal = false;
|
||||
// Use a fixed velocity to start the animation.
|
||||
float velocityPxPerS = DynamicResource.provider(mLauncher)
|
||||
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
|
||||
PointF velocity = new PointF(0, -velocityPxPerS);
|
||||
getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
|
||||
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
|
||||
true /* animateOverviewScrim */, launcherView).getAnimators());
|
||||
// We play StaggeredWorkspaceAnim as a part of the closing window animation.
|
||||
playWorkspaceReveal = false;
|
||||
}
|
||||
} else {
|
||||
anim.play(getFallbackClosingWindowAnimators(appTargets));
|
||||
}
|
||||
|
@ -1584,8 +1587,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
|||
// targets list because it is already visible). In that case, we force
|
||||
// invisibility on touch down, and only reset it after the animation to home
|
||||
// is initialized.
|
||||
if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|
||||
|| mLauncher.isForceInvisible()) {
|
||||
if (launcherIsForceInvisibleOrOpening) {
|
||||
addCujInstrumentation(
|
||||
anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
|
||||
// Only register the content animation for cancellation when state changes
|
||||
|
|
|
@ -195,7 +195,7 @@ import java.util.function.Supplier;
|
|||
if (hasAnyFlag(changedFlags, FLAG_RESUMED)) {
|
||||
boolean isResumed = isResumed();
|
||||
ObjectAnimator anim = mIconAlignmentForResumedState
|
||||
.animateToValue(getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
|
||||
.animateToValue(isResumed ? 1 : 0)
|
||||
.setDuration(duration);
|
||||
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
|
@ -351,9 +351,16 @@ import java.util.function.Supplier;
|
|||
|
||||
private void endGestureStateOverride(boolean finishedToApp) {
|
||||
mCallbacks.removeListener(this);
|
||||
|
||||
// Update the resumed state immediately to ensure a seamless handoff
|
||||
boolean launcherResumed = !finishedToApp;
|
||||
mIconAlignmentForResumedState.updateValue(launcherResumed ? 1 : 0);
|
||||
|
||||
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
|
||||
updateStateForFlag(FLAG_RESUMED, launcherResumed);
|
||||
applyState();
|
||||
|
||||
|
||||
TaskbarStashController controller = mControllers.taskbarStashController;
|
||||
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
|
||||
controller.applyState();
|
||||
|
|
|
@ -244,7 +244,7 @@ public class LauncherSwipeHandlerV2 extends
|
|||
}
|
||||
}
|
||||
|
||||
return mActivity.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
|
||||
return mActivity.getFirstMatchForAppClose(launchCookieItemId,
|
||||
runningTaskView.getTask().key.getComponent().getPackageName(),
|
||||
UserHandle.of(runningTaskView.getTask().key.userId));
|
||||
}
|
||||
|
|
|
@ -22,8 +22,10 @@ import android.graphics.Rect;
|
|||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -215,6 +217,13 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
|||
public void setDp(DeviceProfile dp) {
|
||||
mDp = dp;
|
||||
updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
params.weight = dp.isVerticalBarLayout() ? 1 : 0;
|
||||
findViewById(R.id.action_split_space).setLayoutParams(params);
|
||||
|
||||
requestLayout();
|
||||
|
||||
mSplitButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
|
|
|
@ -332,6 +332,8 @@
|
|||
<dimen name="overview_task_margin">0dp</dimen>
|
||||
<dimen name="overview_task_margin_focused">0dp</dimen>
|
||||
<dimen name="overview_task_margin_grid">0dp</dimen>
|
||||
<dimen name="overview_actions_button_spacing">0dp</dimen>
|
||||
<dimen name="overview_actions_button_spacing_grid">0dp</dimen>
|
||||
<dimen name="overview_actions_margin_gesture">0dp</dimen>
|
||||
<dimen name="overview_actions_top_margin_gesture_grid_portrait">0dp</dimen>
|
||||
<dimen name="overview_actions_bottom_margin_gesture_grid_portrait">0dp</dimen>
|
||||
|
|
|
@ -187,6 +187,7 @@ public class DeviceProfile {
|
|||
public final int overviewActionsMarginThreeButtonPx;
|
||||
public final int overviewActionsTopMarginGesturePx;
|
||||
public final int overviewActionsBottomMarginGesturePx;
|
||||
public final int overviewActionsButtonSpacing;
|
||||
public int overviewPageSpacing;
|
||||
public int overviewRowSpacing;
|
||||
public int overviewGridSideMargin;
|
||||
|
@ -377,10 +378,14 @@ public class DeviceProfile {
|
|||
overviewActionsBottomMarginGesturePx = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_gesture_grid_portrait);
|
||||
}
|
||||
overviewActionsButtonSpacing = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_button_spacing_grid);
|
||||
} else {
|
||||
overviewActionsTopMarginGesturePx = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_margin_gesture);
|
||||
overviewActionsBottomMarginGesturePx = overviewActionsTopMarginGesturePx;
|
||||
overviewActionsButtonSpacing = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_button_spacing);
|
||||
}
|
||||
overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_margin_three_button);
|
||||
|
|
|
@ -29,6 +29,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
|
|||
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
|
||||
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
|
||||
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
|
||||
|
@ -39,6 +40,7 @@ import static com.android.launcher3.LauncherState.NO_SCALE;
|
|||
import static com.android.launcher3.LauncherState.SPRING_LOADED;
|
||||
import static com.android.launcher3.Utilities.postAsyncCallback;
|
||||
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
|
||||
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
|
||||
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
||||
|
@ -55,6 +57,7 @@ import static com.android.launcher3.popup.SystemShortcut.INSTALL;
|
|||
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
|
||||
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -90,6 +93,7 @@ import android.os.Process;
|
|||
import android.os.StrictMode;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.util.Log;
|
||||
|
@ -215,6 +219,7 @@ import java.io.FileDescriptor;
|
|||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -2674,6 +2679,79 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
|
||||
* animation.
|
||||
*
|
||||
* @param preferredItemId The id of the preferred item to match to if it exists.
|
||||
* @param packageName The package name of the app to match.
|
||||
* @param user The user of the app to match.
|
||||
*/
|
||||
public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
|
||||
final ItemInfoMatcher preferredItem = (info, cn) ->
|
||||
info != null && info.id == preferredItemId;
|
||||
final ItemInfoMatcher packageAndUserAndApp = (info, cn) ->
|
||||
info != null
|
||||
&& info.itemType == ITEM_TYPE_APPLICATION
|
||||
&& info.user.equals(user)
|
||||
&& info.getTargetComponent() != null
|
||||
&& TextUtils.equals(info.getTargetComponent().getPackageName(),
|
||||
packageName);
|
||||
|
||||
if (isInState(LauncherState.ALL_APPS)) {
|
||||
return getFirstMatch(Collections.singletonList(mAppsView.getActiveRecyclerView()),
|
||||
preferredItem, packageAndUserAndApp);
|
||||
} else {
|
||||
List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
|
||||
containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
|
||||
mWorkspace.forEachVisiblePage(page
|
||||
-> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
|
||||
|
||||
// Order: Preferred item by itself or in folder, then by matching package/user
|
||||
if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
|
||||
return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
|
||||
packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
|
||||
} else {
|
||||
// Do not use Folder as a criteria, since it'll cause a crash when trying to draw
|
||||
// FolderAdaptiveIcon as the background.
|
||||
return getFirstMatch(containers, preferredItem, packageAndUserAndApp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first view matching the ordered operators across the given viewgroups in order.
|
||||
* @param containers List of ViewGroups to scan, in order of preference.
|
||||
* @param operators List of operators, in order starting from best matching operator.
|
||||
*/
|
||||
private static View getFirstMatch(Iterable<ViewGroup> containers,
|
||||
final ItemInfoMatcher... operators) {
|
||||
for (ItemInfoMatcher operator : operators) {
|
||||
for (ViewGroup container : containers) {
|
||||
View match = mapOverViewGroup(container, operator);
|
||||
if (match != null) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first view matching the operator in the given ViewGroups, or null if none.
|
||||
* Forward iteration matters.
|
||||
*/
|
||||
private static View mapOverViewGroup(ViewGroup container, ItemInfoMatcher op) {
|
||||
final int itemCount = container.getChildCount();
|
||||
for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
|
||||
View item = container.getChildAt(itemIdx);
|
||||
if (op.matchesInfo((ItemInfo) item.getTag())) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
|
||||
ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
|
||||
.setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
|
||||
|
|
|
@ -26,7 +26,6 @@ import static com.android.launcher3.LauncherState.HINT_STATE;
|
|||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.SPRING_LOADED;
|
||||
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
|
||||
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
|
||||
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
|
||||
|
@ -51,7 +50,6 @@ import android.os.Handler;
|
|||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
@ -3146,62 +3144,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
return layouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
|
||||
* animation.
|
||||
*
|
||||
* @param preferredItemId The id of the preferred item to match to if it exists.
|
||||
* @param packageName The package name of the app to match.
|
||||
* @param user The user of the app to match.
|
||||
*/
|
||||
public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
|
||||
final ItemOperator preferredItem = (ItemInfo info, View view) ->
|
||||
info != null && info.id == preferredItemId;
|
||||
final ItemOperator preferredItemInFolder = (info, view) -> {
|
||||
if (info instanceof FolderInfo) {
|
||||
FolderInfo folderInfo = (FolderInfo) info;
|
||||
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
|
||||
if (preferredItem.evaluate(shortcutInfo, view)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
final ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
|
||||
info != null
|
||||
&& info.itemType == ITEM_TYPE_APPLICATION
|
||||
&& info.user.equals(user)
|
||||
&& info.getTargetComponent() != null
|
||||
&& TextUtils.equals(info.getTargetComponent().getPackageName(),
|
||||
packageName);
|
||||
final ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
|
||||
if (info instanceof FolderInfo) {
|
||||
FolderInfo folderInfo = (FolderInfo) info;
|
||||
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
|
||||
if (packageAndUserAndApp.evaluate(shortcutInfo, view)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
List<CellLayout> cellLayouts = new ArrayList<>(getPanelCount() + 1);
|
||||
cellLayouts.add(getHotseat());
|
||||
forEachVisiblePage(page -> cellLayouts.add((CellLayout) page));
|
||||
|
||||
// Order: Preferred item, App icons in hotseat/workspace, app in folder in hotseat/workspace
|
||||
if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
|
||||
return getFirstMatch(cellLayouts, preferredItem, preferredItemInFolder,
|
||||
packageAndUserAndApp, packageAndUserAndAppInFolder);
|
||||
} else {
|
||||
// Do not use Folder as a criteria, since it'll cause a crash when trying to draw
|
||||
// FolderAdaptiveIcon as the background.
|
||||
return getFirstMatch(cellLayouts, preferredItem, packageAndUserAndApp);
|
||||
}
|
||||
}
|
||||
|
||||
public View getHomescreenIconByItemId(final int id) {
|
||||
return getFirstMatch((info, v) -> info != null && info.id == id);
|
||||
}
|
||||
|
@ -3227,23 +3169,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
return value[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first view matching the ordered operators across the given cell layouts by order.
|
||||
* @param cellLayouts List of CellLayouts to scan, in order of preference.
|
||||
* @param operators List of operators, in order starting from best matching operator.
|
||||
*/
|
||||
View getFirstMatch(Iterable<CellLayout> cellLayouts, final ItemOperator... operators) {
|
||||
for (ItemOperator operator : operators) {
|
||||
for (CellLayout cellLayout : cellLayouts) {
|
||||
View match = mapOverCellLayout(cellLayout, operator);
|
||||
if (match != null) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void clearDropTargets() {
|
||||
mapOverItems(new ItemOperator() {
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.ComponentName;
|
|||
import android.os.UserHandle;
|
||||
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
|
||||
|
@ -85,8 +86,16 @@ public interface ItemInfoMatcher {
|
|||
}
|
||||
|
||||
static ItemInfoMatcher ofShortcutKeys(Set<ShortcutKey> keys) {
|
||||
return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
|
||||
keys.contains(ShortcutKey.fromItemInfo(info));
|
||||
return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& keys.contains(ShortcutKey.fromItemInfo(info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a matcher for items within folders.
|
||||
*/
|
||||
static ItemInfoMatcher forFolderMatch(ItemInfoMatcher childOperator) {
|
||||
return (info, cn) -> info instanceof FolderInfo && ((FolderInfo) info).contents.stream()
|
||||
.anyMatch(childOperator::matchesInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -158,7 +158,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
|
|||
if (mContract == null) {
|
||||
return;
|
||||
}
|
||||
View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(-1,
|
||||
View icon = mLauncher.getFirstMatchForAppClose(-1,
|
||||
mContract.componentName.getPackageName(), mContract.user);
|
||||
|
||||
boolean iconChanged = mIcon != icon;
|
||||
|
@ -182,7 +182,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
|
|||
lp.topMargin = Math.round(mIconPosition.top);
|
||||
}
|
||||
}
|
||||
if (iconChanged && !mIconBounds.isEmpty()) {
|
||||
if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) {
|
||||
// Record the icon display
|
||||
setCurrentIconVisible(true);
|
||||
Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());
|
||||
|
|
|
@ -80,12 +80,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
|||
assertTrue(message, failed);
|
||||
}
|
||||
|
||||
private int pagesPerScreen() {
|
||||
return mLauncher.isTwoPanels() ? 2 : 1;
|
||||
}
|
||||
|
||||
private boolean isWorkspaceScrollable(Launcher launcher) {
|
||||
return launcher.getWorkspace().getPageCount() > pagesPerScreen();
|
||||
public static boolean isWorkspaceScrollable(Launcher launcher) {
|
||||
return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount();
|
||||
}
|
||||
|
||||
private int getCurrentWorkspacePage(Launcher launcher) {
|
||||
|
@ -192,7 +188,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
|||
executeOnLauncher(
|
||||
launcher -> assertEquals(
|
||||
"Ensuring workspace scrollable didn't switch to next screen",
|
||||
pagesPerScreen(), getCurrentWorkspacePage(launcher)));
|
||||
workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
|
||||
executeOnLauncher(
|
||||
launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
|
||||
isWorkspaceScrollable(launcher)));
|
||||
|
@ -209,7 +205,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
|||
workspace.flingForward();
|
||||
executeOnLauncher(
|
||||
launcher -> assertEquals("Flinging forward didn't switch workspace to next screen",
|
||||
pagesPerScreen(), getCurrentWorkspacePage(launcher)));
|
||||
workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
|
||||
assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
|
||||
|
||||
// Test starting a workspace app.
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* 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.ui.workspace;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.tapl.Workspace;
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest;
|
||||
import com.android.launcher3.ui.TaplTestsLauncher3;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Tests for two panel workspace.
|
||||
*
|
||||
* Note running these tests will clear the workspace on the device.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest {
|
||||
|
||||
Workspace mWorkspace;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
TaplTestsLauncher3.initialize(this);
|
||||
mWorkspace = mLauncher.getWorkspace();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragIconToRightPanel() {
|
||||
if (!mLauncher.isTwoPanels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre verifying the screens
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 1);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Maps", "Play Store");
|
||||
assertItemsOnPage(launcher, 1, "Chrome");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragIconToPage2() {
|
||||
if (!mLauncher.isTwoPanels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre verifying the screens
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3);
|
||||
assertItemsOnPage(launcher, 0, "Play Store");
|
||||
assertPageEmpty(launcher, 1);
|
||||
assertItemsOnPage(launcher, 2, "Maps");
|
||||
assertPageEmpty(launcher, 3);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragIconToPage3() {
|
||||
if (!mLauncher.isTwoPanels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre verifying the screens
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Phone"), 3);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
assertPageEmpty(launcher, 2);
|
||||
assertItemsOnPage(launcher, 3, "Phone");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
|
||||
if (!mLauncher.isTwoPanels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre verifying the screens
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 3);
|
||||
mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 0);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3);
|
||||
assertItemsOnPage(launcher, 0, "Play Store");
|
||||
assertPageEmpty(launcher, 1);
|
||||
assertItemsOnPage(launcher, 2, "Chrome");
|
||||
assertItemsOnPage(launcher, 3, "Maps");
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), -1);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3);
|
||||
assertItemsOnPage(launcher, 0, "Play Store");
|
||||
assertItemsOnPage(launcher, 1, "Maps");
|
||||
assertItemsOnPage(launcher, 2, "Chrome");
|
||||
assertPageEmpty(launcher, 3);
|
||||
});
|
||||
|
||||
// Move Chrome to the right panel as well, to make sure pages are not deleted whichever
|
||||
// page is the empty one
|
||||
mWorkspace.flingForward();
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Chrome"), 1);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3);
|
||||
assertItemsOnPage(launcher, 0, "Play Store");
|
||||
assertItemsOnPage(launcher, 1, "Maps");
|
||||
assertPageEmpty(launcher, 2);
|
||||
assertItemsOnPage(launcher, 3, "Chrome");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEmptyPagesGetRemovedIfBothPagesAreEmpty() {
|
||||
if (!mLauncher.isTwoPanels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre verifying the screens
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), 2);
|
||||
mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Camera"), 1);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3);
|
||||
assertItemsOnPage(launcher, 0, "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
assertItemsOnPage(launcher, 2, "Play Store");
|
||||
assertItemsOnPage(launcher, 3, "Camera");
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Camera"), -1);
|
||||
mWorkspace.flingForward();
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), -2);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertItemsOnPage(launcher, 1, "Camera");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMiddleEmptyPagesGetRemoved() {
|
||||
if (!mLauncher.isTwoPanels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pre verifying the screens
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1);
|
||||
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
|
||||
assertPageEmpty(launcher, 1);
|
||||
});
|
||||
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
|
||||
mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Messages"), 3);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
|
||||
assertItemsOnPage(launcher, 0, "Play Store");
|
||||
assertPageEmpty(launcher, 1);
|
||||
assertItemsOnPage(launcher, 2, "Maps");
|
||||
assertPageEmpty(launcher, 3);
|
||||
assertPageEmpty(launcher, 4);
|
||||
assertItemsOnPage(launcher, 5, "Messages");
|
||||
});
|
||||
|
||||
mWorkspace.flingBackward();
|
||||
mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
|
||||
|
||||
executeOnLauncher(launcher -> {
|
||||
assertPagesExist(launcher, 0, 1, 4, 5);
|
||||
assertItemsOnPage(launcher, 0, "Play Store");
|
||||
assertPageEmpty(launcher, 1);
|
||||
assertItemsOnPage(launcher, 4, "Maps");
|
||||
assertItemsOnPage(launcher, 5, "Messages");
|
||||
});
|
||||
}
|
||||
|
||||
private void assertPageEmpty(Launcher launcher, int pageId) {
|
||||
CellLayout page = launcher.getWorkspace().getScreenWithId(pageId);
|
||||
assertNotNull("Page " + pageId + " does NOT exist.", page);
|
||||
assertEquals("Page " + pageId + " is NOT empty. Number of items on the page:", 0,
|
||||
page.getShortcutsAndWidgets().getChildCount());
|
||||
}
|
||||
|
||||
private void assertPagesExist(Launcher launcher, int... pageIds) {
|
||||
int pageCount = launcher.getWorkspace().getPageCount();
|
||||
assertEquals("Existing page count does NOT match.", pageIds.length, pageCount);
|
||||
for (int i = 0; i < pageCount; i++) {
|
||||
CellLayout page = (CellLayout) launcher.getWorkspace().getPageAt(i);
|
||||
int pageId = launcher.getWorkspace().getIdForScreen(page);
|
||||
assertEquals("The page's id at index " + i + " does NOT match.", pageId,
|
||||
pageIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertItemsOnPage(Launcher launcher, int pageId, String... itemTitles) {
|
||||
Set<String> itemTitleSet = Arrays.stream(itemTitles).collect(Collectors.toSet());
|
||||
CellLayout page = launcher.getWorkspace().getScreenWithId(pageId);
|
||||
int itemCount = page.getShortcutsAndWidgets().getChildCount();
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
ItemInfo itemInfo = (ItemInfo) page.getShortcutsAndWidgets().getChildAt(i).getTag();
|
||||
if (itemInfo != null) {
|
||||
assertTrue("There was an extra item on page " + pageId + ": " + itemInfo.title,
|
||||
itemTitleSet.remove(itemInfo.title));
|
||||
}
|
||||
}
|
||||
assertTrue("Could NOT find some of the items on page " + pageId + ": "
|
||||
+ itemTitleSet.stream().collect(Collectors.joining(",")),
|
||||
itemTitleSet.isEmpty());
|
||||
}
|
||||
}
|
|
@ -145,16 +145,7 @@ public final class Workspace extends Home {
|
|||
if (!isWorkspaceScrollable(workspace)) {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"dragging icon to a second page of workspace to make it scrollable")) {
|
||||
dragIconToWorkspace(
|
||||
mLauncher,
|
||||
getHotseatAppIcon("Chrome"),
|
||||
new Point(mLauncher.getDevice().getDisplayWidth(),
|
||||
mLauncher.getVisibleBounds(workspace).centerY()),
|
||||
"popup_container",
|
||||
false,
|
||||
false,
|
||||
() -> mLauncher.expectEvent(
|
||||
TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT));
|
||||
dragIcon(workspace, getHotseatAppIcon("Chrome"), pagesPerScreen());
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +154,48 @@ public final class Workspace extends Home {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of pages that are visible on the screen simultaneously.
|
||||
*/
|
||||
public int pagesPerScreen() {
|
||||
return mLauncher.isTwoPanels() ? 2 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drags an icon to the (currentPage + pageDelta) page if the page already exists.
|
||||
* If the target page doesn't exist, the icon will be put onto an existing page that is the
|
||||
* closest to the target page.
|
||||
*
|
||||
* @param appIcon - icon to drag.
|
||||
* @param pageDelta - how many pages should the icon be dragged from the current page.
|
||||
* It can be a negative value.
|
||||
*/
|
||||
public void dragIcon(AppIcon appIcon, int pageDelta) {
|
||||
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"dragging icon to page with delta: " + pageDelta)) {
|
||||
dragIcon(workspace, appIcon, pageDelta);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dragIcon(UiObject2 workspace, AppIcon appIcon, int pageDelta) {
|
||||
int pageWidth = mLauncher.getDevice().getDisplayWidth() / pagesPerScreen();
|
||||
int targetX = (pageWidth / 2) + pageWidth * pageDelta;
|
||||
dragIconToWorkspace(
|
||||
mLauncher,
|
||||
appIcon,
|
||||
new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
|
||||
"popup_container",
|
||||
false,
|
||||
false,
|
||||
() -> mLauncher.expectEvent(
|
||||
TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT));
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
private boolean isWorkspaceScrollable(UiObject2 workspace) {
|
||||
return workspace.getChildCount() > (mLauncher.isTwoPanels() ? 2 : 1);
|
||||
}
|
||||
|
@ -258,10 +291,26 @@ public final class Workspace extends Home {
|
|||
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
|
||||
"want to drag icon to workspace")) {
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
final Point dragStartCenter = dragIconToSpringLoaded(launcher, downTime,
|
||||
Point dragStart = dragIconToSpringLoaded(launcher, downTime,
|
||||
launchable.getObject(), longPressIndicator, expectLongClickEvents);
|
||||
final Point targetDest = dest.get();
|
||||
launcher.movePointer(dragStartCenter, targetDest, 10, downTime, true,
|
||||
Point targetDest = dest.get();
|
||||
int displayX = launcher.getRealDisplaySize().x;
|
||||
|
||||
// Since the destination can be on another page, we need to drag to the edge first
|
||||
// until we reach the target page
|
||||
while (targetDest.x > displayX || targetDest.x < 0) {
|
||||
int edgeX = targetDest.x > 0 ? displayX : 0;
|
||||
Point screenEdge = new Point(edgeX, targetDest.y);
|
||||
launcher.movePointer(dragStart, screenEdge, 10, downTime, true,
|
||||
LauncherInstrumentation.GestureScope.INSIDE);
|
||||
launcher.waitForIdle(); // Wait for the page change to happen
|
||||
targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
|
||||
dragStart = screenEdge;
|
||||
}
|
||||
|
||||
// targetDest.x is now between 0 and displayX so we found the target page,
|
||||
// we just have to put move the icon to the destination and drop it
|
||||
launcher.movePointer(dragStart, targetDest, 10, downTime, true,
|
||||
LauncherInstrumentation.GestureScope.INSIDE);
|
||||
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue