Snap for 9248884 from ccc8e9fcf4 to tm-qpr2-release
Change-Id: Id53a94e60f2dc011dc38b6e784e92cc3366bb085
This commit is contained in:
@@ -43,7 +43,8 @@
|
||||
<!-- for rotating surface by arbitrary degree -->
|
||||
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA" />
|
||||
|
||||
<!--
|
||||
Permissions required for read/write access to the workspace data. These permission name
|
||||
should not conflict with that defined in other apps, as such an app should embed its package
|
||||
|
||||
@@ -18,22 +18,22 @@ package com.android.launcher3.model;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
|
||||
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.content.ComponentName;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
||||
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Task to update model as a result of predicted widgets update */
|
||||
@@ -59,50 +59,43 @@ public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {
|
||||
Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
|
||||
widget -> new ComponentKey(widget.providerName, widget.user)).collect(
|
||||
Collectors.toSet());
|
||||
Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
|
||||
Map<PackageUserKey, List<WidgetItem>> allWidgets =
|
||||
dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
|
||||
|
||||
FixedContainerItems fixedContainerItems =
|
||||
new FixedContainerItems(mPredictorState.containerId);
|
||||
List<WidgetItem> servicePredictedItems = new ArrayList<>();
|
||||
List<WidgetItem> localFilteredWidgets = new ArrayList<>();
|
||||
|
||||
if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
|
||||
for (AppTarget app : mTargets) {
|
||||
PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(),
|
||||
app.getUser());
|
||||
if (allWidgets.containsKey(packageUserKey)) {
|
||||
List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
|
||||
.filter(item ->
|
||||
!widgetsInWorkspace.contains(
|
||||
new ComponentKey(item.componentName, item.user)))
|
||||
.collect(Collectors.toList());
|
||||
if (notAddedWidgets.size() > 0) {
|
||||
// Even an apps have more than one widgets, we only include one widget.
|
||||
fixedContainerItems.items.add(
|
||||
new PendingAddWidgetInfo(
|
||||
notAddedWidgets.get(0).widgetInfo,
|
||||
CONTAINER_WIDGETS_PREDICTION));
|
||||
}
|
||||
}
|
||||
for (AppTarget app : mTargets) {
|
||||
PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
|
||||
List<WidgetItem> widgets = allWidgets.get(packageUserKey);
|
||||
if (widgets == null || widgets.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
Map<ComponentKey, WidgetItem> widgetItems =
|
||||
allWidgets.values().stream().flatMap(List::stream).distinct()
|
||||
.collect(Collectors.toMap(widget -> (ComponentKey) widget,
|
||||
widget -> widget));
|
||||
for (AppTarget app : mTargets) {
|
||||
if (TextUtils.isEmpty(app.getClassName())) {
|
||||
String className = app.getClassName();
|
||||
if (!TextUtils.isEmpty(className)) {
|
||||
WidgetItem item = widgets.stream()
|
||||
.filter(w -> className.equals(w.componentName.getClassName()))
|
||||
.filter(notOnWorkspace)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (item != null) {
|
||||
servicePredictedItems.add(item);
|
||||
continue;
|
||||
}
|
||||
ComponentKey targetWidget = new ComponentKey(
|
||||
new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
|
||||
if (widgetItems.containsKey(targetWidget)) {
|
||||
fixedContainerItems.items.add(
|
||||
new PendingAddWidgetInfo(widgetItems.get(
|
||||
targetWidget).widgetInfo,
|
||||
CONTAINER_WIDGETS_PREDICTION));
|
||||
}
|
||||
}
|
||||
// No widget was added by the service, try local filtering
|
||||
widgets.stream().filter(notOnWorkspace).findFirst()
|
||||
.ifPresent(localFilteredWidgets::add);
|
||||
}
|
||||
if (servicePredictedItems.isEmpty()) {
|
||||
servicePredictedItems.addAll(localFilteredWidgets);
|
||||
}
|
||||
FixedContainerItems fixedContainerItems =
|
||||
new FixedContainerItems(mPredictorState.containerId);
|
||||
servicePredictedItems.forEach(w -> fixedContainerItems.items.add(
|
||||
new PendingAddWidgetInfo(w.widgetInfo, CONTAINER_WIDGETS_PREDICTION)));
|
||||
|
||||
dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
|
||||
bindExtraContainerItems(fixedContainerItems);
|
||||
|
||||
|
||||
@@ -928,4 +928,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
mControllers.dumpLogs(prefix + "\t", pw);
|
||||
mDeviceProfile.dump(this, prefix, pw);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public int getTaskbarAllAppsTopPadding() {
|
||||
return mControllers.taskbarAllAppsController.getTaskbarAllAppsTopPadding();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,12 @@ package com.android.launcher3.taskbar;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Intent;
|
||||
@@ -49,7 +51,6 @@ import com.android.launcher3.DragSource;
|
||||
import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
@@ -70,6 +71,7 @@ import com.android.launcher3.testing.shared.TestProtocol;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.quickstep.util.LogUtils;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -83,7 +85,8 @@ import java.util.function.Predicate;
|
||||
public class TaskbarDragController extends DragController<BaseTaskbarContext> implements
|
||||
TaskbarControllers.LoggableTaskbarController {
|
||||
|
||||
private static boolean DEBUG_DRAG_SHADOW_SURFACE = false;
|
||||
private static final boolean DEBUG_DRAG_SHADOW_SURFACE = false;
|
||||
private static final int ANIM_DURATION_RETURN_ICON_TO_TASKBAR = 300;
|
||||
|
||||
private final int mDragIconSize;
|
||||
private final int[] mTempXY = new int[2];
|
||||
@@ -99,6 +102,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
|
||||
// Animation for the drag shadow back into position after an unsuccessful drag
|
||||
private ValueAnimator mReturnAnimator;
|
||||
private boolean mDisallowGlobalDrag;
|
||||
private boolean mDisallowLongClick;
|
||||
|
||||
public TaskbarDragController(BaseTaskbarContext activity) {
|
||||
super(activity);
|
||||
@@ -110,6 +115,14 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
mControllers = controllers;
|
||||
}
|
||||
|
||||
public void setDisallowGlobalDrag(boolean disallowGlobalDrag) {
|
||||
mDisallowGlobalDrag = disallowGlobalDrag;
|
||||
}
|
||||
|
||||
public void setDisallowLongClick(boolean disallowLongClick) {
|
||||
mDisallowLongClick = disallowLongClick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to start a system drag and drop operation for the given View, using its tag to
|
||||
* generate the ClipDescription and Intent.
|
||||
@@ -131,7 +144,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
View view,
|
||||
@Nullable DragPreviewProvider dragPreviewProvider,
|
||||
@Nullable Point iconShift) {
|
||||
if (!(view instanceof BubbleTextView)) {
|
||||
if (!(view instanceof BubbleTextView) || mDisallowLongClick) {
|
||||
return false;
|
||||
}
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
|
||||
@@ -293,6 +306,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
}
|
||||
|
||||
private void startSystemDrag(BubbleTextView btv) {
|
||||
if (mDisallowGlobalDrag) return;
|
||||
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(btv) {
|
||||
|
||||
@Override
|
||||
@@ -421,6 +435,45 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endDrag() {
|
||||
if (mDisallowGlobalDrag) {
|
||||
// We need to explicitly set deferDragViewCleanupPostAnimation to true here so the
|
||||
// super call doesn't remove it from the drag layer before the animation completes.
|
||||
// This variable gets set in to false in super.dispatchDropComplete() because it
|
||||
// (rightfully so, perhaps) thinks this drag operation has failed, and does its own
|
||||
// internal cleanup.
|
||||
// Another way to approach this would be to make all of overview a drop target and
|
||||
// accept the drop as successful and then run the setupReturnDragAnimator to simulate
|
||||
// drop failure to the user
|
||||
mDragObject.deferDragViewCleanupPostAnimation = true;
|
||||
|
||||
float fromX = mDragObject.x - mDragObject.xOffset;
|
||||
float fromY = mDragObject.y - mDragObject.yOffset;
|
||||
DragView dragView = mDragObject.dragView;
|
||||
setupReturnDragAnimator(fromX, fromY, (View) mDragObject.originalView,
|
||||
(x, y, scale, alpha) -> {
|
||||
dragView.setTranslationX(x);
|
||||
dragView.setTranslationY(y);
|
||||
dragView.setScaleX(scale);
|
||||
dragView.setScaleY(scale);
|
||||
dragView.setAlpha(alpha);
|
||||
});
|
||||
mReturnAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
callOnDragEnd();
|
||||
dragView.remove();
|
||||
dragView.clearAnimation();
|
||||
mReturnAnimator = null;
|
||||
|
||||
}
|
||||
});
|
||||
mReturnAnimator.start();
|
||||
}
|
||||
super.endDrag();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void callOnDragEnd() {
|
||||
super.callOnDragEnd();
|
||||
@@ -432,56 +485,20 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
SurfaceControl dragSurface = dragEvent.getDragSurface();
|
||||
|
||||
// For top level icons, the target is the icon itself
|
||||
View target = btv;
|
||||
Object tag = btv.getTag();
|
||||
if (tag instanceof ItemInfo) {
|
||||
ItemInfo item = (ItemInfo) tag;
|
||||
TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
|
||||
if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
|
||||
// Since all apps closes when the drag starts, target the all apps button instead.
|
||||
target = taskbarViewController.getAllAppsButtonView();
|
||||
} else if (item.container >= 0) {
|
||||
// Since folders close when the drag starts, target the folder icon instead.
|
||||
Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
|
||||
ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
|
||||
target = taskbarViewController.getFirstIconMatch(matcher);
|
||||
} else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
// Find first icon with same package/user as the deep shortcut.
|
||||
Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
|
||||
Collections.singleton(item.getTargetPackage()), item.user);
|
||||
target = taskbarViewController.getFirstIconMatch(packageUserMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish any pending return animation before starting a new drag
|
||||
if (mReturnAnimator != null) {
|
||||
mReturnAnimator.end();
|
||||
}
|
||||
View target = findTaskbarTargetForIconView(btv);
|
||||
|
||||
float fromX = dragEvent.getX() - dragEvent.getOffsetX();
|
||||
float fromY = dragEvent.getY() - dragEvent.getOffsetY();
|
||||
int[] toPosition = target.getLocationOnScreen();
|
||||
float toScale = (float) target.getWidth() / mDragIconSize;
|
||||
float toAlpha = (target == btv) ? 1f : 0f;
|
||||
final ViewRootImpl viewRoot = target.getViewRootImpl();
|
||||
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
|
||||
mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||
mReturnAnimator.setDuration(300);
|
||||
mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
mReturnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float t = animation.getAnimatedFraction();
|
||||
float accelT = Interpolators.ACCEL_2.getInterpolation(t);
|
||||
float scale = 1f - t * (1f - toScale);
|
||||
float alpha = 1f - accelT * (1f - toAlpha);
|
||||
tx.setPosition(dragSurface, Utilities.mapRange(t, fromX, toPosition[0]),
|
||||
Utilities.mapRange(t, fromY, toPosition[1]));
|
||||
tx.setScale(dragSurface, scale, scale);
|
||||
tx.setAlpha(dragSurface, alpha);
|
||||
tx.apply();
|
||||
}
|
||||
});
|
||||
setupReturnDragAnimator(fromX, fromY, btv,
|
||||
(x, y, scale, alpha) -> {
|
||||
tx.setPosition(dragSurface, x, y);
|
||||
tx.setScale(dragSurface, scale, scale);
|
||||
tx.setAlpha(dragSurface, alpha);
|
||||
tx.apply();
|
||||
});
|
||||
|
||||
mReturnAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
private boolean mCanceled = false;
|
||||
|
||||
@@ -517,6 +534,63 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
mReturnAnimator.start();
|
||||
}
|
||||
|
||||
private View findTaskbarTargetForIconView(@NonNull View iconView) {
|
||||
Object tag = iconView.getTag();
|
||||
if (tag instanceof ItemInfo) {
|
||||
ItemInfo item = (ItemInfo) tag;
|
||||
TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
|
||||
if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
|
||||
// Since all apps closes when the drag starts, target the all apps button instead.
|
||||
return taskbarViewController.getAllAppsButtonView();
|
||||
} else if (item.container >= 0) {
|
||||
// Since folders close when the drag starts, target the folder icon instead.
|
||||
Predicate<ItemInfo> matcher = ItemInfoMatcher.forFolderMatch(
|
||||
ItemInfoMatcher.ofItemIds(IntSet.wrap(item.id)));
|
||||
return taskbarViewController.getFirstIconMatch(matcher);
|
||||
} else if (item.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
// Find first icon with same package/user as the deep shortcut.
|
||||
Predicate<ItemInfo> packageUserMatcher = ItemInfoMatcher.ofPackages(
|
||||
Collections.singleton(item.getTargetPackage()), item.user);
|
||||
return taskbarViewController.getFirstIconMatch(packageUserMatcher);
|
||||
}
|
||||
}
|
||||
return iconView;
|
||||
}
|
||||
|
||||
private void setupReturnDragAnimator(float fromX, float fromY, View originalView,
|
||||
TaskbarReturnPropertiesListener animListener) {
|
||||
// Finish any pending return animation before starting a new return
|
||||
if (mReturnAnimator != null) {
|
||||
mReturnAnimator.end();
|
||||
}
|
||||
|
||||
// For top level icons, the target is the icon itself
|
||||
View target = findTaskbarTargetForIconView(originalView);
|
||||
|
||||
int[] toPosition = target.getLocationOnScreen();
|
||||
float toScale = (float) target.getWidth() / mDragIconSize;
|
||||
float toAlpha = (target == originalView) ? 1f : 0f;
|
||||
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
|
||||
final FloatProp mDx = new FloatProp(fromX, toPosition[0], 0,
|
||||
ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.FAST_OUT_SLOW_IN);
|
||||
final FloatProp mDy = new FloatProp(fromY, toPosition[1], 0,
|
||||
ANIM_DURATION_RETURN_ICON_TO_TASKBAR,
|
||||
FAST_OUT_SLOW_IN);
|
||||
final FloatProp mScale = new FloatProp(1f, toScale, 0,
|
||||
ANIM_DURATION_RETURN_ICON_TO_TASKBAR, FAST_OUT_SLOW_IN);
|
||||
final FloatProp mAlpha = new FloatProp(1f, toAlpha, 0,
|
||||
ANIM_DURATION_RETURN_ICON_TO_TASKBAR, Interpolators.ACCEL_2);
|
||||
@Override
|
||||
public void onUpdate(float percent, boolean initOnly) {
|
||||
animListener.updateDragShadow(mDx.value, mDy.value, mScale.value, mAlpha.value);
|
||||
}
|
||||
};
|
||||
mReturnAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||
mReturnAnimator.setDuration(ANIM_DURATION_RETURN_ICON_TO_TASKBAR);
|
||||
mReturnAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
mReturnAnimator.addUpdateListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getX(MotionEvent ev) {
|
||||
// We will resize to fill the screen while dragging, so use screen coordinates. This ensures
|
||||
@@ -540,7 +614,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
|
||||
@Override
|
||||
protected void exitDrag() {
|
||||
if (mDragObject != null) {
|
||||
if (mDragObject != null && !mDisallowGlobalDrag) {
|
||||
mActivity.getDragLayer().removeView(mDragObject.dragView);
|
||||
}
|
||||
}
|
||||
@@ -556,6 +630,10 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
return null;
|
||||
}
|
||||
|
||||
interface TaskbarReturnPropertiesListener {
|
||||
void updateDragShadow(float x, float y, float scale, float alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpLogs(String prefix, PrintWriter pw) {
|
||||
pw.println(prefix + "TaskbarDragController:");
|
||||
@@ -566,5 +644,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
pw.println(prefix + "\tmRegistrationY=" + mRegistrationY);
|
||||
pw.println(prefix + "\tmIsSystemDragInProgress=" + mIsSystemDragInProgress);
|
||||
pw.println(prefix + "\tisInternalDragInProgess=" + super.isDragging());
|
||||
pw.println(prefix + "\tmDisallowGlobalDrag=" + mDisallowGlobalDrag);
|
||||
pw.println(prefix + "\tmDisallowLongClick=" + mDisallowLongClick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ public class TaskbarEduController implements TaskbarControllers.LoggableTaskbarC
|
||||
}
|
||||
|
||||
int getIconLayoutBoundsWidth() {
|
||||
return mControllers.taskbarViewController.getIconLayoutBounds().width();
|
||||
return mControllers.taskbarViewController.getIconLayoutWidth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ import com.android.launcher3.anim.AnimatorListeners;
|
||||
import com.android.launcher3.statemanager.StateManager;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.RecentsAnimationCallbacks;
|
||||
import com.android.quickstep.RecentsAnimationController;
|
||||
@@ -117,6 +119,10 @@ import java.util.StringJoiner;
|
||||
mLauncherState = finalState;
|
||||
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
|
||||
applyState();
|
||||
mControllers.taskbarDragController.setDisallowGlobalDrag(
|
||||
(finalState instanceof OverviewState));
|
||||
mControllers.taskbarDragController.setDisallowLongClick(
|
||||
finalState == LauncherState.OVERVIEW_SPLIT_SELECT);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -293,12 +293,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
int count = getChildCount();
|
||||
int countExcludingQsb = count;
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
if (deviceProfile.isQsbInline) {
|
||||
countExcludingQsb--;
|
||||
}
|
||||
int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
|
||||
int spaceNeeded = getIconLayoutWidth();
|
||||
int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
|
||||
boolean layoutRtl = isLayoutRtl();
|
||||
int iconEnd = right - (right - left - spaceNeeded) / 2;
|
||||
@@ -388,6 +384,18 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
return mIconLayoutBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the space used by the icons
|
||||
*/
|
||||
public int getIconLayoutWidth() {
|
||||
int countExcludingQsb = getChildCount();
|
||||
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
||||
if (deviceProfile.isQsbInline) {
|
||||
countExcludingQsb--;
|
||||
}
|
||||
return countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app icons currently shown in the taskbar.
|
||||
*/
|
||||
|
||||
@@ -199,6 +199,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
|
||||
return mTaskbarView.getIconLayoutBounds();
|
||||
}
|
||||
|
||||
public int getIconLayoutWidth() {
|
||||
return mTaskbarView.getIconLayoutWidth();
|
||||
}
|
||||
|
||||
public View[] getIconViews() {
|
||||
return mTaskbarView.getIconViews();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.launcher3.taskbar.allapps;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.appprediction.PredictionRowView;
|
||||
@@ -123,4 +124,11 @@ public final class TaskbarAllAppsController {
|
||||
.findFixedRowByType(PredictionRowView.class)
|
||||
.setPredictedApps(mPredictedApps);
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
public int getTaskbarAllAppsTopPadding() {
|
||||
// Allow null-pointer since this should only be null if the apps view is not showing.
|
||||
return mAppsView.getActiveRecyclerView().getClipBounds().top;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,7 +963,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
new ActiveGestureLog.CompoundString("on gesture started (animate=false)"));
|
||||
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
|
||||
mGestureStarted = true;
|
||||
SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.android.quickstep.util.TISBindHelper;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
|
||||
@@ -112,6 +113,13 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: {
|
||||
return getTISBinderUIProperty(Bundle::putInt, tisBinder ->
|
||||
tisBinder.getTaskbarManager()
|
||||
.getCurrentActivityContext()
|
||||
.getTaskbarAllAppsTopPadding());
|
||||
}
|
||||
}
|
||||
|
||||
return super.call(method, arg, extras);
|
||||
@@ -159,4 +167,16 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Bundle getTISBinderUIProperty(
|
||||
BundleSetter<T> bundleSetter, Function<TouchInteractionService.TISBinder, T> provider) {
|
||||
Bundle response = new Bundle();
|
||||
|
||||
runOnTISBinder(tisBinder -> bundleSetter.set(
|
||||
response,
|
||||
TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
||||
provider.apply(tisBinder)));
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,17 +343,6 @@ public class SystemUiProxy implements ISystemUiProxy {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifySwipeUpGestureStarted() {
|
||||
if (mSystemUiProxy != null) {
|
||||
try {
|
||||
mSystemUiProxy.notifySwipeUpGestureStarted();
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPrioritizedRotation(int rotation) {
|
||||
if (mSystemUiProxy != null) {
|
||||
|
||||
@@ -59,6 +59,7 @@ import android.util.Log;
|
||||
import android.view.Choreographer;
|
||||
import android.view.InputEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
@@ -226,12 +227,6 @@ public class TouchInteractionService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onTip(int actionType, int viewType) {
|
||||
// Please delete this method from the interface
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onAssistantAvailable(boolean available) {
|
||||
@@ -250,10 +245,9 @@ public class TouchInteractionService extends Service
|
||||
});
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
|
||||
boolean gestureSwipeLeft) {
|
||||
// Remove this method from the interface
|
||||
@Override
|
||||
public void onNavigationBarSurface(SurfaceControl surface) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
|
||||
@@ -26,7 +26,7 @@ public class TabletSplitToConfirmTimings
|
||||
public int getPlaceholderIconFadeInStart() { return 167; }
|
||||
public int getPlaceholderIconFadeInEnd() { return 250; }
|
||||
public int getStagedRectSlideStart() { return 0; }
|
||||
public int getStagedRectSlideEnd() { return 383; }
|
||||
public int getStagedRectSlideEnd() { return 500; }
|
||||
|
||||
public int getDuration() { return TABLET_CONFIRM_DURATION; }
|
||||
}
|
||||
|
||||
@@ -99,6 +99,10 @@ public class ClearAllButton extends Button {
|
||||
return false;
|
||||
}
|
||||
|
||||
public float getScrollAlpha() {
|
||||
return mScrollAlpha;
|
||||
}
|
||||
|
||||
public void setContentAlpha(float alpha) {
|
||||
if (mContentAlpha != alpha) {
|
||||
mContentAlpha = alpha;
|
||||
|
||||
@@ -82,6 +82,8 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
private static final int INDEX_FULLSCREEN_ALPHA = 2;
|
||||
private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
|
||||
private static final int INDEX_SHARE_TARGET_ALPHA = 4;
|
||||
private static final int INDEX_SCROLL_ALPHA = 5;
|
||||
private static final int NUM_ALPHAS = 6;
|
||||
|
||||
public @interface SplitButtonHiddenFlags { }
|
||||
public static final int FLAG_IS_NOT_TABLET = 1 << 0;
|
||||
@@ -126,7 +128,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5);
|
||||
mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS);
|
||||
mMultiValueAlpha.setUpdateVisibility(true);
|
||||
|
||||
findViewById(R.id.action_screenshot).setOnClickListener(this);
|
||||
@@ -247,6 +249,10 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
|
||||
}
|
||||
|
||||
public MultiProperty getIndexScrollAlpha() {
|
||||
return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
|
||||
*/
|
||||
|
||||
@@ -1913,6 +1913,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
}
|
||||
int scroll = mOrientationHandler.getPrimaryScroll(this);
|
||||
mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
|
||||
|
||||
// Clear all button alpha was set by the previous line.
|
||||
mActionsView.getIndexScrollAlpha().setValue(1 - mClearAllButton.getScrollAlpha());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-25
@@ -41,7 +41,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.icons.ComponentWithLabel;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
||||
@@ -136,21 +135,21 @@ public final class WidgetsPredicationUpdateTaskTest {
|
||||
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
|
||||
throws Exception {
|
||||
// WHEN newPredicationTask is executed with app predication of 5 apps.
|
||||
AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
|
||||
AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
|
||||
mUserHandle);
|
||||
AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
|
||||
AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
|
||||
mUserHandle);
|
||||
AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
|
||||
mUserHandle);
|
||||
AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
|
||||
AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
|
||||
mUserHandle);
|
||||
AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
|
||||
AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
|
||||
mUserHandle);
|
||||
mModelHelper.executeTaskForTest(
|
||||
newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
|
||||
.forEach(Runnable::run);
|
||||
|
||||
// THEN only 3 widgets are returned because
|
||||
// THEN only 2 widgets are returned because
|
||||
// 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
|
||||
// excluded from the result.
|
||||
// 2. app3 doesn't have a widget.
|
||||
@@ -159,45 +158,39 @@ public final class WidgetsPredicationUpdateTaskTest {
|
||||
.stream()
|
||||
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(recommendedWidgets).hasSize(3);
|
||||
assertThat(recommendedWidgets).hasSize(2);
|
||||
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
|
||||
assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
|
||||
assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
|
||||
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
|
||||
public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty()
|
||||
throws Exception {
|
||||
if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WHEN newPredicationTask is executed with 5 predicated widgets.
|
||||
AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
|
||||
mUserHandle);
|
||||
AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
|
||||
// Not installed widget
|
||||
AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
|
||||
mUserHandle);
|
||||
// Not installed app
|
||||
AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
|
||||
mUserHandle);
|
||||
// Not installed widget
|
||||
AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
|
||||
// Workspace added widgets
|
||||
AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
|
||||
mUserHandle);
|
||||
AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
|
||||
mUserHandle);
|
||||
mModelHelper.executeTaskForTest(
|
||||
newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1)))
|
||||
newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)))
|
||||
.forEach(Runnable::run);
|
||||
|
||||
// THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
|
||||
// THEN only 2 widgets are returned because the launcher only filters out non-exist widgets.
|
||||
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
|
||||
.stream()
|
||||
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(recommendedWidgets).hasSize(3);
|
||||
assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
|
||||
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
|
||||
assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
|
||||
assertThat(recommendedWidgets).hasSize(2);
|
||||
// Another widget from the same package
|
||||
assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
|
||||
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
|
||||
}
|
||||
|
||||
private void assertWidgetInfo(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -143,6 +143,7 @@ import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.LauncherDragController;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderGridOrganizer;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.icons.BitmapRenderer;
|
||||
@@ -1192,7 +1193,6 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
mOverlayManager.onActivityResumed(this);
|
||||
}
|
||||
|
||||
AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE);
|
||||
DragView.removeAllViews(this);
|
||||
TraceHelper.INSTANCE.endSection(traceToken);
|
||||
}
|
||||
@@ -1705,6 +1705,10 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
outState.remove(RUNTIME_STATE_WIDGET_PANEL);
|
||||
}
|
||||
|
||||
// We close any open folders and shortcut containers that are not safe for rebind,
|
||||
// and we need to make sure this state is reflected.
|
||||
AbstractFloatingView.closeAllOpenViewsExcept(
|
||||
this, isStarted() && !isForceInvisible(), TYPE_REBIND_SAFE);
|
||||
finishAutoCancelActionMode();
|
||||
|
||||
if (mPendingRequestArgs != null) {
|
||||
@@ -2815,16 +2819,30 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
}
|
||||
|
||||
return v;
|
||||
} 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
|
||||
return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
|
||||
packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
|
||||
}
|
||||
|
||||
// Look for the item inside the folder at the current page
|
||||
Folder folder = Folder.getOpen(this);
|
||||
if (folder != null) {
|
||||
View v = getFirstMatch(Collections.singletonList(
|
||||
folder.getContent().getCurrentCellLayout().getShortcutsAndWidgets()),
|
||||
preferredItem,
|
||||
packageAndUserAndApp);
|
||||
if (v == null) {
|
||||
folder.close(isStarted() && !isForceInvisible());
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
|
||||
packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -206,10 +206,6 @@ public final class FeatureFlags {
|
||||
public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
|
||||
"ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
|
||||
|
||||
public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag(
|
||||
"ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true,
|
||||
"Enables a local filter for recommended widgets.");
|
||||
|
||||
public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
|
||||
"Sends a notification whenever launcher encounters an uncaught exception.");
|
||||
|
||||
|
||||
@@ -219,6 +219,11 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
||||
return response;
|
||||
}
|
||||
|
||||
case TestProtocol.REQUEST_ALL_APPS_TOP_PADDING: {
|
||||
return getLauncherUIProperty(Bundle::putInt,
|
||||
l -> l.getAppsView().getActiveRecyclerView().getClipBounds().top);
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -112,6 +112,9 @@ public final class TestProtocol {
|
||||
"get-activities-created-count";
|
||||
public static final String REQUEST_GET_ACTIVITIES = "get-activities";
|
||||
public static final String REQUEST_HAS_TIS = "has-touch-interaction-service";
|
||||
public static final String REQUEST_TASKBAR_ALL_APPS_TOP_PADDING =
|
||||
"taskbar-all-apps-top-padding";
|
||||
public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding";
|
||||
|
||||
public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size";
|
||||
public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center";
|
||||
|
||||
@@ -46,8 +46,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
super(launcher);
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
mHeight = mLauncher.getVisibleBounds(allAppsContainer).height();
|
||||
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
|
||||
"apps_list_view");
|
||||
final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
|
||||
// Wait for the recycler to populate.
|
||||
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
|
||||
verifyNotFrozen("All apps freeze flags upon opening all apps");
|
||||
@@ -78,6 +77,11 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
|
||||
return false;
|
||||
}
|
||||
if (iconCenterInRecyclerTopPadding(appListRecycler, icon)) {
|
||||
LauncherInstrumentation.log(
|
||||
"hasClickableIcon: icon center is under the app list recycler's top padding.");
|
||||
return false;
|
||||
}
|
||||
if (iconBounds.bottom > displayBottom) {
|
||||
LauncherInstrumentation.log("hasClickableIcon: icon bottom below bottom offset");
|
||||
return false;
|
||||
@@ -92,6 +96,13 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
iconCenter.x, iconCenter.y);
|
||||
}
|
||||
|
||||
private boolean iconCenterInRecyclerTopPadding(UiObject2 appListRecycler, UiObject2 icon) {
|
||||
final Point iconCenter = icon.getVisibleCenter();
|
||||
|
||||
return iconCenter.y <= mLauncher.getVisibleBounds(appListRecycler).top
|
||||
+ getAppsListRecyclerTopPadding();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an icon. If the icon doesn't exist, return null.
|
||||
* Scrolls the app list when needed to make sure the icon is visible.
|
||||
@@ -105,9 +116,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"getting app icon " + appName + " on all apps")) {
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
|
||||
"apps_list_view");
|
||||
final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
|
||||
final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
|
||||
|
||||
int deviceHeight = mLauncher.getRealDisplaySize().y;
|
||||
int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
|
||||
@@ -128,10 +137,9 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
mLauncher.getVisibleBounds(icon).top
|
||||
< bottomGestureStartOnScreen)
|
||||
.collect(Collectors.toList()),
|
||||
hasSearchBox()
|
||||
? mLauncher.getVisibleBounds(searchBox).bottom
|
||||
- mLauncher.getVisibleBounds(allAppsContainer).top
|
||||
: 0);
|
||||
mLauncher.getVisibleBounds(appListRecycler).top
|
||||
+ getAppsListRecyclerTopPadding()
|
||||
- mLauncher.getVisibleBounds(allAppsContainer).top);
|
||||
verifyActiveContainer();
|
||||
final int newScroll = getAllAppsScroll();
|
||||
mLauncher.assertTrue(
|
||||
@@ -180,16 +188,22 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
|
||||
protected abstract boolean hasSearchBox();
|
||||
|
||||
protected abstract int getAppsListRecyclerTopPadding();
|
||||
|
||||
private void scrollBackToBeginning() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to scroll back in all apps")) {
|
||||
LauncherInstrumentation.log("Scrolling to the beginning");
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
|
||||
final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
|
||||
|
||||
int attempts = 0;
|
||||
final Rect margins = new Rect(
|
||||
0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
|
||||
/* left= */ 0,
|
||||
mLauncher.getVisibleBounds(appListRecycler).top
|
||||
+ getAppsListRecyclerTopPadding() + 1,
|
||||
/* right= */ 0,
|
||||
/* bottom= */ 5);
|
||||
|
||||
for (int scroll = getAllAppsScroll();
|
||||
scroll != 0;
|
||||
@@ -220,6 +234,10 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
}
|
||||
|
||||
private UiObject2 getAppListRecycler(UiObject2 allAppsContainer) {
|
||||
return mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view");
|
||||
}
|
||||
|
||||
private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
|
||||
return mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps");
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.launcher3.tapl;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.launcher3.testing.shared.TestProtocol;
|
||||
|
||||
/**
|
||||
* Operations on AllApps opened from the Taskbar.
|
||||
*/
|
||||
@@ -48,4 +50,10 @@ public class AllAppsFromTaskbar extends AllApps {
|
||||
protected boolean hasSearchBox() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAppsListRecyclerTopPadding() {
|
||||
return mLauncher.getTestInfo(TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING)
|
||||
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,42 +244,38 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
* Returns if clear all button is visible.
|
||||
*/
|
||||
public boolean isClearAllVisible() {
|
||||
return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
|
||||
return verifyActiveContainer().hasObject(
|
||||
mLauncher.getOverviewObjectSelector("clear_all"));
|
||||
}
|
||||
|
||||
protected boolean isActionsViewVisible() {
|
||||
if (!hasTasks() || isClearAllVisible()) {
|
||||
return false;
|
||||
}
|
||||
OverviewTask task = mLauncher.isTablet() ? getFocusedTaskForTablet() : getCurrentTask();
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
// In tablets, if focused task is not in center, overview actions aren't visible.
|
||||
if (mLauncher.isTablet()
|
||||
&& Math.abs(task.getExactCenterX() - mLauncher.getExactScreenCenterX()) >= 1) {
|
||||
return false;
|
||||
}
|
||||
// Overview actions aren't visible for split screen tasks.
|
||||
return !task.isTaskSplit();
|
||||
}
|
||||
|
||||
private void verifyActionsViewVisibility() {
|
||||
if (!hasTasks() || !isActionsViewVisible()) {
|
||||
return;
|
||||
}
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to assert overview actions view visibility")) {
|
||||
if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) {
|
||||
mLauncher.waitUntilOverviewObjectGone("action_buttons");
|
||||
} else {
|
||||
if (isActionsViewVisible()) {
|
||||
mLauncher.waitForOverviewObject("action_buttons");
|
||||
} else {
|
||||
mLauncher.waitUntilOverviewObjectGone("action_buttons");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if focused task is currently snapped task in tablet grid overview.
|
||||
*/
|
||||
private boolean isOverviewSnappedToFocusedTaskForTablet() {
|
||||
OverviewTask focusedTask = getFocusedTaskForTablet();
|
||||
if (focusedTask == null) {
|
||||
return false;
|
||||
}
|
||||
return Math.abs(focusedTask.getExactCenterX() - mLauncher.getExactScreenCenterX()) < 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Overview focused task if it exists.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.launcher3.tapl;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.launcher3.testing.shared.TestProtocol;
|
||||
|
||||
public class HomeAllApps extends AllApps {
|
||||
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
|
||||
|
||||
@@ -47,6 +49,12 @@ public class HomeAllApps extends AllApps {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAppsListRecyclerTopPadding() {
|
||||
return mLauncher.getTestInfo(TestProtocol.REQUEST_ALL_APPS_TOP_PADDING)
|
||||
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
|
||||
* @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
|
||||
|
||||
Reference in New Issue
Block a user