Merge "Updating icon badging logic"
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 0f92274..25b39ed 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS"/>
<uses-permission android:name="android.permission.REMOVE_TASKS"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<uses-permission android:name="android.permission.STATUS_BAR"/>
<uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index dd8afc2..1c7b509 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -51,7 +51,6 @@
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:drawableStart="@drawable/ic_split_screen"
android:text="@string/action_split"
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 31c0f5f..3a4bb10 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -39,4 +39,8 @@
<string name="wellbeing_provider_pkg" translatable="false"/>
<integer name="max_depth_blur_radius">23</integer>
+
+ <!-- Accessibility actions -->
+ <item type="id" name="action_move_to_top_or_left" />
+ <item type="id" name="action_move_to_bottom_or_right" />
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 151b8e4..70a4a7d 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -233,4 +233,9 @@
<string name="taskbar_edu_close">Close</string>
<!-- Text on button to finish a tutorial [CHAR_LIMIT=16] -->
<string name="taskbar_edu_done">Done</string>
+
+ <!-- Label for moving drop target to the top or left side of the screen, depending on orientation (from the taskbar only). -->
+ <string name="move_drop_target_top_or_left">Move to top/left</string>
+ <!-- Label for moving drop target to the bottom or right side of the screen, depending on orientation (from the taskbar only). -->
+ <string name="move_drop_target_bottom_or_right">Move to bottom/right</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
index 96559cb..962fd91 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
@@ -44,7 +44,7 @@
@Override
protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
- QuickstepLauncher launcher = (QuickstepLauncher) mLauncher;
+ QuickstepLauncher launcher = (QuickstepLauncher) mContext;
if (action == PIN_PREDICTION) {
if (launcher.getHotseatPredictionController() == null) {
return false;
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index e82c900..6ab49f8 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -161,8 +161,7 @@
}
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
for (ItemInfo info : itemsIdMap) {
- FolderInfo parent = info.container > 0
- ? (FolderInfo) itemsIdMap.get(info.container) : null;
+ FolderInfo parent = getContainer(info, itemsIdMap);
StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
}
additionalSnapshotEvents(instanceId);
@@ -199,8 +198,7 @@
}
for (ItemInfo info : itemsIdMap) {
- FolderInfo parent = info.container > 0
- ? (FolderInfo) itemsIdMap.get(info.container) : null;
+ FolderInfo parent = getContainer(info, itemsIdMap);
LauncherAtom.ItemInfo itemInfo = info.buildProto(parent);
Log.d(TAG, itemInfo.toString());
StatsEvent statsEvent = StatsLogCompatManager.buildStatsEvent(itemInfo,
@@ -222,6 +220,22 @@
}
}
+ private static FolderInfo getContainer(ItemInfo info, IntSparseArrayMap<ItemInfo> itemsIdMap) {
+ if (info.container > 0) {
+ ItemInfo containerInfo = itemsIdMap.get(info.container);
+
+ if (!(containerInfo instanceof FolderInfo)) {
+ Log.e(TAG, String.format(
+ "Item info: %s found with invalid container: %s",
+ info,
+ containerInfo));
+ }
+ // Allow crash to help debug b/173838775
+ return (FolderInfo) containerInfo;
+ }
+ return null;
+ }
+
@Override
public void validateData() {
super.validateData();
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index 90c035f..f1e6747 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -60,6 +60,7 @@
@Override
protected void onDestroy() {
+ super.onDestroy();
mRecentsActivity.setTaskbarUIController(null);
mRecentsActivity.getStateManager().removeStateListener(mStateListener);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 7d23439..2622700 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -99,6 +99,7 @@
@Override
protected void onDestroy() {
+ super.onDestroy();
onLauncherResumedOrPaused(false);
mTaskbarLauncherStateController.onDestroy();
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index f6e0426..ce1e8b6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -31,12 +31,14 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.LayoutRes;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.graphics.Rect;
import android.graphics.Region;
@@ -85,6 +87,7 @@
private static final int FLAG_DISABLE_RECENTS = 1 << 8;
private static final int FLAG_DISABLE_BACK = 1 << 9;
private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
+ private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 10;
private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
@@ -151,7 +154,9 @@
mPropertyHolders.add(new StatePropertyHolder(
mControllers.taskbarViewController.getTaskbarIconAlpha()
.getProperty(ALPHA_INDEX_KEYGUARD),
- flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
+ flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
+ && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0,
+ MultiValueAlpha.VALUE, 1, 0));
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
.getKeyguardBgTaskbar(),
@@ -285,6 +290,7 @@
int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
+ boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
// TODO(b/202218289) we're getting IME as not visible on lockscreen from system
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
@@ -294,6 +300,7 @@
updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
+ updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
if (mA11yButton != null) {
// Only used in 3 button
@@ -445,6 +452,12 @@
return mFloatingRotationButtonBounds.contains((int) ev.getX(), (int) ev.getY());
}
+ public void onConfigurationChanged(@Config int configChanges) {
+ if (mFloatingRotationButton != null) {
+ mFloatingRotationButton.onConfigurationChanged(configChanges);
+ }
+ }
+
public void onDestroy() {
mPropertyHolders.clear();
mControllers.rotationButtonController.unregisterListeners();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index bc68173..883515e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -30,6 +30,7 @@
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo.Config;
import android.content.pm.LauncherApps;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -52,6 +53,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
@@ -112,6 +114,8 @@
// The flag to know if the window is excluded from magnification region computation.
private boolean mIsExcludeFromMagnificationRegion = false;
+ private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
+
public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider) {
@@ -147,6 +151,8 @@
mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+ mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
+
// Construct controllers.
mControllers = new TaskbarControllers(this,
new TaskbarDragController(this),
@@ -208,6 +214,10 @@
mWindowManager.addView(mDragLayer, mWindowLayoutParams);
}
+ public void onConfigurationChanged(@Config int configChanges) {
+ mControllers.onConfigurationChanged(configChanges);
+ }
+
public boolean isThreeButtonNav() {
return mNavMode == Mode.THREE_BUTTONS;
}
@@ -328,6 +338,11 @@
return mControllers.taskbarPopupController.getPopupDataProvider();
}
+ @Override
+ public View.AccessibilityDelegate getAccessibilityDelegate() {
+ return mAccessibilityDelegate;
+ }
+
/**
* Sets a new data-source for this taskbar instance
*/
@@ -370,6 +385,7 @@
mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
fromInit);
+ mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
}
/**
@@ -558,4 +574,9 @@
}
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
}
+
+ public void showPopupMenuForIcon(BubbleTextView btv) {
+ setTaskbarWindowFullscreen(true);
+ btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index a7c5d4d..488c822 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -15,10 +15,15 @@
*/
package com.android.launcher3.taskbar;
+import android.content.pm.ActivityInfo.Config;
+
import androidx.annotation.NonNull;
import com.android.systemui.shared.rotation.RotationButtonController;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Hosts various taskbar controllers to facilitate passing between one another.
*/
@@ -43,6 +48,9 @@
/** Do not store this controller, as it may change at runtime. */
@NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
+ private boolean mAreAllControllersInitialized;
+ private final List<Runnable> mPostInitCallbacks = new ArrayList<>();
+
public TaskbarControllers(TaskbarActivityContext taskbarActivityContext,
TaskbarDragController taskbarDragController,
TaskbarNavButtonController navButtonController,
@@ -81,6 +89,8 @@
* in constructors for now, as some controllers may still be waiting for init().
*/
public void init(TaskbarSharedState sharedState) {
+ mAreAllControllersInitialized = false;
+
taskbarDragController.init(this);
navbarButtonsViewController.init(this);
rotationButtonController.init();
@@ -93,6 +103,16 @@
taskbarStashController.init(this, sharedState);
taskbarEduController.init(this);
taskbarPopupController.init(this);
+
+ mAreAllControllersInitialized = true;
+ for (Runnable postInitCallback : mPostInitCallbacks) {
+ postInitCallback.run();
+ }
+ mPostInitCallbacks.clear();
+ }
+
+ public void onConfigurationChanged(@Config int configChanges) {
+ navbarButtonsViewController.onConfigurationChanged(configChanges);
}
/**
@@ -110,4 +130,17 @@
taskbarAutohideSuspendController.onDestroy();
taskbarPopupController.onDestroy();
}
+
+ /**
+ * If all controllers are already initialized, runs the given callback immediately. Otherwise,
+ * queues it to run after calling init() on all controllers. This should likely be used in any
+ * case where one controller is telling another controller to do something inside init().
+ */
+ public void runAfterInit(Runnable callback) {
+ if (mAreAllControllersInitialized) {
+ callback.run();
+ } else {
+ mPostInitCallbacks.add(callback);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index b3a9f8d..3315f8c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -51,6 +51,8 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ClipDescriptionCompat;
import com.android.systemui.shared.system.LauncherAppsCompat;
@@ -88,6 +90,21 @@
* @return Whether {@link View#startDragAndDrop} started successfully.
*/
protected boolean startDragOnLongClick(View view) {
+ return startDragOnLongClick(view, null, null);
+ }
+
+ protected boolean startDragOnLongClick(
+ DeepShortcutView shortcutView, Point iconShift) {
+ return startDragOnLongClick(
+ shortcutView.getBubbleText(),
+ new ShortcutDragPreviewProvider(shortcutView.getIconView(), iconShift),
+ iconShift);
+ }
+
+ private boolean startDragOnLongClick(
+ View view,
+ @Nullable DragPreviewProvider dragPreviewProvider,
+ @Nullable Point iconShift) {
if (!(view instanceof BubbleTextView)) {
return false;
}
@@ -96,7 +113,10 @@
mActivity.setTaskbarWindowFullscreen(true);
btv.post(() -> {
- startInternalDrag(btv);
+ DragView dragView = startInternalDrag(btv, dragPreviewProvider);
+ if (iconShift != null) {
+ dragView.animateShift(-iconShift.x, -iconShift.y);
+ }
btv.getIcon().setIsDisabled(true);
mControllers.taskbarAutohideSuspendController.updateFlag(
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, true);
@@ -104,7 +124,8 @@
return true;
}
- private void startInternalDrag(BubbleTextView btv) {
+ private DragView startInternalDrag(
+ BubbleTextView btv, @Nullable DragPreviewProvider dragPreviewProvider) {
float iconScale = btv.getIcon().getAnimatedScale();
// Clear the pressed state if necessary
@@ -112,7 +133,8 @@
btv.setPressed(false);
btv.clearPressedBackground();
- final DragPreviewProvider previewProvider = new DragPreviewProvider(btv);
+ final DragPreviewProvider previewProvider = dragPreviewProvider == null
+ ? new DragPreviewProvider(btv) : dragPreviewProvider;
final Drawable drawable = previewProvider.createDrawable();
final float scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
int dragLayerX = mTempXY[0];
@@ -149,7 +171,7 @@
}
}
- startDrag(
+ return startDrag(
drawable,
/* view = */ null,
/* originalView = */ btv,
@@ -241,7 +263,8 @@
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
- shadowSize.set(mDragIconSize, mDragIconSize);
+ int iconSize = Math.max(mDragIconSize, btv.getWidth());
+ shadowSize.set(iconSize, iconSize);
// The registration point was taken before the icon scaled to mDragIconSize, so
// offset the registration to where the touch is on the new size.
int offsetX = (mDragIconSize - mDragObject.dragView.getDragRegionWidth()) / 2;
@@ -273,6 +296,12 @@
});
intent = new Intent();
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+ launcherApps.getShortcutIntent(
+ item.getIntent().getPackage(),
+ item.getDeepShortcutId(),
+ null,
+ item.user));
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 152b255..f3c8cf3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
@@ -68,9 +67,6 @@
private int mPrevState;
private int mState;
- private LauncherState mTargetStateOverride = null;
- private LauncherState mTargetStateOverrideForStateTransition = null;
-
private boolean mIsAnimatingToLauncherViaGesture;
private boolean mIsAnimatingToLauncherViaResume;
@@ -79,7 +75,6 @@
@Override
public void onStateTransitionStart(LauncherState toState) {
- mTargetStateOverrideForStateTransition = toState;
updateStateForFlag(FLAG_TRANSITION_STATE_START_STASHED,
toState.isTaskbarStashed());
applyState();
@@ -134,18 +129,6 @@
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
animatorSet.play(stashController.applyStateWithoutStart(duration));
animatorSet.play(applyState(duration, false));
- animatorSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mTargetStateOverride = null;
- animator.removeListener(this);
- }
-
- @Override
- public void onAnimationStart(Animator animator) {
- mTargetStateOverride = toState;
- }
- });
TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
callbacks.addListener(listener);
@@ -284,7 +267,6 @@
// Reset hotseat alpha to default
mLauncher.getHotseat().setIconsAlpha(1);
}
- mTargetStateOverrideForStateTransition = null;
}
@Override
@@ -296,8 +278,6 @@
animatorSet.play(mIconAlignmentForLauncherState.animateToValue(
getCurrentIconAlignmentRatioForLauncherState(),
isTransitionStateStashed ? 0 : 1));
- } else {
- mTargetStateOverrideForStateTransition = null;
}
}
@@ -318,20 +298,14 @@
}
private void onIconAlignmentRatioChangedForStateTransition() {
- onIconAlignmentRatioChanged(
- mTargetStateOverrideForStateTransition != null
- ? mTargetStateOverrideForStateTransition
- : mLauncher.getStateManager().getState(),
- this::getCurrentIconAlignmentRatioForLauncherState);
+ onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioForLauncherState);
}
private void onIconAlignmentRatioChanged() {
- onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride
- : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio);
+ onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatio);
}
- private void onIconAlignmentRatioChanged(LauncherState state,
- Supplier<Float> alignmentSupplier) {
+ private void onIconAlignmentRatioChanged(Supplier<Float> alignmentSupplier) {
if (mControllers == null) {
return;
}
@@ -341,7 +315,8 @@
mTaskbarBackgroundAlpha.updateValue(1 - alignment);
- setIconAlpha(state, alignment);
+ // Switch taskbar and hotseat in last frame
+ setTaskbarViewVisible(alignment < 1);
}
private float getCurrentIconAlignmentRatio() {
@@ -352,15 +327,6 @@
return mIconAlignmentForLauncherState.value;
}
- private void setIconAlpha(LauncherState state, float progress) {
- if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
- // If the hotseat icons are visible, then switch taskbar in last frame
- setTaskbarViewVisible(progress < 1);
- } else {
- mIconAlphaForHome.setValue(1 - progress);
- }
- }
-
private void setTaskbarViewVisible(boolean isVisible) {
mIconAlphaForHome.setValue(isVisible ? 1 : 0);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 8a86157..7f2e37c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -30,8 +30,8 @@
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.net.Uri;
+import android.os.Handler;
import android.provider.Settings;
-import android.util.Log;
import android.view.Display;
import androidx.annotation.NonNull;
@@ -94,7 +94,8 @@
Display display =
service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
- mNavButtonController = new TaskbarNavButtonController(service);
+ mNavButtonController = new TaskbarNavButtonController(service,
+ SystemUiProxy.INSTANCE.get(mContext), new Handler());
mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
mComponentCallbacks = new ComponentCallbacks() {
private Configuration mOldConfig = mContext.getResources().getConfiguration();
@@ -107,6 +108,11 @@
if ((configDiff & configsRequiringRecreate) != 0) {
// Color has changed, recreate taskbar to reload background color & icons.
recreateTaskbar();
+ } else {
+ // Config change might be handled without re-creating the taskbar
+ if (mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.onConfigurationChanged(configDiff);
+ }
}
mOldConfig = newConfig;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 6dcfe56..37a9b5e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -184,9 +184,12 @@
}
mContainer.updateHotseatItems(hotseatItemInfos);
- mControllers.taskbarStashController.updateStateForFlag(
- TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, isHotseatEmpty);
- mControllers.taskbarStashController.applyState();
+ final boolean finalIsHotseatEmpty = isHotseatEmpty;
+ mControllers.runAfterInit(() -> {
+ mControllers.taskbarStashController.updateStateForFlag(
+ TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, finalIsHotseatEmpty);
+ mControllers.taskbarStashController.applyState();
+ });
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index ae23eda..d233365 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -19,8 +19,10 @@
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.os.Bundle;
+import android.os.Handler;
import androidx.annotation.IntDef;
@@ -40,6 +42,13 @@
*/
public class TaskbarNavButtonController {
+ /** Allow some time in between the long press for back and recents. */
+ static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
+ static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;
+
+ private long mLastScreenPinLongPress;
+ private boolean mScreenPinned;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
BUTTON_BACK,
@@ -57,10 +66,20 @@
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
- private final TouchInteractionService mService;
+ private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
+ private int mLongPressedButtons = 0;
- public TaskbarNavButtonController(TouchInteractionService service) {
+ private final TouchInteractionService mService;
+ private final SystemUiProxy mSystemUiProxy;
+ private final Handler mHandler;
+
+ private final Runnable mResetLongPress = this::resetScreenUnpin;
+
+ public TaskbarNavButtonController(TouchInteractionService service,
+ SystemUiProxy systemUiProxy, Handler handler) {
mService = service;
+ mSystemUiProxy = systemUiProxy;
+ mHandler = handler;
}
public void onButtonClick(@TaskbarButton int buttonType) {
@@ -72,13 +91,13 @@
navigateHome();
break;
case BUTTON_RECENTS:
- navigateToOverview();;
+ navigateToOverview();
break;
case BUTTON_IME_SWITCH:
showIMESwitcher();
break;
case BUTTON_A11Y:
- notifyImeClick(false /* longClick */);
+ notifyA11yClick(false /* longClick */);
break;
}
}
@@ -89,46 +108,98 @@
startAssistant();
return true;
case BUTTON_A11Y:
- notifyImeClick(true /* longClick */);
+ notifyA11yClick(true /* longClick */);
return true;
case BUTTON_BACK:
- case BUTTON_IME_SWITCH:
case BUTTON_RECENTS:
+ mLongPressedButtons |= buttonType;
+ return determineScreenUnpin();
+ case BUTTON_IME_SWITCH:
default:
return false;
}
}
+ /**
+ * Checks if the user has long pressed back and recents buttons
+ * "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms
+ * If so, then requests the system to turn off screen pinning.
+ *
+ * @return true if the long press is a valid user action in attempting to unpin an app
+ * Will always return {@code false} when screen pinning is not active.
+ * NOTE: Returning true does not mean that screen pinning has stopped
+ */
+ private boolean determineScreenUnpin() {
+ long timeNow = System.currentTimeMillis();
+ if (!mScreenPinned) {
+ return false;
+ }
+
+ if (mLastScreenPinLongPress == 0) {
+ // First button long press registered, just mark time and wait for second button press
+ mLastScreenPinLongPress = System.currentTimeMillis();
+ mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET);
+ return true;
+ }
+
+ if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) {
+ // Too long in-between presses, reset the clock
+ resetScreenUnpin();
+ return false;
+ }
+
+ if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) {
+ // Hooray! They did it (finally...)
+ mSystemUiProxy.stopScreenPinning();
+ mHandler.removeCallbacks(mResetLongPress);
+ resetScreenUnpin();
+ }
+ return true;
+ }
+
+ private void resetScreenUnpin() {
+ mLongPressedButtons = 0;
+ mLastScreenPinLongPress = 0;
+ }
+
+ public void updateSysuiFlags(int sysuiFlags) {
+ mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
+ }
+
private void navigateHome() {
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
}
private void navigateToOverview() {
+ if (mScreenPinned) {
+ return;
+ }
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
}
private void executeBack() {
- SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+ mSystemUiProxy.onBackPressed();
}
private void showIMESwitcher() {
- SystemUiProxy.INSTANCE.getNoCreate().onImeSwitcherPressed();
+ mSystemUiProxy.onImeSwitcherPressed();
}
- private void notifyImeClick(boolean longClick) {
- SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+ private void notifyA11yClick(boolean longClick) {
if (longClick) {
- systemUiProxy.notifyAccessibilityButtonLongClicked();
+ mSystemUiProxy.notifyAccessibilityButtonLongClicked();
} else {
- systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
+ mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
}
}
private void startAssistant() {
+ if (mScreenPinned) {
+ return;
+ }
Bundle args = new Bundle();
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
- SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
- systemUiProxy.startAssistant(args);
+ mSystemUiProxy.startAssistant(args);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 06c75fc..2dee506 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,6 +15,10 @@
*/
package com.android.launcher3.taskbar;
+import android.graphics.Point;
+import android.view.MotionEvent;
+import android.view.View;
+
import androidx.annotation.NonNull;
import com.android.launcher3.BubbleTextView;
@@ -30,6 +34,7 @@
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.popup.PopupLiveUpdateHandler;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.PackageUserKey;
@@ -131,8 +136,14 @@
(PopupContainerWithArrow) context.getLayoutInflater().inflate(
R.layout.popup_container, context.getDragLayer(), false);
container.addOnAttachStateChangeListener(
- new PopupLiveUpdateHandler<>(mContext, container));
+ new PopupLiveUpdateHandler<TaskbarActivityContext>(mContext, container) {
+ @Override
+ protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
+ showForIcon(originalIcon);
+ }
+ });
// TODO (b/198438631): configure for taskbar/context
+ container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
container.populateAndShow(icon,
mPopupDataProvider.getShortcutCountForItem(item),
@@ -145,4 +156,43 @@
container.requestFocus();
return container;
}
+
+ private class TaskbarPopupItemDragHandler implements
+ PopupContainerWithArrow.PopupItemDragHandler {
+
+ protected final Point mIconLastTouchPos = new Point();
+
+ TaskbarPopupItemDragHandler() {}
+
+ @Override
+ public boolean onTouch(View view, MotionEvent ev) {
+ // Touched a shortcut, update where it was touched so we can drag from there on
+ // long click.
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ // Return early if not the correct view
+ if (!(v.getParent() instanceof DeepShortcutView)) return false;
+
+ DeepShortcutView sv = (DeepShortcutView) v.getParent();
+ sv.setWillDrawIcon(false);
+
+ // Move the icon to align with the center-top of the touch point
+ Point iconShift = new Point();
+ iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
+ iconShift.y = mIconLastTouchPos.y - mContext.getDeviceProfile().iconSizePx;
+
+ mControllers.taskbarDragController.startDragOnLongClick(sv, iconShift);
+
+ return false;
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
new file mode 100644
index 0000000..1589582
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DEEP_SHORTCUTS;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.SHORTCUTS_AND_NOTIFICATIONS;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.ShortcutUtil;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.LauncherAppsCompat;
+
+import java.util.List;
+
+/**
+ * Accessibility delegate for the Taskbar. This provides an accessible interface for taskbar
+ * features.
+ */
+public class TaskbarShortcutMenuAccessibilityDelegate
+ extends BaseAccessibilityDelegate<TaskbarActivityContext> {
+
+ public static final int MOVE_TO_TOP_OR_LEFT = R.id.action_move_to_top_or_left;
+ public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
+
+ private final LauncherApps mLauncherApps;
+
+ public TaskbarShortcutMenuAccessibilityDelegate(TaskbarActivityContext context) {
+ super(context);
+ mLauncherApps = context.getSystemService(LauncherApps.class);
+
+ mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
+ R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
+ mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new LauncherAction(DEEP_SHORTCUTS,
+ R.string.shortcuts_menu_with_notifications_description, KeyEvent.KEYCODE_S));
+ mActions.put(MOVE_TO_TOP_OR_LEFT, new LauncherAction(
+ MOVE_TO_TOP_OR_LEFT, R.string.move_drop_target_top_or_left, KeyEvent.KEYCODE_L));
+ mActions.put(MOVE_TO_BOTTOM_OR_RIGHT, new LauncherAction(
+ MOVE_TO_BOTTOM_OR_RIGHT,
+ R.string.move_drop_target_bottom_or_right,
+ KeyEvent.KEYCODE_R));
+ }
+
+ @Override
+ protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
+ if (ShortcutUtil.supportsShortcuts(item) && FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
+ out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null
+ ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
+ }
+ out.add(mActions.get(MOVE_TO_TOP_OR_LEFT));
+ out.add(mActions.get(MOVE_TO_BOTTOM_OR_RIGHT));
+ }
+
+ @Override
+ protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
+ if (item instanceof WorkspaceItemInfo
+ && (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
+ WorkspaceItemInfo info = (WorkspaceItemInfo) item;
+ int side = action == MOVE_TO_TOP_OR_LEFT
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ SystemUiProxy.INSTANCE.get(mContext).startShortcut(
+ info.getIntent().getPackage(),
+ info.getDeepShortcutId(),
+ side,
+ /* bundleOpts= */ null,
+ info.user);
+ } else {
+ SystemUiProxy.INSTANCE.get(mContext).startIntent(
+ LauncherAppsCompat.getMainActivityLaunchIntent(
+ mLauncherApps,
+ item.getIntent().getComponent(),
+ /* startActivityOptions= */null,
+ item.user),
+ new Intent(), side, null);
+ }
+ return true;
+ } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
+ mContext.showPopupMenuForIcon((BubbleTextView) host);
+
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
+ return false;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index f713dca..f6bc785 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -18,6 +18,8 @@
import android.graphics.Rect;
import android.view.View;
+import androidx.annotation.CallSuper;
+
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -33,11 +35,15 @@
// Initialized in init.
protected TaskbarControllers mControllers;
+ @CallSuper
protected void init(TaskbarControllers taskbarControllers) {
mControllers = taskbarControllers;
}
- protected void onDestroy() { }
+ @CallSuper
+ protected void onDestroy() {
+ mControllers = null;
+ }
protected boolean isTaskbarTouchable() {
return true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index d74b6c5..c85b256 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -34,6 +34,7 @@
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.util.FloatProperty;
+import android.util.Pair;
import androidx.annotation.NonNull;
@@ -101,11 +102,13 @@
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
PagedOrientationHandler orientationHandler =
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
- FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
- TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
- mLauncher.getDeviceProfile());
- setter.setFloat(mRecentsView, taskViewsFloat,
+ Pair<FloatProperty, FloatProperty> taskViewsFloat =
+ orientationHandler.getSplitSelectTaskOffset(
+ TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+ mLauncher.getDeviceProfile());
+ setter.setFloat(mRecentsView, taskViewsFloat.first,
toState.getSplitSelectTranslation(mLauncher), LINEAR);
+ setter.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index b21d677..19897a1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -66,6 +66,12 @@
// In Overview, we may be layering app surfaces behind Launcher, so we need to notify
// DepthController to prevent optimizations which might occlude the layers behind
mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
+
+ if (isSplitSelectionState(state)) {
+ mRecentsView.applySplitPrimaryScrollOffset();
+ } else {
+ mRecentsView.resetSplitPrimaryScrollOffset();
+ }
}
@Override
@@ -90,9 +96,10 @@
LauncherState currentState = mLauncher.getStateManager().getState();
if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) {
builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
+ }
+ if (isSplitSelectionState(toState)) {
mRecentsView.applySplitPrimaryScrollOffset();
- } else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) {
- builder.add(mRecentsView.cancelSplitSelect(true).buildAnim());
+ } else {
mRecentsView.resetSplitPrimaryScrollOffset();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d396018..a4eff87 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -21,13 +21,11 @@
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemProperties;
-import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
import com.android.launcher3.util.Themes;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.LayoutUtils;
@@ -66,10 +64,7 @@
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
RecentsView recentsView = launcher.getOverviewPanel();
- Workspace workspace = launcher.getWorkspace();
- View workspacePage = workspace.getPageAt(workspace.getCurrentPage());
- float workspacePageWidth = workspacePage != null && workspacePage.getWidth() != 0
- ? workspacePage.getWidth() : launcher.getDeviceProfile().availableWidthPx;
+ float workspacePageWidth = launcher.getDeviceProfile().getWorkspaceWidth();
recentsView.getTaskSize(sTempRect);
float scale = (float) sTempRect.width() / workspacePageWidth;
float parallaxFactor = 0.5f;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 106375a..4f5f27a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -31,11 +31,6 @@
}
@Override
- public void onBackPressed(Launcher launcher) {
- launcher.getStateManager().goToState(OVERVIEW);
- }
-
- @Override
public int getVisibleElements(Launcher launcher) {
return SPLIT_PLACHOLDER_VIEW;
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 097850f..c5f4a53 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -36,7 +36,6 @@
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.StagedSplitBounds;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
@@ -220,26 +219,6 @@
return newTasks;
}
- public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "RecentTasksList:");
- writer.println(prefix + " mChangeId=" + mChangeId);
- writer.println(prefix + " mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
- for (GroupTask task : mResultsUi) {
- writer.println(prefix + " t1=" + task.task1.key.id
- + " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1"));
- }
- writer.println(prefix + " ]");
- int currentUserId = Process.myUserHandle().getIdentifier();
- ArrayList<GroupedRecentTaskInfo> rawTasks =
- mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
- writer.println(prefix + " rawTasks=[");
- for (GroupedRecentTaskInfo task : rawTasks) {
- writer.println(prefix + " t1=" + task.mTaskInfo1.taskId
- + " t2=" + (task.mTaskInfo2 != null ? task.mTaskInfo2.taskId : "-1"));
- }
- writer.println(prefix + " ]");
- }
-
private static class TaskLoadResult extends ArrayList<GroupTask> {
final int mRequestId;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index dd6392c..b502676 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -23,6 +23,7 @@
import android.view.RemoteAnimationTarget;
import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
@@ -77,7 +78,7 @@
public void notifyAnimationCanceled() {
mCancelled = true;
- onAnimationCanceled(null);
+ onAnimationCanceled(new HashMap<>());
}
// Called only in Q platform
@@ -167,16 +168,17 @@
* Callback from the system when the recents animation is canceled. {@param thumbnailData}
* is passed back for rendering screenshot to replace live tile.
*/
- default void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {}
+ default void onRecentsAnimationCanceled(
+ @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {}
/**
* Callback made whenever the recents animation is finished.
*/
- default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
+ default void onRecentsAnimationFinished(@NonNull RecentsAnimationController controller) {}
/**
* Callback made when a task started from the recents is ready for an app transition.
*/
- default void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {}
+ default void onTasksAppeared(@NonNull RemoteAnimationTargetCompat[] appearedTaskTarget) {}
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 5d77a6e..e539a8c 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,7 +43,6 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -221,11 +220,6 @@
mThumbnailChangeListeners.remove(listener);
}
- public void dump(String prefix, PrintWriter writer) {
- writer.println(prefix + "RecentsModel:");
- mTaskList.dump(" ", writer);
- }
-
/**
* Listener for receiving various task properties changes
*/
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index c8abd14..6c623bc 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -83,16 +83,14 @@
MAIN_EXECUTOR.execute(() -> clearProxy());
};
- // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
- // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is
- // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
- // in case SysUI needs to rebind.
- private IPipAnimationListener mPipAnimationListener;
- private ISplitScreenListener mSplitScreenListener;
- private IStartingWindowListener mStartingWindowListener;
- private ISmartspaceCallback mSmartspaceCallback;
- private IRecentTasksListener mRecentTasksListener;
- private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
+ // Save the listeners passed into the proxy since when set/register these listeners,
+ // setProxy may not have been called, eg. OverviewProxyService is not connected yet.
+ private IPipAnimationListener mPendingPipAnimationListener;
+ private ISplitScreenListener mPendingSplitScreenListener;
+ private IStartingWindowListener mPendingStartingWindowListener;
+ private ISmartspaceCallback mPendingSmartspaceCallback;
+ private IRecentTasksListener mPendingRecentTasksListener;
+ private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -169,23 +167,29 @@
mRecentTasks = recentTasks;
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
- if (mPipAnimationListener != null && mPip != null) {
- setPinnedStackAnimationListener(mPipAnimationListener);
+ if (mPendingPipAnimationListener != null && mPip != null) {
+ setPinnedStackAnimationListener(mPendingPipAnimationListener);
+ mPendingPipAnimationListener = null;
}
- if (mSplitScreenListener != null && mSplitScreen != null) {
- registerSplitScreenListener(mSplitScreenListener);
+ if (mPendingSplitScreenListener != null && mSplitScreen != null) {
+ registerSplitScreenListener(mPendingSplitScreenListener);
+ mPendingSplitScreenListener = null;
}
- if (mStartingWindowListener != null && mStartingWindow != null) {
- setStartingWindowListener(mStartingWindowListener);
+ if (mPendingStartingWindowListener != null && mStartingWindow != null) {
+ setStartingWindowListener(mPendingStartingWindowListener);
+ mPendingStartingWindowListener = null;
}
- if (mSmartspaceCallback != null && mSmartspaceTransitionController != null) {
- setSmartspaceCallback(mSmartspaceCallback);
+ if (mPendingSmartspaceCallback != null && mSmartspaceTransitionController != null) {
+ setSmartspaceCallback(mPendingSmartspaceCallback);
+ mPendingSmartspaceCallback = null;
}
- for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
- registerRemoteTransition(mRemoteTransitions.get(i));
+ for (int i = mPendingRemoteTransitions.size() - 1; i >= 0; --i) {
+ registerRemoteTransition(mPendingRemoteTransitions.get(i));
}
- if (mRecentTasksListener != null && mRecentTasks != null) {
- registerRecentTasksListener(mRecentTasksListener);
+ mPendingRemoteTransitions.clear();
+ if (mPendingRecentTasksListener != null && mRecentTasks != null) {
+ registerRecentTasksListener(mPendingRecentTasksListener);
+ mPendingRecentTasksListener = null;
}
if (mPendingSetNavButtonAlpha != null) {
@@ -509,8 +513,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
}
+ } else {
+ mPendingPipAnimationListener = listener;
}
- mPipAnimationListener = listener;
}
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
@@ -548,8 +553,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerSplitScreenListener");
}
+ } else {
+ mPendingSplitScreenListener = listener;
}
- mSplitScreenListener = listener;
}
public void unregisterSplitScreenListener(ISplitScreenListener listener) {
@@ -560,17 +566,17 @@
Log.w(TAG, "Failed call unregisterSplitScreenListener");
}
}
- mSplitScreenListener = null;
+ mPendingSplitScreenListener = null;
}
/** Start multiple tasks in split-screen simultaneously. */
public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
- @SplitConfigurationOptions.StagePosition int sidePosition,
+ @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
RemoteTransitionCompat remoteTransition) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
- sidePosition, remoteTransition.getTransition());
+ sidePosition, splitRatio, remoteTransition.getTransition());
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTask");
}
@@ -582,22 +588,22 @@
*/
public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
- sideOptions, sidePosition, adapter);
+ sideOptions, sidePosition, splitRatio, adapter);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTasksWithLegacyTransition");
}
}
}
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ public void startShortcut(String packageName, String shortcutId, int position,
Bundle options, UserHandle user) {
if (mSplitScreen != null) {
try {
- mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options,
+ mSplitScreen.startShortcut(packageName, shortcutId, position, options,
user);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcut");
@@ -605,11 +611,11 @@
}
}
- public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+ public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
Bundle options) {
if (mSplitScreen != null) {
try {
- mSplitScreen.startIntent(intent, fillInIntent, stage, position, options);
+ mSplitScreen.startIntent(intent, fillInIntent, position, options);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntent");
}
@@ -681,8 +687,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
+ } else {
+ mPendingRemoteTransitions.add(remoteTransition);
}
- mRemoteTransitions.add(remoteTransition);
}
public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
@@ -693,7 +700,7 @@
Log.w(TAG, "Failed call registerRemoteTransition");
}
}
- mRemoteTransitions.remove(remoteTransition);
+ mPendingRemoteTransitions.remove(remoteTransition);
}
//
@@ -710,8 +717,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call setStartingWindowListener", e);
}
+ } else {
+ mPendingStartingWindowListener = listener;
}
- mStartingWindowListener = listener;
}
//
@@ -725,8 +733,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call setStartingWindowListener", e);
}
+ } else {
+ mPendingSmartspaceCallback = callback;
}
- mSmartspaceCallback = callback;
}
//
@@ -740,8 +749,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRecentTasksListener", e);
}
+ } else {
+ mPendingRecentTasksListener = listener;
}
- mRecentTasksListener = listener;
}
public void unregisterRecentTasksListener(IRecentTasksListener listener) {
@@ -752,7 +762,7 @@
Log.w(TAG, "Failed call unregisterRecentTasksListener");
}
}
- mRecentTasksListener = null;
+ mPendingRecentTasksListener = null;
}
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index e77ec78..97fc6d7 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -316,7 +316,7 @@
mt[i] = localMt;
Matrix localMti = new Matrix();
- localMti.invert(localMt);
+ localMt.invert(localMti);
mti[i] = localMti;
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index d1f39d2..707b905 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -948,7 +948,6 @@
pw.println(" resumed=" + resumed);
pw.println(" mConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
- RecentsModel.INSTANCE.get(this).dump("", pw);
pw.println("ProtoTrace:");
pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index fcff858..5b4eb8b 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -31,18 +31,19 @@
import androidx.annotation.Nullable;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -73,6 +74,7 @@
@Override
public void startHome() {
mActivity.startHome();
+ AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
/**
@@ -207,10 +209,6 @@
@Override
public void onStateTransitionStart(RecentsState toState) {
- if (toState == HOME) {
- // Clean-up logic that occurs when recents is no longer in use/visible.
- reset();
- }
setOverviewStateEnabled(true);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.isFullScreen());
@@ -219,6 +217,10 @@
@Override
public void onStateTransitionComplete(RecentsState finalState) {
+ if (finalState == HOME) {
+ // Clean-up logic that occurs when recents is no longer in use/visible.
+ reset();
+ }
boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
setFreezeViewVisibility(false);
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
new file mode 100644
index 0000000..861ff96
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -0,0 +1,93 @@
+/*
+ * 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.quickstep.util;
+
+import android.annotation.CallSuper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Animation that moves launcher icons and widgets from center to the sides (final position)
+ */
+public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
+
+ private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+
+ private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
+ private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
+
+ public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
+ mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
+ new LauncherViewsMoveFromCenterTranslationApplier());
+ }
+
+ @CallSuper
+ @Override
+ public void onTransitionStarted() {
+ mMoveFromCenterAnimation.updateDisplayProperties();
+ onPrepareViewsForAnimation();
+ onTransitionProgress(0f);
+ }
+
+ @CallSuper
+ @Override
+ public void onTransitionProgress(float progress) {
+ mMoveFromCenterAnimation.onTransitionProgress(progress);
+ }
+
+ @CallSuper
+ @Override
+ public void onTransitionFinished() {
+ mMoveFromCenterAnimation.onTransitionFinished();
+ mMoveFromCenterAnimation.clearRegisteredViews();
+
+ mOriginalClipChildren.clear();
+ mOriginalClipToPadding.clear();
+ }
+
+ protected void onPrepareViewsForAnimation() {
+
+ }
+
+ protected void registerViewForAnimation(View view) {
+ mMoveFromCenterAnimation.registerViewForAnimation(view);
+ }
+
+ protected void disableClipping(ViewGroup view) {
+ mOriginalClipToPadding.put(view, view.getClipToPadding());
+ mOriginalClipChildren.put(view, view.getClipChildren());
+ view.setClipToPadding(false);
+ view.setClipChildren(false);
+ }
+
+ protected void restoreClipping(ViewGroup view) {
+ final Boolean originalClipToPadding = mOriginalClipToPadding.get(view);
+ if (originalClipToPadding != null) {
+ view.setClipToPadding(originalClipToPadding);
+ }
+ final Boolean originalClipChildren = mOriginalClipChildren.get(view);
+ if (originalClipChildren != null) {
+ view.setClipChildren(originalClipChildren);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index b39412b..6b6bd6a 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -18,14 +18,17 @@
import static com.android.launcher3.Utilities.comp;
import android.annotation.Nullable;
-import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.core.view.OneShotPreDrawListener;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
/**
@@ -43,6 +46,7 @@
private HorizontalInsettableView mQsbInsettable;
private final ScopedUnfoldTransitionProgressProvider mProgressProvider;
+ private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider;
public LauncherUnfoldAnimationController(
Launcher launcher,
@@ -51,10 +55,19 @@
mLauncher = launcher;
mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
unfoldTransitionProgressProvider);
+ mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
+ WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
+ mNaturalOrientationProgressProvider.init();
+ // Animated in all orientations
mProgressProvider.addCallback(new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
windowManager));
- mProgressProvider.addCallback(new QsbAnimationListener());
+
+ // Animated only in natural orientation
+ mNaturalOrientationProgressProvider
+ .addCallback(new QsbAnimationListener());
+ mNaturalOrientationProgressProvider
+ .addCallback(new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager));
}
/**
@@ -66,17 +79,8 @@
mQsbInsettable = (HorizontalInsettableView) hotseat.getQsb();
}
- final ViewTreeObserver obs = mLauncher.getWorkspace().getViewTreeObserver();
- obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- if (obs.isAlive()) {
- mProgressProvider.setReadyToHandleTransition(true);
- obs.removeOnPreDrawListener(this);
- }
- return true;
- }
- });
+ OneShotPreDrawListener.add(mLauncher.getWorkspace(),
+ () -> mProgressProvider.setReadyToHandleTransition(true));
}
/**
@@ -92,6 +96,7 @@
*/
public void onDestroy() {
mProgressProvider.destroy();
+ mNaturalOrientationProgressProvider.destroy();
}
private class QsbAnimationListener implements TransitionProgressListener {
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index c784d82..d310893 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -30,7 +31,6 @@
import android.view.SurfaceControl;
import android.window.TransitionInfo;
-
import androidx.annotation.Nullable;
import com.android.launcher3.statehandlers.DepthController;
@@ -95,7 +95,7 @@
public void setSecondTaskId(Task task, Consumer<Boolean> callback) {
mSecondTask = task;
launchTasks(mInitialTask, mSecondTask, mStagePosition, callback,
- false /* freezeTaskList */);
+ false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
}
/**
@@ -107,14 +107,15 @@
TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
groupedTaskView.getTaskIdAttributeContainers();
launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(),
- taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList);
+ taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList,
+ groupedTaskView.getSplitRatio());
}
/**
* @param stagePosition representing location of task1
*/
public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
- Consumer<Boolean> callback, boolean freezeTaskList) {
+ Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
// Assume initial task is for top/left part of screen
final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
? new int[]{task1.key.id, task2.key.id}
@@ -123,7 +124,7 @@
RemoteSplitLaunchTransitionRunner animationRunner =
new RemoteSplitLaunchTransitionRunner(task1, task2);
mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
- null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+ null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
ActivityThread.currentActivityThread().getApplicationThread()));
} else {
@@ -140,7 +141,7 @@
}
mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
- adapter);
+ splitRatio, adapter);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
new file mode 100644
index 0000000..dc97dd6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.quickstep.util;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
+
+/**
+ * Animation that moves hotseat icons from center to the sides (final position)
+ */
+public class UnfoldMoveFromCenterHotseatAnimator extends BaseUnfoldMoveFromCenterAnimator {
+
+ private final Launcher mLauncher;
+
+ public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager) {
+ super(windowManager);
+ mLauncher = launcher;
+ }
+
+ @Override
+ protected void onPrepareViewsForAnimation() {
+ Hotseat hotseat = mLauncher.getHotseat();
+
+ ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
+ disableClipping(hotseat);
+
+ for (int i = 0; i < hotseatIcons.getChildCount(); i++) {
+ View child = hotseatIcons.getChildAt(i);
+ registerViewForAnimation(child);
+ }
+ }
+
+ @Override
+ public void onTransitionFinished() {
+ restoreClipping(mLauncher.getHotseat());
+ super.onTransitionFinished();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 95403b2..3d72398 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -16,44 +16,28 @@
package com.android.quickstep.util;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
-import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
-
-import java.util.HashMap;
-import java.util.Map;
/**
* Animation that moves launcher icons and widgets from center to the sides (final position)
*/
-public class UnfoldMoveFromCenterWorkspaceAnimator
- implements UnfoldTransitionProgressProvider.TransitionProgressListener {
+public class UnfoldMoveFromCenterWorkspaceAnimator extends BaseUnfoldMoveFromCenterAnimator {
private final Launcher mLauncher;
- private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
-
- private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
- private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
+ super(windowManager);
mLauncher = launcher;
- mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
- new LauncherViewsMoveFromCenterTranslationApplier());
}
@Override
- public void onTransitionStarted() {
- mMoveFromCenterAnimation.updateDisplayProperties();
-
+ protected void onPrepareViewsForAnimation() {
Workspace workspace = mLauncher.getWorkspace();
- Hotseat hotseat = mLauncher.getHotseat();
// App icons and widgets
workspace
@@ -65,57 +49,17 @@
for (int i = 0; i < itemsContainer.getChildCount(); i++) {
View child = itemsContainer.getChildAt(i);
- mMoveFromCenterAnimation.registerViewForAnimation(child);
+ registerViewForAnimation(child);
}
});
disableClipping(workspace);
-
- // Hotseat icons
- ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
- disableClipping(hotseat);
-
- for (int i = 0; i < hotseatIcons.getChildCount(); i++) {
- View child = hotseatIcons.getChildAt(i);
- mMoveFromCenterAnimation.registerViewForAnimation(child);
- }
-
- onTransitionProgress(0f);
- }
-
- @Override
- public void onTransitionProgress(float progress) {
- mMoveFromCenterAnimation.onTransitionProgress(progress);
}
@Override
public void onTransitionFinished() {
- mMoveFromCenterAnimation.onTransitionFinished();
- mMoveFromCenterAnimation.clearRegisteredViews();
-
restoreClipping(mLauncher.getWorkspace());
mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page));
- restoreClipping(mLauncher.getHotseat());
-
- mOriginalClipChildren.clear();
- mOriginalClipToPadding.clear();
- }
-
- private void disableClipping(ViewGroup view) {
- mOriginalClipToPadding.put(view, view.getClipToPadding());
- mOriginalClipChildren.put(view, view.getClipChildren());
- view.setClipToPadding(false);
- view.setClipChildren(false);
- }
-
- private void restoreClipping(ViewGroup view) {
- final Boolean originalClipToPadding = mOriginalClipToPadding.get(view);
- if (originalClipToPadding != null) {
- view.setClipToPadding(originalClipToPadding);
- }
- final Boolean originalClipChildren = mOriginalClipChildren.get(view);
- if (originalClipChildren != null) {
- view.setClipChildren(originalClipChildren);
- }
+ super.onTransitionFinished();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 325ec04..a343e0a 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -129,9 +129,7 @@
public void update(RectF position, float progress, float windowRadius) {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
- float dX = mIsRtl
- ? position.left - (lp.getMarginStart() - lp.width)
- : position.left - lp.getMarginStart();
+ float dX = position.left - lp.getMarginStart();
float dY = position.top - lp.topMargin;
setTranslationX(dX);
@@ -157,16 +155,10 @@
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
lp.topMargin = Math.round(pos.top);
- if (mIsRtl) {
- lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right));
- } else {
- lp.setMarginStart(Math.round(pos.left));
- }
+ lp.setMarginStart(Math.round(pos.left));
// Set the properties here already to make sure they are available when running the first
// animation frame.
- int left = mIsRtl
- ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
- : lp.leftMargin;
+ int left = lp.leftMargin;
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 4771d1e..9311261 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,5 +1,6 @@
package com.android.quickstep.views;
+import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -122,6 +123,14 @@
invalidate();
}
+ public float getSplitRatio() {
+ if (mSplitBoundsConfig != null) {
+ return mSplitBoundsConfig.appsStackedVertically
+ ? mSplitBoundsConfig.topTaskPercent : mSplitBoundsConfig.leftTaskPercent;
+ }
+ return DEFAULT_SPLIT_RATIO;
+ }
+
@Override
public boolean offerTouchToChildren(MotionEvent event) {
computeAndSetIconTouchDelegate(mIconView2, mIcon2CenterCoords, mIcon2TouchDelegate);
@@ -148,16 +157,27 @@
@Nullable
@Override
public RunnableList launchTaskAnimated() {
- getRecentsView().getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
- null /*callback*/,
+ if (mTask == null || mSecondaryTask == null) {
+ return null;
+ }
+
+ RunnableList endCallback = new RunnableList();
+ RecentsView recentsView = getRecentsView();
+ // Callbacks run from remote animation when recents animation not currently running
+ recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
+ success -> endCallback.executeAllAndDestroy(),
false /* freezeTaskList */);
- return null;
+
+ // Callbacks get run from recentsView for case when recents animation already running
+ recentsView.addSideTaskLaunchCallback(endCallback);
+ return endCallback;
}
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
- STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList);
+ STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+ getSplitRatio());
}
@Override
@@ -238,4 +258,10 @@
super.updateSnapshotRadius();
mSnapshotView2.setFullscreenParams(mCurrentFullscreenParams);
}
+
+ @Override
+ protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ super.setIconAndDimTransitionProgress(progress, invert);
+ mIconView2.setAlpha(mIconView.getAlpha());
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index a2e9e57..3e06f55 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -32,6 +32,7 @@
import androidx.annotation.Nullable;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.statehandlers.DepthController;
@@ -70,6 +71,7 @@
@Override
public void startHome() {
mActivity.getStateManager().goToState(NORMAL);
+ AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
@Override
@@ -92,10 +94,6 @@
@Override
public void onStateTransitionStart(LauncherState toState) {
- if (toState == NORMAL || toState == SPRING_LOADED) {
- // Clean-up logic that occurs when recents is no longer in use/visible.
- reset();
- }
setOverviewStateEnabled(toState.overviewUi);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
@@ -104,6 +102,10 @@
@Override
public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == NORMAL || finalState == SPRING_LOADED) {
+ // Clean-up logic that occurs when recents is no longer in use/visible.
+ reset();
+ }
boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
setOverlayEnabled(isOverlayEnabled);
setFreezeViewVisibility(false);
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index b6bf59f..81c07a6 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -22,6 +22,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.FrameLayout;
import androidx.annotation.IntDef;
@@ -80,7 +81,7 @@
private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
private final MultiValueAlpha mMultiValueAlpha;
- private View mSplitButton;
+ private Button mSplitButton;
@ActionsHiddenFlags
private int mHiddenFlags;
@@ -215,6 +216,10 @@
mDp = dp;
updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
requestLayout();
+
+ mSplitButton.setCompoundDrawablesWithIntrinsicBounds(
+ (dp.isLandscape ? R.drawable.ic_split_horizontal : R.drawable.ic_split_vertical),
+ 0, 0, 0);
}
public void setSplitButtonVisible(boolean visible) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 3aa8d46..2ad586d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -107,6 +107,7 @@
import android.widget.OverScroller;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
@@ -181,6 +182,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -822,7 +824,7 @@
@Override
public void onTaskIconChanged(String pkg, UserHandle user) {
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView tv = getTaskViewAt(i);
+ TaskView tv = requireTaskViewAt(i);
Task task = tv.getTask();
if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
&& task.key.userId == user.getIdentifier()) {
@@ -1039,6 +1041,17 @@
}
}
+ public boolean isTaskViewFullyVisible(TaskView tv) {
+ if (showAsGrid()) {
+ int screenStart = mOrientationHandler.getPrimaryScroll(this);
+ int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
+ return isTaskViewFullyWithinBounds(tv, screenStart, screenEnd);
+ } else {
+ // For now, just check if it's the active task
+ return indexOfChild(tv) == getNextPage();
+ }
+ }
+
@Nullable
private TaskView getLastGridTaskView() {
return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
@@ -1085,6 +1098,15 @@
&& taskEnd <= end);
}
+ private boolean isTaskViewFullyWithinBounds(TaskView tv, int start, int end) {
+ int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
+ showAsFullscreen(), showAsGrid());
+ int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment(
+ showAsFullscreen()));
+ int taskEnd = taskStart + taskSize;
+ return taskStart >= start && taskEnd <= end;
+ }
+
/**
* Returns true if the task is in expected scroll position.
*
@@ -1104,7 +1126,7 @@
}
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
int[] taskIds = taskView.getTaskIds();
if (taskIds[0] == taskId || taskIds[1] == taskId) {
return taskView;
@@ -1121,9 +1143,6 @@
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTasks = null;
- if (mSplitSelectStateController.isSplitSelectActive()) {
- cancelSplitSelect(false);
- }
// Remove grouped tasks and recycle once we exit overview
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
@@ -1181,7 +1200,7 @@
if (showAsGrid()) {
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) {
// Keep consuming events to pass to delegate
return true;
@@ -1418,7 +1437,7 @@
if (runningTaskId != -1) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
- targetPage = indexOfChild(getTaskViewAt(0));
+ targetPage = indexOfChild(requireTaskViewAt(0));
}
} else if (currentTaskId != -1) {
currentTaskView = getTaskViewByTaskId(currentTaskId);
@@ -1452,7 +1471,7 @@
private void removeTasksViewsAndClearAllButton() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
- removeView(getTaskViewAt(i));
+ removeView(requireTaskViewAt(i));
}
if (indexOfChild(mClearAllButton) != -1) {
removeView(mClearAllButton);
@@ -1498,7 +1517,7 @@
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
taskView.resetViewTransforms();
taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
@@ -1508,6 +1527,16 @@
}
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // resetTaskVisuals is called at the end of dismiss animation which could update
+ // primary and secondary translation of the live tile cut out. We will need to do so
+ // here accordingly.
+ runActionOnRemoteHandles(remoteTargetHandle -> {
+ TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
+ simulator.taskPrimaryTranslation.value = 0;
+ simulator.taskSecondaryTranslation.value = 0;
+ simulator.fullScreenProgress.value = 0;
+ simulator.recentsViewScale.value = 1;
+ });
// Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
// null.
if (!mRunningTaskShowScreenshot) {
@@ -1529,7 +1558,7 @@
mFullscreenProgress = fullscreenProgress;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
+ requireTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
}
mClearAllButton.setFullscreenProgress(fullscreenProgress);
@@ -1656,7 +1685,7 @@
float accumulatedTranslationX = 0;
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
taskView.updateTaskSize();
taskView.getPrimaryNonGridTranslationProperty().set(taskView, accumulatedTranslationX);
taskView.getSecondaryNonGridTranslationProperty().set(taskView, 0f);
@@ -1802,7 +1831,7 @@
// Update the task data for the in/visible children
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
Task task = taskView.getTask();
int index = indexOfChild(taskView);
boolean visible;
@@ -1945,7 +1974,7 @@
}
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
if (taskView.getTaskViewId() == taskViewId) {
return taskView;
}
@@ -2043,7 +2072,7 @@
int runningIndex = getCurrentPage();
AnimatorSet as = new AnimatorSet();
for (int i = 0; i < getTaskViewCount(); i++) {
- View taskView = getTaskViewAt(i);
+ View taskView = requireTaskViewAt(i);
if (runningIndex == i && taskView.getAlpha() != 0) {
continue;
}
@@ -2054,7 +2083,7 @@
private void updateChildTaskOrientations() {
for (int i = 0; i < getTaskViewCount(); i++) {
- getTaskViewAt(i).setOrientationState(mOrientationState);
+ requireTaskViewAt(i).setOrientationState(mOrientationState);
}
TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mActivity, TYPE_TASK_MENU);
if (tv != null) {
@@ -2259,7 +2288,7 @@
mTaskIconScaledDown = isScaledDown;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- getTaskViewAt(i).setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
+ requireTaskViewAt(i).setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
}
}
}
@@ -2275,7 +2304,7 @@
mTaskIconScaledDown = false;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
taskView.setIconScaleAnimStartProgress(0f);
taskView.animateIconScaleAndDimIntoView();
}
@@ -2347,7 +2376,7 @@
mTopRowIdSet.clear();
}
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
int taskWidthAndSpacing = taskView.getLayoutParams().width + mPageSpacing;
// Evenly distribute tasks between rows unless rearranging due to task dismissal, in
// which case keep tasks in their respective rows. For the running task, don't join
@@ -2412,7 +2441,7 @@
if (j == focusedTaskIndex) {
continue;
}
- widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+ widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
}
float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -2431,7 +2460,7 @@
if (j == focusedTaskIndex) {
continue;
}
- widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+ widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
}
float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -2509,7 +2538,7 @@
}
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
+ snappedTaskNonGridScrollAdjustment);
}
@@ -2550,7 +2579,7 @@
mGridProgress = gridProgress;
for (int i = 0; i < taskCount; i++) {
- getTaskViewAt(i).setGridProgress(gridProgress);
+ requireTaskViewAt(i).setGridProgress(gridProgress);
}
mClearAllButton.setGridProgress(gridProgress);
}
@@ -2713,7 +2742,7 @@
mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
// Pick the next focused task from the preferred row.
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
if (taskView == dismissedTaskView) {
continue;
}
@@ -2816,7 +2845,7 @@
+ (taskCount - 1) * halfAdditionalDismissTranslationOffset,
END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
anim.setFloat(taskView, TaskView.GRID_END_TRANSLATION_X, longGridRowWidthDiff,
clampToProgress(LINEAR, dismissTranslationInterpolationEnd, 1));
dismissTranslationInterpolationEnd = Utilities.boundToRange(
@@ -3132,7 +3161,7 @@
// Rebalance tasks in the grid
int highestVisibleTaskIndex = getHighestVisibleTaskIndex();
if (highestVisibleTaskIndex < Integer.MAX_VALUE) {
- TaskView taskView = getTaskViewAt(highestVisibleTaskIndex);
+ TaskView taskView = requireTaskViewAt(highestVisibleTaskIndex);
boolean shouldRebalance;
int screenStart = mOrientationHandler.getPrimaryScroll(
@@ -3185,6 +3214,7 @@
}
}
}
+ pageBeginTransition();
setCurrentPage(pageToSnapTo);
// Update various scroll-dependent UI.
dispatchScrollChanged();
@@ -3229,7 +3259,7 @@
IntArray topArray = new IntArray(mTopRowIdSet.size());
int taskViewCount = getTaskViewCount();
for (int i = 0; i < taskViewCount; i++) {
- int taskViewId = getTaskViewAt(i).getTaskViewId();
+ int taskViewId = requireTaskViewAt(i).getTaskViewId();
if (mTopRowIdSet.contains(taskViewId)) {
topArray.add(taskViewId);
}
@@ -3248,7 +3278,7 @@
IntArray bottomArray = new IntArray(bottomRowIdArraySize);
int taskViewCount = getTaskViewCount();
for (int i = 0; i < taskViewCount; i++) {
- int taskViewId = getTaskViewAt(i).getTaskViewId();
+ int taskViewId = requireTaskViewAt(i).getTaskViewId();
if (!mTopRowIdSet.contains(taskViewId) && taskViewId != mFocusedTaskViewId) {
bottomArray.add(taskViewId);
}
@@ -3318,7 +3348,7 @@
int count = getTaskViewCount();
for (int i = 0; i < count; i++) {
- addDismissedTaskAnimations(getTaskViewAt(i), duration, anim);
+ addDismissedTaskAnimations(requireTaskViewAt(i), duration, anim);
}
mPendingAnimation = anim;
@@ -3441,7 +3471,7 @@
mContentAlpha = alpha;
int runningTaskId = getTaskIdsForRunningTaskView()[0];
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
- TaskView child = getTaskViewAt(i);
+ TaskView child = requireTaskViewAt(i);
int[] childTaskIds = child.getTaskIds();
if (!mRunningTaskTileHidden ||
(childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
@@ -3542,6 +3572,14 @@
return child instanceof TaskView ? (TaskView) child : null;
}
+ /**
+ * A version of {@link #getTaskViewAt} when the caller is sure about the input index.
+ */
+ @NonNull
+ private TaskView requireTaskViewAt(int index) {
+ return Objects.requireNonNull(getTaskViewAt(index));
+ }
+
public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
mOnEmptyMessageUpdatedListener = listener;
}
@@ -3758,7 +3796,7 @@
protected void setTaskViewsResistanceTranslation(float translation) {
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView task = getTaskViewAt(i);
+ TaskView task = requireTaskViewAt(i);
task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
}
runActionOnRemoteHandles(
@@ -3768,14 +3806,14 @@
private void updateTaskViewsSnapshotRadius() {
for (int i = 0; i < getTaskViewCount(); i++) {
- getTaskViewAt(i).updateSnapshotRadius();
+ requireTaskViewAt(i).updateSnapshotRadius();
}
}
protected void setTaskViewsPrimarySplitTranslation(float translation) {
mTaskViewsPrimarySplitTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView task = getTaskViewAt(i);
+ TaskView task = requireTaskViewAt(i);
task.getPrimarySplitTranslationProperty().set(task, translation);
}
}
@@ -3783,7 +3821,7 @@
protected void setTaskViewsSecondarySplitTranslation(float translation) {
mTaskViewsSecondarySplitTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
if (taskView == mSplitHiddenTaskView) {
continue;
}
@@ -3797,7 +3835,7 @@
public void applySplitPrimaryScrollOffset() {
if (isSplitPlaceholderFirstInGrid()) {
for (int i = 0; i < getTaskViewCount(); i++) {
- getTaskViewAt(i).setSplitScrollOffsetPrimary(mSplitPlaceholderSize);
+ requireTaskViewAt(i).setSplitScrollOffsetPrimary(mSplitPlaceholderSize);
}
} else if (isSplitPlaceholderLastInGrid()) {
mClearAllButton.setSplitSelectScrollOffsetPrimary(-mSplitPlaceholderSize);
@@ -3839,7 +3877,7 @@
*/
public void resetSplitPrimaryScrollOffset() {
for (int i = 0; i < getTaskViewCount(); i++) {
- getTaskViewAt(i).setSplitScrollOffsetPrimary(0);
+ requireTaskViewAt(i).setSplitScrollOffsetPrimary(0);
}
mClearAllButton.setSplitSelectScrollOffsetPrimary(0);
}
@@ -3919,112 +3957,9 @@
pendingAnimation.buildAnim().start();
}
- public PendingAnimation cancelSplitSelect(boolean animate) {
- SplitSelectStateController splitController = mSplitSelectStateController;
- @StagePosition int stagePosition = splitController.getActiveSplitStagePosition();
- Rect initialBounds = splitController.getInitialBounds();
- splitController.resetState();
- int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
- PendingAnimation pendingAnim = new PendingAnimation(duration);
- mSplitToast.cancel();
- mSplitUnsupportedToast.cancel();
- if (!animate) {
- resetFromSplitSelectionState();
- return pendingAnim;
- }
-
- addViewInLayout(mSplitHiddenTaskView, mSplitHiddenTaskViewIndex,
- mSplitHiddenTaskView.getLayoutParams());
- mSplitHiddenTaskView.setAlpha(0);
- int[] oldScroll = new int[getChildCount()];
- getPageScrolls(oldScroll, false,
- view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
-
- int[] newScroll = new int[getChildCount()];
- getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
-
- boolean needsCurveUpdates = false;
- for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
- View child = getChildAt(i);
- if (child == mSplitHiddenTaskView) {
- TaskView taskView = (TaskView) child;
-
- int dir = mOrientationHandler.getSplitTaskViewDismissDirection(stagePosition,
- mActivity.getDeviceProfile());
- FloatProperty<TaskView> dismissingTaskViewTranslate;
- Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
- taskView.getRight(), taskView.getBottom());
- int distanceDelta = 0;
- if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
- dismissingTaskViewTranslate = taskView
- .getSecondaryDissmissTranslationProperty();
- distanceDelta = initialBounds.top - hiddenBounds.top;
- taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
- hiddenBounds.bottom);
- } else {
- dismissingTaskViewTranslate = taskView
- .getPrimaryDismissTranslationProperty();
- distanceDelta = initialBounds.left - hiddenBounds.left;
- taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
- initialBounds.bottom);
- if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
- distanceDelta *= -1;
- }
- }
- pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
- dismissingTaskViewTranslate,
- distanceDelta));
- pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
- } else {
- // If insertion is on last index (furthest from clear all), we directly add the view
- // else we translate all views to the right of insertion index further right,
- // ignore views to left
- if (showAsGrid()) {
- // TODO(b/186800707) handle more elegantly for grid
- continue;
- }
- int scrollDiff = newScroll[i] - oldScroll[i];
- if (scrollDiff != 0) {
- FloatProperty translationProperty = child instanceof TaskView
- ? ((TaskView) child).getPrimaryDismissTranslationProperty()
- : mOrientationHandler.getPrimaryViewTranslate();
-
- ResourceProvider rp = DynamicResource.provider(mActivity);
- SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
- .setDampingRatio(
- rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio))
- .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness));
- pendingAnim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
- .setDuration(duration), ACCEL, sp);
- needsCurveUpdates = true;
- }
- }
- }
-
- if (needsCurveUpdates) {
- pendingAnim.addOnFrameCallback(this::updateCurveProperties);
- }
-
- pendingAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // TODO(b/186800707) Figure out how to undo for grid view
- // Need to handle cases where dismissed task is
- // * Top Row
- // * Bottom Row
- // * Focused Task
- updateGridProperties();
- resetFromSplitSelectionState();
- updateScrollSynchronously();
- }
- });
-
- return pendingAnim;
- }
-
/** TODO(b/181707736) More gracefully handle exiting split selection state */
private void resetFromSplitSelectionState() {
- if (!showAsGrid()) {
+ if (!mActivity.getDeviceProfile().overviewShowAsGrid) {
int pageToSnapTo = mCurrentPage;
if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
pageToSnapTo += 1;
@@ -4066,8 +4001,8 @@
mTaskViewDeadZoneRect.setEmpty();
int count = getTaskViewCount();
if (count > 0) {
- final View taskView = getTaskViewAt(0);
- getTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
+ final View taskView = requireTaskViewAt(0);
+ requireTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
taskView.getBottom());
}
@@ -4548,7 +4483,7 @@
final int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- TaskView taskView = getTaskViewAt(i);
+ TaskView taskView = requireTaskViewAt(i);
float scrollDiff = taskView.getScrollAdjustment(showAsFullscreen, showAsGrid);
int pageScroll = newPageScrolls[i] + (int) scrollDiff;
if ((mIsRtl && pageScroll < clearAllScroll + clearAllWidth)
@@ -4716,7 +4651,7 @@
int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
- getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
+ requireTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
}
}
@@ -4870,7 +4805,7 @@
mColorTint = tintAmount;
for (int i = 0; i < getTaskViewCount(); i++) {
- getTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
+ requireTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
}
Drawable scrimBg = mActivity.getScrimView().getBackground();
@@ -4939,6 +4874,62 @@
}
@Override
+ public boolean scrollLeft() {
+ if (!showAsGrid()) {
+ return super.scrollLeft();
+ }
+
+ int targetPage = getNextPage();
+ if (targetPage >= 0) {
+ // Find the next page that is not fully visible.
+ TaskView taskView = getTaskViewAt(targetPage);
+ while ((taskView == null || isTaskViewFullyVisible(taskView)) && targetPage - 1 >= 0) {
+ taskView = getTaskViewAt(--targetPage);
+ }
+ // Target a scroll where targetPage is on left of screen but still fully visible.
+ int lastTaskEnd = (mIsRtl
+ ? mLastComputedGridSize.left
+ : mLastComputedGridSize.right)
+ + (mIsRtl ? mPageSpacing : -mPageSpacing);
+ int normalTaskEnd = mIsRtl
+ ? mLastComputedGridTaskSize.left
+ : mLastComputedGridTaskSize.right;
+ int targetScroll = getScrollForPage(targetPage) + normalTaskEnd - lastTaskEnd;
+ // Find a page that is close to targetScroll while not over it.
+ while (targetPage - 1 >= 0
+ && (mIsRtl
+ ? getScrollForPage(targetPage - 1) < targetScroll
+ : getScrollForPage(targetPage - 1) > targetScroll)) {
+ targetPage--;
+ }
+ snapToPage(targetPage);
+ return true;
+ }
+
+ return mAllowOverScroll;
+ }
+
+ @Override
+ public boolean scrollRight() {
+ if (!showAsGrid()) {
+ return super.scrollRight();
+ }
+
+ int targetPage = getNextPage();
+ if (targetPage < getChildCount()) {
+ // Find the next page that is not fully visible.
+ TaskView taskView = getTaskViewAt(targetPage);
+ while ((taskView != null && isTaskViewFullyVisible(taskView))
+ && targetPage + 1 < getChildCount()) {
+ taskView = getTaskViewAt(++targetPage);
+ }
+ snapToPage(targetPage);
+ return true;
+ }
+ return mAllowOverScroll;
+ }
+
+ @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
dispatchScrollChanged();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index e8077cf..467d225 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -599,7 +599,9 @@
if (confirmSecondSplitSelectApp()) {
return;
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+ RecentsView recentsView = getRecentsView();
+ RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
return;
}
@@ -612,9 +614,7 @@
}
mIsClickableAsLiveTile = false;
- RecentsView recentsView = getRecentsView();
RemoteAnimationTargets targets;
- RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
if (remoteTargetHandles.length == 1) {
targets = remoteTargetHandles[0].getTransformParams().getTargetSet();
} else {
@@ -912,7 +912,7 @@
return deviceProfile.overviewShowAsGrid && !isFocusedTask();
}
- private void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
if (invert) {
progress = 1 - progress;
}
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
new file mode 100644
index 0000000..ba1a60d
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -0,0 +1,159 @@
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TaskbarNavButtonControllerTest {
+
+ private final static int DISPLAY_ID = 2;
+
+ @Mock
+ SystemUiProxy mockSystemUiProxy;
+ @Mock
+ TouchInteractionService mockService;
+ @Mock
+ OverviewCommandHelper mockCommandHelper;
+ @Mock
+ Handler mockHandler;
+
+ private TaskbarNavButtonController mNavButtonController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
+ when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
+ mNavButtonController = new TaskbarNavButtonController(mockService,
+ mockSystemUiProxy, mockHandler);
+ }
+
+ @Test
+ public void testPressBack() {
+ mNavButtonController.onButtonClick(BUTTON_BACK);
+ verify(mockSystemUiProxy, times(1)).onBackPressed();
+ }
+
+ @Test
+ public void testPressImeSwitcher() {
+ mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
+ verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
+ }
+
+ @Test
+ public void testPressA11yShortClick() {
+ mNavButtonController.onButtonClick(BUTTON_A11Y);
+ verify(mockSystemUiProxy, times(1))
+ .notifyAccessibilityButtonClicked(DISPLAY_ID);
+ }
+
+ @Test
+ public void testPressA11yLongClick() {
+ mNavButtonController.onButtonLongClick(BUTTON_A11Y);
+ verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
+ }
+
+ @Test
+ public void testLongPressHome() {
+ mNavButtonController.onButtonLongClick(BUTTON_HOME);
+ verify(mockSystemUiProxy, times(1)).startAssistant(any());
+ }
+
+ @Test
+ public void testPressHome() {
+ mNavButtonController.onButtonClick(BUTTON_HOME);
+ verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
+ }
+
+ @Test
+ public void testPressRecents() {
+ mNavButtonController.onButtonClick(BUTTON_RECENTS);
+ verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
+ }
+
+ @Test
+ public void testPressRecentsWithScreenPinned() {
+ mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+ mNavButtonController.onButtonClick(BUTTON_RECENTS);
+ verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
+ }
+
+ @Test
+ public void testLongPressBackRecentsNotPinned() {
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+ }
+
+ @Test
+ public void testLongPressBackRecentsPinned() {
+ mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ verify(mockSystemUiProxy, times(1)).stopScreenPinning();
+ }
+
+ @Test
+ public void testLongPressBackRecentsTooLongPinned() {
+ mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ try {
+ Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+ }
+
+ @Test
+ public void testLongPressBackRecentsMultipleAttemptPinned() {
+ mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ try {
+ Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+
+ // Try again w/in threshold
+ mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+ mNavButtonController.onButtonLongClick(BUTTON_BACK);
+ verify(mockSystemUiProxy, times(1)).stopScreenPinning();
+ }
+
+ @Test
+ public void testLongPressHomeScreenPinned() {
+ mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+ mNavButtonController.onButtonLongClick(BUTTON_HOME);
+ verify(mockSystemUiProxy, times(0)).startAssistant(any());
+ }
+}
diff --git a/res/drawable/ic_split_horizontal.xml b/res/drawable/ic_split_horizontal.xml
new file mode 100644
index 0000000..ee710d0
--- /dev/null
+++ b/res/drawable/ic_split_horizontal.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="16dp"
+ android:viewportWidth="20"
+ android:viewportHeight="16">
+ <path
+ android:pathData="M18,14L13,14L13,2L18,2L18,14ZM20,14L20,2C20,0.9 19.1,-0 18,-0L13,-0C11.9,-0 11,0.9 11,2L11,14C11,15.1 11.9,16 13,16L18,16C19.1,16 20,15.1 20,14ZM7,14L2,14L2,2L7,2L7,14ZM9,14L9,2C9,0.9 8.1,-0 7,-0L2,-0C0.9,-0 -0,0.9 -0,2L-0,14C-0,15.1 0.9,16 2,16L7,16C8.1,16 9,15.1 9,14Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_left.xml b/res/drawable/ic_split_left.xml
new file mode 100644
index 0000000..fc9f699
--- /dev/null
+++ b/res/drawable/ic_split_left.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="16dp"
+ android:viewportWidth="20"
+ android:viewportHeight="16">
+ <path
+ android:pathData="M-0,2L-0,14C-0,15.1 0.9,16 2,16L7,16C8.1,16 9,15.1 9,14L9,2C9,0.9 8.1,-0 7,-0L2,-0C0.9,-0 -0,0.9 -0,2ZM13,2L18,2L18,14L13,14L13,2ZM11,2L11,14C11,15.1 11.9,16 13,16L18,16C19.1,16 20,15.1 20,14L20,2C20,0.9 19.1,-0 18,-0L13,-0C11.9,-0 11,0.9 11,2Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_right.xml b/res/drawable/ic_split_right.xml
new file mode 100644
index 0000000..cc15622
--- /dev/null
+++ b/res/drawable/ic_split_right.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="16dp"
+ android:viewportWidth="20"
+ android:viewportHeight="16">
+ <path
+ android:pathData="M20,14L20,2C20,0.9 19.1,-0 18,-0L13,-0C11.9,-0 11,0.9 11,2L11,14C11,15.1 11.9,16 13,16L18,16C19.1,16 20,15.1 20,14ZM7,14L2,14L2,2L7,2L7,14ZM9,14L9,2C9,0.9 8.1,-0 7,-0L2,-0C0.9,-0 -0,0.9 -0,2L-0,14C-0,15.1 0.9,16 2,16L7,16C8.1,16 9,15.1 9,14Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_top.xml b/res/drawable/ic_split_top.xml
new file mode 100644
index 0000000..f8c15bd
--- /dev/null
+++ b/res/drawable/ic_split_top.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="20dp"
+ android:viewportWidth="16"
+ android:viewportHeight="20">
+ <path
+ android:pathData="M14,0H2C0.9,0 0,0.9 0,2V7C0,8.1 0.9,9 2,9H14C15.1,9 16,8.1 16,7V2C16,0.9 15.1,0 14,0ZM14,13V18H2V13H14ZM14,11H2C0.9,11 0,11.9 0,13V18C0,19.1 0.9,20 2,20H14C15.1,20 16,19.1 16,18V13C16,11.9 15.1,11 14,11Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_vertical.xml b/res/drawable/ic_split_vertical.xml
new file mode 100644
index 0000000..9bc9785
--- /dev/null
+++ b/res/drawable/ic_split_vertical.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M18,4V9H6V4H18ZM18,2H6C4.9,2 4,2.9 4,4V9C4,10.1 4.9,11 6,11H18C19.1,11 20,10.1 20,9V4C20,2.9 19.1,2 18,2ZM18,15V20H6V15H18ZM18,13H6C4.9,13 4,13.9 4,15V20C4,21.1 4.9,22 6,22H18C19.1,22 20,21.1 20,20V15C20,13.9 19.1,13 18,13Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ea6ddbe..878ef3f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -329,6 +329,9 @@
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
+<!-- Base Swipe Detector, speed in dp/s -->
+ <dimen name="base_swift_detector_fling_release_velocity">1dp</dimen>
+
<!-- Overview placeholder to compile in Launcher3 without Quickstep -->
<dimen name="task_thumbnail_icon_size">0dp</dimen>
<dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 4ee192a..5d41741 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -50,7 +50,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderIcon;
@@ -304,7 +304,7 @@
@Override
public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
- if (delegate instanceof LauncherAccessibilityDelegate) {
+ if (delegate instanceof BaseAccessibilityDelegate) {
super.setAccessibilityDelegate(delegate);
} else {
// NO-OP
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 5e67bf0..9fad2eb 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -814,15 +814,23 @@
Point padding = getTotalWorkspacePadding();
int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
- int cellLayoutTotalPadding =
- isTwoPanels ? 4 * cellLayoutPaddingLeftRightPx : 2 * cellLayoutPaddingLeftRightPx;
- int screenWidthPx = availableWidthPx - padding.x - cellLayoutTotalPadding;
+ int screenWidthPx = getWorkspaceWidth(padding);
result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacePx.x, numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y
- cellLayoutBottomPaddingPx, cellLayoutBorderSpacePx.y, inv.numRows);
return result;
}
+ public int getWorkspaceWidth() {
+ return getWorkspaceWidth(getTotalWorkspacePadding());
+ }
+
+ public int getWorkspaceWidth(Point workspacePadding) {
+ int cellLayoutTotalPadding =
+ isTwoPanels ? 4 * cellLayoutPaddingLeftRightPx : 2 * cellLayoutPaddingLeftRightPx;
+ return availableWidthPx - workspacePadding.x - cellLayoutTotalPadding;
+ }
+
public Point getTotalWorkspacePadding() {
updateWorkspacePadding();
return new Point(workspacePadding.left + workspacePadding.right,
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d844b87..2ebedf7 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -286,7 +286,11 @@
mExtraAttrs = closestProfile.extraAttrs;
iconSize = displayOption.iconSizes;
- iconBitmapSize = ResourceUtils.pxFromDp(iconSize[INDEX_DEFAULT], metrics);
+ float maxIconSize = iconSize[0];
+ for (int i = 1; i < iconSize.length; i++) {
+ maxIconSize = Math.max(maxIconSize, iconSize[i]);
+ }
+ iconBitmapSize = ResourceUtils.pxFromDp(maxIconSize, metrics);
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
iconTextSize = displayOption.textSizes;
@@ -555,13 +559,10 @@
}
out.multiply(1.0f / weights);
- // Since the bitmaps are persisted, ensure that the default bitmap size is same as
+ // Since the bitmaps are persisted, ensure that all bitmap sizes are not larger than
// predefined size to avoid cache invalidation
- out.iconSizes[INDEX_DEFAULT] =
- closestPoint.iconSizes[INDEX_DEFAULT];
- for (int i = INDEX_DEFAULT + 1; i < COUNT_SIZES; i++) {
- out.iconSizes[i] = Math.min(out.iconSizes[i],
- out.iconSizes[INDEX_DEFAULT]);
+ for (int i = INDEX_DEFAULT; i < COUNT_SIZES; i++) {
+ out.iconSizes[i] = Math.min(out.iconSizes[i], closestPoint.iconSizes[i]);
}
return out;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0656125..ab9b980 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -115,8 +115,8 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.accessibility.BaseAccessibilityDelegate.LauncherAction;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.LauncherAction;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AllAppsTransitionController;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 1ce7ebe..2c14f07 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1749,20 +1749,23 @@
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
final boolean pagesFlipped = isPageOrderFlipped();
- int offset = (mAllowOverScroll ? 0 : 1);
- info.setScrollable(getPageCount() > offset);
- if (getCurrentPage() < getPageCount() - offset) {
+ info.setScrollable(getPageCount() > 0);
+ int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+ if (getCurrentPage() < getPageCount() - getPanelCount()
+ || (getCurrentPage() == getPageCount() - getPanelCount()
+ && primaryScroll != getScrollForPage(getPageCount() - getPanelCount()))) {
info.addAction(pagesFlipped ?
- AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
- : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
+ : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(mIsRtl ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
: AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
}
- if (getCurrentPage() >= offset) {
+ if (getCurrentPage() > 0
+ || (getCurrentPage() == 0 && primaryScroll != getScrollForPage(0))) {
info.addAction(pagesFlipped ?
- AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
- : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
+ : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(mIsRtl ?
AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
: AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
@@ -1807,16 +1810,16 @@
} break;
case android.R.id.accessibilityActionPageRight: {
if (!mIsRtl) {
- return scrollRight();
+ return scrollRight();
} else {
- return scrollLeft();
+ return scrollLeft();
}
}
case android.R.id.accessibilityActionPageLeft: {
if (!mIsRtl) {
- return scrollLeft();
+ return scrollLeft();
} else {
- return scrollRight();
+ return scrollRight();
}
}
}
diff --git a/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java
new file mode 100644
index 0000000..14b2431
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java
@@ -0,0 +1,192 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class BaseAccessibilityDelegate<T extends Context & ActivityContext>
+ extends View.AccessibilityDelegate implements DragController.DragListener {
+
+ public enum DragType {
+ ICON,
+ FOLDER,
+ WIDGET
+ }
+
+ public static class DragInfo {
+ public DragType dragType;
+ public ItemInfo info;
+ public View item;
+ }
+
+ protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
+ protected final T mContext;
+
+ protected DragInfo mDragInfo = null;
+
+ protected BaseAccessibilityDelegate(T context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ if (host.getTag() instanceof ItemInfo) {
+ ItemInfo item = (ItemInfo) host.getTag();
+
+ List<LauncherAction> actions = new ArrayList<>();
+ getSupportedActions(host, item, actions);
+ actions.forEach(la -> info.addAction(la.accessibilityAction));
+
+ if (!itemSupportsLongClick(host, item)) {
+ info.setLongClickable(false);
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ }
+ }
+ }
+
+ /**
+ * Adds all the accessibility actions that can be handled.
+ */
+ protected abstract void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out);
+
+ private boolean itemSupportsLongClick(View host, ItemInfo info) {
+ return PopupContainerWithArrow.canShow(host, info);
+ }
+
+ protected boolean itemSupportsAccessibleDrag(ItemInfo item) {
+ if (item instanceof WorkspaceItemInfo) {
+ // Support the action unless the item is in a context menu.
+ return item.screenId >= 0
+ && item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+ }
+ return (item instanceof LauncherAppWidgetInfo)
+ || (item instanceof FolderInfo);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if ((host.getTag() instanceof ItemInfo)
+ && performAction(host, (ItemInfo) host.getTag(), action, false)) {
+ return true;
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+
+ protected abstract boolean performAction(
+ View host, ItemInfo item, int action, boolean fromKeyboard);
+
+ @Thunk
+ protected void announceConfirmation(String confirmation) {
+ mContext.getDragLayer().announceForAccessibility(confirmation);
+
+ }
+
+ public boolean isInAccessibleDrag() {
+ return mDragInfo != null;
+ }
+
+ public DragInfo getDragInfo() {
+ return mDragInfo;
+ }
+
+ /**
+ * @param clickedTarget the actual view that was clicked
+ * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
+ * as the actual drop location otherwise the views center is used.
+ */
+ public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
+ String confirmation) {
+ if (!isInAccessibleDrag()) return;
+
+ int[] loc = new int[2];
+ if (dropLocation == null) {
+ loc[0] = clickedTarget.getWidth() / 2;
+ loc[1] = clickedTarget.getHeight() / 2;
+ } else {
+ loc[0] = dropLocation.centerX();
+ loc[1] = dropLocation.centerY();
+ }
+
+ mContext.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
+ mContext.getDragController().completeAccessibleDrag(loc);
+
+ if (!TextUtils.isEmpty(confirmation)) {
+ announceConfirmation(confirmation);
+ }
+ }
+
+ protected abstract boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard);
+
+
+ @Override
+ public void onDragEnd() {
+ mContext.getDragController().removeDragListener(this);
+ mDragInfo = null;
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ // No-op
+ }
+
+ public class LauncherAction {
+ public final int keyCode;
+ public final AccessibilityNodeInfo.AccessibilityAction accessibilityAction;
+
+ private final BaseAccessibilityDelegate<T> mDelegate;
+
+ public LauncherAction(int id, int labelRes, int keyCode) {
+ this.keyCode = keyCode;
+ accessibilityAction = new AccessibilityNodeInfo.AccessibilityAction(
+ id, mContext.getString(labelRes));
+ mDelegate = BaseAccessibilityDelegate.this;
+ }
+
+ /**
+ * Invokes the action for the provided host
+ */
+ public boolean invokeFromKeyboard(View host) {
+ if (host != null && host.getTag() instanceof ItemInfo) {
+ return mDelegate.performAction(
+ host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
+ } else {
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 157df5d..18c05eb 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -10,27 +10,19 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.Bundle;
import android.os.Handler;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.SparseArray;
import android.view.KeyEvent;
import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
-import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.Folder;
@@ -57,7 +49,7 @@
import java.util.Collections;
import java.util.List;
-public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener {
+public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Launcher> {
private static final String TAG = "LauncherAccessibilityDelegate";
@@ -73,25 +65,8 @@
public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
public static final int SHORTCUTS_AND_NOTIFICATIONS = R.id.action_shortcuts_and_notifications;
- public enum DragType {
- ICON,
- FOLDER,
- WIDGET
- }
-
- public static class DragInfo {
- public DragType dragType;
- public ItemInfo info;
- public View item;
- }
-
- protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
- protected final Launcher mLauncher;
-
- private DragInfo mDragInfo = null;
-
public LauncherAccessibilityDelegate(Launcher launcher) {
- mLauncher = launcher;
+ super(launcher);
mActions.put(REMOVE, new LauncherAction(
REMOVE, R.string.remove_drop_target_label, KeyEvent.KEYCODE_X));
@@ -116,25 +91,6 @@
}
@Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- if (host.getTag() instanceof ItemInfo) {
- ItemInfo item = (ItemInfo) host.getTag();
-
- List<LauncherAction> actions = new ArrayList<>();
- getSupportedActions(host, item, actions);
- actions.forEach(la -> info.addAction(la.accessibilityAction));
-
- if (!itemSupportsLongClick(host, item)) {
- info.setLongClickable(false);
- info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
- }
- }
- }
-
- /**
- * Adds all the accessibility actions that can be handled.
- */
protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
// If the request came from keyboard, do not add custom shortcuts as that is already
// exposed as a direct shortcut
@@ -143,7 +99,7 @@
? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
}
- for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
+ for (ButtonDropTarget target : mContext.getDropTargetBar().getDropTargets()) {
if (target.supportsAccessibilityDrop(item, host)) {
out.add(mActions.get(target.getAccessibilityAction()));
}
@@ -183,31 +139,7 @@
return result;
}
- private boolean itemSupportsLongClick(View host, ItemInfo info) {
- return PopupContainerWithArrow.canShow(host, info);
- }
-
- private boolean itemSupportsAccessibleDrag(ItemInfo item) {
- if (item instanceof WorkspaceItemInfo) {
- // Support the action unless the item is in a context menu.
- return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION;
- }
- return (item instanceof LauncherAppWidgetInfo)
- || (item instanceof FolderInfo);
- }
-
@Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if ((host.getTag() instanceof ItemInfo)
- && performAction(host, (ItemInfo) host.getTag(), action, false)) {
- return true;
- }
- return super.performAccessibilityAction(host, action, args);
- }
-
- /**
- * Performs the provided action on the host
- */
protected boolean performAction(final View host, final ItemInfo item, int action,
boolean fromKeyboard) {
if (action == ACTION_LONG_CLICK) {
@@ -226,36 +158,36 @@
if (screenId == -1) {
return false;
}
- mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
+ mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
if (item instanceof AppInfo) {
WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
- mLauncher.getModelWriter().addItemToDatabase(info,
+ mContext.getModelWriter().addItemToDatabase(info,
Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- mLauncher.bindItems(
+ mContext.bindItems(
Collections.singletonList(info),
/* forceAnimateIcons= */ true,
/* focusFirstItemForAccessibility= */ true);
announceConfirmation(R.string.item_added_to_workspace);
} else if (item instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) item;
- Workspace workspace = mLauncher.getWorkspace();
+ Workspace workspace = mContext.getWorkspace();
workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
- mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
+ mContext.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
}
else if (item instanceof WorkspaceItemInfo) {
WorkspaceItemInfo info = ((WorkspaceItemInfo) item).clone();
- mLauncher.getModelWriter().addItemToDatabase(info,
+ mContext.getModelWriter().addItemToDatabase(info,
Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- mLauncher.bindItems(Collections.singletonList(info), true, true);
+ mContext.bindItems(Collections.singletonList(info), true, true);
}
}));
return true;
} else if (action == MOVE_TO_WORKSPACE) {
- Folder folder = Folder.getOpen(mLauncher);
+ Folder folder = Folder.getOpen(mContext);
folder.close(true);
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
folder.getInfo().remove(info, false);
@@ -265,14 +197,14 @@
if (screenId == -1) {
return false;
}
- mLauncher.getModelWriter().moveItemInDatabase(info,
+ mContext.getModelWriter().moveItemInDatabase(info,
Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
// Bind the item in next frame so that if a new workspace page was created,
// it will get laid out.
new Handler().post(() -> {
- mLauncher.bindItems(Collections.singletonList(item), true);
+ mContext.bindItems(Collections.singletonList(item), true);
announceConfirmation(R.string.item_moved);
});
return true;
@@ -280,15 +212,15 @@
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
List<OptionItem> actions = getSupportedResizeActions(host, info);
Rect pos = new Rect();
- mLauncher.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
- ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions, false);
+ mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
+ ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
popup.requestFocus();
popup.setOnCloseCallback(host::requestFocus);
return true;
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
} else {
- for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
+ for (ButtonDropTarget dropTarget : mContext.getDropTargetBar().getDropTargets()) {
if (dropTarget.supportsAccessibilityDrop(item, host)
&& action == dropTarget.getAccessibilityAction()) {
dropTarget.onAccessibilityDrop(host, item);
@@ -315,7 +247,7 @@
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
- actions.add(new OptionItem(mLauncher,
+ actions.add(new OptionItem(mContext,
R.string.action_increase_width,
R.drawable.ic_widget_width_increase,
IGNORE,
@@ -323,7 +255,7 @@
}
if (info.spanX > info.minSpanX && info.spanX > 1) {
- actions.add(new OptionItem(mLauncher,
+ actions.add(new OptionItem(mContext,
R.string.action_decrease_width,
R.drawable.ic_widget_width_decrease,
IGNORE,
@@ -334,7 +266,7 @@
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
- actions.add(new OptionItem(mLauncher,
+ actions.add(new OptionItem(mContext,
R.string.action_increase_height,
R.drawable.ic_widget_height_increase,
IGNORE,
@@ -342,7 +274,7 @@
}
if (info.spanY > info.minSpanY && info.spanY > 1) {
- actions.add(new OptionItem(mLauncher,
+ actions.add(new OptionItem(mContext,
R.string.action_decrease_height,
R.drawable.ic_widget_height_decrease,
IGNORE,
@@ -382,58 +314,20 @@
}
layout.markCellsAsOccupiedForView(host);
- WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
+ WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mContext,
info.spanX, info.spanY);
host.requestLayout();
- mLauncher.getModelWriter().updateItemInDatabase(info);
- announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
+ mContext.getModelWriter().updateItemInDatabase(info);
+ announceConfirmation(mContext.getString(R.string.widget_resized, info.spanX, info.spanY));
return true;
}
@Thunk void announceConfirmation(int resId) {
- announceConfirmation(mLauncher.getResources().getString(resId));
+ announceConfirmation(mContext.getResources().getString(resId));
}
- @Thunk void announceConfirmation(String confirmation) {
- mLauncher.getDragLayer().announceForAccessibility(confirmation);
-
- }
-
- public boolean isInAccessibleDrag() {
- return mDragInfo != null;
- }
-
- public DragInfo getDragInfo() {
- return mDragInfo;
- }
-
- /**
- * @param clickedTarget the actual view that was clicked
- * @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
- * as the actual drop location otherwise the views center is used.
- */
- public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
- String confirmation) {
- if (!isInAccessibleDrag()) return;
-
- int[] loc = new int[2];
- if (dropLocation == null) {
- loc[0] = clickedTarget.getWidth() / 2;
- loc[1] = clickedTarget.getHeight() / 2;
- } else {
- loc[0] = dropLocation.centerX();
- loc[1] = dropLocation.centerY();
- }
-
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
- mLauncher.getDragController().completeAccessibleDrag(loc);
-
- if (!TextUtils.isEmpty(confirmation)) {
- announceConfirmation(confirmation);
- }
- }
-
- private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
+ @Override
+ protected boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
if (!itemSupportsAccessibleDrag(info)) {
return false;
}
@@ -449,8 +343,8 @@
}
Rect pos = new Rect();
- mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
- mLauncher.getDragController().addDragListener(this);
+ mContext.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
+ mContext.getDragController().addDragListener(this);
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
@@ -458,31 +352,20 @@
options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
if (fromKeyboard) {
- KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
- .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
+ KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mContext.getLayoutInflater()
+ .inflate(R.layout.keyboard_drag_and_drop, mContext.getDragLayer(), false);
popup.showForIcon(item, info, options);
} else {
- ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+ ItemLongClickListener.beginDrag(item, mContext, info, options);
}
return true;
}
- @Override
- public void onDragStart(DragObject dragObject, DragOptions options) {
- // No-op
- }
-
- @Override
- public void onDragEnd() {
- mLauncher.getDragController().removeDragListener(this);
- mDragInfo = null;
- }
-
/**
* Find empty space on the workspace and returns the screenId.
*/
protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
- Workspace workspace = mLauncher.getWorkspace();
+ Workspace workspace = mContext.getWorkspace();
IntArray workspaceScreens = workspace.getScreenOrder();
int screenId;
@@ -520,29 +403,4 @@
}
return screenId;
}
-
- public class LauncherAction {
- public final int keyCode;
- public final AccessibilityAction accessibilityAction;
-
- private final LauncherAccessibilityDelegate mDelegate;
-
- public LauncherAction(int id, int labelRes, int keyCode) {
- this.keyCode = keyCode;
- accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes));
- mDelegate = LauncherAccessibilityDelegate.this;
- }
-
- /**
- * Invokes the action for the provided host
- */
- public boolean invokeFromKeyboard(View host) {
- if (host != null && host.getTag() instanceof ItemInfo) {
- return mDelegate.performAction(
- host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
- } else {
- return false;
- }
- }
- }
}
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index bf5a24b..fb847ec 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -71,12 +71,12 @@
if (screenId == -1) {
return false;
}
- mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
- mLauncher.getModelWriter().addItemToDatabase(info,
+ mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
+ mContext.getModelWriter().addItemToDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- mLauncher.bindItems(Collections.singletonList(info), true);
- AbstractFloatingView.closeAllOpenViews(mLauncher);
+ mContext.bindItems(Collections.singletonList(info), true);
+ AbstractFloatingView.closeAllOpenViews(mContext);
announceConfirmation(R.string.item_added_to_workspace);
}));
return true;
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index a331924..a8624dd 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -22,7 +22,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.R;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
+import com.android.launcher3.accessibility.BaseAccessibilityDelegate.DragType;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 98cb5ae..1fa6360 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -95,6 +95,9 @@
public static final BooleanFlag ENABLE_ONE_SEARCH = new DeviceFlag("ENABLE_ONE_SEARCH", false,
"Use homescreen search box to complete allApps searches");
+ public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
+ "COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
+
public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(
"ENABLE_TWOLINE_ALLAPPS", false, "Enables two line label inside all apps.");
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 13ad90e..84612de 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -31,6 +31,8 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Workspace;
@@ -215,6 +217,18 @@
}
public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
+ addItem(context, item, newItem, null);
+ }
+
+ public synchronized void addItem(
+ Context context, ItemInfo item, boolean newItem, @Nullable LoaderMemoryLogger logger) {
+ if (logger != null) {
+ logger.addLog(
+ Log.DEBUG,
+ TAG,
+ String.format("Adding item to ID map: %s", item.toString()),
+ /* stackTrace= */ null);
+ }
itemsIdMap.put(item.id, item);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 47df538..08b38e8 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -383,18 +383,23 @@
info.cellY = getInt(cellYIndex);
}
+ public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+ checkAndAddItem(info, dataModel, null);
+ }
+
/**
* Adds the {@param info} to {@param dataModel} if it does not overlap with any other item,
* otherwise marks it for deletion.
*/
- public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+ public void checkAndAddItem(
+ ItemInfo info, BgDataModel dataModel, LoaderMemoryLogger logger) {
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
// Ensure that it is a valid intent. An exception here will
// cause the item loading to get skipped
ShortcutKey.fromItemInfo(info);
}
if (checkItemPlacement(info)) {
- dataModel.addItem(mContext, info, false);
+ dataModel.addItem(mContext, info, false, logger);
} else {
markDeleted("Item position overlap");
}
diff --git a/src/com/android/launcher3/model/LoaderMemoryLogger.java b/src/com/android/launcher3/model/LoaderMemoryLogger.java
new file mode 100644
index 0000000..f48efcb
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderMemoryLogger.java
@@ -0,0 +1,91 @@
+/*
+ * 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.model;
+
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Helper logger that collects logs while {@code LoaderTask#run} executes and prints them all iff
+ * an exception is caught in {@code LoaderTask#run}.
+ */
+public class LoaderMemoryLogger {
+
+ private static final String TAG = "LoaderMemoryLogger";
+
+ private final ArrayList<LogEntry> mLogEntries = new ArrayList<>();
+
+ protected LoaderMemoryLogger() {}
+
+ protected void addLog(int logLevel, String tag, String log) {
+ addLog(logLevel, tag, log, null);
+ }
+
+ protected void addLog(
+ int logLevel, String tag, String log, Exception stackTrace) {
+ switch (logLevel) {
+ case Log.ASSERT:
+ case Log.ERROR:
+ case Log.DEBUG:
+ case Log.INFO:
+ case Log.VERBOSE:
+ case Log.WARN:
+ mLogEntries.add(new LogEntry(logLevel, tag, log, stackTrace));
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid log level provided: " + logLevel);
+
+ }
+ }
+
+ protected void clearLogs() {
+ mLogEntries.clear();
+ }
+
+ protected void printLogs() {
+ for (LogEntry logEntry : mLogEntries) {
+ String tag = String.format("%s: %s", TAG, logEntry.mLogTag);
+ String logString = logEntry.mStackStrace == null
+ ? logEntry.mLogString
+ : String.format(
+ "%s\n%s",
+ logEntry.mLogString,
+ Log.getStackTraceString(logEntry.mStackStrace));
+
+ Log.println(logEntry.mLogLevel, tag, logString);
+ }
+ clearLogs();
+ }
+
+ private static class LogEntry {
+
+ protected final int mLogLevel;
+ protected final String mLogTag;
+ protected final String mLogString;
+ @Nullable protected final Exception mStackStrace;
+
+ protected LogEntry(
+ int logLevel, String logTag, String logString, @Nullable Exception stackStrace) {
+ mLogLevel = logLevel;
+ mLogTag = logTag;
+ mLogString = logString;
+ mStackStrace = stackStrace;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index a4f6f7a..2a0f9a6 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -52,6 +52,8 @@
import android.util.LongSparseArray;
import android.util.TimingLogger;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -197,11 +199,12 @@
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
TimingLogger logger = new TimingLogger(TAG, "run");
+ LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
Trace.beginSection("LoadWorkspace");
try {
- loadWorkspace(allShortcuts);
+ loadWorkspace(allShortcuts, memoryLogger);
} finally {
Trace.endSection();
}
@@ -311,9 +314,13 @@
mModelDelegate.modelLoadComplete();
transaction.commit();
+ memoryLogger.clearLogs();
} catch (CancellationException e) {
// Loader stopped, ignore
logASplit(logger, "Cancelled");
+ } catch (Exception e) {
+ memoryLogger.printLogs();
+ throw e;
} finally {
logger.dumpToLog();
}
@@ -325,13 +332,21 @@
this.notify();
}
- private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
+ private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
- null /* selection */);
+ null /* selection */, logger);
}
- protected void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri,
- String selection) {
+ protected void loadWorkspace(
+ List<ShortcutInfo> allDeepShortcuts, Uri contentUri, String selection) {
+ loadWorkspace(allDeepShortcuts, contentUri, selection, null);
+ }
+
+ protected void loadWorkspace(
+ List<ShortcutInfo> allDeepShortcuts,
+ Uri contentUri,
+ String selection,
+ @Nullable LoaderMemoryLogger logger) {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -635,7 +650,7 @@
}
}
- c.checkAndAddItem(info, mBgDataModel);
+ c.checkAndAddItem(info, mBgDataModel, logger);
} else {
throw new RuntimeException("Unexpected null WorkspaceItemInfo");
}
@@ -654,7 +669,7 @@
// no special handling required for restored folders
c.markRestored();
- c.checkAndAddItem(folderInfo, mBgDataModel);
+ c.checkAndAddItem(folderInfo, mBgDataModel, logger);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
diff --git a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
index 731f439..3e3f633 100644
--- a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
@@ -18,6 +18,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
@@ -86,4 +87,9 @@
}
}
}
+
+ @Override
+ protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
+ PopupContainerWithArrow.showForIcon(originalIcon);
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index aa8c70b..0097705 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -152,6 +152,10 @@
};
}
+ public void setPopupItemDragHandler(PopupItemDragHandler popupItemDragHandler) {
+ mPopupItemDragHandler = popupItemDragHandler;
+ }
+
public PopupItemDragHandler getItemDragHandler() {
return mPopupItemDragHandler;
}
diff --git a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
index 194c22f..c5d5452 100644
--- a/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/PopupLiveUpdateHandler.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.view.View;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.notification.NotificationContainer;
@@ -36,7 +37,7 @@
*
* @param <T> The activity on which the popup shows
*/
-public class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
+public abstract class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
PopupDataProvider.PopupDataChangeListener, View.OnAttachStateChangeListener {
protected final T mContext;
@@ -103,6 +104,8 @@
@Override
public void onSystemShortcutsUpdated() {
mPopupContainerWithArrow.close(true);
- PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon());
+ showPopupContainerForIcon(mPopupContainerWithArrow.getOriginalIcon());
}
+
+ protected abstract void showPopupContainerForIcon(BubbleTextView originalIcon);
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index cecbb0d..c166bfc 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -23,12 +23,12 @@
import android.graphics.drawable.Drawable;
import android.view.View;
-import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.views.ActivityContext;
/**
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -45,7 +45,8 @@
@Override
public Drawable createDrawable() {
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+ int size = ActivityContext.lookupContext(mView.getContext())
+ .getDeviceProfile().iconSizePx;
return new FastBitmapDrawable(
BitmapRenderer.createHardwareBitmap(
size + blurSizeOutline,
@@ -59,7 +60,7 @@
private Bitmap createDragBitmapLegacy() {
Drawable d = mView.getBackground();
Rect bounds = getDrawableBounds(d);
- int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+ int size = ActivityContext.lookupContext(mView.getContext()).getDeviceProfile().iconSizePx;
final Bitmap b = Bitmap.createBitmap(
size + blurSizeOutline,
size + blurSizeOutline,
@@ -84,9 +85,9 @@
@Override
public float getScaleAndPosition(Drawable preview, int[] outPos) {
- Launcher launcher = Launcher.getLauncher(mView.getContext());
+ ActivityContext context = ActivityContext.lookupContext(mView.getContext());
int iconSize = getDrawableBounds(mView.getBackground()).width();
- float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
+ float scale = context.getDragLayer().getLocationInDragLayer(mView, outPos);
int iconLeft = mView.getPaddingStart();
if (Utilities.isRtl(mView.getResources())) {
@@ -98,7 +99,7 @@
+ mPositionShift.x);
outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2
+ mPositionShift.y);
- float size = launcher.getDeviceProfile().iconSizePx;
+ float size = context.getDeviceProfile().iconSizePx;
return scale * iconSize / size;
}
}
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 1276ece..52c3581 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -17,6 +17,7 @@
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import android.content.Context;
import android.graphics.PointF;
import android.util.Log;
import android.view.MotionEvent;
@@ -26,6 +27,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.R;
+
import java.util.LinkedList;
import java.util.Queue;
@@ -44,10 +47,9 @@
private static final boolean DBG = false;
private static final String TAG = "BaseSwipeDetector";
private static final float ANIMATION_DURATION = 1200;
- /** The minimum release velocity in pixels per millisecond that triggers fling.*/
- private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
private static final PointF sTempPoint = new PointF();
+ private final float mReleaseVelocity;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
protected final boolean mIsRtl;
@@ -64,6 +66,7 @@
private boolean mIsSettingState;
protected boolean mIgnoreSlopWhenSettling;
+ protected Context mContext;
private enum ScrollState {
IDLE,
@@ -71,10 +74,14 @@
SETTLING // onDragEnd
}
- protected BaseSwipeDetector(@NonNull ViewConfiguration config, boolean isRtl) {
+ protected BaseSwipeDetector(@NonNull Context context, @NonNull ViewConfiguration config,
+ boolean isRtl) {
mTouchSlop = config.getScaledTouchSlop();
mMaxVelocity = config.getScaledMaximumFlingVelocity();
mIsRtl = isRtl;
+ mContext = context;
+ mReleaseVelocity = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.base_swift_detector_fling_release_velocity);
}
public static long calculateDuration(float velocity, float progressNeeded) {
@@ -120,7 +127,7 @@
}
public boolean isFling(float velocity) {
- return Math.abs(velocity) > RELEASE_VELOCITY_PX_MS;
+ return Math.abs(velocity) > mReleaseVelocity;
}
public boolean onTouchEvent(MotionEvent ev) {
@@ -236,7 +243,7 @@
} else {
mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop;
mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop;
- }
+ }
}
protected abstract boolean shouldScrollStart(PointF displacement);
diff --git a/src/com/android/launcher3/touch/BothAxesSwipeDetector.java b/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
index 944391e..6e2f0d8 100644
--- a/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
@@ -21,7 +21,6 @@
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
@@ -43,13 +42,7 @@
private int mScrollDirections;
public BothAxesSwipeDetector(@NonNull Context context, @NonNull Listener l) {
- this(ViewConfiguration.get(context), l, Utilities.isRtl(context.getResources()));
- }
-
- @VisibleForTesting
- protected BothAxesSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
- boolean isRtl) {
- super(config, isRtl);
+ super(context, ViewConfiguration.get(context), Utilities.isRtl(context.getResources()));
mListener = l;
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index e127074..0c39067 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -36,6 +36,7 @@
import android.graphics.RectF;
import android.graphics.drawable.ShapeDrawable;
import android.util.FloatProperty;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
@@ -356,7 +357,7 @@
public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
// Add "left" side of phone which is actually the top
return Collections.singletonList(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_left,
+ R.drawable.ic_split_left, R.string.split_screen_position_left,
STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
}
@@ -462,8 +463,8 @@
}
@Override
- public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
- DeviceProfile deviceProfile) {
- return primary;
+ public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
+ FloatProperty secondary, DeviceProfile deviceProfile) {
+ return new Pair<>(primary, secondary);
}
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index d954552..54b30fb 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -24,6 +24,7 @@
import android.graphics.RectF;
import android.graphics.drawable.ShapeDrawable;
import android.util.FloatProperty;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -113,8 +114,8 @@
float getSecondaryValue(float x, float y);
boolean isLayoutNaturalToLauncher();
- FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
- DeviceProfile deviceProfile);
+ Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
+ FloatProperty secondary, DeviceProfile deviceProfile);
int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
/**
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index fbc335c..b9f1b66 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -35,6 +35,7 @@
import android.graphics.RectF;
import android.graphics.drawable.ShapeDrawable;
import android.util.FloatProperty;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
@@ -369,28 +370,27 @@
public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
List<SplitPositionOption> options = new ArrayList<>(1);
// Add both left and right options if we're in tablet mode
- // TODO: Add in correct icons
if (dp.isTablet && dp.isLandscape) {
options.add(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_right,
+ R.drawable.ic_split_right, R.string.split_screen_position_right,
STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
options.add(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_left,
+ R.drawable.ic_split_left, R.string.split_screen_position_left,
STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
} else {
if (dp.isSeascape()) {
// Add left/right options
options.add(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_right,
+ R.drawable.ic_split_right, R.string.split_screen_position_right,
STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
} else if (dp.isLandscape) {
options.add(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_left,
+ R.drawable.ic_split_left, R.string.split_screen_position_left,
STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
} else {
// Only add top option
options.add(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_top,
+ R.drawable.ic_split_top, R.string.split_screen_position_top,
STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
}
}
@@ -571,12 +571,12 @@
}
@Override
- public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
- DeviceProfile dp) {
- if (dp.isLandscape) { // or seascape
- return primary;
+ public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
+ FloatProperty secondary, DeviceProfile deviceProfile) {
+ if (deviceProfile.isLandscape) { // or seascape
+ return new Pair<>(primary, secondary);
} else {
- return secondary;
+ return new Pair<>(secondary, primary);
}
}
}
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 539e3f8..ce2e136 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -115,7 +115,7 @@
public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
// Add "right" option which is actually the top
return Collections.singletonList(new SplitPositionOption(
- R.drawable.ic_split_screen, R.string.split_screen_position_right,
+ R.drawable.ic_split_right, R.string.split_screen_position_right,
STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
}
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index f751b7d..5c599c0 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -106,13 +106,15 @@
public SingleAxisSwipeDetector(@NonNull Context context, @NonNull Listener l,
@NonNull Direction dir) {
- this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
+ super(context, ViewConfiguration.get(context), Utilities.isRtl(context.getResources()));
+ mListener = l;
+ mDir = dir;
}
@VisibleForTesting
- protected SingleAxisSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
- @NonNull Direction dir, boolean isRtl) {
- super(config, isRtl);
+ protected SingleAxisSwipeDetector(@NonNull Context context, @NonNull ViewConfiguration config,
+ @NonNull Listener l, @NonNull Direction dir, boolean isRtl) {
+ super(context, config, isRtl);
mListener = l;
mDir = dir;
}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 53b1c3e..cb714b2 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -68,6 +68,11 @@
public @interface StageType {}
///////////////////////////////////
+ /**
+ * Default split ratio for launching app pair from overview.
+ */
+ public static final float DEFAULT_SPLIT_RATIO = 0.5f;
+
public static class SplitPositionOption {
public final int iconResId;
public final int textResId;
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index e731706..c0ec9d8 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -458,7 +458,7 @@
if (mIconLoadResult != null && mIconLoadResult.isIconLoaded) {
setVisibility(View.VISIBLE);
}
- if (!mIsOpening) {
+ if (!mIsOpening && mOriginalIcon != null) {
// When closing an app, we want the item on the workspace to be invisible immediately
setIconAndDotVisible(mOriginalIcon, false);
}
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index 49fcd2e..f945819 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -28,8 +28,9 @@
import android.view.MotionEvent;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -44,7 +45,7 @@
private static final long HIDE_DURATION_MS = 180;
private static final int TIMEOUT_DURATION_MS = 4000;
- private final BaseDraggingActivity mActivity;
+ private final ActivityContext mActivity;
private Runnable mOnDismissed;
public Snackbar(Context context, AttributeSet attrs) {
@@ -53,12 +54,19 @@
public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mActivity = BaseDraggingActivity.fromContext(context);
+ mActivity = ActivityContext.lookupContext(context);
inflate(context, R.layout.snackbar, this);
}
- public static void show(BaseDraggingActivity activity, int labelStringResId,
- int actionStringResId, Runnable onDismissed, Runnable onActionClicked) {
+ /** Show a snackbar with just a label. */
+ public static <T extends Context & ActivityContext> void show(T activity, int labelStringRedId,
+ Runnable onDismissed) {
+ show(activity, labelStringRedId, NO_ID, onDismissed, null);
+ }
+
+ /** Show a snackbar with a label and action. */
+ public static <T extends Context & ActivityContext> void show(T activity, int labelStringResId,
+ int actionStringResId, Runnable onDismissed, @Nullable Runnable onActionClicked) {
closeOpenViews(activity, true, TYPE_SNACKBAR);
Snackbar snackbar = new Snackbar(activity, null);
// Set some properties here since inflated xml only contains the children.
@@ -87,13 +95,30 @@
params.setMargins(0, 0, 0, marginBottom + insets.bottom);
TextView labelView = snackbar.findViewById(R.id.label);
- TextView actionView = snackbar.findViewById(R.id.action);
String labelText = res.getString(labelStringResId);
- String actionText = res.getString(actionStringResId);
- int totalContentWidth = (int) (labelView.getPaint().measureText(labelText)
- + actionView.getPaint().measureText(actionText))
+ labelView.setText(labelText);
+
+ TextView actionView = snackbar.findViewById(R.id.action);
+ float actionWidth;
+ if (actionStringResId != NO_ID) {
+ String actionText = res.getString(actionStringResId);
+ actionWidth = actionView.getPaint().measureText(actionText)
+ + actionView.getPaddingRight() + actionView.getPaddingLeft();
+ actionView.setText(actionText);
+ actionView.setOnClickListener(v -> {
+ if (onActionClicked != null) {
+ onActionClicked.run();
+ }
+ snackbar.mOnDismissed = null;
+ snackbar.close(true);
+ });
+ } else {
+ actionWidth = 0;
+ actionView.setVisibility(GONE);
+ }
+
+ int totalContentWidth = (int) (labelView.getPaint().measureText(labelText) + actionWidth)
+ labelView.getPaddingRight() + labelView.getPaddingLeft()
- + actionView.getPaddingRight() + actionView.getPaddingLeft()
+ padding * 2;
if (totalContentWidth > params.width) {
// The text doesn't fit in our standard width so update width to accommodate.
@@ -113,17 +138,8 @@
params.width = maxWidth;
}
}
- labelView.setText(labelText);
- actionView.setText(actionText);
- actionView.setOnClickListener(v -> {
- if (onActionClicked != null) {
- onActionClicked.run();
- }
- snackbar.mOnDismissed = null;
- snackbar.close(true);
- });
- snackbar.mOnDismissed = onDismissed;
+ snackbar.mOnDismissed = onDismissed;
snackbar.setAlpha(0);
snackbar.setScaleX(0.8f);
snackbar.setScaleY(0.8f);
diff --git a/tests/Android.bp b/tests/Android.bp
index c329ece..3670c37 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -23,7 +23,7 @@
// Source code used for test
filegroup {
name: "launcher-tests-src",
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: ["src/**/*.java"],
}
// Source code used for oop test helpers
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
new file mode 100644
index 0000000..8a4590a
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -0,0 +1,201 @@
+package com.android.launcher3.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.util.Pair;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.LauncherModelHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link AddWorkspaceItemsTask}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AddWorkspaceItemsTaskTest {
+
+ private final ComponentName mComponent1 = new ComponentName("a", "b");
+ private final ComponentName mComponent2 = new ComponentName("b", "b");
+
+ private Context mTargetContext;
+ private InvariantDeviceProfile mIdp;
+ private LauncherAppState mAppState;
+ private LauncherModelHelper mModelHelper;
+
+ private IntArray mExistingScreens;
+ private IntArray mNewScreens;
+ private IntSparseArrayMap<GridOccupancy> mScreenOccupancy;
+
+ @Before
+ public void setup() {
+ mModelHelper = new LauncherModelHelper();
+ mTargetContext = mModelHelper.sandboxContext;
+ mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
+ mIdp.numColumns = mIdp.numRows = 5;
+ mAppState = LauncherAppState.getInstance(mTargetContext);
+
+ mExistingScreens = new IntArray();
+ mScreenOccupancy = new IntSparseArrayMap<>();
+ mNewScreens = new IntArray();
+ }
+
+ @After
+ public void tearDown() {
+ mModelHelper.destroy();
+ }
+
+ private AddWorkspaceItemsTask newTask(ItemInfo... items) {
+ List<Pair<ItemInfo, Object>> list = new ArrayList<>();
+ for (ItemInfo item : items) {
+ list.add(Pair.create(item, null));
+ }
+ return new AddWorkspaceItemsTask(list);
+ }
+
+ @Test
+ public void testFindSpaceForItem_prefers_second() throws Exception {
+ // First screen has only one hole of size 1
+ int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+ // Second screen has 2 holes of sizes 3x2 and 2x3
+ setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
+
+ int[] spaceFound = newTask().findSpaceForItem(
+ mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1);
+ assertEquals(1, spaceFound[0]);
+ assertTrue(mScreenOccupancy.get(spaceFound[0])
+ .isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
+
+ // Find a larger space
+ spaceFound = newTask().findSpaceForItem(
+ mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 2, 3);
+ assertEquals(2, spaceFound[0]);
+ assertTrue(mScreenOccupancy.get(spaceFound[0])
+ .isRegionVacant(spaceFound[1], spaceFound[2], 2, 3));
+ }
+
+ @Test
+ public void testFindSpaceForItem_adds_new_screen() throws Exception {
+ // First screen has 2 holes of sizes 3x2 and 2x3
+ setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
+
+ IntArray oldScreens = mExistingScreens.clone();
+ int[] spaceFound = newTask().findSpaceForItem(
+ mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 3, 3);
+ assertFalse(oldScreens.contains(spaceFound[0]));
+ assertTrue(mNewScreens.contains(spaceFound[0]));
+ }
+
+ @Test
+ public void testAddItem_existing_item_ignored() throws Exception {
+ WorkspaceItemInfo info = new WorkspaceItemInfo();
+ info.intent = new Intent().setComponent(mComponent1);
+
+ // Setup a screen with a hole
+ setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+ // Nothing was added
+ assertTrue(mModelHelper.executeTaskForTest(newTask(info)).isEmpty());
+ }
+
+ @Test
+ public void testAddItem_some_items_added() throws Exception {
+ Callbacks callbacks = mock(Callbacks.class);
+ Executors.MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(callbacks)).get();
+
+ WorkspaceItemInfo info = new WorkspaceItemInfo();
+ info.intent = new Intent().setComponent(mComponent1);
+
+ WorkspaceItemInfo info2 = new WorkspaceItemInfo();
+ info2.intent = new Intent().setComponent(mComponent2);
+
+ // Setup a screen with a hole
+ setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+ mModelHelper.executeTaskForTest(newTask(info, info2)).get(0).run();
+ ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
+ ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class);
+
+ // only info2 should be added because info was already added to the workspace
+ // in setupWorkspaceWithHoles()
+ verify(callbacks).bindAppsAdded(any(IntArray.class), notAnimated.capture(),
+ animated.capture());
+ assertTrue(notAnimated.getValue().isEmpty());
+
+ assertEquals(1, animated.getValue().size());
+ assertTrue(animated.getValue().contains(info2));
+ }
+
+ private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) throws Exception {
+ return mModelHelper.executeSimpleTask(
+ model -> writeWorkspaceWithHoles(model, startId, screenId, holes));
+ }
+
+ private int writeWorkspaceWithHoles(
+ BgDataModel bgDataModel, int startId, int screenId, Rect... holes) {
+ GridOccupancy occupancy = new GridOccupancy(mIdp.numColumns, mIdp.numRows);
+ occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true);
+ for (Rect r : holes) {
+ occupancy.markCells(r, false);
+ }
+
+ mExistingScreens.add(screenId);
+ mScreenOccupancy.append(screenId, occupancy);
+
+ for (int x = 0; x < mIdp.numColumns; x++) {
+ for (int y = 0; y < mIdp.numRows; y++) {
+ if (!occupancy.cells[x][y]) {
+ continue;
+ }
+
+ WorkspaceItemInfo info = new WorkspaceItemInfo();
+ info.intent = new Intent().setComponent(mComponent1);
+ info.id = startId++;
+ info.screenId = screenId;
+ info.cellX = x;
+ info.cellY = y;
+ info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ bgDataModel.addItem(mTargetContext, info, false);
+
+ ContentWriter writer = new ContentWriter(mTargetContext);
+ info.writeToValues(writer);
+ writer.put(Favorites._ID, info.id);
+ mTargetContext.getContentResolver().insert(Favorites.CONTENT_URI,
+ writer.getValues(mTargetContext));
+ }
+ }
+ return startId;
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
deleted file mode 100644
index e315658..0000000
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * 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.model
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.graphics.Rect
-import android.util.Pair
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherAppState
-import com.android.launcher3.LauncherSettings
-import com.android.launcher3.model.data.ItemInfo
-import com.android.launcher3.model.data.WorkspaceItemInfo
-import com.android.launcher3.util.*
-import com.android.launcher3.util.IntArray
-import org.junit.After
-import org.junit.Assert.*
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.*
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.verify
-import kotlin.collections.ArrayList
-
-/**
- * Tests for [AddWorkspaceItemsTask]
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AddWorkspaceItemsTaskTest {
-
- @Captor
- private lateinit var animatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
-
- @Captor
- private lateinit var notAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
-
- @Mock
- private lateinit var dataModelCallbacks: BgDataModel.Callbacks
-
- private lateinit var mTargetContext: Context
- private lateinit var mIdp: InvariantDeviceProfile
- private lateinit var mAppState: LauncherAppState
- private lateinit var mModelHelper: LauncherModelHelper
- private lateinit var mExistingScreens: IntArray
- private lateinit var mNewScreens: IntArray
- private lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy>
-
- private val emptyScreenHoles = listOf(Rect(0, 0, 5, 5))
- private val fullScreenHoles = emptyList<Rect>()
-
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- mModelHelper = LauncherModelHelper()
- mTargetContext = mModelHelper.sandboxContext
- mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext]
- mIdp.numRows = 5
- mIdp.numColumns = mIdp.numRows
- mAppState = LauncherAppState.getInstance(mTargetContext)
- mExistingScreens = IntArray()
- mScreenOccupancy = IntSparseArrayMap()
- mNewScreens = IntArray()
- Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(dataModelCallbacks) }.get()
- }
-
- @After
- fun tearDown() {
- mModelHelper.destroy()
- }
-
- @Test
- fun justEnoughSpaceOnFirstScreen_whenFindSpaceForItem_thenReturnFirstScreenId() {
- setupWorkspacesWithHoles(
- screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
- // 2 holes of sizes 3x2 and 2x3
- screen2 = listOf(Rect(2, 0, 5, 2), Rect(0, 2, 2, 5)),
- )
-
- val spaceFound = newTask().findSpaceForItem(
- mAppState, mModelHelper.bgDataModel, mExistingScreens, mNewScreens, 1, 1)
- assertEquals(1, spaceFound[0])
- assertTrue(mScreenOccupancy[spaceFound[0]]
- .isRegionVacant(spaceFound[1], spaceFound[2], 1, 1))
- }
-
- @Test
- fun notEnoughSpaceOnFirstScreen_whenFindSpaceForItem_thenReturnSecondScreenId() {
- setupWorkspacesWithHoles(
- screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
- // 2 holes of sizes 3x2 and 2x3
- screen2 = listOf(Rect(2, 0, 5, 2), Rect(0, 2, 2, 5)),
- )
-
- // Find a larger space
- val spaceFound = newTask().findSpaceForItem(
- mAppState, mModelHelper.bgDataModel, mExistingScreens, mNewScreens, 2, 3)
- assertEquals(2, spaceFound[0])
- assertTrue(mScreenOccupancy[spaceFound[0]]
- .isRegionVacant(spaceFound[1], spaceFound[2], 2, 3))
- }
-
- @Test
- fun notEnoughSpaceOnExistingScreens_whenFindSpaceForItem_thenReturnNewScreenId() {
- setupWorkspacesWithHoles(
- // 2 holes of sizes 3x2 and 2x3
- screen1 = listOf(Rect(2, 0, 5, 2), Rect(0, 2, 2, 5)),
- // 2 holes of sizes 1x2 and 2x2
- screen2 = listOf(Rect(1, 0, 2, 2), Rect(3, 2, 5, 4)),
- )
-
- val oldScreens = mExistingScreens.clone()
- val spaceFound = newTask().findSpaceForItem(
- mAppState, mModelHelper.bgDataModel, mExistingScreens, mNewScreens, 3, 3)
- assertFalse(oldScreens.contains(spaceFound[0]))
- assertTrue(mNewScreens.contains(spaceFound[0]))
- }
-
- @Test
- fun enoughSpaceOnFirstScreen_whenTaskRuns_thenAddItemToFirstScreen() {
- val workspaceHoles = createWorkspaceHoles(
- screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 space
- screen2 = listOf(Rect(2, 0, 5, 2)), // 3x2 space
- )
- val addedItems = testAddItems(workspaceHoles, getNewItem())
- assertEquals(1, addedItems.size)
- assertEquals(1, addedItems.first().itemInfo.screenId)
- }
-
- @Test
- fun firstPageIsFull_whenTaskRuns_thenAddItemToSecondScreen() {
- val workspaceHoles = createWorkspaceHoles(
- screen1 = fullScreenHoles,
- )
- val addedItems = testAddItems(workspaceHoles, getNewItem())
- assertEquals(1, addedItems.size)
- assertEquals(2, addedItems.first().itemInfo.screenId)
- }
-
- @Test
- fun firstScreenIsEmptyButSecondIsNotEmpty_whenTaskRuns_thenAddItemToSecondScreen() {
- val workspaceHoles = createWorkspaceHoles(
- screen1 = emptyScreenHoles,
- screen2 = listOf(Rect(2, 0, 5, 2)), // 3x2 space
- )
- val addedItems = testAddItems(workspaceHoles, getNewItem())
- assertEquals(1, addedItems.size)
- assertEquals(2, addedItems.first().itemInfo.screenId)
- }
-
- @Test
- fun twoEmptyMiddleScreens_whenTaskRuns_thenAddItemToThirdScreen() {
- val workspaceHoles = createWorkspaceHoles(
- screen1 = emptyScreenHoles,
- screen2 = emptyScreenHoles,
- screen3 = listOf(Rect(1, 1, 4, 4)), // 3x3 space
- )
- val addedItems = testAddItems(workspaceHoles, getNewItem())
- assertEquals(1, addedItems.size)
- assertEquals(3, addedItems.first().itemInfo.screenId)
- }
-
- @Test
- fun allPagesAreFull_whenTaskRuns_thenAddItemToNewScreen() {
- val workspaceHoles = createWorkspaceHoles(
- screen1 = fullScreenHoles,
- screen2 = fullScreenHoles,
- )
- val addedItems = testAddItems(workspaceHoles, getNewItem())
- assertEquals(1, addedItems.size)
- assertEquals(3, addedItems.first().itemInfo.screenId)
- }
-
- @Test
- fun firstTwoPagesAreFull_and_ThirdPageIsEmpty_whenTaskRuns_thenAddItemToThirdPage() {
- val workspaceHoles = createWorkspaceHoles(
- screen1 = fullScreenHoles,
- screen2 = fullScreenHoles,
- screen3 = emptyScreenHoles
- )
- val addedItems = testAddItems(workspaceHoles, getNewItem())
- assertEquals(1, addedItems.size)
- assertEquals(3, addedItems.first().itemInfo.screenId)
- }
-
- @Test
- fun itemIsAlreadyAdded_whenTaskRun_thenIgnoreItem() {
- val task = newTask(getExistingItem())
- setupWorkspacesWithHoles(
- screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
- )
-
- // Nothing was added
- assertTrue(mModelHelper.executeTaskForTest(task).isEmpty())
- }
-
- @Test
- fun newAndExistingItems_whenTaskRun_thenAddOnlyTheNewOne() {
- val newItem = getNewItem()
- val workspaceHoles = createWorkspaceHoles(
- screen1 = listOf(Rect(2, 2, 3, 3)), // 1x1 hole
- )
- val addedItems = testAddItems(workspaceHoles, getExistingItem(), newItem)
- assertEquals(1, addedItems.size)
- val addedItem = addedItems.first()
- assert(addedItem.isAnimated)
- val addedItemInfo = addedItem.itemInfo
- assertEquals(1, addedItemInfo.screenId)
- assertEquals(newItem, addedItemInfo)
- }
-
- private fun testAddItems(
- workspaceHoles: List<List<Rect>>,
- vararg itemsToAdd: WorkspaceItemInfo
- ): List<AddedItem> {
- setupWorkspaces(workspaceHoles)
- mModelHelper.executeTaskForTest(newTask(*itemsToAdd))[0].run()
-
- verify(dataModelCallbacks).bindAppsAdded(any(),
- notAnimatedItemArgumentCaptor.capture(), animatedItemArgumentCaptor.capture())
-
- val addedItems = mutableListOf<AddedItem>()
- addedItems.addAll(animatedItemArgumentCaptor.value.map { AddedItem(it, true) })
- addedItems.addAll(notAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
- return addedItems
- }
-
- private fun setupWorkspaces(workspaceHoles: List<List<Rect>>) {
- var nextItemId = 1
- var screenId = 1
- workspaceHoles.forEach { holes ->
- nextItemId = setupWorkspace(nextItemId, screenId++, *holes.toTypedArray())
- }
- }
-
- private fun setupWorkspace(startId: Int, screenId: Int, vararg holes: Rect): Int {
- return mModelHelper.executeSimpleTask { dataModel ->
- writeWorkspaceWithHoles(dataModel, startId, screenId, *holes)
- }
- }
-
- private fun writeWorkspaceWithHoles(
- bgDataModel: BgDataModel,
- itemStartId: Int,
- screenId: Int,
- vararg holes: Rect,
- ): Int {
- var itemId = itemStartId
- val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
- occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
- holes.forEach { holeRect ->
- occupancy.markCells(holeRect, false)
- }
- mExistingScreens.add(screenId)
- mScreenOccupancy.append(screenId, occupancy)
- for (x in 0 until mIdp.numColumns) {
- for (y in 0 until mIdp.numRows) {
- if (!occupancy.cells[x][y]) {
- continue
- }
- val info = getExistingItem()
- info.id = itemId++
- info.screenId = screenId
- info.cellX = x
- info.cellY = y
- info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP
- bgDataModel.addItem(mTargetContext, info, false)
- val writer = ContentWriter(mTargetContext)
- info.writeToValues(writer)
- writer.put(LauncherSettings.Favorites._ID, info.id)
- mTargetContext.contentResolver.insert(LauncherSettings.Favorites.CONTENT_URI,
- writer.getValues(mTargetContext))
- }
- }
- return itemId
- }
-
- private fun setupWorkspacesWithHoles(
- screen1: List<Rect>? = null,
- screen2: List<Rect>? = null,
- screen3: List<Rect>? = null,
- ) = createWorkspaceHoles(screen1, screen2, screen3)
- .let(this::setupWorkspaces)
-
- private fun createWorkspaceHoles(
- screen1: List<Rect>? = null,
- screen2: List<Rect>? = null,
- screen3: List<Rect>? = null,
- ): List<List<Rect>> = listOfNotNull(screen1, screen2, screen3)
-
- private fun newTask(vararg items: ItemInfo): AddWorkspaceItemsTask =
- items.map { Pair.create(it, Any()) }
- .toMutableList()
- .let(::AddWorkspaceItemsTask)
-
- private fun getExistingItem() = WorkspaceItemInfo()
- .apply { intent = Intent().setComponent(ComponentName("a", "b")) }
-
- private fun getNewItem() = WorkspaceItemInfo()
- .apply { intent = Intent().setComponent(ComponentName("b", "b")) }
-}
-
-private data class AddedItem(
- val itemInfo: ItemInfo,
- val isAnimated: Boolean
-)
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
index 472e1a1..260f556 100644
--- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -58,6 +59,7 @@
private TouchEventGenerator mGenerator;
private SingleAxisSwipeDetector mDetector;
private int mTouchSlop;
+ Context mContext;
@Mock
private SingleAxisSwipeDetector.Listener mMockListener;
@@ -69,12 +71,13 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mGenerator = new TouchEventGenerator((ev) -> mDetector.onTouchEvent(ev));
- ViewConfiguration orgConfig = ViewConfiguration
- .get(InstrumentationRegistry.getTargetContext());
+ mContext = InstrumentationRegistry.getTargetContext();
+ ViewConfiguration orgConfig = ViewConfiguration.get(mContext);
doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
.getScaledMaximumFlingVelocity();
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, VERTICAL, false);
mDetector.setDetectableScrollConditions(DIRECTION_BOTH, false);
mTouchSlop = orgConfig.getScaledTouchSlop();
doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
@@ -84,7 +87,8 @@
@Test
public void testDragStart_verticalPositive() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, VERTICAL, false);
mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 - mTouchSlop);
@@ -94,7 +98,8 @@
@Test
public void testDragStart_verticalNegative() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, VERTICAL, false);
mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
@@ -112,7 +117,8 @@
@Test
public void testDragStart_horizontalPositive() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, HORIZONTAL, false);
mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
@@ -123,7 +129,8 @@
@Test
public void testDragStart_horizontalNegative() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, HORIZONTAL, false);
mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
@@ -134,7 +141,8 @@
@Test
public void testDragStart_horizontalRtlPositive() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, HORIZONTAL, true);
mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
mGenerator.put(0, 100, 100);
@@ -145,7 +153,8 @@
@Test
public void testDragStart_horizontalRtlNegative() {
- mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
+ mDetector = new SingleAxisSwipeDetector(mContext,
+ mMockConfig, mMockListener, HORIZONTAL, true);
mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
mGenerator.put(0, 100, 100);
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index d5479fb..3eb8cf1 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -212,7 +212,7 @@
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get overview actions")) {
verifyActiveContainer();
- UiObject2 overviewActions = mLauncher.waitForLauncherObject("action_buttons");
+ UiObject2 overviewActions = mLauncher.waitForOverviewObject("action_buttons");
return new OverviewActions(overviewActions, mLauncher);
}
}
@@ -224,19 +224,16 @@
return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
}
- /* TODO(b/197630182): Once b/188790554 is fixed, remove instanceof check. Currently, when
- swiping from app to overview in Fallback Recents, taskbar remains and no action buttons
- are visible, so we are only testing Overview for now, not BaseOverview. */
private void verifyActionsViewVisibility() {
- if (!(this instanceof Overview) || !hasTasks()) {
+ if (!hasTasks()) {
return;
}
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to assert overview actions view visibility")) {
if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) {
- mLauncher.waitUntilLauncherObjectGone("action_buttons");
+ mLauncher.waitUntilOverviewObjectGone("action_buttons");
} else {
- mLauncher.waitForLauncherObject("action_buttons");
+ mLauncher.waitForOverviewObject("action_buttons");
}
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3485dd1..91b1bc7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1029,6 +1029,10 @@
waitUntilGoneBySelector(getLauncherObjectSelector(resId));
}
+ void waitUntilOverviewObjectGone(String resId) {
+ waitUntilGoneBySelector(getOverviewObjectSelector(resId));
+ }
+
void waitUntilLauncherObjectGone(BySelector selector) {
waitUntilGoneBySelector(makeLauncherSelector(selector));
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 6e7264a..0bac2ca 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -77,7 +77,8 @@
mLauncher.scroll(
widgetsContainer,
Direction.UP,
- new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0),
+ new Rect(0, 0, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1,
+ 0),
FLING_STEPS, false);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
verifyActiveContainer();