Snap for 11399958 from cac7c6f682 to 24Q2-release

Change-Id: I9a5c16bf02104ffbb1e47a8b3e9b0f340f44955c
This commit is contained in:
Android Build Coastguard Worker
2024-02-03 02:20:38 +00:00
29 changed files with 430 additions and 50 deletions
@@ -71,6 +71,7 @@ import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.common.bubbles.BubbleInfo;
@@ -101,8 +102,8 @@ public class BubbleBarController extends IBubblesListener.Stub {
*
* @see #onTaskbarRecreated()
*/
private static boolean sBubbleBarEnabled =
SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
private static boolean sBubbleBarEnabled = Flags.enableBubbleBar()
|| SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
/** Whether showing bubbles in the launcher bubble bar is enabled. */
public static boolean isBubbleBarEnabled() {
@@ -111,8 +112,10 @@ public class BubbleBarController extends IBubblesListener.Stub {
/** Re-reads the value of the flag from SystemProperties when taskbar is recreated. */
public static void onTaskbarRecreated() {
sBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
sBubbleBarEnabled = Flags.enableBubbleBar()
|| SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
}
private static final int MASK_HIDE_BUBBLE_BAR = SYSUI_STATE_BOUNCER_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
@@ -28,10 +28,8 @@ import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.Toast;
import android.window.SplashScreen;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
@@ -63,8 +61,7 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
// Log metric
StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
logger.log(LAUNCHER_SPLIT_WIDGET_ATTEMPT);
Toast.makeText(hostView.getContext(), R.string.split_widgets_not_supported,
Toast.LENGTH_SHORT).show();
mLauncher.handleIncorrectSplitTargetSelection();
return true;
}
Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
@@ -36,6 +36,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -676,7 +677,7 @@ public class QuickstepLauncher extends Launcher {
splitSelectSource.alreadyRunningTaskId = taskWasFound
? foundTask.key.id
: INVALID_TASK_ID;
if (FeatureFlags.enableSplitContextually()) {
if (enableSplitContextually()) {
startSplitToHome(splitSelectSource);
} else {
recentsView.initiateSplitSelect(splitSelectSource);
@@ -761,7 +762,7 @@ public class QuickstepLauncher extends Launcher {
super.onPause();
if (FeatureFlags.enableSplitContextually()) {
if (enableSplitContextually()) {
// If Launcher pauses before both split apps are selected, exit split screen.
if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
!mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
@@ -1352,6 +1353,15 @@ public class QuickstepLauncher extends Launcher {
return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
}
@Override
public boolean handleIncorrectSplitTargetSelection() {
if (enableSplitContextually() && !mSplitSelectStateController.isSplitSelectActive()) {
return false;
}
mSplitSelectStateController.getSplitInstructionsView().goBoing();
return true;
}
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
@@ -371,8 +371,8 @@ public class FallbackSwipeHandler extends
if (mSpringAnim != null) {
mSpringAnim.onTargetPositionChanged();
}
mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
}
mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK);
maybeSendEndMessage();
} catch (Exception e) {
// Ignore
@@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.Flags.useActivityOverlay;
import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
import static com.android.launcher3.LauncherPrefs.backedUpItem;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
@@ -1159,6 +1160,14 @@ public class TouchInteractionService extends Service {
boolean launcherResumedThroughShellTransition =
gestureState.getActivityInterface().isResumed()
&& !previousGestureState.isRecentsAnimationRunning();
// If a task fragment within Launcher is resumed
boolean launcherChildActivityResumed = useActivityOverlay()
&& runningTask != null
&& runningTask.isHomeTask()
&& mOverviewComponentObserver.isHomeAndOverviewSame()
&& !launcherResumedThroughShellTransition
&& !previousGestureState.isRecentsAnimationRunning();
if (gestureState.getActivityInterface().isInLiveTileMode()) {
return createOverviewInputConsumer(
previousGestureState,
@@ -1185,9 +1194,11 @@ public class TouchInteractionService extends Service {
? "launcher resumed through a shell transition"
: "forceOverviewInputConsumer == true"))
.append(", trying to use overview input consumer"));
} else if (mDeviceState.isGestureBlockedTask(runningTask)) {
} else if (mDeviceState.isGestureBlockedTask(runningTask) || launcherChildActivityResumed) {
return getDefaultInputConsumer(reasonString.append(SUBSTRING_PREFIX)
.append("is gesture-blocked task, trying to use default input consumer"));
.append(launcherChildActivityResumed
? "is launcher child-task, trying to use default input consumer"
: "is gesture-blocked task, trying to use default input consumer"));
} else {
reasonString.append(SUBSTRING_PREFIX)
.append("using OtherActivityInputConsumer");
@@ -86,6 +86,7 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
private final int mMinIconBackgroundHeight;
private final int mMaxIconBackgroundCornerRadius;
private final float mMinIconBackgroundCornerRadius;
private AnimatorSet mAnimator;
private int mMaxWidth = Integer.MAX_VALUE;
@@ -316,11 +317,13 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
}
protected void revealAnim(boolean isRevealing) {
cancelInProgressAnimations();
if (isRevealing) {
boolean isRtl = isLayoutRtl();
bringToFront();
((AnimatedVectorDrawable) mIconArrowView.getDrawable()).start();
AnimatorSet anim = new AnimatorSet();
mAnimator = new AnimatorSet();
float backgroundScaleY = mMaxIconBackgroundHeight / (float) mMinIconBackgroundHeight;
float maxCornerSize = Math.min(mMaxIconBackgroundHeight / 2f,
mMaxIconBackgroundCornerRadius);
@@ -341,7 +344,7 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
mIconTextMaxWidth + maxCornerSize);
}
});
anim.playTogether(
mAnimator.playTogether(
expandedTextRevealAnim,
ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_Y,
backgroundScaleY),
@@ -367,9 +370,9 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 1),
ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X,
isRtl ? -arrowTranslationX : arrowTranslationX));
anim.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
anim.setInterpolator(EMPHASIZED);
anim.start();
mAnimator.setDuration(MENU_BACKGROUND_REVEAL_DURATION);
mAnimator.setInterpolator(EMPHASIZED);
mAnimator.start();
} else {
((AnimatedVectorDrawable) mIconArrowView.getDrawable()).reverse();
float maxCornerSize = Math.min(mMaxIconBackgroundHeight / 2f,
@@ -386,8 +389,8 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
mIconTextExpandedView.getHeight() / 2f, 0);
}
});
AnimatorSet anim = new AnimatorSet();
anim.playTogether(
mAnimator = new AnimatorSet();
mAnimator.playTogether(
expandedTextClipAnim,
ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_X, 1),
ObjectAnimator.ofFloat(mIconViewBackgroundCornersStart, SCALE_Y, 1),
@@ -403,9 +406,9 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
ObjectAnimator.ofFloat(mIconTextCollapsedView, ALPHA, 1),
ObjectAnimator.ofFloat(mIconTextExpandedView, ALPHA, 0),
ObjectAnimator.ofFloat(mIconArrowView, TRANSLATION_X, 0));
anim.setDuration(MENU_BACKGROUND_HIDE_DURATION);
anim.setInterpolator(EMPHASIZED);
anim.start();
mAnimator.setDuration(MENU_BACKGROUND_HIDE_DURATION);
mAnimator.setInterpolator(EMPHASIZED);
mAnimator.start();
}
}
@@ -425,6 +428,16 @@ public class IconAppChipView extends FrameLayout implements TaskViewIcon {
mIconTextExpandedView.setAlpha(0);
mIconArrowView.setTranslationX(0);
((AnimatedVectorDrawable) mIconArrowView.getDrawable()).reset();
mAnimator = null;
}
private void cancelInProgressAnimations() {
// We null the `AnimatorSet` because it holds references to the `Animators` which aren't
// expecting to be mutable and will cause a crash if they are re-used.
if (mAnimator != null && mAnimator.isStarted()) {
mAnimator.cancel();
mAnimator = null;
}
}
@Override
@@ -24,6 +24,7 @@ import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -69,6 +70,8 @@ public class TaskMenuView extends AbstractFloatingView {
private TextView mTaskName;
@Nullable
private AnimatorSet mOpenCloseAnimator;
@Nullable
private ValueAnimator mRevealAnimator;
@Nullable private Runnable mOnClosingStartCallback;
private TaskView mTaskView;
private TaskIdAttributeContainer mTaskContainer;
@@ -290,13 +293,18 @@ public class TaskMenuView extends AbstractFloatingView {
private void animateOpenOrClosed(boolean closing) {
if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
mOpenCloseAnimator.end();
mOpenCloseAnimator.cancel();
}
mOpenCloseAnimator = new AnimatorSet();
final Animator revealAnimator = createOpenCloseOutlineProvider()
.createRevealAnimator(this, closing);
revealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
// If we're opening, we just start from the beginning as a new `TaskMenuView` is created
// each time we do the open animation so there will never be a partial value here.
float revealAnimationStartProgress = 0f;
if (closing && mRevealAnimator != null) {
revealAnimationStartProgress = 1f - mRevealAnimator.getAnimatedFraction();
}
mRevealAnimator = createOpenCloseOutlineProvider()
.createRevealAnimator(this, closing, revealAnimationStartProgress);
mRevealAnimator.setInterpolator(enableOverviewIconMenu() ? Interpolators.EMPHASIZED
: Interpolators.DECELERATE);
if (enableOverviewIconMenu()) {
@@ -349,7 +357,7 @@ public class TaskMenuView extends AbstractFloatingView {
mOpenCloseAnimator.playTogether(translationXAnim, menuTranslationXAnim);
}
mOpenCloseAnimator.playTogether(revealAnimator,
mOpenCloseAnimator.playTogether(mRevealAnimator,
ObjectAnimator.ofFloat(
mTaskContainer.getThumbnailView(), DIM_ALPHA,
closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
@@ -378,6 +386,7 @@ public class TaskMenuView extends AbstractFloatingView {
mIsOpen = false;
resetOverviewIconMenu();
mActivity.getDragLayer().removeView(this);
mRevealAnimator = null;
}
private void resetOverviewIconMenu() {
@@ -1137,9 +1137,8 @@ public class TaskView extends FrameLayout implements Reusable {
DeviceProfile dp = mActivity.getDeviceProfile();
if (enableOverviewIconMenu() && iconView instanceof IconAppChipView) {
((IconAppChipView) iconView).revealAnim(/* isRevealing= */ true);
return TaskMenuView.showForTask(menuContainer, () -> {
((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false);
});
return TaskMenuView.showForTask(menuContainer,
() -> ((IconAppChipView) iconView).revealAnim(/* isRevealing= */ false));
} else if (dp.isTablet) {
int alignedOptionIndex = 0;
if (getRecentsView().isOnGridBottomRow(menuContainer.getTaskView()) && dp.isLandscape) {
+3 -1
View File
@@ -56,7 +56,7 @@
<!-- App Widget resize frame -->
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
<dimen name="resize_frame_margin">22dp</dimen>
<dimen name="resize_frame_margin">23dp</dimen>
<dimen name="resize_frame_invalid_drag_across_two_panel_opacity_margin">24dp</dimen>
<!-- App widget reconfigure button -->
@@ -196,6 +196,8 @@
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_spacing">2dp</dimen>
<dimen name="widget_list_horizontal_margin">16dp</dimen>
<!-- Margin applied to the recycler view with search bar & the list of widget apps below it. -->
<dimen name="widget_list_left_pane_horizontal_margin">0dp</dimen>
<dimen name="widget_list_horizontal_margin_two_pane">24dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
@@ -20,6 +20,7 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
@@ -42,6 +43,7 @@ public class AllAppsSearchBarController
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener,
OnFocusChangeListener {
private static final String TAG = "AllAppsSearchBarController";
protected ActivityContext mLauncher;
protected SearchCallback<AdapterItem> mCallback;
protected ExtendedEditText mInput;
@@ -122,6 +124,7 @@ public class AllAppsSearchBarController
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
Log.i(TAG, "User tapped ime search button");
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem();
}
@@ -800,6 +800,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
return;
}
int size = getIconsInReadingOrder().size();
if (size <= 1) {
Log.d(TAG, "Couldn't animate folder closed because there's " + size + " icons");
closeComplete(false);
post(this::announceAccessibilityChanges);
return;
}
mContent.completePendingPageChanges();
mContent.snapToPageImmediately(mContent.getDestinationPage());
@@ -33,6 +33,7 @@ import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemFactory;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -102,6 +103,11 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
Objects.requireNonNull(item.getIntent()))) {
continue;
}
if (item instanceof ItemInfoWithIcon
&& ((ItemInfoWithIcon) item).isArchived()) {
continue;
}
}
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
@@ -18,10 +18,12 @@ package com.android.launcher3.model;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static com.android.launcher3.Flags.enableSupportForArchiving;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.appwidget.AppWidgetManager;
@@ -276,6 +278,7 @@ public class ItemInstallQueue {
return intent;
}
@SuppressWarnings("NewApi")
public Pair<ItemInfo, Object> getItemInfo(Context context) {
switch (itemType) {
case ITEM_TYPE_APPLICATION: {
@@ -297,6 +300,9 @@ public class ItemInstallQueue {
} else {
lai = laiList.get(0);
si.intent = makeLaunchIntent(lai);
if (enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
si.runtimeStatusFlags |= FLAG_ARCHIVED;
}
}
LauncherAppState.getInstance(context).getIconCache()
.getTitleAndIcon(si, () -> lai, usePackageIcon, false);
@@ -150,6 +150,12 @@ public class TestInformationHandler implements ResourceBasedOverride {
}, this::getCurrentActivity);
}
case TestProtocol.REQUEST_CELL_LAYOUT_BOARDER_HEIGHT: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
mDeviceProfile.cellLayoutBorderSpacePx.y);
return response;
}
case TestProtocol.REQUEST_SYSTEM_GESTURE_REGION: {
return getUIProperty(Bundle::putParcelable, activity -> {
WindowInsetsCompat insets = WindowInsetsCompat.toWindowInsetsCompat(
@@ -16,6 +16,8 @@
package com.android.launcher3.util;
import static com.android.launcher3.Flags.enableSupportForArchiving;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -112,8 +114,7 @@ public class PackageManagerHelper {
@NonNull final UserHandle user, final int flags) {
try {
ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
? null : info;
return !isPackageInstalledOrArchived(info) || !info.enabled ? null : info;
} catch (PackageManager.NameNotFoundException e) {
return null;
}
@@ -253,4 +254,11 @@ public class PackageManagerHelper {
}
return 100;
}
/** Returns true in case app is installed on the device or in archived state. */
@SuppressWarnings("NewApi")
private boolean isPackageInstalledOrArchived(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
enableSupportForArchiving() && info.isArchived);
}
}
@@ -154,6 +154,19 @@ public interface ActivityContext {
return false;
}
/**
* Handle user tapping on unsupported target when in split selection mode.
* See {@link #isSplitSelectionActive()}
*
* @return {@code true} if this method will handle the incorrect target selection,
* {@code false} if it could not be handled or if not possible to handle based on
* current split state
*/
default boolean handleIncorrectSplitTargetSelection() {
// Overridden
return false;
}
/**
* The root view to support drag-and-drop and popup support.
*/
@@ -175,7 +175,6 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
if (!mTmpPosition.equals(mIconPosition)) {
mIconPosition.set(mTmpPosition);
sendIconInfo();
LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
lp.width = Math.round(mIconPosition.width());
@@ -184,6 +183,9 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
lp.topMargin = Math.round(mIconPosition.top);
}
}
sendIconInfo();
if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) {
// Record the icon display
setCurrentIconVisible(true);
@@ -197,7 +199,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements
}
private void sendIconInfo() {
if (mContract != null && !mIconPosition.isEmpty()) {
if (mContract != null) {
mContract.sendEndPosition(mIconPosition, mLauncher, mSurfaceView.getSurfaceControl());
}
}
@@ -72,14 +72,21 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContentHorizontalMargin = getResources().getDimensionPixelSize(
R.dimen.widget_list_horizontal_margin);
mContentHorizontalMargin = getWidgetListHorizontalMargin();
mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
R.dimen.widget_cell_horizontal_padding);
mNavBarScrimPaint = new Paint();
mNavBarScrimPaint.setColor(Themes.getNavBarScrimColor(mActivityContext));
}
/**
* Returns the margins to be applied to the left and right of the widget apps list.
*/
protected int getWidgetListHorizontalMargin() {
return getResources().getDimensionPixelSize(
R.dimen.widget_list_horizontal_margin);
}
protected int getScrimColor(Context context) {
return context.getResources().getColor(R.color.widgets_picker_scrim);
}
@@ -142,8 +149,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
@Px int contentHorizontalMargin = getResources().getDimensionPixelSize(
R.dimen.widget_list_horizontal_margin);
@Px int contentHorizontalMargin = getWidgetListHorizontalMargin();
if (contentHorizontalMargin != mContentHorizontalMargin) {
onContentHorizontalMarginChanged(contentHorizontalMargin);
mContentHorizontalMargin = contentHorizontalMargin;
@@ -302,6 +302,12 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
};
}
@Override
protected int getWidgetListHorizontalMargin() {
return getResources().getDimensionPixelSize(
R.dimen.widget_list_left_pane_horizontal_margin);
}
@Override
protected boolean isTwoPane() {
return true;
+1
View File
@@ -176,6 +176,7 @@ android_library {
name: "launcher-testing-shared",
srcs: [
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java",
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt"
],
resource_dirs: [],
manifest: "multivalentTests/shared/AndroidManifest.xml",
@@ -121,6 +121,7 @@ public final class TestProtocol {
public static final String REQUEST_IS_TABLET = "is-tablet";
public static final String REQUEST_NUM_ALL_APPS_COLUMNS = "num-all-apps-columns";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
public static final String REQUEST_CELL_LAYOUT_BOARDER_HEIGHT = "cell-layout-boarder-height";
public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
public static final String REQUEST_SHELL_DRAG_READY = "shell-drag-ready";
public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT =
@@ -66,7 +66,7 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
}
public CellLayoutBoard(int width, int height) {
mWidget = new char[width + 1][height + 1];
mWidget = new char[width][height];
this.mWidth = width;
this.mHeight = height;
for (int x = 0; x < mWidget.length; x++) {
@@ -371,8 +371,8 @@ public class CellLayoutBoard implements Comparable<CellLayoutBoard> {
s.append("\n");
maxX = Math.min(maxX, mWidget.length);
maxY = Math.min(maxY, mWidget[0].length);
for (int y = 0; y <= maxY; y++) {
for (int x = 0; x <= maxX; x++) {
for (int y = 0; y < maxY; y++) {
for (int x = 0; x < maxX; x++) {
s.append(mWidget[x][y]);
}
s.append('\n');
@@ -27,7 +27,7 @@ open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerato
* usually less than 100.
* @return a randomly generated board filled with icons and widgets.
*/
open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard? {
open fun generateBoard(width: Int, height: Int, remainingEmptySpaces: Int): CellLayoutBoard {
val cellLayoutBoard = CellLayoutBoard(width, height)
return fillBoard(cellLayoutBoard, Rect(0, 0, width, height), remainingEmptySpaces)
}
@@ -39,8 +39,8 @@ open class RandomBoardGenerator(generator: Random) : DeterministicRandomGenerato
): CellLayoutBoard {
var remainingEmptySpaces = remainingEmptySpacesArg
if (area.height() * area.width() <= 0) return board
val width = getRandom(1, area.width() - 1)
val height = getRandom(1, area.height() - 1)
val width = getRandom(1, area.width())
val height = getRandom(1, area.height())
val x = area.left + getRandom(0, area.width() - width)
val y = area.top + getRandom(0, area.height() - height)
if (remainingEmptySpaces > 0) {
@@ -405,6 +405,11 @@ public final class LauncherInstrumentation {
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
int getCellLayoutBoarderHeight() {
return getTestInfo(TestProtocol.REQUEST_CELL_LAYOUT_BOARDER_HEIGHT)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
int getFocusedTaskHeightForTablet() {
return getTestInfo(TestProtocol.REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET).getInt(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -57,7 +57,8 @@ public class WidgetResizeFrame {
Rect originalWidgetSize = widget.getVisibleBounds();
Point targetStart = bottomResizeHandle.getVisibleCenter();
Point targetDest = bottomResizeHandle.getVisibleCenter();
targetDest.offset(0, originalWidgetSize.height());
targetDest.offset(0,
originalWidgetSize.height() + mLauncher.getCellLayoutBoarderHeight());
final long downTime = SystemClock.uptimeMillis();
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetStart,
@@ -62,5 +62,6 @@ public class TaplAllAppsIconsWorkingTest extends AbstractLauncherUiTest {
} finally {
allApps.unfreeze();
}
mLauncher.goHome();
}
}
@@ -0,0 +1,179 @@
/*
* 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.celllayout
import android.content.Context
import android.graphics.Point
import android.util.Log
import android.view.View
import androidx.core.view.get
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.CellLayout
import com.android.launcher3.celllayout.board.CellLayoutBoard
import com.android.launcher3.celllayout.board.IconPoint
import com.android.launcher3.celllayout.board.PermutedBoardComparator
import com.android.launcher3.celllayout.board.WidgetRect
import com.android.launcher3.celllayout.testgenerator.RandomBoardGenerator
import com.android.launcher3.util.ActivityContextWrapper
import com.android.launcher3.views.DoubleShadowBubbleTextView
import java.util.Random
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
private class HotseatReorderTestCase(
val startBoard: CellLayoutBoard,
val endBoard: CellLayoutBoard
) {
override fun toString(): String {
return "$startBoard#endBoard:\n$endBoard"
}
}
class HotseatReorderUnitTest {
private val applicationContext: Context =
ActivityContextWrapper(ApplicationProvider.getApplicationContext())
@JvmField @Rule var cellLayoutBuilder = UnitTestCellLayoutBuilderRule()
/**
* This test generates random CellLayout configurations and then try to reorder it and makes
* sure the result is a valid board meaning it didn't remove any widget or icon.
*/
@Test
fun generateValidTests() {
val generator = Random(Companion.SEED.toLong())
for (i in 0 until Companion.TOTAL_OF_CASES_GENERATED) {
// Using a new seed so that we can replicate the same test cases.
val seed = generator.nextInt()
Log.d(Companion.TAG, "Seed = $seed")
val testCase: HotseatReorderTestCase =
generateRandomTestCase(RandomBoardGenerator(Random(seed.toLong())))
Log.d(Companion.TAG, "testCase = $testCase")
Assert.assertTrue(
"invalid case $i",
PermutedBoardComparator().compare(testCase.startBoard, testCase.endBoard) == 0
)
}
}
private fun addViewInCellLayout(
cellLayout: CellLayout,
cellX: Int,
cellY: Int,
spanX: Int,
spanY: Int,
isWidget: Boolean
) {
val cell =
if (isWidget) View(applicationContext)
else DoubleShadowBubbleTextView(applicationContext)
cell.layoutParams = CellLayoutLayoutParams(cellX, cellY, spanX, spanY)
cellLayout.addViewToCellLayout(
cell,
-1,
cell.id,
cell.layoutParams as CellLayoutLayoutParams,
true
)
}
private fun solve(board: CellLayoutBoard): CellLayout {
val cl = cellLayoutBuilder.createCellLayout(board.width, board.height, false)
// The views have to be sorted or the result can vary
board.icons
.map(IconPoint::getCoord)
.sortedWith(
Comparator.comparing { p: Any -> (p as Point).x }
.thenComparing { p: Any -> (p as Point).y }
)
.forEach { p ->
addViewInCellLayout(
cellLayout = cl,
cellX = p.x,
cellY = p.y,
spanX = 1,
spanY = 1,
isWidget = false
)
}
board.widgets
.sortedWith(
Comparator.comparing(WidgetRect::getCellX).thenComparing(WidgetRect::getCellY)
)
.forEach { widget ->
addViewInCellLayout(
cl,
widget.cellX,
widget.cellY,
widget.spanX,
widget.spanY,
isWidget = true
)
}
if (cl.makeSpaceForHotseatMigration(true)) {
commitTempPosition(cl)
}
return cl
}
private fun commitTempPosition(cellLayout: CellLayout) {
val count = cellLayout.shortcutsAndWidgets.childCount
for (i in 0 until count) {
val params = cellLayout.shortcutsAndWidgets[i].layoutParams as CellLayoutLayoutParams
params.cellX = params.tmpCellX
params.cellY = params.tmpCellY
}
}
private fun boardFromCellLayout(cellLayout: CellLayout): CellLayoutBoard {
val views = mutableListOf<View>()
for (i in 0 until cellLayout.shortcutsAndWidgets.childCount) {
views.add(cellLayout.shortcutsAndWidgets.getChildAt(i))
}
return CellLayoutTestUtils.viewsToBoard(views, cellLayout.countX, cellLayout.countY)
}
private fun generateRandomTestCase(
boardGenerator: RandomBoardGenerator
): HotseatReorderTestCase {
val width: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE)
val height: Int = boardGenerator.getRandom(3, Companion.MAX_BOARD_SIZE)
val targetWidth: Int = boardGenerator.getRandom(1, width - 2)
val targetHeight: Int = boardGenerator.getRandom(1, height - 2)
val board: CellLayoutBoard =
boardGenerator.generateBoard(width, height, targetWidth * targetHeight)
val finishBoard: CellLayoutBoard = boardFromCellLayout(solve(board))
return HotseatReorderTestCase(board, finishBoard)
}
companion object {
private const val MAX_BOARD_SIZE = 13
/**
* There is nothing special about this numbers, the random seed is just to be able to
* reproduce the test cases and the height and width is a random number similar to what
* users expect on their devices
*/
private const val SEED = -194162315
private const val TOTAL_OF_CASES_GENERATED = 300
private const val TAG = "HotseatReorderUnitTest"
}
}
@@ -161,11 +161,12 @@ public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest {
public int getWidgetId() throws InterruptedException {
Intent intent = blockingGetExtraIntent();
assertNotNull(intent);
assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
assertNotNull("Null EXTRA_INTENT", intent);
assertEquals("Intent action is not ACTION_APPWIDGET_CONFIGURE",
AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction());
int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
LauncherAppWidgetInfo.NO_ID);
assertNotSame(widgetId, LauncherAppWidgetInfo.NO_ID);
assertNotSame("Widget id is NO_ID", widgetId, LauncherAppWidgetInfo.NO_ID);
return widgetId;
}
}
@@ -0,0 +1,83 @@
/*
* 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.util;
import static com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
/** Unit tests for {@link PackageManagerHelper}. */
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class PackageManagerHelperTest {
@Rule
public ExpectedException exception = ExpectedException.none();
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final String TEST_PACKAGE = "com.android.test.package";
private static final int TEST_USER = 2;
private Context mContext;
private LauncherApps mLauncherApps;
private PackageManagerHelper mPackageManagerHelper;
@Before
public void setup() {
mContext = mock(Context.class);
mLauncherApps = mock(LauncherApps.class);
when(mContext.getSystemService(eq(LauncherApps.class))).thenReturn(mLauncherApps);
mPackageManagerHelper = new PackageManagerHelper(mContext);
}
@Test
@RequiresFlagsEnabled(FLAG_ENABLE_SUPPORT_FOR_ARCHIVING)
public void getApplicationInfo_archivedApp_appInfoIsNotNull()
throws PackageManager.NameNotFoundException {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.isArchived = true;
when(mLauncherApps.getApplicationInfo(TEST_PACKAGE, 0 /* flags */,
UserHandle.of(TEST_USER)))
.thenReturn(applicationInfo);
assertThat(mPackageManagerHelper.getApplicationInfo(TEST_PACKAGE, UserHandle.of(TEST_USER),
0 /* flags */))
.isNotNull();
}
}