Merge "[Dev options][DW flag] Update all usage of enable_desktop_windowing_mode flag to use DesktopModeFlags" into main
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
index 8e3d44f..31aca03 100644
--- a/quickstep/src/com/android/quickstep/util/AnimUtils.java
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -17,18 +17,28 @@
package com.android.quickstep.util;
import static com.android.app.animation.Interpolators.clampToProgress;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.animation.AnimatorSet;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.animation.Interpolator;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.views.RecentsViewContainer;
/**
* Utility class containing methods to help manage animations, interpolators, and timings.
*/
public class AnimUtils {
+ private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
+
/**
* Fetches device-specific timings for the Overview > Split animation
* (splitscreen initiated from Overview).
@@ -59,6 +69,33 @@
}
/**
+ * Synchronizes the timing for the split dismiss animation to the current transition to
+ * NORMAL (launcher home/workspace)
+ */
+ public static void goToNormalStateWithSplitDismissal(@NonNull StateManager stateManager,
+ @NonNull RecentsViewContainer container,
+ @NonNull StatsLogManager.LauncherEvent exitReason,
+ @NonNull SplitAnimationController animationController) {
+ StateAnimationConfig config = new StateAnimationConfig();
+ BaseState startState = stateManager.getState();
+ long duration = startState.getTransitionDuration(container.asContext(),
+ false /*isToState*/);
+ if (duration == 0) {
+ // Case where we're in contextual on workspace (NORMAL), which by default has 0
+ // transition duration
+ duration = DURATION_DEFAULT_SPLIT_DISMISS;
+ }
+ config.duration = duration;
+ AnimatorSet stateAnim = stateManager.createAtomicAnimation(
+ startState, NORMAL, config);
+ AnimatorSet dismissAnim = animationController
+ .createPlaceholderDismissAnim(container, exitReason, duration);
+ stateAnim.play(dismissAnim);
+ stateManager.setCurrentAnimation(stateAnim, NORMAL);
+ stateAnim.start();
+ }
+
+ /**
* Returns a IRemoteCallback which completes the provided list as a result
*/
public static IRemoteCallback completeRunnableListCallback(RunnableList list) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index e48a7c6..b2d2302 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -54,6 +54,7 @@
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.systemui.shared.recents.model.Task;
@@ -91,10 +92,12 @@
protected void handleStartHome(boolean animated) {
StateManager stateManager = getStateManager();
animated &= stateManager.shouldAnimateStateChange();
- stateManager.goToState(NORMAL, animated);
- if (FeatureFlags.enableSplitContextually()) {
- mSplitSelectStateController.getSplitAnimationController()
- .playPlaceholderDismissAnim(mContainer, LAUNCHER_SPLIT_SELECTION_EXIT_HOME);
+ if (mSplitSelectStateController.isSplitSelectActive()) {
+ AnimUtils.goToNormalStateWithSplitDismissal(stateManager, mContainer,
+ LAUNCHER_SPLIT_SELECTION_EXIT_HOME,
+ mSplitSelectStateController.getSplitAnimationController());
+ } else {
+ stateManager.goToState(NORMAL, animated);
}
AbstractFloatingView.closeAllOpenViews(mContainer, animated);
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 3d994e8..f6393e4 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -16,13 +16,11 @@
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON;
import static com.android.settingslib.widget.theme.R.dimen.settingslib_preferred_minimum_touch_target;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -42,9 +40,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitSelectStateController;
/**
@@ -57,7 +54,6 @@
public class SplitInstructionsView extends LinearLayout {
private static final int BOUNCE_DURATION = 250;
private static final float BOUNCE_HEIGHT = 20;
- private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
private final RecentsViewContainer mContainer;
public boolean mIsCurrentlyAnimating = false;
@@ -165,25 +161,11 @@
private void exitSplitSelection() {
RecentsView recentsView = mContainer.getOverviewPanel();
SplitSelectStateController splitSelectController = recentsView.getSplitSelectController();
-
StateManager stateManager = recentsView.getStateManager();
- BaseState startState = stateManager.getState();
- long duration = startState.getTransitionDuration(mContainer.asContext(), false);
- if (duration == 0) {
- // Case where we're in contextual on workspace (NORMAL), which by default has 0
- // transition duration
- duration = DURATION_DEFAULT_SPLIT_DISMISS;
- }
- StateAnimationConfig config = new StateAnimationConfig();
- config.duration = duration;
- AnimatorSet stateAnim = stateManager.createAtomicAnimation(
- startState, NORMAL, config);
- AnimatorSet dismissAnim = splitSelectController.getSplitAnimationController()
- .createPlaceholderDismissAnim(mContainer,
- LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON, duration);
- stateAnim.play(dismissAnim);
- stateManager.setCurrentAnimation(stateAnim, NORMAL);
- stateAnim.start();
+
+ AnimUtils.goToNormalStateWithSplitDismissal(stateManager, mContainer,
+ LAUNCHER_SPLIT_SELECTION_EXIT_CANCEL_BUTTON,
+ splitSelectController.getSplitAnimationController());
}
void ensureProperRotation() {
diff --git a/res/drawable/rounded_action_button.xml b/res/drawable/rounded_action_button.xml
index ddd3042..ebfa996 100644
--- a/res/drawable/rounded_action_button.xml
+++ b/res/drawable/rounded_action_button.xml
@@ -22,8 +22,5 @@
<stroke
android:width="1dp"
android:color="?attr/materialColorSurfaceContainerLow" />
- <padding
- android:left="@dimen/rounded_button_padding"
- android:right="@dimen/rounded_button_padding" />
</shape>
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
index c581ae3..a45d585 100644
--- a/res/layout/work_apps_edu.xml
+++ b/res/layout/work_apps_edu.xml
@@ -44,8 +44,7 @@
<FrameLayout
android:layout_width="@dimen/rounded_button_width"
android:layout_height="@dimen/rounded_button_width"
- android:background="@drawable/rounded_action_button"
- android:padding="@dimen/rounded_button_padding">
+ android:background="@drawable/rounded_action_button">
<ImageButton
android:id="@+id/action_btn"
android:layout_width="@dimen/x_icon_size"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 05724e2..af91b5a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -172,8 +172,6 @@
<dimen name="padded_rounded_button_height">48dp</dimen>
<dimen name="rounded_button_height">48dp</dimen>
<dimen name="rounded_button_radius">200dp</dimen>
- <dimen name="rounded_button_padding">8dp</dimen>
-
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index a448228..53fed20 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -746,7 +746,8 @@
* | +--+ |
* | |
* +----------------+
- * This would be case delta % 4 == 2:
+ * This would be case delta % 4 == 2: // This is case was reverted to previous behaviour which
+ * doesn't match the illustration due to b/353965234
* +-------------+
* | |
* | |
@@ -768,7 +769,6 @@
int delta) {
int rdelta = ((delta % 4) + 4) % 4;
int origLeft = inOutBounds.left;
- int origTop = inOutBounds.top;
switch (rdelta) {
case 0:
return;
@@ -780,8 +780,6 @@
return;
case 2:
inOutBounds.left = parentWidth - inOutBounds.right;
- inOutBounds.top = parentHeight - inOutBounds.bottom;
- inOutBounds.bottom = parentHeight - origTop;
inOutBounds.right = parentWidth - origLeft;
return;
case 3:
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index db693f0..8b1f42b 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -157,7 +157,8 @@
isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
isEventOverAccessibleDropTargetBar(ev);
if (!isOverFolderOrSearchBar) {
- sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
+ sendTapOutsideFolderAccessibilityEvent(
+ currentFolder.getIsEditingName());
mHoverPointClosesFolder = true;
return true;
}
@@ -167,7 +168,8 @@
isOverFolderOrSearchBar = isEventOverView(topView, ev) ||
isEventOverAccessibleDropTargetBar(ev);
if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
- sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
+ sendTapOutsideFolderAccessibilityEvent(
+ currentFolder.getIsEditingName());
mHoverPointClosesFolder = true;
return true;
} else if (!isOverFolderOrSearchBar) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 175ab4e..3edf1f2 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -136,7 +136,8 @@
* We avoid measuring {@link #mContent} with a 0 width or height, as this
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
*/
- private static final int MIN_CONTENT_DIMEN = 5;
+ @VisibleForTesting
+ static final int MIN_CONTENT_DIMEN = 5;
public static final int STATE_CLOSED = 0;
public static final int STATE_ANIMATING = 1;
@@ -144,7 +145,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_CLOSED, STATE_ANIMATING, STATE_OPEN})
- public @interface FolderState {}
+ public @interface FolderState {
+ }
/**
* Time for which the scroll hint is shown before automatically changing page.
@@ -165,7 +167,7 @@
private static final int FOLDER_COLOR_ANIMATION_DURATION = 200;
private static final int REORDER_DELAY = 250;
- private static final int ON_EXIT_CLOSE_DELAY = 400;
+ static final int ON_EXIT_CLOSE_DELAY = 400;
private static final Rect sTempRect = new Rect();
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
@@ -185,10 +187,10 @@
|| itemType == ITEM_TYPE_APP_PAIR;
}
- private final Alarm mReorderAlarm = new Alarm(Looper.getMainLooper());
- private final Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper());
- private final Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper());
- final Alarm mScrollPauseAlarm = new Alarm(Looper.getMainLooper());
+ private Alarm mReorderAlarm = new Alarm(Looper.getMainLooper());
+ private Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper());
+ private Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper());
+ private Alarm mScrollPauseAlarm = new Alarm(Looper.getMainLooper());
final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
@@ -198,7 +200,7 @@
// Folder can be displayed in Launcher's activity or a separate window (e.g. Taskbar).
// Anything specific to Launcher should use mLauncherDelegate, otherwise should
// use mActivityContext.
- protected final LauncherDelegate mLauncherDelegate;
+ protected LauncherDelegate mLauncherDelegate;
protected final ActivityContext mActivityContext;
protected DragController mDragController;
@@ -211,7 +213,7 @@
@Thunk
FolderPagedView mContent;
- public FolderNameEditText mFolderName;
+ private FolderNameEditText mFolderName;
private PageIndicatorDots mPageIndicator;
protected View mFooter;
@@ -235,10 +237,10 @@
private OnFolderStateChangedListener mPriorityOnFolderStateChangedListener;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mRearrangeOnClose = false;
- boolean mItemsInvalidated = false;
+ private boolean mItemsInvalidated = false;
private View mCurrentDragView;
private boolean mIsExternalDrag;
- private boolean mDragInProgress = false;
+ private boolean mIsDragInProgress = false;
private boolean mDeleteFolderOnDropCompleted = false;
private boolean mSuppressFolderDeletion = false;
private boolean mItemAddedBackToSelfViaIcon = false;
@@ -251,7 +253,7 @@
private int mScrollAreaOffset;
@Thunk
- int mScrollHintDir = SCROLL_NONE;
+ private int mScrollHintDir = SCROLL_NONE;
@Thunk
int mCurrentScrollDir = SCROLL_NONE;
@@ -316,9 +318,9 @@
| InputType.TYPE_TEXT_FLAG_CAP_WORDS);
mFolderName.forceDisableSuggestions(true);
mFolderName.setPadding(mFolderName.getPaddingLeft(),
- (mFooterHeight - mFolderName.getLineHeight()) / 2,
+ (getFooterHeight() - mFolderName.getLineHeight()) / 2,
mFolderName.getPaddingRight(),
- (mFooterHeight - mFolderName.getLineHeight()) / 2);
+ (getFooterHeight() - mFolderName.getLineHeight()) / 2);
mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
@@ -326,42 +328,54 @@
public boolean onLongClick(View v) {
// Return if global dragging is not enabled
- if (!mLauncherDelegate.isDraggingEnabled()) return true;
+ if (!getIsLauncherDraggingEnabled()) return true;
return startDrag(v, new DragOptions());
}
+ @VisibleForTesting
+ boolean getIsLauncherDraggingEnabled() {
+ return mLauncherDelegate.isDraggingEnabled();
+ }
+
public boolean startDrag(View v, DragOptions options) {
Object tag = v.getTag();
if (tag instanceof ItemInfo item) {
mEmptyCellRank = item.rank;
mCurrentDragView = v;
- mDragController.addDragListener(this);
- if (options.isAccessibleDrag) {
- mDragController.addDragListener(new AccessibleDragListenerAdapter(
- mContent, FolderAccessibilityHelper::new) {
- @Override
- protected void enableAccessibleDrag(boolean enable,
- @Nullable DragObject dragObject) {
- super.enableAccessibleDrag(enable, dragObject);
- mFooter.setImportantForAccessibility(enable
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- }
- });
- }
-
- mLauncherDelegate.beginDragShared(v, this, options);
+ addDragListener(options);
+ callBeginDragShared(v, options);
}
return true;
}
+ void callBeginDragShared(View v, DragOptions options) {
+ mLauncherDelegate.beginDragShared(v, this, options);
+ }
+
+ void addDragListener(DragOptions options) {
+ getDragController().addDragListener(this);
+ if (!options.isAccessibleDrag) {
+ return;
+ }
+ getDragController().addDragListener(new AccessibleDragListenerAdapter(
+ mContent, FolderAccessibilityHelper::new) {
+ @Override
+ protected void enableAccessibleDrag(boolean enable,
+ @Nullable DragObject dragObject) {
+ super.enableAccessibleDrag(enable, dragObject);
+ mFooter.setImportantForAccessibility(enable
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
+ });
+ }
+
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
if (dragObject.dragSource != this) {
return;
}
-
mContent.removeItem(mCurrentDragView);
mItemsInvalidated = true;
@@ -370,29 +384,23 @@
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
mInfo.remove(dragObject.dragInfo, true);
}
- mDragInProgress = true;
+ mIsDragInProgress = true;
mItemAddedBackToSelfViaIcon = false;
}
@Override
public void onDragEnd() {
- if (mIsExternalDrag && mDragInProgress) {
+ if (mIsExternalDrag && mIsDragInProgress) {
completeDragExit();
}
- mDragInProgress = false;
- mDragController.removeDragListener(this);
- }
-
- public boolean isEditingName() {
- return mIsEditingName;
+ mIsDragInProgress = false;
+ getDragController().removeDragListener(this);
}
public void startEditingFolderName() {
- post(() -> {
- showLabelSuggestions();
- mFolderName.setHint("");
- mIsEditingName = true;
- });
+ showLabelSuggestions();
+ mFolderName.setHint("");
+ mIsEditingName = true;
}
@Override
@@ -460,7 +468,11 @@
return mFolderIcon;
}
- public void setDragController(DragController dragController) {
+ DragController getDragController() {
+ return mDragController;
+ }
+
+ void setDragController(DragController dragController) {
mDragController = dragController;
}
@@ -541,7 +553,7 @@
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
* rest of the suggestions to InputMethodManager.
*/
- private void showLabelSuggestions() {
+ void showLabelSuggestions() {
if (mInfo.suggestedFolderNames == null) {
return;
}
@@ -635,11 +647,11 @@
*/
public void beginExternalDrag() {
mIsExternalDrag = true;
- mDragInProgress = true;
+ mIsDragInProgress = true;
// Since this folder opened by another controller, it might not get onDrop or
// onDropComplete. Perform cleanup once drag-n-drop ends.
- mDragController.addDragListener(this);
+ getDragController().addDragListener(this);
ArrayList<ItemInfo> items = new ArrayList<>(mInfo.getContents());
mEmptyCellRank = items.size();
@@ -663,16 +675,12 @@
* is played.
*/
private void animateOpen(List<ItemInfo> items, int pageNo) {
- if (items == null || items.size() <= 1) {
- Log.d(TAG, "Couldn't animate folder open because items is: " + items);
+ if (!shouldAnimateOpen(items)) {
return;
}
Folder openFolder = getOpen(mActivityContext);
- if (openFolder != null && openFolder != this) {
- // Close any open folder before opening a folder.
- openFolder.close(true);
- }
+ closeOpenFolder(openFolder);
mContent.bindItems(items);
centerAboutIcon();
@@ -686,7 +694,7 @@
// There was a one-off crash where the folder had a parent already.
if (getParent() == null) {
dragLayer.addView(this);
- mDragController.addDropTarget(this);
+ getDragController().addDropTarget(this);
} else {
if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
@@ -735,7 +743,7 @@
// Do not update the flag if we are in drag mode. The flag will be updated, when we
// actually drop the icon.
- final boolean updateAnimationFlag = !mDragInProgress;
+ final boolean updateAnimationFlag = !mIsDragInProgress;
anim.addListener(new AnimatorListenerAdapter() {
@SuppressLint("InlinedApi")
@@ -769,12 +777,36 @@
anim.start();
// Make sure the folder picks up the last drag move even if the finger doesn't move.
- if (mDragController.isDragging()) {
- mDragController.forceTouchMove();
+ if (getDragController().isDragging()) {
+ getDragController().forceTouchMove();
}
mContent.verifyVisibleHighResIcons(mContent.getNextPage());
}
+ /**
+ * Determines whether we should animate the folder opening.
+ */
+ boolean shouldAnimateOpen(List<ItemInfo> items) {
+ if (items == null || items.size() <= 1) {
+ Log.d(TAG, "Couldn't animate folder open because items is: " + items);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * If there's a folder already open, we want to close it before opening another one.
+ */
+ @VisibleForTesting
+ boolean closeOpenFolder(Folder openFolder) {
+ if (openFolder != null && openFolder != this) {
+ // Close any open folder before opening a folder.
+ openFolder.close(true);
+ return true;
+ }
+ return false;
+ }
+
@Override
protected boolean isOfType(int type) {
return (type & TYPE_FOLDER) != 0;
@@ -788,7 +820,7 @@
mCurrentAnimator.cancel();
}
- if (isEditingName()) {
+ if (mIsEditingName) {
mFolderName.dispatchBackKey();
}
@@ -872,7 +904,7 @@
if (parent != null) {
parent.removeView(this);
}
- mDragController.removeDropTarget(this);
+ getDragController().removeDropTarget(this);
clearFocus();
if (mFolderIcon != null) {
mFolderIcon.setVisibility(View.VISIBLE);
@@ -893,12 +925,12 @@
mRearrangeOnClose = false;
}
if (getItemCount() <= 1) {
- if (!mDragInProgress && !mSuppressFolderDeletion) {
+ if (!mIsDragInProgress && !mSuppressFolderDeletion) {
replaceFolderWithFinalItem();
- } else if (mDragInProgress) {
+ } else if (mIsDragInProgress) {
mDeleteFolderOnDropCompleted = true;
}
- } else if (!mDragInProgress) {
+ } else if (!mIsDragInProgress) {
mContent.unbindItems();
}
mSuppressFolderDeletion = false;
@@ -1018,7 +1050,8 @@
}
}
- private void clearDragInfo() {
+ @VisibleForTesting
+ void clearDragInfo() {
mCurrentDragView = null;
mIsExternalDrag = false;
}
@@ -1059,7 +1092,8 @@
if (getItemCount() <= 1) {
mDeleteFolderOnDropCompleted = true;
}
- if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
+ if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon
+ && target != this) {
replaceFolderWithFinalItem();
}
} else {
@@ -1090,7 +1124,7 @@
}
mDeleteFolderOnDropCompleted = false;
- mDragInProgress = false;
+ mIsDragInProgress = false;
mItemAddedBackToSelfViaIcon = false;
mCurrentDragView = null;
@@ -1133,7 +1167,7 @@
}
public void notifyDrop() {
- if (mDragInProgress) {
+ if (mIsDragInProgress) {
mItemAddedBackToSelfViaIcon = true;
}
}
@@ -1176,28 +1210,41 @@
}
protected int getContentAreaHeight() {
- DeviceProfile grid = mActivityContext.getDeviceProfile();
- int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y
- - mFooterHeight;
- int height = Math.min(maxContentAreaHeight,
+ int height = Math.min(getMaxContentAreaHeight(),
mContent.getDesiredHeight());
return Math.max(height, MIN_CONTENT_DIMEN);
}
- private int getContentAreaWidth() {
+ @VisibleForTesting
+ int getMaxContentAreaHeight() {
+ DeviceProfile grid = mActivityContext.getDeviceProfile();
+ return grid.availableHeightPx - grid.getTotalWorkspacePadding().y
+ - getFooterHeight();
+ }
+
+ @VisibleForTesting
+ int getContentAreaWidth() {
return Math.max(mContent.getDesiredWidth(), MIN_CONTENT_DIMEN);
}
- private int getFolderWidth() {
+ @VisibleForTesting
+ int getFolderWidth() {
return getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
}
- private int getFolderHeight() {
+ @VisibleForTesting
+ int getFolderHeight() {
return getFolderHeight(getContentAreaHeight());
}
- private int getFolderHeight(int contentAreaHeight) {
- return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mFooterHeight;
+ @VisibleForTesting
+ int getFolderHeight(int contentAreaHeight) {
+ return getPaddingTop() + getPaddingBottom() + contentAreaHeight + getFooterHeight();
+ }
+
+ @VisibleForTesting
+ int getFooterHeight() {
+ return mFooterHeight;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
@@ -1367,7 +1414,7 @@
}
// Clear the drag info, as it is no longer being dragged.
- mDragInProgress = false;
+ mIsDragInProgress = false;
if (mContent.getPageCount() > 1) {
// The animation has already been shown while opening the folder.
@@ -1436,7 +1483,8 @@
}
}
- private View getViewForInfo(final ItemInfo item) {
+ @VisibleForTesting
+ View getViewForInfo(final ItemInfo item) {
return mContent.iterateOverItems((info, view) -> info == item);
}
@@ -1494,7 +1542,7 @@
if (hasFocus) {
mFromLabelState = mInfo.getFromLabelState();
mFromTitle = mInfo.title;
- startEditingFolderName();
+ post(this::startEditingFolderName);
} else {
StatsLogger statsLogger = mStatsLogManager.logger()
.withItemInfo(mInfo)
@@ -1627,7 +1675,7 @@
/** Navigation bar back key or hardware input back key has been issued. */
@Override
public void onBackInvoked() {
- if (isEditingName()) {
+ if (mIsEditingName) {
mFolderName.dispatchBackKey();
} else {
super.onBackInvoked();
@@ -1639,7 +1687,7 @@
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
BaseDragLayer dl = (BaseDragLayer) getParent();
- if (isEditingName()) {
+ if (mIsEditingName) {
if (!dl.isEventOverView(mFolderName, ev)) {
mFolderName.dispatchBackKey();
return true;
@@ -1686,6 +1734,95 @@
return mContent;
}
+ @VisibleForTesting
+ void setItemAddedBackToSelfViaIcon(boolean value) {
+ mItemAddedBackToSelfViaIcon = value;
+ }
+
+ @VisibleForTesting
+ boolean getItemAddedBackToSelfViaIcon() {
+ return mItemAddedBackToSelfViaIcon;
+ }
+
+ @VisibleForTesting
+ void setIsDragInProgress(boolean value) {
+ mIsDragInProgress = value;
+ }
+
+ @VisibleForTesting
+ boolean getIsDragInProgress() {
+ return mIsDragInProgress;
+ }
+
+ @VisibleForTesting
+ View getCurrentDragView() {
+ return mCurrentDragView;
+ }
+
+ @VisibleForTesting
+ void setCurrentDragView(View view) {
+ mCurrentDragView = view;
+ }
+
+ @VisibleForTesting
+ boolean getItemsInvalidated() {
+ return mItemsInvalidated;
+ }
+
+ @VisibleForTesting
+ void setItemsInvalidated(boolean value) {
+ mItemsInvalidated = value;
+ }
+
+ @VisibleForTesting
+ boolean getIsExternalDrag() {
+ return mIsExternalDrag;
+ }
+
+ @VisibleForTesting
+ void setIsExternalDrag(boolean value) {
+ mIsExternalDrag = value;
+ }
+
+ public boolean getIsEditingName() {
+ return mIsEditingName;
+ }
+
+ @VisibleForTesting
+ void setIsEditingName(boolean value) {
+ mIsEditingName = value;
+ }
+
+ @VisibleForTesting
+ void setFolderName(FolderNameEditText value) {
+ mFolderName = value;
+ }
+
+ @VisibleForTesting
+ FolderNameEditText getFolderName() {
+ return mFolderName;
+ }
+
+ @VisibleForTesting
+ boolean getIsOpen() {
+ return mIsOpen;
+ }
+
+ @VisibleForTesting
+ void setIsOpen(boolean value) {
+ mIsOpen = value;
+ }
+
+ @VisibleForTesting
+ boolean getRearrangeOnClose() {
+ return mRearrangeOnClose;
+ }
+
+ @VisibleForTesting
+ void setRearrangeOnClose(boolean value) {
+ mRearrangeOnClose = value;
+ }
+
/** Returns the height of the current folder's bottom edge from the bottom of the screen. */
private int getHeightFromBottom() {
BaseDragLayer.LayoutParams layoutParams = (BaseDragLayer.LayoutParams) getLayoutParams();
@@ -1696,10 +1833,15 @@
}
@VisibleForTesting
- public boolean getDeleteFolderOnDropCompleted() {
+ boolean getDeleteFolderOnDropCompleted() {
return mDeleteFolderOnDropCompleted;
}
+ @VisibleForTesting
+ void setDeleteFolderOnDropCompleted(boolean value) {
+ mDeleteFolderOnDropCompleted = value;
+ }
+
/**
* Save this listener for the special case of when we update the state and concurrently
* add another listener to {@link #mOnFolderStateChangedListeners} to avoid a
@@ -1709,7 +1851,13 @@
mPriorityOnFolderStateChangedListener = listener;
}
- private void setState(@FolderState int newState) {
+ @VisibleForTesting
+ int getState() {
+ return mState;
+ }
+
+ @VisibleForTesting
+ void setState(@FolderState int newState) {
mState = newState;
if (mPriorityOnFolderStateChangedListener != null) {
mPriorityOnFolderStateChangedListener.onFolderStateChanged(mState);
@@ -1721,6 +1869,60 @@
}
}
+ @VisibleForTesting
+ Alarm getOnExitAlarm() {
+ return mOnExitAlarm;
+ }
+
+ @VisibleForTesting
+ void setOnExitAlarm(Alarm value) {
+ mOnExitAlarm = value;
+ }
+
+ @VisibleForTesting
+ Alarm getReorderAlarm() {
+ return mReorderAlarm;
+ }
+
+ @VisibleForTesting
+ void setReorderAlarm(Alarm value) {
+ mReorderAlarm = value;
+ }
+
+ @VisibleForTesting
+ Alarm getOnScrollHintAlarm() {
+ return mOnScrollHintAlarm;
+ }
+
+ @VisibleForTesting
+ void setOnScrollHintAlarm(Alarm value) {
+ mOnScrollHintAlarm = value;
+ }
+
+ @VisibleForTesting
+ Alarm getScrollPauseAlarm() {
+ return mScrollPauseAlarm;
+ }
+
+ @VisibleForTesting
+ void setScrollPauseAlarm(Alarm value) {
+ mScrollPauseAlarm = value;
+ }
+
+ @VisibleForTesting
+ int getScrollHintDir() {
+ return mScrollHintDir;
+ }
+
+ @VisibleForTesting
+ void setScrollHintDir(int value) {
+ mScrollHintDir = value;
+ }
+
+ @VisibleForTesting
+ int getScrollAreaOffset() {
+ return mScrollAreaOffset;
+ }
/**
* Adds the provided listener to the running list of Folder listeners
* {@link #mOnFolderStateChangedListeners}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 3c4cf5a..588a6db 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -256,8 +256,8 @@
mFolder.getContent(), contentStart, contentEnd, finalRadius, !mIsOpening));
// Fade in the folder name, as the text can overlap the icons when grid size is small.
- mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
- play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
+ mFolder.getFolderName().setAlpha(mIsOpening ? 0f : 1f);
+ play(a, getAnimator(mFolder.getFolderName(), View.ALPHA, 0, 1),
mIsOpening ? FOLDER_NAME_ALPHA_DURATION : 0,
mIsOpening ? mDuration - FOLDER_NAME_ALPHA_DURATION : FOLDER_NAME_ALPHA_DURATION);
@@ -318,7 +318,7 @@
mFolder.mFooter.setScaleX(1f);
mFolder.mFooter.setScaleY(1f);
mFolder.mFooter.setTranslationX(0f);
- mFolder.mFolderName.setAlpha(1f);
+ mFolder.getFolderName().setAlpha(1f);
mFolder.setClipChildren(mFolderClipChildren);
mFolder.setClipToPadding(mFolderClipToPadding);
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index e9859cf..a0b695a 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -459,7 +459,7 @@
mInfo.setTitle(newTitle, mFolder.mLauncherDelegate.getModelWriter());
onTitleChanged(mInfo.title);
- mFolder.mFolderName.setText(mInfo.title);
+ mFolder.getFolderName().setText(mInfo.title);
// Logging for folder creation flow
StatsLogManager.newInstance(getContext()).logger()
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 1b5ef42..9dc2d24 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -373,8 +373,8 @@
// Update footer
mPageIndicator.setVisibility(getPageCount() > 1 ? View.VISIBLE : View.GONE);
// Set the gravity as LEFT or RIGHT instead of START, as START depends on the actual text.
- mFolder.mFolderName.setGravity(getPageCount() > 1 ?
- (mIsRtl ? Gravity.RIGHT : Gravity.LEFT) : Gravity.CENTER_HORIZONTAL);
+ mFolder.getFolderName().setGravity(getPageCount() > 1
+ ? (mIsRtl ? Gravity.RIGHT : Gravity.LEFT) : Gravity.CENTER_HORIZONTAL);
}
public int getDesiredWidth() {
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index e44ea1d..a691e45 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -43,6 +43,7 @@
import android.view.animation.OvershootInterpolator;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
@@ -131,7 +132,8 @@
private float mCurrentPosition;
private float mFinalPosition;
private boolean mIsScrollPaused;
- private boolean mIsTwoPanels;
+ @VisibleForTesting
+ boolean mIsTwoPanels;
private ObjectAnimator mAnimator;
private @Nullable ObjectAnimator mAlphaAnimator;
@@ -477,6 +479,21 @@
return sTempRect;
}
+ @VisibleForTesting
+ int getActivePage() {
+ return mActivePage;
+ }
+
+ @VisibleForTesting
+ int getNumPages() {
+ return mNumPages;
+ }
+
+ @VisibleForTesting
+ float getCurrentPosition() {
+ return mCurrentPosition;
+ }
+
private class MyOutlineProver extends ViewOutlineProvider {
@Override
diff --git a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
index 5a26087..d0aa7a8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
@@ -376,9 +376,10 @@
Utilities.rotateBounds(rect, 100, 100, 1)
assertEquals(Rect(70, 40, 80, 80), rect)
- rect = Rect(20, 70, 60, 80)
- Utilities.rotateBounds(rect, 100, 100, 2)
- assertEquals(Rect(40, 20, 80, 30), rect)
+ // case removed for b/28435189
+ // rect = Rect(20, 70, 60, 80)
+ // Utilities.rotateBounds(rect, 100, 100, 2)
+ // assertEquals(Rect(40, 20, 80, 30), rect)
rect = Rect(20, 70, 60, 80)
Utilities.rotateBounds(rect, 100, 100, 3)
diff --git a/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt b/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
new file mode 100644
index 0000000..b491f17
--- /dev/null
+++ b/tests/src/com/android/launcher3/folder/FolderNameInfosTest.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2024 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.folder
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.folder.FolderNameInfos.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+data class Label(val index: Int, val label: String, val score: Float)
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FolderNameInfosTest {
+
+ companion object {
+ val statusList =
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ }
+
+ @Test
+ fun status() {
+ assertStatus(statusList)
+ assertStatus(
+ listOf(
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ )
+ assertStatus(
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ )
+ )
+ assertStatus(
+ listOf(
+ SUCCESS,
+ HAS_PRIMARY,
+ HAS_SUGGESTIONS,
+ )
+ )
+ }
+
+ fun assertStatus(statusList: List<Int>) {
+ var infos = FolderNameInfos()
+ statusList.forEach { infos.setStatus(it) }
+ assert(infos.status() == statusList.sum()) {
+ "There is an overlap on the status constants!"
+ }
+ }
+
+ @Test
+ fun hasPrimary() {
+ assertHasPrimary(
+ createNameInfos(listOf(Label(0, "label", 1f)), statusList),
+ hasPrimary = true
+ )
+ assertHasPrimary(
+ createNameInfos(listOf(Label(1, "label", 1f)), statusList),
+ hasPrimary = false
+ )
+ assertHasPrimary(
+ createNameInfos(
+ listOf(Label(0, "label", 1f)),
+ listOf(
+ ERROR_NO_PROVIDER,
+ ERROR_APP_LOOKUP_FAILED,
+ ERROR_ALL_APP_LOOKUP_FAILED,
+ ERROR_NO_LABELS_GENERATED,
+ ERROR_LABEL_LOOKUP_FAILED,
+ ERROR_ALL_LABEL_LOOKUP_FAILED,
+ ERROR_NO_PACKAGES,
+ )
+ ),
+ hasPrimary = false
+ )
+ }
+
+ private fun assertHasPrimary(nameInfos: FolderNameInfos, hasPrimary: Boolean) =
+ assert(nameInfos.hasPrimary() == hasPrimary)
+
+ private fun createNameInfos(labels: List<Label>?, statusList: List<Int>?): FolderNameInfos {
+ val infos = FolderNameInfos()
+ labels?.forEach { infos.setLabel(it.index, it.label, it.score) }
+ statusList?.forEach { infos.setStatus(it) }
+ return infos
+ }
+
+ @Test
+ fun hasSuggestions() {
+ assertHasSuggestions(
+ createNameInfos(listOf(Label(0, "label", 1f)), null),
+ hasSuggestions = true
+ )
+ assertHasSuggestions(createNameInfos(null, null), hasSuggestions = false)
+ // There is a max of 4 suggestions
+ assertHasSuggestions(
+ createNameInfos(listOf(Label(5, "label", 1f)), null),
+ hasSuggestions = false
+ )
+ assertHasSuggestions(
+ createNameInfos(
+ listOf(
+ Label(0, "label", 1f),
+ Label(1, "label", 1f),
+ Label(2, "label", 1f),
+ Label(3, "label", 1f)
+ ),
+ null
+ ),
+ hasSuggestions = true
+ )
+ }
+
+ private fun assertHasSuggestions(nameInfos: FolderNameInfos, hasSuggestions: Boolean) =
+ assert(nameInfos.hasSuggestions() == hasSuggestions)
+
+ @Test
+ fun hasContains() {
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "label3", 1f),
+ Label(3, "label4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "label3", -1f),
+ contains = true
+ )
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "label3", 1f),
+ Label(3, "label4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "label5", -1f),
+ contains = false
+ )
+ assertContains(
+ createNameInfos(null, null),
+ label = Label(-1, "label1", -1f),
+ contains = false
+ )
+ assertContains(
+ createNameInfos(
+ listOf(
+ Label(0, "label1", 1f),
+ Label(1, "label2", 1f),
+ Label(2, "lAbel3", 1f),
+ Label(3, "lEbel4", 1f)
+ ),
+ null
+ ),
+ label = Label(-1, "LaBEl3", -1f),
+ contains = true
+ )
+ }
+
+ private fun assertContains(nameInfos: FolderNameInfos, label: Label, contains: Boolean) =
+ assert(nameInfos.contains(label.label) == contains)
+}
diff --git a/tests/src/com/android/launcher3/folder/FolderTest.kt b/tests/src/com/android/launcher3/folder/FolderTest.kt
index e1daa74..4eb335e 100644
--- a/tests/src/com/android/launcher3/folder/FolderTest.kt
+++ b/tests/src/com/android/launcher3/folder/FolderTest.kt
@@ -18,23 +18,56 @@
import android.content.Context
import android.graphics.Point
+import android.view.KeyEvent
import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.TextView
+import androidx.core.view.isVisible
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.launcher3.Alarm
+import com.android.launcher3.DragSource
import com.android.launcher3.DropTarget.DragObject
import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
+import com.android.launcher3.OnAlarmListener
+import com.android.launcher3.R
import com.android.launcher3.celllayout.board.FolderPoint
import com.android.launcher3.celllayout.board.TestWorkspaceBuilder
+import com.android.launcher3.dragndrop.DragController
+import com.android.launcher3.dragndrop.DragOptions
+import com.android.launcher3.dragndrop.DragView
+import com.android.launcher3.folder.Folder.MIN_CONTENT_DIMEN
+import com.android.launcher3.folder.Folder.ON_EXIT_CLOSE_DELAY
+import com.android.launcher3.folder.Folder.SCROLL_LEFT
+import com.android.launcher3.folder.Folder.SCROLL_NONE
+import com.android.launcher3.folder.Folder.STATE_ANIMATING
+import com.android.launcher3.folder.Folder.STATE_CLOSED
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.util.ActivityContextWrapper
import com.android.launcher3.util.ModelTestExtensions.clearModelDb
+import java.util.ArrayList
import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertFalse
+import junit.framework.TestCase.assertNull
+import junit.framework.TestCase.assertTrue
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
+import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.whenever
/** Tests for [Folder] */
@SmallTest
@@ -44,7 +77,7 @@
private val context: Context =
ActivityContextWrapper(ApplicationProvider.getApplicationContext())
private val workspaceBuilder = TestWorkspaceBuilder(context)
- private val folder: Folder = Mockito.spy(Folder(context, null))
+ private val folder: Folder = spy(Folder(context, null))
@After
fun tearDown() {
@@ -60,8 +93,10 @@
folder.mContent = Mockito.mock(FolderPagedView::class.java)
val dragLayout = Mockito.mock(View::class.java)
val dragObject = Mockito.mock(DragObject::class.java)
- assertEquals(folder.deleteFolderOnDropCompleted, false)
+ folder.deleteFolderOnDropCompleted = false
+
folder.onDropCompleted(dragLayout, dragObject, true)
+
verify(folder, times(1)).replaceFolderWithFinalItem()
assertEquals(folder.deleteFolderOnDropCompleted, false)
}
@@ -74,12 +109,819 @@
folder.mContent = Mockito.mock(FolderPagedView::class.java)
val dragLayout = Mockito.mock(View::class.java)
val dragObject = Mockito.mock(DragObject::class.java)
- assertEquals(folder.deleteFolderOnDropCompleted, false)
+ folder.deleteFolderOnDropCompleted = false
+
folder.onDropCompleted(dragLayout, dragObject, true)
+
verify(folder, times(0)).replaceFolderWithFinalItem()
assertEquals(folder.deleteFolderOnDropCompleted, false)
}
+ @Test
+ fun `Test that we accept valid item type ITEM_TYPE_APPLICATION`() {
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_APPLICATION
+
+ val willAcceptResult = Folder.willAccept(itemInfo)
+
+ assertTrue(willAcceptResult)
+ }
+
+ @Test
+ fun `Test that we accept valid item type ITEM_TYPE_DEEP_SHORTCUT`() {
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_DEEP_SHORTCUT
+
+ val willAcceptResult = Folder.willAccept(itemInfo)
+
+ assertTrue(willAcceptResult)
+ }
+
+ @Test
+ fun `Test that we accept valid item type ITEM_TYPE_APP_PAIR`() {
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_APP_PAIR
+
+ val willAcceptResult = Folder.willAccept(itemInfo)
+
+ assertTrue(willAcceptResult)
+ }
+
+ @Test
+ fun `Test that we do not accept invalid item type ITEM_TYPE_APPWIDGET`() {
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_APPWIDGET
+
+ val willAcceptResult = Folder.willAccept(itemInfo)
+
+ assertFalse(willAcceptResult)
+ }
+
+ @Test
+ fun `Test that we do not accept invalid item type ITEM_TYPE_FOLDER`() {
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_FOLDER
+
+ val willAcceptResult = Folder.willAccept(itemInfo)
+
+ assertFalse(willAcceptResult)
+ }
+
+ @Test
+ fun `We should not animate open if items is null or less than or equal to 1`() {
+ folder.mInfo = Mockito.mock(FolderInfo::class.java)
+ val shouldAnimateOpenResult = folder.shouldAnimateOpen(null)
+
+ assertFalse(shouldAnimateOpenResult)
+ assertFalse(
+ folder.shouldAnimateOpen(arrayListOf<ItemInfo>(Mockito.mock(ItemInfo::class.java)))
+ )
+ }
+
+ @Test
+ fun `We should animate open if items greater than 1`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ folder.mInfo = folderInfo
+
+ val shouldAnimateOpenResult = folder.shouldAnimateOpen(folder.mInfo.getContents())
+
+ assertTrue(shouldAnimateOpenResult)
+ }
+
+ @Test
+ fun `Should be true if there is an open folder`() {
+ val closeOpenFolderResult = folder.closeOpenFolder(Mockito.mock(Folder::class.java))
+
+ assertTrue(closeOpenFolderResult)
+ }
+
+ @Test
+ fun `Should be false if the open folder is this folder`() {
+ val closeOpenFolderResult = folder.closeOpenFolder(folder)
+
+ assertFalse(closeOpenFolderResult)
+ }
+
+ @Test
+ fun `Should be false if there is not an open folder`() {
+ val closeOpenFolderResult = folder.closeOpenFolder(null)
+
+ assertFalse(closeOpenFolderResult)
+ }
+
+ @Test
+ fun `If drag is in progress we should set mItemAddedBackToSelfViaIcon to true`() {
+ folder.itemAddedBackToSelfViaIcon = false
+ folder.isDragInProgress = true
+
+ folder.notifyDrop()
+
+ assertTrue(folder.itemAddedBackToSelfViaIcon)
+ }
+
+ @Test
+ fun `If drag is not in progress we should not set mItemAddedBackToSelfViaIcon to true`() {
+ folder.itemAddedBackToSelfViaIcon = false
+ folder.isDragInProgress = false
+
+ folder.notifyDrop()
+
+ assertFalse(folder.itemAddedBackToSelfViaIcon)
+ }
+
+ @Test
+ fun `If launcher dragging is not enabled onLongClick should return true`() {
+ `when`(folder.isLauncherDraggingEnabled).thenReturn(false)
+
+ val onLongClickResult = folder.onLongClick(Mockito.mock(View::class.java))
+
+ assertTrue(onLongClickResult)
+ }
+
+ @Test
+ fun `If launcher dragging is enabled we should return startDrag result`() {
+ `when`(folder.isLauncherDraggingEnabled).thenReturn(true)
+ val viewMock = Mockito.mock(View::class.java)
+ val dragOptions = Mockito.mock(DragOptions::class.java)
+
+ val onLongClickResult = folder.onLongClick(viewMock)
+
+ assertEquals(onLongClickResult, folder.startDrag(viewMock, dragOptions))
+ verify(folder, times(1)).startDrag(viewMock, dragOptions)
+ }
+
+ @Test
+ fun `Verify start drag works as intended when view is instanceof ItemInfo`() {
+ val itemInfo = ItemInfo()
+ itemInfo.rank = 5
+ val viewMock = Mockito.mock(View::class.java)
+ val dragOptions = DragOptions()
+ `when`(viewMock.tag).thenReturn(itemInfo)
+ folder.dragController = Mockito.mock(DragController::class.java)
+
+ folder.startDrag(viewMock, dragOptions)
+
+ assertEquals(folder.mEmptyCellRank, 5)
+ assertEquals(folder.currentDragView, viewMock)
+ verify(folder, times(1)).addDragListener(dragOptions)
+ verify(folder, times(1)).callBeginDragShared(viewMock, dragOptions)
+ }
+
+ @Test
+ fun `Verify start drag works as intended when view is not instanceof ItemInfo`() {
+ val viewMock = Mockito.mock(View::class.java)
+ val dragOptions = DragOptions()
+
+ folder.startDrag(viewMock, dragOptions)
+
+ verify(folder, times(0)).addDragListener(dragOptions)
+ verify(folder, times(0)).callBeginDragShared(viewMock, dragOptions)
+ }
+
+ @Test
+ fun `Verify that onDragStart has an effect if dragSource is this folder`() {
+ folder.itemsInvalidated = false
+ folder.isDragInProgress = false
+ folder.itemAddedBackToSelfViaIcon = true
+ folder.currentDragView = Mockito.mock(View::class.java)
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ folder.mInfo = spy(folderInfo)
+ val dragObject = DragObject(context)
+ dragObject.dragInfo = Mockito.mock(ItemInfo::class.java)
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ dragObject.dragSource = folder
+
+ folder.onDragStart(dragObject, DragOptions())
+
+ verify(folder.mContent, times(1)).removeItem(folder.currentDragView)
+ verify(folder.mInfo, times(1)).remove(dragObject.dragInfo, true)
+ assertTrue(folder.itemsInvalidated)
+ assertTrue(folder.isDragInProgress)
+ assertFalse(folder.itemAddedBackToSelfViaIcon)
+ }
+
+ @Test
+ fun `Verify that onDragStart has no effects if dragSource is not this folder`() {
+ folder.itemsInvalidated = false
+ folder.isDragInProgress = false
+ folder.itemAddedBackToSelfViaIcon = true
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ val dragObject = DragObject(context)
+ dragObject.dragSource = Mockito.mock(DragSource::class.java)
+
+ folder.onDragStart(dragObject, DragOptions())
+
+ verify(folder.mContent, times(0)).removeItem(folder.currentDragView)
+ assertFalse(folder.itemsInvalidated)
+ assertFalse(folder.isDragInProgress)
+ assertTrue(folder.itemAddedBackToSelfViaIcon)
+ }
+
+ @Test
+ fun `Verify onDragEnd that we call completeDragExit and set drag in progress false`() {
+ doNothing().`when`(folder).completeDragExit()
+ folder.isExternalDrag = true
+ folder.isDragInProgress = true
+ folder.dragController = Mockito.mock(DragController::class.java)
+
+ folder.onDragEnd()
+
+ verify(folder, times(1)).completeDragExit()
+ verify(folder.dragController, times(1)).removeDragListener(folder)
+ assertFalse(folder.isDragInProgress)
+ }
+
+ @Test
+ fun `Verify onDragEnd that we do not call completeDragExit and set drag in progress false`() {
+ folder.isExternalDrag = false
+ folder.isDragInProgress = true
+ folder.dragController = Mockito.mock(DragController::class.java)
+
+ folder.onDragEnd()
+
+ verify(folder, times(0)).completeDragExit()
+ verify(folder.dragController, times(1)).removeDragListener(folder)
+ assertFalse(folder.isDragInProgress)
+ }
+
+ @Test
+ fun `startEditingFolderName should set hint to empty and showLabelSuggestions`() {
+ doNothing().`when`(folder).showLabelSuggestions()
+ folder.isEditingName = false
+ folder.folderName = FolderNameEditText(context)
+ folder.folderName.hint = "hello"
+
+ folder.startEditingFolderName()
+
+ verify(folder, times(1)).showLabelSuggestions()
+ assertEquals("", folder.folderName.hint)
+ assertTrue(folder.isEditingName)
+ }
+
+ @Test
+ fun `Ensure we set the title and hint correctly onBackKey when we have a new title`() {
+ val expectedHint = null
+ val expectedTitle = "hello"
+ folder.isEditingName = true
+ folder.folderName = spy(FolderNameEditText(context))
+ folder.folderName.setText(expectedTitle)
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ folder.mInfo = spy(folderInfo)
+ folder.mInfo.title = "world"
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
+
+ folder.onBackKey()
+
+ assertEquals(expectedTitle, folder.mInfo.title)
+ verify(folder.mFolderIcon, times(1)).onTitleChanged(expectedTitle)
+ assertEquals(expectedHint, folder.folderName.hint)
+ assertFalse(folder.isEditingName)
+ verify(folder.folderName, times(1)).clearFocus()
+ }
+
+ @Test
+ fun `Ensure we set the title and hint correctly onBackKey when we do not have a new title`() {
+ val expectedHint = context.getString(R.string.folder_hint_text)
+ val expectedTitle = ""
+ folder.isEditingName = true
+ folder.folderName = spy(FolderNameEditText(context))
+ folder.folderName.setText(expectedTitle)
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ folder.mInfo = spy(folderInfo)
+ folder.mInfo.title = "world"
+ folder.mFolderIcon = Mockito.mock(FolderIcon::class.java)
+
+ folder.onBackKey()
+
+ assertEquals(expectedTitle, folder.mInfo.title)
+ verify(folder.mFolderIcon, times(1)).onTitleChanged(expectedTitle)
+ assertEquals(expectedHint, folder.folderName.hint)
+ assertFalse(folder.isEditingName)
+ verify(folder.folderName, times(1)).clearFocus()
+ }
+
+ @Test
+ fun `ensure onEditorAction calls dispatchBackKey when actionId is IME_ACTION_DONE`() {
+ folder.folderName = Mockito.mock(FolderNameEditText::class.java)
+
+ val result =
+ folder.onEditorAction(
+ Mockito.mock(TextView::class.java),
+ EditorInfo.IME_ACTION_DONE,
+ Mockito.mock(KeyEvent::class.java)
+ )
+
+ assertTrue(result)
+ verify(folder.folderName, times(1)).dispatchBackKey()
+ }
+
+ @Test
+ fun `ensure onEditorAction does not call dispatchBackKey when actionId is not IME_ACTION_DONE`() {
+ folder.folderName = Mockito.mock(FolderNameEditText::class.java)
+
+ val result =
+ folder.onEditorAction(
+ Mockito.mock(TextView::class.java),
+ EditorInfo.IME_ACTION_NONE,
+ Mockito.mock(KeyEvent::class.java)
+ )
+
+ assertFalse(result)
+ verify(folder.folderName, times(0)).dispatchBackKey()
+ }
+
+ @Test
+ fun `in completeDragExit we close the folder when mIsOpen`() {
+ doNothing().`when`(folder).close(true)
+ folder.setIsOpen(true)
+ folder.rearrangeOnClose = false
+
+ folder.completeDragExit()
+
+ verify(folder, times(1)).close(true)
+ assertTrue(folder.rearrangeOnClose)
+ }
+
+ @Test
+ fun `in completeDragExit we want to rearrange on close when it is animating`() {
+ folder.setIsOpen(false)
+ folder.rearrangeOnClose = false
+ folder.state = STATE_ANIMATING
+
+ folder.completeDragExit()
+
+ verify(folder, times(0)).close(true)
+ assertTrue(folder.rearrangeOnClose)
+ }
+
+ @Test
+ fun `in completeDragExit we want to call rearrangeChildren and clearDragInfo when not open and not animating`() {
+ doNothing().`when`(folder).rearrangeChildren()
+ doNothing().`when`(folder).clearDragInfo()
+ folder.setIsOpen(false)
+ folder.rearrangeOnClose = false
+ folder.state = STATE_CLOSED
+
+ folder.completeDragExit()
+
+ verify(folder, times(0)).close(true)
+ assertFalse(folder.rearrangeOnClose)
+ verify(folder, times(1)).rearrangeChildren()
+ verify(folder, times(1)).clearDragInfo()
+ }
+
+ @Test
+ fun `clearDragInfo should set current drag view to null and isExternalDrag to false`() {
+ folder.currentDragView = Mockito.mock(DragView::class.java)
+ folder.isExternalDrag = true
+
+ folder.clearDragInfo()
+
+ assertNull(folder.currentDragView)
+ assertFalse(folder.isExternalDrag)
+ }
+
+ @Test
+ fun `onDragExit should set alarm if drag is not complete`() {
+ folder.onExitAlarm = Mockito.mock(Alarm::class.java)
+ val dragObject = Mockito.mock(DragObject::class.java)
+ dragObject.dragComplete = false
+
+ folder.onDragExit(dragObject)
+
+ verify(folder.onExitAlarm, times(1)).setOnAlarmListener(folder.mOnExitAlarmListener)
+ verify(folder.onExitAlarm, times(1)).setAlarm(ON_EXIT_CLOSE_DELAY.toLong())
+ }
+
+ @Test
+ fun `onDragExit should not set alarm if drag is complete`() {
+ folder.onExitAlarm = Mockito.mock(Alarm::class.java)
+ val dragObject = Mockito.mock(DragObject::class.java)
+ dragObject.dragComplete = true
+
+ folder.onDragExit(dragObject)
+
+ verify(folder.onExitAlarm, times(0)).setOnAlarmListener(folder.mOnExitAlarmListener)
+ verify(folder.onExitAlarm, times(0)).setAlarm(ON_EXIT_CLOSE_DELAY.toLong())
+ }
+
+ @Test
+ fun `onDragExit should not clear scroll hint if already SCROLL_NONE`() {
+ folder.onExitAlarm = Mockito.mock(Alarm::class.java)
+ val dragObject = Mockito.mock(DragObject::class.java)
+ folder.scrollHintDir = SCROLL_NONE
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+
+ folder.onDragExit(dragObject)
+
+ verify(folder.mContent, times(0)).clearScrollHint()
+ }
+
+ @Test
+ fun `onDragExit should clear scroll hint if not SCROLL_NONE and then set scroll hint to scroll none`() {
+ folder.onExitAlarm = Mockito.mock(Alarm::class.java)
+ val dragObject = Mockito.mock(DragObject::class.java)
+ folder.scrollHintDir = SCROLL_LEFT
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+
+ folder.onDragExit(dragObject)
+
+ verify(folder.mContent, times(1)).clearScrollHint()
+ assertEquals(folder.scrollHintDir, SCROLL_NONE)
+ }
+
+ @Test
+ fun `onDragExit we should cancel reorder pause and hint alarms`() {
+ folder.onExitAlarm = Mockito.mock(Alarm::class.java)
+ val dragObject = Mockito.mock(DragObject::class.java)
+ folder.scrollHintDir = SCROLL_NONE
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ folder.reorderAlarm = Mockito.mock(Alarm::class.java)
+ folder.onScrollHintAlarm = Mockito.mock(Alarm::class.java)
+ folder.scrollPauseAlarm = Mockito.mock(Alarm::class.java)
+
+ folder.onDragExit(dragObject)
+
+ verify(folder.reorderAlarm, times(1)).cancelAlarm()
+ verify(folder.onScrollHintAlarm, times(1)).cancelAlarm()
+ verify(folder.scrollPauseAlarm, times(1)).cancelAlarm()
+ assertEquals(folder.scrollHintDir, SCROLL_NONE)
+ }
+
+ @Test
+ fun `when calling prepareAccessibilityDrop we should cancel pending reorder alarm and call onAlarm`() {
+ folder.reorderAlarm = Mockito.mock(Alarm::class.java)
+ folder.mReorderAlarmListener = Mockito.mock(OnAlarmListener::class.java)
+ `when`(folder.reorderAlarm.alarmPending()).thenReturn(true)
+
+ folder.prepareAccessibilityDrop()
+
+ verify(folder.reorderAlarm, times(1)).cancelAlarm()
+ verify(folder.mReorderAlarmListener, times(1)).onAlarm(folder.reorderAlarm)
+ }
+
+ @Test
+ fun `when calling prepareAccessibilityDrop we should not do anything if there is no pending alarm`() {
+ folder.reorderAlarm = Mockito.mock(Alarm::class.java)
+ folder.mReorderAlarmListener = Mockito.mock(OnAlarmListener::class.java)
+ `when`(folder.reorderAlarm.alarmPending()).thenReturn(false)
+
+ folder.prepareAccessibilityDrop()
+
+ verify(folder.reorderAlarm, times(0)).cancelAlarm()
+ verify(folder.mReorderAlarmListener, times(0)).onAlarm(folder.reorderAlarm)
+ }
+
+ @Test
+ fun `isDropEnabled should be true as long as state is not STATE_ANIMATING`() {
+ folder.state = STATE_CLOSED
+
+ val isDropEnabled = folder.isDropEnabled
+
+ assertTrue(isDropEnabled)
+ }
+
+ @Test
+ fun `isDropEnabled should be false if state is STATE_ANIMATING`() {
+ folder.state = STATE_ANIMATING
+
+ val isDropEnabled = folder.isDropEnabled
+
+ assertFalse(isDropEnabled)
+ }
+
+ @Test
+ fun `getItemCount should return the number of items in the folder`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ folder.mInfo = folderInfo
+
+ val itemCount = folder.itemCount
+
+ assertEquals(itemCount, 2)
+ }
+
+ @Test
+ fun `hideItem should set the visibility of the corresponding ItemInfo to invisible`() {
+ val itemInfo = ItemInfo()
+ val view = View(context)
+ view.isVisible = true
+ doReturn(view).whenever(folder).getViewForInfo(itemInfo)
+
+ folder.hideItem(itemInfo)
+
+ assertFalse(view.isVisible)
+ }
+
+ @Test
+ fun `showItem should set the visibility of the corresponding ItemInfo to visible`() {
+ val itemInfo = ItemInfo()
+ val view = View(context)
+ view.isVisible = false
+ doReturn(view).whenever(folder).getViewForInfo(itemInfo)
+
+ folder.showItem(itemInfo)
+
+ assertTrue(view.isVisible)
+ }
+
+ @Test
+ fun `onDragEnter should cancel exit alarm and set the scroll area offset to dragRegionWidth divided by two minus xOffset`() {
+ folder.mPrevTargetRank = 1
+ val dragObject = Mockito.mock(DragObject::class.java)
+ val dragView = Mockito.mock(DragView::class.java)
+ dragObject.dragView = dragView
+ folder.onExitAlarm = Mockito.mock(Alarm::class.java)
+ `when`(dragObject.dragView.getDragRegionWidth()).thenReturn(100)
+ dragObject.xOffset = 20
+
+ folder.onDragEnter(dragObject)
+
+ verify(folder.onExitAlarm, times(1)).cancelAlarm()
+ assertEquals(-1, folder.mPrevTargetRank)
+ assertEquals(30, folder.scrollAreaOffset)
+ }
+
+ @Test
+ fun `acceptDrop should return true with the correct item type as a parameter`() {
+ val dragObject = Mockito.mock(DragObject::class.java)
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_APP_PAIR
+ dragObject.dragInfo = itemInfo
+
+ val result = folder.acceptDrop(dragObject)
+
+ assertTrue(result)
+ }
+
+ @Test
+ fun `acceptDrop should return false with the incorrect item type as a parameter`() {
+ val dragObject = Mockito.mock(DragObject::class.java)
+ val itemInfo = Mockito.mock(ItemInfo::class.java)
+ itemInfo.itemType = ITEM_TYPE_APPWIDGET
+ dragObject.dragInfo = itemInfo
+
+ val result = folder.acceptDrop(dragObject)
+
+ assertFalse(result)
+ }
+
+ @Test
+ fun `rearrangeChildren should return early if content view are not bound`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ folder.itemsInvalidated = false
+ doReturn(false).whenever(folder.mContent).areViewsBound()
+
+ folder.rearrangeChildren()
+
+ verify(folder.mContent, times(0)).arrangeChildren(folder.iconsInReadingOrder)
+ assertFalse(folder.itemsInvalidated)
+ }
+
+ @Test
+ fun `rearrangeChildren should call arrange children and invalidate items`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ folder.itemsInvalidated = false
+ doReturn(true).whenever(folder.mContent).areViewsBound()
+ val iconsInReadingOrderList = ArrayList<View>()
+ `when`(folder.iconsInReadingOrder).thenReturn(iconsInReadingOrderList)
+ doNothing().`when`(folder.mContent).arrangeChildren(iconsInReadingOrderList)
+
+ folder.rearrangeChildren()
+
+ verify(folder.mContent, times(1)).arrangeChildren(folder.iconsInReadingOrder)
+ assertTrue(folder.itemsInvalidated)
+ }
+
+ @Test
+ fun `getItemCount should return the size of info getContents size`() {
+ val folderInfo =
+ workspaceBuilder.createFolderInCell(FolderPoint(Point(1, 0), TWO_ICON_FOLDER_TYPE), 0)
+ folder.mInfo = folderInfo
+
+ val itemCount = folder.itemCount
+
+ assertEquals(2, itemCount)
+ }
+
+ @Test
+ fun `replaceFolderWithFinalItem should set mDestroyed to true if we replace folder with final item`() {
+ val launcherDelegate = Mockito.mock(LauncherDelegate::class.java)
+ folder.mLauncherDelegate = launcherDelegate
+ `when`(folder.mLauncherDelegate.replaceFolderWithFinalItem(folder)).thenReturn(true)
+
+ folder.replaceFolderWithFinalItem()
+
+ assertTrue(folder.isDestroyed)
+ }
+
+ @Test
+ fun `replaceFolderWithFinalItem should set mDestroyed to false if we do not replace folder with final item`() {
+ val launcherDelegate = Mockito.mock(LauncherDelegate::class.java)
+ folder.mLauncherDelegate = launcherDelegate
+ `when`(folder.mLauncherDelegate.replaceFolderWithFinalItem(folder)).thenReturn(false)
+
+ folder.replaceFolderWithFinalItem()
+
+ assertFalse(folder.isDestroyed)
+ }
+
+ @Test
+ fun `getContentAreaHeight should return maxContentAreaHeight`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.mContent.desiredHeight).thenReturn(100)
+ `when`(folder.maxContentAreaHeight).thenReturn(50)
+
+ val height = folder.contentAreaHeight
+
+ assertEquals(50, height)
+ }
+
+ @Test
+ fun `getContentAreaHeight should return desiredHeight`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.mContent.desiredHeight).thenReturn(50)
+ `when`(folder.maxContentAreaHeight).thenReturn(100)
+
+ val height = folder.contentAreaHeight
+
+ assertEquals(50, height)
+ }
+
+ @Test
+ fun `getContentAreaHeight should return MIN_CONTENT_DIMEN`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.mContent.desiredHeight).thenReturn(1)
+ `when`(folder.maxContentAreaHeight).thenReturn(2)
+
+ val height = folder.contentAreaHeight
+
+ assertEquals(MIN_CONTENT_DIMEN, height)
+ }
+
+ @Test
+ fun `getContentAreaWidth should return desired width`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.mContent.desiredWidth).thenReturn(50)
+
+ val width = folder.contentAreaWidth
+
+ assertEquals(50, width)
+ }
+
+ @Test
+ fun `getContentAreaWidth should return MIN_CONTENT_DIMEN`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.mContent.desiredWidth).thenReturn(1)
+
+ val width = folder.contentAreaWidth
+
+ assertEquals(MIN_CONTENT_DIMEN, width)
+ }
+
+ @Test
+ fun `getFolderWidth should return padding left plus padding right plus desired width`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.mContent.desiredWidth).thenReturn(1)
+ `when`(folder.paddingLeft).thenReturn(10)
+ `when`(folder.paddingRight).thenReturn(10)
+
+ val width = folder.folderWidth
+
+ assertEquals(21, width)
+ }
+
+ @Test
+ fun `getFolderHeight with no params should return getFolderHeight`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.contentAreaHeight).thenReturn(100)
+ `when`(folder.getFolderHeight(folder.contentAreaHeight)).thenReturn(120)
+
+ val height = folder.folderHeight
+
+ assertEquals(120, height)
+ }
+
+ @Test
+ fun `getFolderWidth with contentAreaHeight should return padding top plus padding bottom plus contentAreaHeight plus footer height`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ `when`(folder.footerHeight).thenReturn(100)
+ `when`(folder.paddingTop).thenReturn(10)
+ `when`(folder.paddingBottom).thenReturn(10)
+
+ val height = folder.getFolderHeight(100)
+
+ assertEquals(220, height)
+ }
+
+ @Test
+ fun `onRemove should call removeItem with the correct views`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ val items =
+ arrayListOf<ItemInfo>(
+ Mockito.mock(ItemInfo::class.java),
+ Mockito.mock(ItemInfo::class.java)
+ )
+ val view1 = Mockito.mock(View::class.java)
+ val view2 = Mockito.mock(View::class.java)
+ doReturn(view1).whenever(folder).getViewForInfo(items[0])
+ doReturn(view2).whenever(folder).getViewForInfo(items[1])
+ doReturn(2).whenever(folder).itemCount
+
+ folder.onRemove(items)
+
+ verify(folder.mContent, times(1)).removeItem(view1)
+ verify(folder.mContent, times(1)).removeItem(view2)
+ }
+
+ @Test
+ fun `onRemove should set mRearrangeOnClose to true and not call rearrangeChildren if animating`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ folder.state = STATE_ANIMATING
+ val items =
+ arrayListOf<ItemInfo>(
+ Mockito.mock(ItemInfo::class.java),
+ Mockito.mock(ItemInfo::class.java)
+ )
+ val view1 = Mockito.mock(View::class.java)
+ val view2 = Mockito.mock(View::class.java)
+ doReturn(view1).whenever(folder).getViewForInfo(items[0])
+ doReturn(view2).whenever(folder).getViewForInfo(items[1])
+ doReturn(2).whenever(folder).itemCount
+
+ folder.onRemove(items)
+
+ assertTrue(folder.rearrangeOnClose)
+ verify(folder, times(0)).rearrangeChildren()
+ }
+
+ @Test
+ fun `onRemove should set not change mRearrangeOnClose and not call rearrangeChildren if not animating`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ folder.state = STATE_CLOSED
+ folder.rearrangeOnClose = false
+ val items =
+ arrayListOf<ItemInfo>(
+ Mockito.mock(ItemInfo::class.java),
+ Mockito.mock(ItemInfo::class.java)
+ )
+ val view1 = Mockito.mock(View::class.java)
+ val view2 = Mockito.mock(View::class.java)
+ doReturn(view1).whenever(folder).getViewForInfo(items[0])
+ doReturn(view2).whenever(folder).getViewForInfo(items[1])
+ doReturn(2).whenever(folder).itemCount
+
+ folder.onRemove(items)
+
+ assertFalse(folder.rearrangeOnClose)
+ verify(folder, times(1)).rearrangeChildren()
+ }
+
+ @Test
+ fun `onRemove should call close if mIsOpen is true and item count is less than or equal to one`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ val items =
+ arrayListOf<ItemInfo>(
+ Mockito.mock(ItemInfo::class.java),
+ Mockito.mock(ItemInfo::class.java)
+ )
+ val view1 = Mockito.mock(View::class.java)
+ val view2 = Mockito.mock(View::class.java)
+ doReturn(view1).whenever(folder).getViewForInfo(items[0])
+ doReturn(view2).whenever(folder).getViewForInfo(items[1])
+ doReturn(1).whenever(folder).itemCount
+ folder.setIsOpen(true)
+ doNothing().`when`(folder).close(true)
+
+ folder.onRemove(items)
+
+ verify(folder, times(1)).close(true)
+ }
+
+ @Test
+ fun `onRemove should call replaceFolderWithFinalItem if mIsOpen is false and item count is less than or equal to one`() {
+ folder.mContent = Mockito.mock(FolderPagedView::class.java)
+ val items =
+ arrayListOf<ItemInfo>(
+ Mockito.mock(ItemInfo::class.java),
+ Mockito.mock(ItemInfo::class.java)
+ )
+ val view1 = Mockito.mock(View::class.java)
+ val view2 = Mockito.mock(View::class.java)
+ doReturn(view1).whenever(folder).getViewForInfo(items[0])
+ doReturn(view2).whenever(folder).getViewForInfo(items[1])
+ doReturn(1).whenever(folder).itemCount
+ folder.setIsOpen(false)
+
+ folder.onRemove(items)
+
+ verify(folder, times(1)).replaceFolderWithFinalItem()
+ }
+
companion object {
const val TWO_ICON_FOLDER_TYPE = 'A'
}
diff --git a/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt b/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt
new file mode 100644
index 0000000..9a8f957
--- /dev/null
+++ b/tests/src/com/android/launcher3/pageindicators/PageIndicatorDotsTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 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.pageindicators
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.util.ActivityContextWrapper
+import junit.framework.TestCase.assertEquals
+import org.junit.Test
+import org.mockito.Mockito
+
+class PageIndicatorDotsTest {
+
+ private val context: Context =
+ ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ private val pageIndicatorDots: PageIndicatorDots = Mockito.spy(PageIndicatorDots(context))
+
+ @Test
+ fun `setActiveMarker should set the active page to the parameter passed`() {
+ pageIndicatorDots.setActiveMarker(2)
+
+ assertEquals(2, pageIndicatorDots.activePage)
+ }
+
+ @Test
+ fun `setActiveMarker should set the active page to the parameter passed divided by two in two panel layouts`() {
+ pageIndicatorDots.mIsTwoPanels = true
+
+ pageIndicatorDots.setActiveMarker(5)
+
+ assertEquals(2, pageIndicatorDots.activePage)
+ }
+
+ @Test
+ fun `setMarkersCount should set the number of pages to the passed parameter and if the last page gets removed we want to go to the previous page`() {
+ pageIndicatorDots.setMarkersCount(3)
+
+ assertEquals(3, pageIndicatorDots.numPages)
+ }
+
+ @Test
+ fun `for setMarkersCount if the last page gets removed we want to go to the previous page`() {
+ pageIndicatorDots.setActiveMarker(2)
+
+ pageIndicatorDots.setMarkersCount(2)
+
+ assertEquals(1, pageIndicatorDots.activePage)
+ assertEquals(pageIndicatorDots.activePage.toFloat(), pageIndicatorDots.currentPosition)
+ }
+}