Merge "[Refactor] Extract work profile related logic from AllAppsContainerView to WorkProfileManager" into sc-v2-dev
diff --git a/build.gradle b/build.gradle
index a7eef13..617738a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,6 @@
repositories {
mavenCentral()
google()
- jcenter()
}
dependencies {
classpath GRADLE_CLASS_PATH
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index cc65cbf..710e2e0 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -105,6 +105,23 @@
<!-- Unused. Included only for compatibility with parent class. -->
<Button
+ android:id="@+id/action_split"
+ style="@style/GoOverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_split_screen"
+ android:text="@string/action_split"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor"
+ android:visibility="gone" />
+
+ <Space
+ android:id="@+id/action_split_space"
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1"
+ android:visibility="gone" />
+
+ <Button
android:id="@+id/action_share"
style="@style/GoOverviewActionButton"
android:layout_width="wrap_content"
diff --git a/gradle.properties b/gradle.properties
index 7f4c609..d5c1d77 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,7 @@
# Until all the dependencies move to android X
android.useAndroidX = true
android.enableJetifier = true
+org.gradle.parallel=true
ANDROID_X_VERSION=1+
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c649082..34cd1c2 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -27,6 +27,7 @@
<dimen name="task_menu_corner_radius">22dp</dimen>
<dimen name="task_menu_item_corner_radius">4dp</dimen>
<dimen name="task_menu_spacing">2dp</dimen>
+ <dimen name="task_menu_width_grid">200dp</dimen>
<dimen name="overview_proactive_row_height">48dp</dimen>
<dimen name="overview_proactive_row_bottom_margin">16dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index d24d752..c80818a 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -35,6 +36,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.ServiceConnection;
+import android.graphics.Insets;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
@@ -43,6 +45,7 @@
import android.os.IBinder;
import android.util.Log;
import android.view.View;
+import android.view.WindowInsets;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -353,6 +356,7 @@
mAppTransitionManager = new QuickstepTransitionManager(this);
mAppTransitionManager.registerRemoteAnimations();
+ mAppTransitionManager.registerRemoteTransitions();
internalBindToTIS();
}
@@ -638,4 +642,17 @@
mDepthController.dump(prefix, writer);
}
}
+
+ @Override
+ public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
+ WindowInsets oldInsets) {
+ // Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
+ // would count towards it). This is used for the bottom protection in All Apps for example.
+ if (SysUINavigationMode.getMode(this) == NO_BUTTON) {
+ Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+ Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+ oldTappableInsets.right, 0);
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 89cc1f6..b8d00bd 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -28,17 +28,24 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.mapBoundToRange;
import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
+import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
+import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
@@ -50,20 +57,24 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.Size;
import android.view.SurfaceControl;
@@ -86,8 +97,11 @@
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.FloatingIconView;
@@ -96,8 +110,11 @@
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.util.AppCloseConfig;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.FloatingWidgetView;
@@ -1179,10 +1196,183 @@
}
/**
- * Animator that controls the transformations of the windows the targets that are closing.
+ * Returns view on the workspace that corresponds to the closing app in the list of app targets
*/
- private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets) {
+ private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat[] appTargets) {
+ for (RemoteAnimationTargetCompat appTarget : appTargets) {
+ if (appTarget.mode == MODE_CLOSING) {
+ View workspaceView = findWorkspaceView(appTarget);
+ if (workspaceView != null) {
+ return workspaceView;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns view on the workspace that corresponds to the {@param runningTaskTarget}.
+ */
+ private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat runningTaskTarget) {
+ if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
+ return null;
+ }
+
+ final ComponentName[] taskInfoActivities = new ComponentName[] {
+ runningTaskTarget.taskInfo.baseActivity,
+ runningTaskTarget.taskInfo.origActivity,
+ runningTaskTarget.taskInfo.realActivity,
+ runningTaskTarget.taskInfo.topActivity};
+
+ String packageName = null;
+ for (ComponentName component : taskInfoActivities) {
+ if (component != null && component.getPackageName() != null) {
+ packageName = component.getPackageName();
+ break;
+ }
+ }
+
+ if (packageName == null) {
+ return null;
+ }
+
+ // Find the associated item info for the launch cookie (if available), note that predicted
+ // apps actually have an id of -1, so use another default id here
+ final ArrayList<IBinder> launchCookies = runningTaskTarget.taskInfo.launchCookies == null
+ ? new ArrayList<>()
+ : runningTaskTarget.taskInfo.launchCookies;
+
+ int launchCookieItemId = NO_MATCHING_ID;
+ for (IBinder cookie : launchCookies) {
+ Integer itemId = ObjectWrapper.unwrap(cookie);
+ if (itemId != null) {
+ launchCookieItemId = itemId;
+ break;
+ }
+ }
+
+ return mLauncher.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
+ packageName, UserHandle.of(runningTaskTarget.taskInfo.userId));
+ }
+
+ private @NonNull RectF getDefaultWindowTargetRect() {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ final int halfIconSize = dp.iconSizePx / 2;
+ float primaryDimension = orientationHandler
+ .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
+ float secondaryDimension = orientationHandler
+ .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
+ final float targetX = primaryDimension / 2f;
+ final float targetY = secondaryDimension - dp.hotseatBarSizePx;
+ return new RectF(targetX - halfIconSize, targetY - halfIconSize,
+ targetX + halfIconSize, targetY + halfIconSize);
+ }
+
+ /**
+ * Closing animator that animates the window into its final location on the workspace.
+ */
+ private void getClosingWindowAnimators(AnimatorSet animation,
+ RemoteAnimationTargetCompat[] targets, View workspaceView) {
+ FloatingIconView floatingIconView = null;
+ FloatingWidgetView floatingWidget = null;
+ RectF targetRect = new RectF();
+
+ RemoteAnimationTargetCompat runningTaskTarget = null;
+ boolean isTransluscent = false;
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == MODE_CLOSING) {
+ runningTaskTarget = target;
+ isTransluscent = runningTaskTarget.isTranslucent;
+ break;
+ }
+ }
+
+ // Get floating view and target rect.
+ if (workspaceView instanceof LauncherAppWidgetHostView) {
+ Size windowSize = new Size(mDeviceProfile.availableWidthPx,
+ mDeviceProfile.availableHeightPx);
+ int fallbackBackgroundColor =
+ FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget);
+ floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher,
+ (LauncherAppWidgetHostView) workspaceView, targetRect, windowSize,
+ mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
+ isTransluscent, fallbackBackgroundColor);
+ } else if (workspaceView != null) {
+ floatingIconView = getFloatingIconView(mLauncher, workspaceView,
+ true /* hideOriginal */, targetRect, false /* isOpening */);
+ } else {
+ targetRect.set(getDefaultWindowTargetRect());
+ }
+
+ final RectF startRect = new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
+ RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mLauncher);
+
+ // Hook up floating views to the closing window animators.
+ if (floatingIconView != null) {
+ anim.addAnimatorListener(floatingIconView);
+ floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ floatingIconView.setFastFinishRunnable(anim::end);
+ FloatingIconView finalFloatingIconView = floatingIconView;
+
+ // We want the window alpha to be 0 once this threshold is met, so that the
+ // FolderIconView can be seen morphing into the icon shape.
+ final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+
+ RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) {
+ @Override
+ public void onUpdate(@Nullable AppCloseConfig values, RectF currentRectF,
+ float progress) {
+ finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress,
+ windowAlphaThreshold, getCornerRadius(progress), false);
+
+ super.onUpdate(values, currentRectF, progress);
+ }
+ };
+ anim.addOnUpdateListener(runner);
+ } else if (floatingWidget != null) {
+ anim.addAnimatorListener(floatingWidget);
+ floatingWidget.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ floatingWidget.setFastFinishRunnable(anim::end);
+
+ final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
+ FloatingWidgetView finalFloatingWidget = floatingWidget;
+ RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect) {
+ @Override
+ public void onUpdate(@Nullable AppCloseConfig values, RectF currentRectF,
+ float progress) {
+ final float fallbackBackgroundAlpha =
+ 1 - mapBoundToRange(progress, 0.8f, 1, 0, 1, EXAGGERATED_EASE);
+ final float foregroundAlpha =
+ mapBoundToRange(progress, 0.5f, 1, 0, 1, EXAGGERATED_EASE);
+ finalFloatingWidget.update(currentRectF, floatingWidgetAlpha, foregroundAlpha,
+ fallbackBackgroundAlpha, 1 - progress);
+
+ super.onUpdate(values, currentRectF, progress);
+ }
+ };
+ anim.addOnUpdateListener(runner);
+ }
+
+ // Use a fixed velocity to start the animation.
+ float velocityPxPerS = DynamicResource.provider(mLauncher)
+ .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
+ PointF velocity = new PointF(0, -velocityPxPerS);
+ animation.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
+ true /* animateOverviewScrim */, workspaceView).getAnimators());
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ anim.start(mLauncher, velocity);
+ }
+ });
+ }
+
+ /**
+ * Closing window animator that moves the window down and offscreen.
+ */
+ private Animator getFallbackClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets) {
final int rotationChange = getRotationChange(appTargets);
SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
Matrix matrix = new Matrix();
@@ -1321,7 +1511,7 @@
LauncherAnimationRunner.AnimationResult result) {
if (mLauncher.isDestroyed()) {
AnimatorSet anim = new AnimatorSet();
- anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
+ anim.play(getFallbackClosingWindowAnimators(appTargets));
result.setAnimation(anim, mLauncher.getApplicationContext());
return;
}
@@ -1348,9 +1538,23 @@
if (anim == null) {
anim = new AnimatorSet();
- anim.play(mFromUnlock
- ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
- : getClosingWindowAnimators(appTargets, wallpaperTargets));
+
+ boolean playFallBackAnimation = mLauncher.isInState(LauncherState.ALL_APPS)
+ && (launcherIsATargetWithMode(appTargets, MODE_OPENING)
+ || mLauncher.isForceInvisible());
+
+ View workspaceView = findWorkspaceView(appTargets);
+ boolean playWorkspaceReveal = true;
+ if (mFromUnlock) {
+ anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
+ } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
+ && !playFallBackAnimation) {
+ getClosingWindowAnimators(anim, appTargets, workspaceView);
+ // We play StaggeredWorkspaceAnim as a part of the closing window animation.
+ playWorkspaceReveal = false;
+ } else {
+ anim.play(getFallbackClosingWindowAnimators(appTargets));
+ }
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
@@ -1378,7 +1582,9 @@
}
});
} else {
- anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+ if (playWorkspaceReveal) {
+ anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+ }
}
}
}
@@ -1527,4 +1733,73 @@
mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
}
}
+
+ /**
+ * RectFSpringAnim update listener to be used for app to home animation.
+ */
+ private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
+ private final RemoteAnimationTargetCompat[] mAppTargets;
+ private final Matrix mMatrix = new Matrix();
+ private final Point mTmpPos = new Point();
+ private final Rect mCurrentRect = new Rect();
+ private final float mStartRadius;
+ private final float mEndRadius;
+ private final SurfaceTransactionApplier mSurfaceApplier;
+
+ SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect) {
+ mAppTargets = appTargets;
+ mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+ mEndRadius = Math.max(1, targetRect.width()) / 2f;
+ mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
+ }
+
+ public float getCornerRadius(float progress) {
+ return Utilities.mapRange(progress, mStartRadius, mEndRadius);
+ }
+
+ @Override
+ public void onUpdate(@Nullable AppCloseConfig values, RectF currentRectF, float progress) {
+ SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
+ for (int i = mAppTargets.length - 1; i >= 0; i--) {
+ RemoteAnimationTargetCompat target = mAppTargets[i];
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+ if (target.localBounds != null) {
+ mTmpPos.set(target.localBounds.left, target.localBounds.top);
+ } else {
+ mTmpPos.set(target.position.x, target.position.y);
+ }
+
+ if (target.mode == MODE_CLOSING) {
+ float alpha = getWindowAlpha(progress);
+ currentRectF.round(mCurrentRect);
+
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mCurrentRect)
+ .withAlpha(alpha)
+ .withCornerRadius(getCornerRadius(progress));
+ } else if (target.mode == MODE_OPENING) {
+ mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
+ builder.withMatrix(mMatrix)
+ .withAlpha(1f);
+ }
+ params[i] = builder.build();
+ }
+ mSurfaceApplier.scheduleApply(params);
+ }
+
+ protected float getWindowAlpha(float progress) {
+ // Alpha interpolates between [1, 0] between progress values [start, end]
+ final float start = 0f;
+ final float end = 0.85f;
+
+ if (progress <= start) {
+ return 1f;
+ }
+ if (progress >= end) {
+ return 0f;
+ }
+ return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index a6844e4..63e7390 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -15,12 +15,15 @@
*/
package com.android.launcher3.hybridhotseat;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
- .LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
import android.content.Intent;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
import android.view.View;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
@@ -47,6 +50,8 @@
*/
public class HotseatEduController {
+ private static final String TAG = "HotseatEduController";
+
public static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
@@ -188,8 +193,12 @@
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
mNewScreens = IntArray.wrap(pageId);
}
- for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
- View child = mHotseat.getChildAt(i, 0);
+ boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+ int hotseatItemsNum = mLauncher.getDeviceProfile().numShownHotseatIcons;
+ for (int i = 0; i < hotseatItemsNum; i++) {
+ int x = isPortrait ? i : 0;
+ int y = isPortrait ? 0 : hotseatItemsNum - i - 1;
+ View child = mHotseat.getChildAt(x, y);
if (child == null || child.getTag() == null) continue;
ItemInfo tag = (ItemInfo) child.getTag();
if (tag.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) continue;
@@ -229,8 +238,7 @@
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(getSettingsIntent()));
} else {
- new ArrowTipView(mLauncher).show(
- mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+ showHotseatArrowTip(true, mLauncher.getString(R.string.hotseat_tip_no_empty_slots));
}
}
@@ -251,15 +259,50 @@
if (requiresMigration && canMigrateToFirstPage) {
showDialog();
} else {
- new ArrowTipView(mLauncher).show(mLauncher.getString(
+ if (showHotseatArrowTip(requiresMigration, mLauncher.getString(
requiresMigration ? R.string.hotseat_tip_no_empty_slots
- : R.string.hotseat_auto_enrolled),
- mHotseat.getTop());
- mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+ : R.string.hotseat_auto_enrolled))) {
+ mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
+ }
finishOnboarding();
}
}
+ /**
+ * Finds a child suitable child in hotseat and shows arrow tip pointing at it.
+ *
+ * @param usePinned used to determine target view. If true, will use the first matching pinned
+ * item. Otherwise, will use the first predicted child
+ * @param message String to be shown inside the arrowView
+ * @return whether suitable child was found and tip was shown
+ */
+ private boolean showHotseatArrowTip(boolean usePinned, String message) {
+ int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
+ boolean isPortrait = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+
+ BubbleTextView tipTargetView = null;
+ for (int i = childCount - 1; i > -1; i--) {
+ int x = isPortrait ? i : 0;
+ int y = isPortrait ? 0 : i;
+ View v = mHotseat.getShortcutsAndWidgets().getChildAt(x, y);
+ if (v instanceof BubbleTextView && v.getTag() instanceof WorkspaceItemInfo) {
+ ItemInfo info = (ItemInfo) v.getTag();
+ boolean isPinned = info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ if (isPinned == usePinned) {
+ tipTargetView = (BubbleTextView) v;
+ break;
+ }
+ }
+ }
+ if (tipTargetView == null) {
+ Log.e(TAG, "Unable to find suitable view for ArrowTip");
+ return false;
+ }
+ Rect bounds = mLauncher.getViewBounds(tipTargetView);
+ new ArrowTipView(mLauncher).show(message, Gravity.END, bounds.centerX(), bounds.top);
+ return true;
+ }
+
void showDialog() {
if (mPredictedApps == null || mPredictedApps.isEmpty()) {
return;
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index c7c2567..c41f2ce 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -77,6 +77,11 @@
mContent = this;
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ setTranslationShift(TRANSLATION_SHIFT_CLOSED);
+ }
@Override
protected void onFinishInflate() {
@@ -200,9 +205,9 @@
}
AbstractFloatingView.closeAllOpenViews(mActivityContext);
attachToContainer();
- mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
animateOpen();
populatePreview(predictions);
+ mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
}
/**
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 86562c4..85d9f01 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -57,7 +57,6 @@
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.Snackbar;
import java.util.ArrayList;
@@ -153,38 +152,15 @@
*/
public void showEdu() {
mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
- if (mPredictedItems.isEmpty()) {
- // launcher has empty predictions set
- Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
- showDiscoveryTip();
- } else {
- HotseatEduController eduController = new HotseatEduController(mLauncher);
- eduController.setPredictedApps(mPredictedItems.stream()
- .map(i -> (WorkspaceItemInfo) i)
- .collect(Collectors.toList()));
- eduController.showEdu();
- }
+ HotseatEduController eduController = new HotseatEduController(mLauncher);
+ eduController.setPredictedApps(mPredictedItems.stream()
+ .map(i -> (WorkspaceItemInfo) i)
+ .collect(Collectors.toList()));
+ eduController.showEdu();
}));
}
/**
- * Shows educational tip for hotseat if user does not go through Tips app.
- */
- private void showDiscoveryTip() {
- if (getPredictedIcons().isEmpty()) {
- new ArrowTipView(mLauncher).show(
- mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
- } else {
- Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- }
- }
-
- /**
* Returns if hotseat client has predictions
*/
public boolean hasPredictions() {
@@ -510,6 +486,16 @@
}
}
+ /**
+ * Called when user completes adding item requiring a config activity to the hotseat
+ */
+ public void onDeferredDrop(int cellX, int cellY) {
+ View child = mHotseat.getChildAt(cellX, cellY);
+ if (child instanceof PredictedAppIcon) {
+ removeIconWithoutNotify((PredictedAppIcon) child);
+ }
+ }
+
private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index a9ff03b..02170ab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -107,7 +107,7 @@
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
flags -> {
- if (!supportsStashing()) {
+ if (!supportsVisualStashing()) {
return false;
}
boolean inApp = (flags & FLAG_IN_APP) != 0;
@@ -141,7 +141,7 @@
mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha();
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
- mIsStashedInApp = supportsStashing()
+ mIsStashedInApp = supportsManualStashing()
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
updateStateForFlag(FLAG_STASHED_IN_APP, mIsStashedInApp);
@@ -150,10 +150,18 @@
}
/**
+ * Returns whether the taskbar can visually stash into a handle based on the current device
+ * state.
+ */
+ private boolean supportsVisualStashing() {
+ return !mActivity.isThreeButtonNav();
+ }
+
+ /**
* Returns whether the user can manually stash the taskbar based on the current device state.
*/
- private boolean supportsStashing() {
- return !mActivity.isThreeButtonNav()
+ private boolean supportsManualStashing() {
+ return supportsVisualStashing()
&& (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
}
@@ -206,7 +214,7 @@
* @return Whether we started an animation to either be newly stashed or unstashed.
*/
public boolean updateAndAnimateIsStashedInApp(boolean isStashedInApp) {
- if (!supportsStashing()) {
+ if (!supportsManualStashing()) {
return false;
}
if (mIsStashedInApp != isStashedInApp) {
@@ -307,7 +315,7 @@
* unstashed state.
*/
public void startStashHint(boolean animateForward) {
- if (isStashed() || !supportsStashing()) {
+ if (isStashed() || !supportsManualStashing()) {
// Already stashed, no need to hint in that direction.
return;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 9e93829..9050ddc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -69,6 +70,7 @@
import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -139,6 +141,15 @@
}
@Override
+ protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
+ int cellY, PendingRequestArgs args) {
+ if (container == CONTAINER_HOTSEAT) {
+ mHotseatPredictionController.onDeferredDrop(cellX, cellY);
+ }
+ super.completeAddShortcut(data, container, screenId, cellX, cellY, args);
+ }
+
+ @Override
protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
return new QuickstepAccessibilityDelegate(this);
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 6298bb8..9fa0f1a 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -536,9 +536,4 @@
pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
}
}
-
- /** Called when OverviewService is bound to this process */
- void onOverviewServiceBound() {
- // Do nothing
- }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index ae6ea79..b0bd747 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -309,13 +309,6 @@
}
@Override
- void onOverviewServiceBound() {
- final BaseQuickstepLauncher activity = getCreatedActivity();
- if (activity == null) return;
- activity.getAppTransitionManager().registerRemoteTransitions();
- }
-
- @Override
public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
long duration, RecentsAnimationCallbacks callbacks) {
LauncherTaskbarUIController uiController = getTaskbarController();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index aea2d4c..61540d1 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
@@ -54,6 +53,8 @@
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import com.android.wm.shell.transition.IShellTransitions;
+import java.util.ArrayList;
+
/**
* Holds the reference to SystemUI.
*/
@@ -81,6 +82,7 @@
private ISplitScreenListener mPendingSplitScreenListener;
private IStartingWindowListener mPendingStartingWindowListener;
private ISmartspaceCallback mPendingSmartspaceCallback;
+ private final ArrayList<RemoteTransitionCompat> mPendingRemoteTransitions = new ArrayList<>();
// Used to dedupe calls to SystemUI
private int mLastShelfHeight;
@@ -161,6 +163,10 @@
setSmartspaceCallback(mPendingSmartspaceCallback);
mPendingSmartspaceCallback = null;
}
+ for (int i = mPendingRemoteTransitions.size() - 1; i >= 0; --i) {
+ registerRemoteTransition(mPendingRemoteTransitions.get(i));
+ }
+ mPendingRemoteTransitions.clear();
if (mPendingSetNavButtonAlpha != null) {
mPendingSetNavButtonAlpha.run();
@@ -688,6 +694,8 @@
} catch (RemoteException e) {
Log.w(TAG, "Failed call registerRemoteTransition");
}
+ } else {
+ mPendingRemoteTransitions.add(remoteTransition);
}
}
@@ -699,6 +707,7 @@
Log.w(TAG, "Failed call registerRemoteTransition");
}
}
+ mPendingRemoteTransitions.remove(remoteTransition);
}
//
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 978fb57..e3e7b33 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -51,6 +51,7 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -63,11 +64,16 @@
public class TaskOverlayFactory implements ResourceBasedOverride {
public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
- DeviceProfile deviceProfile) {
+ DeviceProfile deviceProfile, TaskIdAttributeContainer taskContainer) {
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
+ boolean hasMultipleTasks = taskView.getTaskIds()[1] != -1;
for (TaskShortcutFactory menuOption : MENU_OPTIONS) {
- SystemShortcut shortcut = menuOption.getShortcut(activity, taskView);
+ if (hasMultipleTasks && !menuOption.showForSplitscreen()) {
+ continue;
+ }
+
+ SystemShortcut shortcut = menuOption.getShortcut(activity, taskContainer);
if (shortcut == null) {
continue;
}
@@ -87,7 +93,7 @@
if (!canLauncherRotate && isInLandscape) {
// Add screenshot action to task menu.
SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
- .getShortcut(activity, taskView);
+ .getShortcut(activity, taskContainer);
if (screenshotShortcut != null) {
shortcuts.add(screenshotShortcut);
}
@@ -95,7 +101,7 @@
// Add modal action only if display orientation is the same as the device orientation.
if (orientedState.getDisplayRotation() == ROTATION_0) {
SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
- .getShortcut(activity, taskView);
+ .getShortcut(activity, taskContainer);
if (modalShortcut != null) {
shortcuts.add(modalShortcut);
}
@@ -105,7 +111,7 @@
}
- public static void addSplitOptions(List<SystemShortcut> outShortcuts,
+ private static void addSplitOptions(List<SystemShortcut> outShortcuts,
BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
int[] taskViewTaskIds = taskView.getTaskIds();
boolean alreadyHasMultipleTasks = taskViewTaskIds[0] != -1 &&
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 559125e..dbb5f10 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -45,6 +45,7 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
@@ -61,9 +62,25 @@
* Represents a system shortcut that can be shown for a recent task.
*/
public interface TaskShortcutFactory {
- SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
+ SystemShortcut getShortcut(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer);
- TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
+ default boolean showForSplitscreen() {
+ return false;
+ }
+
+ TaskShortcutFactory APP_INFO = new TaskShortcutFactory() {
+ @Override
+ public SystemShortcut getShortcut(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ return new AppInfo(activity, taskContainer.getItemInfo());
+ }
+
+ @Override
+ public boolean showForSplitscreen() {
+ return true;
+ }
+ };
abstract class MultiWindowFactory implements TaskShortcutFactory {
@@ -82,15 +99,16 @@
protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
@Override
- public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
- final Task task = taskView.getTask();
+ public SystemShortcut getShortcut(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ final Task task = taskContainer.getTask();
if (!task.isDockable) {
return null;
}
if (!isAvailable(activity, task.key.displayId)) {
return null;
}
- return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this,
+ return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskContainer, this,
mLauncherEvent);
}
}
@@ -123,13 +141,14 @@
private final LauncherEvent mLauncherEvent;
public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
- TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) {
- super(iconRes, textRes, activity, taskView.getItemInfo());
+ TaskIdAttributeContainer taskContainer, MultiWindowFactory factory,
+ LauncherEvent launcherEvent) {
+ super(iconRes, textRes, activity, taskContainer.getItemInfo());
mLauncherEvent = launcherEvent;
mHandler = new Handler(Looper.getMainLooper());
- mTaskView = taskView;
+ mTaskView = taskContainer.getTaskView();
mRecentsView = activity.getOverviewPanel();
- mThumbnailView = taskView.getThumbnail();
+ mThumbnailView = taskContainer.getThumbnailView();
mFactory = factory;
}
@@ -233,11 +252,13 @@
}
@Override
- public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) {
- SystemShortcut shortcut = super.getShortcut(activity, taskView);
+ public SystemShortcut getShortcut(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ SystemShortcut shortcut = super.getShortcut(activity, taskContainer);
if (shortcut != null && FeatureFlags.ENABLE_SPLIT_SELECT.get()) {
// Disable if there's only one recent app for split screen
- shortcut.setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
+ shortcut.setEnabled(taskContainer.getTaskView().
+ getRecentsView().getTaskViewCount() > 1);
}
return shortcut;
}
@@ -284,7 +305,7 @@
}
};
- TaskShortcutFactory PIN = (activity, tv) -> {
+ TaskShortcutFactory PIN = (activity, taskContainer) -> {
if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
return null;
}
@@ -295,7 +316,7 @@
// We shouldn't be able to pin while an app is locked.
return null;
}
- return new PinSystemShortcut(activity, tv);
+ return new PinSystemShortcut(activity, taskContainer);
};
class PinSystemShortcut extends SystemShortcut {
@@ -304,9 +325,11 @@
private final TaskView mTaskView;
- public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
- super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, tv.getItemInfo());
- mTaskView = tv;
+ public PinSystemShortcut(BaseDraggingActivity target,
+ TaskIdAttributeContainer taskContainer) {
+ super(R.drawable.ic_pin, R.string.recent_task_option_pin, target,
+ taskContainer.getItemInfo());
+ mTaskView = taskContainer.getTaskView();
}
@Override
@@ -320,20 +343,22 @@
}
}
- TaskShortcutFactory INSTALL = (activity, view) ->
+ TaskShortcutFactory INSTALL = (activity, taskContainer) ->
InstantAppResolver.newInstance(activity).isInstantApp(activity,
- view.getTask().getTopComponent().getPackageName())
- ? new SystemShortcut.Install(activity, view.getItemInfo()) : null;
+ taskContainer.getTask().getTopComponent().getPackageName())
+ ? new SystemShortcut.Install(activity, taskContainer.getItemInfo()) : null;
- TaskShortcutFactory WELLBEING = (activity, view) ->
- WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
+ TaskShortcutFactory WELLBEING = (activity, taskContainer) ->
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo());
- TaskShortcutFactory SCREENSHOT = (activity, tv) -> tv.getThumbnail().getTaskOverlay()
- .getScreenshotShortcut(activity, tv.getItemInfo());
+ TaskShortcutFactory SCREENSHOT = (activity, taskContainer) ->
+ taskContainer.getThumbnailView().getTaskOverlay()
+ .getScreenshotShortcut(activity, taskContainer.getItemInfo());
- TaskShortcutFactory MODAL = (activity, tv) -> {
+ TaskShortcutFactory MODAL = (activity, taskContainer) -> {
if (ENABLE_OVERVIEW_SELECTIONS.get()) {
- return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
+ return taskContainer.getThumbnailView()
+ .getTaskOverlay().getModalStateSystemShortcut(taskContainer.getItemInfo());
}
return null;
};
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 3ce12c5..bb8473b 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -179,12 +179,6 @@
smartspaceTransitionController);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
- mDeviceState.runOnUserUnlocked(() -> {
- final BaseActivityInterface ai =
- mOverviewComponentObserver.getActivityInterface();
- if (ai == null) return;
- ai.onOverviewServiceBound();
- });
});
sIsInitialized = true;
}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index b1b4b54..72d3731 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,5 +1,8 @@
package com.android.quickstep.views;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -11,7 +14,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
@@ -72,7 +74,9 @@
super.bind(primary, orientedState);
mSecondaryTask = secondary;
mTaskIdContainer[1] = secondary.key.id;
- mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2);
+ mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
+ STAGE_POSITION_BOTTOM_OR_RIGHT);
+ mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
mSnapshotView2.bind(secondary);
mSplitBoundsConfig = splitBoundsConfig;
}
@@ -112,6 +116,10 @@
}
}
+ protected boolean showTaskMenuWithContainer(IconView iconView) {
+ return TaskMenuView.showForTask(mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1]);
+ }
+
public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
mSplitBoundsConfig = stagedSplitBounds;
invalidate();
@@ -143,14 +151,14 @@
@Override
public RunnableList launchTaskAnimated() {
getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
- SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, null /*callback*/);
+ STAGE_POSITION_TOP_OR_LEFT, null /*callback*/);
return null;
}
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
- SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback);
+ STAGE_POSITION_TOP_OR_LEFT, callback);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 8ec600d..cb8e7f7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2725,7 +2725,7 @@
if (showAsGrid) {
dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
isFocusedTaskDismissed = dismissedTaskViewId == mFocusedTaskViewId;
- if (isFocusedTaskDismissed) {
+ if (isFocusedTaskDismissed && !isSplitSelectionActive()) {
nextFocusedTaskFromTop =
mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
// Pick the next focused task from the preferred row.
@@ -2908,7 +2908,8 @@
} else if (child instanceof TaskView) {
TaskView taskView = (TaskView) child;
if (isFocusedTaskDismissed) {
- if (!isSameGridRow(taskView, nextFocusedTaskView)) {
+ if (nextFocusedTaskView != null &&
+ !isSameGridRow(taskView, nextFocusedTaskView)) {
continue;
}
} else {
@@ -2945,7 +2946,7 @@
clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
} else {
float primaryTranslation =
- isFocusedTaskDismissed ? nextFocusedTaskWidth : dismissedTaskWidth;
+ nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? primaryTranslation : -primaryTranslation,
clampToProgress(LINEAR, animationStartProgress,
@@ -3255,7 +3256,7 @@
// Never enough space on phones
return true;
} else if (!mActivity.getDeviceProfile().isLandscape) {
- return false;
+ return true;
}
Rect splitBounds = new Rect();
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index bfc7eea..2cf447f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,6 +16,8 @@
package com.android.quickstep.views;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -39,6 +41,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
@@ -49,6 +52,7 @@
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
/**
* Contains options for a recent task when long-pressing its icon.
@@ -65,6 +69,7 @@
private TextView mTaskName;
private AnimatorSet mOpenCloseAnimator;
private TaskView mTaskView;
+ private TaskIdAttributeContainer mTaskContainer;
private LinearLayout mOptionLayout;
public TaskMenuView(Context context, AttributeSet attrs) {
@@ -129,7 +134,8 @@
// Inset due to margin
PointF additionalInset = pagedOrientationHandler
.getAdditionalInsetForTaskMenu(mTaskInsetMargin);
- int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ int taskTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
float adjustedY = y + taskTopMargin - additionalInset.y;
float adjustedX = x - additionalInset.x;
@@ -137,7 +143,7 @@
// NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
// which would render the X and Y position set here incorrect
setPivotX(0);
- if (mActivity.getDeviceProfile().overviewShowAsGrid) {
+ if (deviceProfile.overviewShowAsGrid) {
// In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
setPivotY(-taskTopMargin);
} else {
@@ -145,9 +151,26 @@
}
setRotation(pagedOrientationHandler.getDegreesRotated());
setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
- mTaskView.getThumbnail(), overscrollShift));
+ mTaskContainer.getThumbnailView(), overscrollShift, deviceProfile));
setY(pagedOrientationHandler.getTaskMenuY(
- adjustedY, mTaskView.getThumbnail(), overscrollShift));
+ adjustedY, mTaskContainer.getThumbnailView(), overscrollShift));
+
+ // TODO(b/193432925) temporary menu placement for split screen task menus
+ TaskIdAttributeContainer[] taskIdAttributeContainers =
+ mTaskView.getTaskIdAttributeContainers();
+ if (taskIdAttributeContainers[0].getStagePosition() != STAGE_POSITION_UNDEFINED) {
+ if (mTaskContainer.getStagePosition() != STAGE_POSITION_BOTTOM_OR_RIGHT) {
+ return;
+ }
+ Rect r = new Rect();
+ mTaskContainer.getThumbnailView().getBoundsOnScreen(r);
+ if (deviceProfile.isLandscape) {
+ setX(r.left);
+ } else {
+ setY(r.top);
+
+ }
+ }
}
public void onRotationChanged() {
@@ -162,19 +185,21 @@
}
}
- public static boolean showForTask(TaskView taskView) {
- BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext());
+ public static boolean showForTask(TaskIdAttributeContainer taskContainer) {
+ BaseDraggingActivity activity = BaseDraggingActivity.fromContext(
+ taskContainer.getTaskView().getContext());
final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate(
R.layout.task_menu, activity.getDragLayer(), false);
- return taskMenuView.populateAndShowForTask(taskView);
+ return taskMenuView.populateAndShowForTask(taskContainer);
}
- private boolean populateAndShowForTask(TaskView taskView) {
+ private boolean populateAndShowForTask(TaskIdAttributeContainer taskContainer) {
if (isAttachedToWindow()) {
return false;
}
mActivity.getDragLayer().addView(this);
- mTaskView = taskView;
+ mTaskView = taskContainer.getTaskView();
+ mTaskContainer = taskContainer;
if (!populateAndLayoutMenu()) {
return false;
}
@@ -192,20 +217,20 @@
/** @return true if successfully able to populate task view menu, false otherwise */
private boolean populateAndLayoutMenu() {
- if (mTaskView.getTask().icon == null) {
+ if (mTaskContainer.getTask().icon == null) {
// Icon may not be loaded
return false;
}
- addMenuOptions(mTaskView);
- orientAroundTaskView(mTaskView);
+ addMenuOptions(mTaskContainer);
+ orientAroundTaskView(mTaskContainer);
return true;
}
- private void addMenuOptions(TaskView taskView) {
- mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
+ private void addMenuOptions(TaskIdAttributeContainer taskContainer) {
+ mTaskName.setText(TaskUtils.getTitle(getContext(), taskContainer.getTask()));
mTaskName.setOnClickListener(v -> close(true));
-
- TaskOverlayFactory.getEnabledShortcuts(taskView, mActivity.getDeviceProfile())
+ TaskOverlayFactory.getEnabledShortcuts(mTaskView, mActivity.getDeviceProfile(),
+ taskContainer)
.forEach(this::addMenuOption);
}
@@ -223,23 +248,33 @@
mOptionLayout.addView(menuOptionView);
}
- private void orientAroundTaskView(TaskView taskView) {
- PagedOrientationHandler orientationHandler = taskView.getPagedOrientationHandler();
+ private void orientAroundTaskView(TaskIdAttributeContainer taskContainer) {
+ RecentsView recentsView = mActivity.getOverviewPanel();
+ PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);
// Get Position
- mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ mActivity.getDragLayer().getDescendantRectRelativeToSelf(mTaskView, sTempRect);
Rect insets = mActivity.getDragLayer().getInsets();
BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
int padding = getResources()
.getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
- params.width = orientationHandler.getTaskMenuWidth(taskView.getThumbnail()) - (2 * padding);
+ if (deviceProfile.overviewShowAsGrid) {
+ // TODO(b/193432925) temporary so it doesn't look terrible on large screen
+ params.width =
+ getContext().getResources().getDimensionPixelSize(R.dimen.task_menu_width_grid);
+ } else {
+ params.width = orientationHandler
+ .getTaskMenuWidth(taskContainer.getThumbnailView(),
+ deviceProfile) - (2 * padding);
+ }
// Gravity set to Left instead of Start as sTempRect.left measures Left distance not Start
params.gravity = Gravity.LEFT;
setLayoutParams(params);
- setScaleX(taskView.getScaleX());
- setScaleY(taskView.getScaleY());
+ setScaleX(mTaskView.getScaleX());
+ setScaleY(mTaskView.getScaleY());
// Set divider spacing
ShapeDrawable divider = new ShapeDrawable(new RectShape());
@@ -248,7 +283,7 @@
mOptionLayout.setShowDividers(SHOW_DIVIDER_MIDDLE);
orientationHandler.setTaskOptionsMenuLayoutOrientation(
- mActivity.getDeviceProfile(), mOptionLayout, dividerSpacing, divider);
+ deviceProfile, mOptionLayout, dividerSpacing, divider);
setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
}
@@ -272,7 +307,7 @@
revealAnimator.setInterpolator(Interpolators.DEACCEL);
mOpenCloseAnimator.playTogether(revealAnimator,
ObjectAnimator.ofFloat(
- mTaskView.getThumbnail(), DIM_ALPHA,
+ mTaskContainer.getThumbnailView(), DIM_ALPHA,
closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 461a89b..053d07c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,13 +18,6 @@
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.view.Gravity.END;
-import static android.view.Gravity.START;
-import static android.view.Gravity.TOP;
-import static android.view.Surface.ROTATION_180;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
@@ -39,6 +32,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -58,7 +52,6 @@
import android.util.FloatProperty;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.Surface;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewGroup;
@@ -86,6 +79,7 @@
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
@@ -466,7 +460,10 @@
* Builds proto for logging
*/
public WorkspaceItemInfo getItemInfo() {
- final Task task = getTask();
+ return getItemInfo(mTask);
+ }
+
+ protected WorkspaceItemInfo getItemInfo(Task task) {
ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
@@ -569,7 +566,8 @@
cancelPendingLoadTasks();
mTask = task;
mTaskIdContainer[0] = mTask.key.id;
- mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView);
+ mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
+ STAGE_POSITION_UNDEFINED);
mSnapshotView.bind(task);
setOrientationState(orientedState);
}
@@ -825,7 +823,7 @@
}
}
- private boolean showTaskMenu() {
+ private boolean showTaskMenu(IconView iconView) {
if (getRecentsView().mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
// Don't show menu when selecting second split screen app
return true;
@@ -837,10 +835,14 @@
} else {
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
.log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
- return TaskMenuView.showForTask(this);
+ return showTaskMenuWithContainer(iconView);
}
}
+ protected boolean showTaskMenuWithContainer(IconView iconView) {
+ return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]);
+ }
+
protected void setIcon(IconView iconView, Drawable icon) {
if (icon != null) {
iconView.setDrawable(icon);
@@ -850,14 +852,14 @@
recentsView.switchToScreenshot(
() -> recentsView.finishRecentsAnimation(true /* toRecents */,
false /* shouldPip */,
- this::showTaskMenu));
+ () -> showTaskMenu(iconView)));
} else {
- showTaskMenu();
+ showTaskMenu(iconView);
}
});
iconView.setOnLongClickListener(v -> {
requestDisallowInterceptTouchEvent(true);
- return showTaskMenu();
+ return showTaskMenu(iconView);
});
} else {
iconView.setDrawable(null);
@@ -1328,8 +1330,9 @@
getContext().getText(R.string.accessibility_close)));
final Context context = getContext();
+ // TODO(b/200609838) Determine which task to run A11y action on when in split screen
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- mActivity.getDeviceProfile())) {
+ mActivity.getDeviceProfile(), mTaskIdAttributeContainer[0])) {
info.addAction(s.createAccessibilityAction(context));
}
@@ -1361,8 +1364,9 @@
return true;
}
+ // TODO(b/200609838) Determine which task to run A11y action on when in split screen
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
- mActivity.getDeviceProfile())) {
+ mActivity.getDeviceProfile(), mTaskIdAttributeContainer[0])) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);
return true;
@@ -1581,20 +1585,40 @@
}
public class TaskIdAttributeContainer {
- private final TaskThumbnailView thumbnailView;
- private final Task task;
+ private final TaskThumbnailView mThumbnailView;
+ private final Task mTask;
+ /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
+ private @SplitConfigurationOptions.StagePosition int mStagePosition;
- public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView) {
- this.task = task;
- this.thumbnailView = thumbnailView;
+ public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView,
+ int stagePosition) {
+ this.mTask = task;
+ this.mThumbnailView = thumbnailView;
+ this.mStagePosition = stagePosition;
}
public TaskThumbnailView getThumbnailView() {
- return thumbnailView;
+ return mThumbnailView;
}
public Task getTask() {
- return task;
+ return mTask;
+ }
+
+ public WorkspaceItemInfo getItemInfo() {
+ return TaskView.this.getItemInfo(mTask);
+ }
+
+ public TaskView getTaskView() {
+ return TaskView.this;
+ }
+
+ public int getStagePosition() {
+ return mStagePosition;
+ }
+
+ void setStagePosition(@SplitConfigurationOptions.StagePosition int stagePosition) {
+ this.mStagePosition = stagePosition;
}
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index e5e560a..17a88e5 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -37,6 +37,7 @@
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
@@ -157,6 +158,7 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
+ @ScreenRecord // b/195673272
public void testOverviewActions() throws Exception {
// Experimenting for b/165029151:
final Overview overview = mLauncher.pressHome().switchToOverview();
diff --git a/res/color-v31/overview_scrim_dark.xml b/res/color-v31/overview_scrim_dark.xml
index 85ede9a..2ab8ecd 100644
--- a/res/color-v31/overview_scrim_dark.xml
+++ b/res/color-v31/overview_scrim_dark.xml
@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
</selector>
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 8afd40b..0fc0ff8 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -67,7 +67,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:visibility="gone"
android:clipToPadding="false" />
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 85f14cd..dfe226a 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -20,7 +20,6 @@
android:id="@+id/widgets_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false"
android:layout_below="@id/collapse_handle"
android:descendantFocusability="afterDescendants"
@@ -30,12 +29,14 @@
android:id="@+id/primary_widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
<com.android.launcher3.widget.picker.WidgetsRecyclerView
android:id="@+id/work_widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
</com.android.launcher3.workprofile.PersonalWorkPagedView>
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index dde82ea..6a5d6cb 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -19,7 +19,7 @@
android:layout_below="@id/collapse_handle"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToPadding="false" />
<!-- SearchAndRecommendationsView without the tab layout as well -->
diff --git a/res/values/config.xml b/res/values/config.xml
index 72959b2..7c681a8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -176,7 +176,7 @@
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
- <dimen name="unlock_staggered_velocity_dp_per_s">4dp</dimen>
+ <dimen name="unlock_staggered_velocity_dp_per_s">2dp</dimen>
<item name="hint_scale_damping_ratio" type="dimen" format="float">0.7</item>
<item name="hint_scale_stiffness" type="dimen" format="float">200</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3d14681..1cf49fc 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -57,6 +57,7 @@
<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_invalid_drag_across_two_panel_opacity_margin">24dp</dimen>
<!-- App widget reconfigure button -->
<dimen name="widget_reconfigure_button_corner_radius">14dp</dimen>
@@ -322,7 +323,7 @@
<!-- Size of the maximum radius for the enforced rounded rectangles. -->
<dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
-<!-- Overview placeholder to compile in Launcer3 without Quickstep -->
+<!-- Overview placeholder to compile in Launcher3 without Quickstep -->
<dimen name="task_thumbnail_icon_size">0dp</dimen>
<dimen name="task_thumbnail_icon_drawable_size">0dp</dimen>
<dimen name="task_thumbnail_icon_drawable_size_grid">0dp</dimen>
@@ -340,6 +341,8 @@
<dimen name="recents_page_spacing">0dp</dimen>
<dimen name="recents_page_spacing_grid">0dp</dimen>
<dimen name="split_placeholder_size">110dp</dimen>
+ <dimen name="task_menu_width_grid">200dp</dimen>
+
<!-- Workspace grid visualization parameters -->
<dimen name="grid_visualization_rounding_radius">22dp</dimen>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ee71146..fef3f8f 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -2,6 +2,7 @@
import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
+import static com.android.launcher3.CellLayout.SPRING_LOADED_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_COMPLETED;
@@ -9,6 +10,8 @@
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -29,6 +32,7 @@
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
@@ -49,12 +53,14 @@
private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
"launcher.reconfigurable_widget_education_tip_seen";
private static final Rect sTmpRect = new Rect();
+ private static final Rect sTmpRect2 = new Rect();
private static final int HANDLE_COUNT = 4;
private static final int INDEX_LEFT = 0;
private static final int INDEX_TOP = 1;
private static final int INDEX_RIGHT = 2;
private static final int INDEX_BOTTOM = 3;
+ private static final float MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE = 0.5f;
private final Launcher mLauncher;
private final DragViewStateAnnouncer mStateAnnouncer;
@@ -103,6 +109,16 @@
private final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
+ private final ViewGroupFocusHelper mDragLayerRelativeCoordinateHelper;
+
+ /**
+ * In the two panel UI, it is not possible to resize a widget to cross its host
+ * {@link CellLayout}'s sibling. When this happens, we gradually reduce the opacity of the
+ * sibling {@link CellLayout} from 1f to
+ * {@link #MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE}.
+ */
+ private final float mDragAcrossTwoPanelOpacityMargin;
+
private boolean mLeftBorderActive;
private boolean mRightBorderActive;
private boolean mTopBorderActive;
@@ -149,6 +165,10 @@
for (int i = 0; i < HANDLE_COUNT; i++) {
mSystemGestureExclusionRects.add(new Rect());
}
+
+ mDragAcrossTwoPanelOpacityMargin = mLauncher.getResources().getDimensionPixelSize(
+ R.dimen.resize_frame_invalid_drag_across_two_panel_opacity_margin);
+ mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
}
@Override
@@ -359,6 +379,37 @@
lp.y = sTmpRect.top;
}
+ // Handle invalid resize across CellLayouts in the two panel UI.
+ if (mCellLayout.getParent() instanceof Workspace) {
+ Workspace workspace = (Workspace) mCellLayout.getParent();
+ CellLayout pairedCellLayout = workspace.getScreenPair(mCellLayout);
+ if (pairedCellLayout != null) {
+ Rect focusedCellLayoutBound = sTmpRect;
+ mDragLayerRelativeCoordinateHelper.viewToRect(mCellLayout, focusedCellLayoutBound);
+ Rect resizeFrameBound = sTmpRect2;
+ findViewById(R.id.widget_resize_frame).getGlobalVisibleRect(resizeFrameBound);
+ float progress = 1f;
+ if (workspace.indexOfChild(pairedCellLayout) < workspace.indexOfChild(mCellLayout)
+ && mDeltaX < 0
+ && resizeFrameBound.left < focusedCellLayoutBound.left) {
+ // Resize from right to left.
+ progress = (mDragAcrossTwoPanelOpacityMargin + mDeltaX)
+ / mDragAcrossTwoPanelOpacityMargin;
+ } else if (workspace.indexOfChild(pairedCellLayout)
+ > workspace.indexOfChild(mCellLayout)
+ && mDeltaX > 0
+ && resizeFrameBound.right > focusedCellLayoutBound.right) {
+ // Resize from left to right.
+ progress = (mDragAcrossTwoPanelOpacityMargin - mDeltaX)
+ / mDragAcrossTwoPanelOpacityMargin;
+ }
+ float alpha = Math.max(MIN_OPACITY_FOR_CELL_LAYOUT_DURING_INVALID_RESIZE, progress);
+ float springLoadedProgress = Math.min(1f, 1f - progress);
+ updateInvalidResizeEffect(mCellLayout, pairedCellLayout, alpha,
+ springLoadedProgress);
+ }
+ }
+
requestLayout();
}
@@ -515,13 +566,24 @@
}
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ final CellLayout pairedCellLayout;
+ if (mCellLayout.getParent() instanceof Workspace) {
+ Workspace workspace = (Workspace) mCellLayout.getParent();
+ pairedCellLayout = workspace.getScreenPair(mCellLayout);
+ } else {
+ pairedCellLayout = null;
+ }
if (!animate) {
lp.width = newWidth;
lp.height = newHeight;
lp.x = newX;
lp.y = newY;
for (int i = 0; i < HANDLE_COUNT; i++) {
- mDragHandles[i].setAlpha(1.0f);
+ mDragHandles[i].setAlpha(1f);
+ }
+ if (pairedCellLayout != null) {
+ updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
+ /* springLoadedProgress= */ 0f);
}
requestLayout();
} else {
@@ -538,6 +600,10 @@
set.play(mFirstFrameAnimatorHelper.addTo(
ObjectAnimator.ofFloat(mDragHandles[i], ALPHA, 1f)));
}
+ if (pairedCellLayout != null) {
+ updateInvalidResizeEffect(mCellLayout, pairedCellLayout, /* alpha= */ 1f,
+ /* springLoadedProgress= */ 0f, /* animatorSet= */ set);
+ }
set.setDuration(SNAP_DURATION);
set.start();
}
@@ -624,6 +690,52 @@
}
}
+ private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
+ float alpha, float springLoadedProgress) {
+ updateInvalidResizeEffect(cellLayout, pairedCellLayout, alpha,
+ springLoadedProgress, /* animatorSet= */ null);
+ }
+
+ private void updateInvalidResizeEffect(CellLayout cellLayout, CellLayout pairedCellLayout,
+ float alpha, float springLoadedProgress, @Nullable AnimatorSet animatorSet) {
+ int childCount = pairedCellLayout.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = pairedCellLayout.getChildAt(i);
+ if (animatorSet != null) {
+ animatorSet.play(
+ mFirstFrameAnimatorHelper.addTo(
+ ObjectAnimator.ofFloat(child, ALPHA, alpha)));
+ } else {
+ child.setAlpha(alpha);
+ }
+ }
+ if (animatorSet != null) {
+ animatorSet.play(mFirstFrameAnimatorHelper.addTo(
+ ObjectAnimator.ofFloat(cellLayout, SPRING_LOADED_PROGRESS,
+ springLoadedProgress)));
+ animatorSet.play(mFirstFrameAnimatorHelper.addTo(
+ ObjectAnimator.ofFloat(pairedCellLayout, SPRING_LOADED_PROGRESS,
+ springLoadedProgress)));
+ } else {
+ cellLayout.setSpringLoadedProgress(springLoadedProgress);
+ pairedCellLayout.setSpringLoadedProgress(springLoadedProgress);
+ }
+
+ boolean shouldShowCellLayoutBorder = springLoadedProgress > 0f;
+ if (animatorSet != null) {
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ cellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ pairedCellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ }
+ });
+ } else {
+ cellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ pairedCellLayout.setIsDragOverlapping(shouldShowCellLayoutBorder);
+ }
+ }
+
@Override
protected boolean isOfType(int type) {
return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3121bfc..ce8cf3c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -33,7 +33,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
-import android.util.Pair;
import android.view.Surface;
import com.android.launcher3.CellLayout.ContainerType;
@@ -215,8 +214,6 @@
// Whether Taskbar will inset the bottom of apps by taskbarSize.
public boolean isTaskbarPresentInApps;
public int taskbarSize;
- // How much of the bottom inset is due to Taskbar rather than other system elements.
- public int nonOverlappingTaskbarInset;
// DragController
public int flingToDeleteThresholdVelocity;
@@ -239,7 +236,7 @@
widthPx = windowBounds.bounds.width();
heightPx = windowBounds.bounds.height();
availableWidthPx = windowBounds.availableSize.x;
- int nonFinalAvailableHeightPx = windowBounds.availableSize.y;
+ availableHeightPx = windowBounds.availableSize.y;
mInfo = info;
// If the device's pixel density was scaled (usually via settings for A11y), use the
@@ -266,15 +263,8 @@
isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
&& FeatureFlags.ENABLE_TASKBAR.get();
if (isTaskbarPresent) {
- // Taskbar will be added later, but provides bottom insets that we should subtract
- // from availableHeightPx.
taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
- nonOverlappingTaskbarInset = taskbarSize - windowBounds.insets.bottom;
- if (nonOverlappingTaskbarInset > 0) {
- nonFinalAvailableHeightPx -= nonOverlappingTaskbarInset;
- }
}
- availableHeightPx = nonFinalAvailableHeightPx;
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
@@ -842,7 +832,7 @@
padding.right = hotseatBarSizePx;
}
} else {
- int hotseatTop = isTaskbarPresent ? taskbarSize : hotseatBarSizePx;
+ int hotseatTop = hotseatBarSizePx;
int paddingBottom = hotseatTop + workspacePageIndicatorHeight
+ workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
if (isTablet) {
@@ -853,8 +843,7 @@
((inv.numColumns - 1) * cellWidthPx)));
availablePaddingX = (int) Math.min(availablePaddingX,
widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
- int hotseatVerticalPadding = isTaskbarPresent ? 0
- : hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
+ int hotseatVerticalPadding = hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom
- (2 * inv.numRows * cellHeightPx) - hotseatVerticalPadding);
padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
@@ -886,9 +875,9 @@
mInsets.right + hotseatBarSidePaddingStartPx, mInsets.bottom);
}
} else if (isTaskbarPresent) {
- int hotseatHeight = workspacePadding.bottom + taskbarSize;
+ int hotseatHeight = workspacePadding.bottom;
int taskbarOffset = getTaskbarOffsetY();
- int hotseatTopDiff = hotseatHeight - taskbarSize - taskbarOffset;
+ int hotseatTopDiff = hotseatHeight - taskbarOffset;
int endOffset = ApiWrapper.getHotseatEndOffset(context);
int requiredWidth = iconSizePx * numShownHotseatIcons;
@@ -938,7 +927,8 @@
: hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
if (isScalableGrid && qsbBottomMarginPx > mInsets.bottom) {
- return Math.min(qsbBottomMarginPx, freeSpace);
+ // Note that taskbarSize = 0 unless isTaskbarPresent.
+ return Math.min(qsbBottomMarginPx + taskbarSize, freeSpace);
} else {
return (int) (freeSpace * QSB_CENTER_FACTOR)
+ (isTaskbarPresent ? taskbarSize : mInsets.bottom);
@@ -1116,10 +1106,7 @@
writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
writer.println(prefix + "\tisTaskbarPresentInApps:" + isTaskbarPresentInApps);
-
writer.println(prefix + pxToDpStr("taskbarSize", taskbarSize));
- writer.println(prefix + pxToDpStr("nonOverlappingTaskbarInset",
- nonOverlappingTaskbarInset));
writer.println(prefix + pxToDpStr("workspacePadding.left", workspacePadding.left));
writer.println(prefix + pxToDpStr("workspacePadding.top", workspacePadding.top));
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 85dd3b3..8a35185 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1264,7 +1264,7 @@
*
* @param data The intent describing the shortcut.
*/
- private void completeAddShortcut(Intent data, int container, int screenId, int cellX,
+ protected void completeAddShortcut(Intent data, int container, int screenId, int cellX,
int cellY, PendingRequestArgs args) {
if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
|| args.getPendingIntent().getComponent() == null) {
@@ -2128,7 +2128,7 @@
actualIds.add(id);
}
int firstId = visibleIds.getArray().get(0);
- int pairId = mWorkspace.getPagePair(firstId);
+ int pairId = mWorkspace.getScreenPair(firstId);
// Double check that actual screenIds contains the visibleId, as empty screens are hidden
// in single panel.
if (actualIds.contains(firstId)) {
@@ -2212,7 +2212,7 @@
// Some empty pages might have been removed while the phone was in a single panel
// mode, so we want to add those empty pages back.
IntSet screenIds = IntSet.wrap(orderedScreenIds);
- orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getPagePair(screenId)));
+ orderedScreenIds.forEach(screenId -> screenIds.add(mWorkspace.getScreenPair(screenId)));
orderedScreenIds = screenIds.getArray();
}
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index f26cfe8..7de2ee4 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -4,15 +4,20 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewDebug;
import android.view.WindowInsets;
+import androidx.annotation.RequiresApi;
+
import com.android.launcher3.graphics.SysUiScrim;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.uioverrides.ApiWrapper;
import java.util.Collections;
import java.util.List;
@@ -42,15 +47,8 @@
}
private void handleSystemWindowInsets(Rect insets) {
- DeviceProfile dp = mActivity.getDeviceProfile();
-
- // Taskbar provides insets, but we don't want that for most Launcher elements so remove it.
- mTempRect.set(insets);
- insets = mTempRect;
- insets.bottom = Math.max(0, insets.bottom - dp.nonOverlappingTaskbarInset);
-
// Update device profile before notifying the children.
- dp.updateInsets(insets);
+ mActivity.getDeviceProfile().updateInsets(insets);
boolean resetState = !insets.equals(mInsets);
setInsets(insets);
@@ -61,12 +59,69 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+ if (Utilities.ATLEAST_R) {
+ insets = updateInsetsDueToTaskbar(insets);
+ Insets systemWindowInsets = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+ mTempRect.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
+ systemWindowInsets.bottom);
+ } else {
+ mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+ }
handleSystemWindowInsets(mTempRect);
return insets;
}
+ /**
+ * Taskbar provides nav bar and tappable insets. However, taskbar is not attached immediately,
+ * and can be destroyed and recreated. Thus, instead of relying on taskbar being present to
+ * get its insets, we calculate them ourselves so they are stable regardless of whether taskbar
+ * is currently attached.
+ *
+ * @param oldInsets The system-provided insets, which we are modifying.
+ * @return The updated insets.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.R)
+ private WindowInsets updateInsetsDueToTaskbar(WindowInsets oldInsets) {
+ if (!ApiWrapper.TASKBAR_DRAWN_IN_PROCESS) {
+ // 3P launchers based on Launcher3 should still be inset like normal.
+ return oldInsets;
+ }
+
+ WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ Resources resources = getResources();
+
+ Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+ Rect newNavInsets = new Rect(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+ oldNavInsets.bottom);
+
+ if (dp.isLandscape) {
+ if (dp.isTablet) {
+ newNavInsets.bottom = ResourceUtils.getNavbarSize(
+ "navigation_bar_height_landscape", resources);
+ } else {
+ int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
+ if (dp.isSeascape()) {
+ newNavInsets.left = navWidth;
+ } else {
+ newNavInsets.right = navWidth;
+ }
+ }
+ } else {
+ newNavInsets.bottom = ResourceUtils.getNavbarSize("navigation_bar_height", resources);
+ }
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
+ updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
+ Insets.of(newNavInsets));
+
+ mActivity.updateWindowInsets(updatedInsetsBuilder, oldInsets);
+
+ return updatedInsetsBuilder.build();
+ }
+
@Override
public void setInsets(Rect insets) {
// If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index a7198a8..d534c5d 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -208,7 +208,7 @@
public void initParentViews(View parent) {
if (mPageIndicatorViewId > -1) {
mPageIndicator = parent.findViewById(mPageIndicatorViewId);
- mPageIndicator.setMarkersCount(getChildCount());
+ mPageIndicator.setMarkersCount(getChildCount() / getPanelCount());
}
}
@@ -830,7 +830,7 @@
private void dispatchPageCountChanged() {
if (mPageIndicator != null) {
- mPageIndicator.setMarkersCount(getChildCount());
+ mPageIndicator.setMarkersCount(getChildCount() / getPanelCount());
}
// This ensures that when children are added, they get the correct transforms / alphas
// in accordance with any scroll effects.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index bd2a14f..c5e9dd2 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -314,8 +314,6 @@
Rect padding = grid.workspacePadding;
setPadding(padding.left, padding.top, padding.right, padding.bottom);
mInsets.set(insets);
- // Increase our bottom insets so we don't overlap with the taskbar.
- mInsets.bottom += grid.nonOverlappingTaskbarInset;
if (mWorkspaceFadeInAdjacentScreens) {
// In landscape mode the page spacing is set to the default.
@@ -651,7 +649,7 @@
// If the icon was dragged from Hotseat, there is no page pair
if (isTwoPanelEnabled() && !(mDragSourceInternal.getParent() instanceof Hotseat)) {
- int pagePairScreenId = getPagePair(dragObject.dragInfo.screenId);
+ int pagePairScreenId = getScreenPair(dragObject.dragInfo.screenId);
CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId);
if (pagePair == null) {
// TODO: after http://b/198820019 is fixed, remove this
@@ -917,16 +915,33 @@
}
/**
- * Returns the page that is shown together with the given page when two panel is enabled.
+ * Returns the screen ID of a page that is shown together with the given page screen ID when the
+ * two panel UI is enabled.
*/
- public int getPagePair(int page) {
- if (page % 2 == 0) {
- return page + 1;
+ public int getScreenPair(int screenId) {
+ if (screenId % 2 == 0) {
+ return screenId + 1;
} else {
- return page - 1;
+ return screenId - 1;
}
}
+ /**
+ * Returns {@link CellLayout} that is shown together with the given {@link CellLayout} when the
+ * two panel UI is enabled.
+ */
+ @Nullable
+ public CellLayout getScreenPair(CellLayout cellLayout) {
+ if (!isTwoPanelEnabled()) {
+ return null;
+ }
+ int screenId = getIdForScreen(cellLayout);
+ if (screenId == -1) {
+ return null;
+ }
+ return getScreenWithId(getScreenPair(screenId));
+ }
+
public void stripEmptyScreens() {
if (mLauncher.isWorkspaceLoading()) {
// Don't strip empty screens if the workspace is still loading.
@@ -959,7 +974,7 @@
Iterator<Integer> removeScreensIterator = removeScreens.iterator();
while (removeScreensIterator.hasNext()) {
int pageToRemove = removeScreensIterator.next();
- int pagePair = getPagePair(pageToRemove);
+ int pagePair = getScreenPair(pageToRemove);
if (!removeScreens.contains(pagePair)) {
// The page pair isn't empty so we want to remove the current page from the
// removable pages' collection
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2032b26..157df5d 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -162,7 +162,8 @@
}
}
- if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
+ if ((item instanceof AppInfo) || (item instanceof WorkspaceItemInfo)
+ || (item instanceof PendingAddItemInfo)) {
out.add(mActions.get(ADD_TO_WORKSPACE));
}
}
@@ -244,6 +245,13 @@
mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
}
+ else if (item instanceof WorkspaceItemInfo) {
+ WorkspaceItemInfo info = ((WorkspaceItemInfo) item).clone();
+ mLauncher.getModelWriter().addItemToDatabase(info,
+ Favorites.CONTAINER_DESKTOP,
+ screenId, coordinates[0], coordinates[1]);
+ mLauncher.bindItems(Collections.singletonList(info), true, true);
+ }
}));
return true;
} else if (action == MOVE_TO_WORKSPACE) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 6d3f309..9a5fd05 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -408,8 +408,7 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
if (Utilities.ATLEAST_Q) {
- mNavBarScrimHeight = insets.getTappableElementInsets().bottom
- - mLauncher.getDeviceProfile().nonOverlappingTaskbarInset;
+ mNavBarScrimHeight = insets.getTappableElementInsets().bottom;
} else {
mNavBarScrimHeight = insets.getStableInsetBottom();
}
@@ -599,7 +598,7 @@
mIsSearching = false;
mHeader.setCollapsed(false);
rebindAdapters();
- getActiveRecyclerView().scrollToTop();
+ mHeader.reset(false);
}
public void onSearchResultsChanged() {
@@ -670,6 +669,7 @@
@Override
public void drawOnScrim(Canvas canvas) {
+ if (!mHeader.isHeaderProtectionSupported()) return;
mHeaderPaint.setColor(mHeaderColor);
mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a6d8552..fc78bea 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -39,7 +39,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.config.FeatureFlags;
@@ -61,7 +60,6 @@
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
- private static final float CONTENT_VISIBLE_MAX_THRESHOLD = 0.5f;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -188,8 +186,7 @@
int visibleElements = state.getVisibleElements(mLauncher);
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
- Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE,
- Interpolators.clampToProgress(LINEAR, 0, CONTENT_VISIBLE_MAX_THRESHOLD));
+ Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
boolean shouldProtectHeader =
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 382f7a7..c067291 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -271,6 +271,10 @@
"QUICK_WALLPAPER_PICKER", false,
"Shows quick wallpaper picker in long-press menu");
+ public static final BooleanFlag ENABLE_BACK_SWIPE_HOME_ANIMATION = getDebugFlag(
+ "ENABLE_BACK_SWIPE_HOME_ANIMATION", true,
+ "Enables home animation to icon when user swipes back.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 9889a80..b74d0fc 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -21,7 +21,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.data.AppInfo;
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.pm.PackageInstallInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -73,13 +72,7 @@
dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
if (si.hasPromiseIconUi()
&& mInstallInfo.packageName.equals(si.getTargetPackage())) {
- int installProgress = mInstallInfo.progress;
-
- si.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING);
- if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
- // Mark this info as broken.
- si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
- }
+ si.setProgressLevel(mInstallInfo);
updates.add(si);
}
});
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 6813b97..a74c02f 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -24,6 +24,7 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -179,6 +180,12 @@
*/
public void setProgressLevel(PackageInstallInfo installInfo) {
setProgressLevel(installInfo.progress, installInfo.state);
+
+ if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
+ FileLog.d(TAG,
+ "Icon info: " + this + " marked broken with install info: " + installInfo,
+ new Exception());
+ }
}
/**
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 690e904..a395709 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -204,7 +204,7 @@
}
@Override
- public ItemInfoWithIcon clone() {
+ public WorkspaceItemInfo clone() {
return new WorkspaceItemInfo(this);
}
}
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index f73d782..c685891 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -268,9 +268,7 @@
} else {
lp.leftMargin = lp.rightMargin = 0;
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.bottomMargin = grid.isTaskbarPresent
- ? grid.workspacePadding.bottom + grid.taskbarSize
- : grid.hotseatBarSizePx + insets.bottom;
+ lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
}
setLayoutParams(lp);
}
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
index fad904f..1797c1f 100644
--- a/src/com/android/launcher3/pm/PackageInstallInfo.java
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -57,4 +57,28 @@
public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
}
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(" + dumpProperties() + ")";
+ }
+
+ private String dumpProperties() {
+ return "componentName=" + componentName
+ + "packageName=" + packageName
+ + " state=" + stateToString()
+ + " progress=" + progress
+ + " user=" + user;
+ }
+
+ private String stateToString() {
+ switch (state) {
+ case STATUS_INSTALLED : return "STATUS_INSTALLED";
+ case STATUS_INSTALLING : return "STATUS_INSTALLING";
+ case STATUS_INSTALLED_DOWNLOADING : return "STATUS_INSTALLED_DOWNLOADING";
+ case STATUS_FAILED : return "STATUS_FAILED";
+ default : return "INVALID STATE";
+ }
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index bc3419a..454dc6e 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -480,12 +480,12 @@
@Override
protected void closeComplete() {
+ super.closeComplete();
PopupContainerWithArrow openPopup = getOpen(mLauncher);
if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
mOriginalIcon.setForceHideDot(false);
}
- super.closeComplete();
}
/**
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 8a35cb3..7a23caa 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -17,11 +17,15 @@
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
+import android.graphics.Insets;
+import android.os.Build;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowInsets;
import androidx.annotation.CallSuper;
+import androidx.annotation.RequiresApi;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherRootView;
@@ -173,4 +177,12 @@
mHandler.removeCallbacks(mHandleDeferredResume);
Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
}
+
+ /**
+ * Gives subclasses a chance to override some window insets (via
+ * {@link android.view.WindowInsets.Builder#setInsets(int, Insets)}).
+ */
+ @RequiresApi(api = Build.VERSION_CODES.R)
+ public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
+ WindowInsets oldInsets) { }
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 8db1dbe..5fe5450 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -59,11 +59,10 @@
float scale = grid.workspaceSpringLoadShrinkFactor;
Rect insets = launcher.getDragLayer().getInsets();
- int insetsBottom = grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom;
float scaledHeight = scale * ws.getNormalChildHeight();
float shrunkTop = insets.top + grid.dropTargetBarSizePx;
- float shrunkBottom = ws.getMeasuredHeight() - insetsBottom
+ float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
- grid.workspacePadding.bottom
- grid.workspaceSpringLoadedBottomSpace;
float totalShrunkSpace = shrunkBottom - shrunkTop;
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 86acff7..5a9c074 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -116,9 +116,7 @@
return getUIProperty(Bundle::putParcelable, activity -> {
WindowInsets insets = activity.getWindow()
.getDecorView().getRootWindowInsets();
- return Insets.subtract(
- insets.getSystemWindowInsets(),
- Insets.of(0, 0, 0, mDeviceProfile.nonOverlappingTaskbarInset));
+ return insets.getSystemWindowInsets();
}, this::getCurrentActivity);
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index ab2652a..4894b3b 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
+/*
+ * Copyright (C) 2015 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.
@@ -17,18 +17,31 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
import android.view.MotionEvent;
+import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.states.StateAnimationConfig;
/**
* TouchController to switch between NORMAL and ALL_APPS state.
*/
public class AllAppsSwipeController extends AbstractStateChangeTouchController {
+ private static final float ALLAPPS_STAGGERED_FADE_THRESHOLD = 0.5f;
+
+ public static final Interpolator ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER =
+ Interpolators.clampToProgress(LINEAR, 0, ALLAPPS_STAGGERED_FADE_THRESHOLD);
+ public static final Interpolator ALLAPPS_STAGGERED_FADE_LATE_RESPONDER =
+ Interpolators.clampToProgress(LINEAR, ALLAPPS_STAGGERED_FADE_THRESHOLD, 1f);
+
public AllAppsSwipeController(Launcher l) {
super(l, SingleAxisSwipeDetector.VERTICAL);
}
@@ -65,12 +78,28 @@
@Override
protected float initCurrentAnimation() {
float range = getShiftRange();
- long maxAccuracy = (long) (2 * range);
+ StateAnimationConfig config = getConfigForStates(mFromState, mToState);
+ config.duration = (long) (2 * range);
+
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, maxAccuracy);
+ .createAnimationToNewWorkspace(mToState, config);
float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
float totalShift = endVerticalShift - startVerticalShift;
return 1 / totalShift;
}
+
+ @Override
+ protected StateAnimationConfig getConfigForStates(LauncherState fromState,
+ LauncherState toState) {
+ StateAnimationConfig config = super.getConfigForStates(fromState, toState);
+ if (fromState == NORMAL && toState == ALL_APPS) {
+ config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+ } else if (fromState == ALL_APPS && toState == NORMAL) {
+ config.setInterpolator(ANIM_SCRIM_FADE, ALLAPPS_STAGGERED_FADE_LATE_RESPONDER);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ALLAPPS_STAGGERED_FADE_EARLY_RESPONDER);
+ }
+ return config;
+ }
}
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 0c3ad1d..3ac25f2 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -20,7 +20,8 @@
import static android.view.Gravity.END;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
-import static android.widget.ListPopupWindow.WRAP_CONTENT;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -257,26 +258,28 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
+ public float getTaskMenuX(float x, View thumbnailView, int overScroll,
+ DeviceProfile deviceProfile) {
return thumbnailView.getMeasuredWidth() + x;
}
@Override
public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y + overScroll;
+ return y + overScroll +
+ (thumbnailView.getMeasuredHeight() - thumbnailView.getMeasuredWidth()) / 2f;
}
@Override
- public int getTaskMenuWidth(View view) {
- return view.getMeasuredHeight();
+ public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+ return view.getMeasuredWidth();
}
@Override
public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
LinearLayout taskMenuLayout, int dividerSpacing,
ShapeDrawable dividerDrawable) {
- taskMenuLayout.setOrientation(LinearLayout.HORIZONTAL);
- dividerDrawable.setIntrinsicWidth(dividerSpacing);
+ taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
+ dividerDrawable.setIntrinsicHeight(dividerSpacing);
taskMenuLayout.setDividerDrawable(dividerDrawable);
}
@@ -284,12 +287,9 @@
public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
LinearLayout viewGroup, DeviceProfile deviceProfile) {
// Phone fake landscape
- viewGroup.setOrientation(LinearLayout.VERTICAL);
- lp.width = 0;
+ viewGroup.setOrientation(LinearLayout.HORIZONTAL);
+ lp.width = MATCH_PARENT;
lp.height = WRAP_CONTENT;
- lp.weight = 1;
- Utilities.setStartMarginForView(viewGroup.findViewById(R.id.text), 0);
- Utilities.setStartMarginForView(viewGroup.findViewById(R.id.icon), 0);
}
@Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 494fe22..19d73a8 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -171,9 +171,16 @@
void setSplitIconParams(View primaryIconView, View secondaryIconView,
int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
- float getTaskMenuX(float x, View thumbnailView, int overScroll);
+
+ /*
+ * The following two methods try to center the TaskMenuView in landscape by finding the center
+ * of the thumbnail view and then subtracting half of the taskMenu width. In this case, the
+ * taskMenu width is the same size as the thumbnail width (what got set below in
+ * getTaskMenuWidth()), so we directly use that in the calculations.
+ */
+ float getTaskMenuX(float x, View thumbnailView, int overScroll, DeviceProfile deviceProfile);
float getTaskMenuY(float y, View thumbnailView, int overScroll);
- int getTaskMenuWidth(View view);
+ int getTaskMenuWidth(View view, DeviceProfile deviceProfile);
/**
* Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
* inside task menu view.
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index a453cae..1b8ebd8 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -20,6 +20,7 @@
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -264,8 +265,14 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
- return x + overScroll;
+ public float getTaskMenuX(float x, View thumbnailView, int overScroll,
+ DeviceProfile deviceProfile) {
+ if (deviceProfile.isLandscape) {
+ return x + overScroll
+ + (thumbnailView.getMeasuredWidth() - thumbnailView.getMeasuredHeight()) / 2f;
+ } else {
+ return x + overScroll;
+ }
}
@Override
@@ -274,43 +281,27 @@
}
@Override
- public int getTaskMenuWidth(View view) {
- return view.getMeasuredWidth();
+ public int getTaskMenuWidth(View view, DeviceProfile deviceProfile) {
+ return deviceProfile.isLandscape && !deviceProfile.overviewShowAsGrid ?
+ view.getMeasuredHeight() :
+ view.getMeasuredWidth();
}
@Override
public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
LinearLayout taskMenuLayout, int dividerSpacing,
ShapeDrawable dividerDrawable) {
- if (deviceProfile.isLandscape && !deviceProfile.isTablet) {
- // Phone landscape
- taskMenuLayout.setOrientation(LinearLayout.HORIZONTAL);
- dividerDrawable.setIntrinsicWidth(dividerSpacing);
- } else {
- // Phone Portrait, LargeScreen Landscape/Portrait
- taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
- dividerDrawable.setIntrinsicHeight(dividerSpacing);
- }
+ taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
+ dividerDrawable.setIntrinsicHeight(dividerSpacing);
taskMenuLayout.setDividerDrawable(dividerDrawable);
}
@Override
public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
LinearLayout viewGroup, DeviceProfile deviceProfile) {
- if (deviceProfile.isLandscape && !deviceProfile.isTablet) {
- // Phone landscape
- viewGroup.setOrientation(LinearLayout.VERTICAL);
- lp.width = 0;
- lp.weight = 1;
- Utilities.setStartMarginForView(viewGroup.findViewById(R.id.text), 0);
- Utilities.setStartMarginForView(viewGroup.findViewById(R.id.icon), 0);
- } else {
- // Phone Portrait, LargeScreen Landscape/Portrait
- viewGroup.setOrientation(LinearLayout.HORIZONTAL);
- lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
- }
-
- lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
+ viewGroup.setOrientation(LinearLayout.HORIZONTAL);
+ lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
+ lp.height = WRAP_CONTENT;
}
@Override
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 4f33ff0..d5851c8 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -81,13 +81,15 @@
}
@Override
- public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
+ public float getTaskMenuX(float x, View thumbnailView, int overScroll,
+ DeviceProfile deviceProfile) {
return x;
}
@Override
public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
- return y + thumbnailView.getMeasuredHeight() + overScroll;
+ return y + overScroll +
+ (thumbnailView.getMeasuredHeight() + thumbnailView.getMeasuredWidth()) / 2f;
}
@Override
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index e449a4b..ce26a66 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -37,6 +37,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
@@ -56,6 +57,7 @@
protected final BaseDraggingActivity mActivity;
private final Handler mHandler = new Handler();
private final int mArrowWidth;
+ private final int mArrowMinOffset;
private boolean mIsPointingUp;
private Runnable mOnClosed;
private View mArrowView;
@@ -69,6 +71,8 @@
mActivity = BaseDraggingActivity.fromContext(context);
mIsPointingUp = isPointingUp;
mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width);
+ mArrowMinOffset = context.getResources().getDimensionPixelSize(
+ R.dimen.dynamic_grid_cell_border_spacing);
init(context);
}
@@ -126,10 +130,10 @@
/**
* Show the ArrowTipView (tooltip) center, start, or end aligned.
*
- * @param text The text to be shown in the tooltip.
- * @param gravity The gravity aligns the tooltip center, start, or end.
+ * @param text The text to be shown in the tooltip.
+ * @param gravity The gravity aligns the tooltip center, start, or end.
* @param arrowMarginStart The margin from start to place arrow (ignored if center)
- * @param top The Y coordinate of the bottom of tooltip.
+ * @param top The Y coordinate of the bottom of tooltip.
* @return The tooltip.
*/
public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
@@ -137,23 +141,28 @@
ViewGroup parent = mActivity.getDragLayer();
parent.addView(this);
+ DeviceProfile grid = mActivity.getDeviceProfile();
+
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
params.gravity = gravity;
+ params.leftMargin = mArrowMinOffset + grid.getInsets().left;
+ params.rightMargin = mArrowMinOffset + grid.getInsets().right;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mArrowView.getLayoutParams();
+
lp.gravity = gravity;
if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
arrowMarginStart = parent.getMeasuredWidth() - arrowMarginStart;
}
if (gravity == Gravity.END) {
- lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart - mArrowWidth);
+ lp.setMarginEnd(Math.max(mArrowMinOffset,
+ parent.getMeasuredWidth() - params.rightMargin - arrowMarginStart
+ - mArrowWidth / 2));
} else if (gravity == Gravity.START) {
- lp.setMarginStart(arrowMarginStart - mArrowWidth / 2);
+ lp.setMarginStart(Math.max(mArrowMinOffset,
+ arrowMarginStart - params.leftMargin - mArrowWidth / 2));
}
requestLayout();
-
- params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
- params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
post(() -> setY(top - (mIsPointingUp ? 0 : getHeight())));
mIsOpen = true;
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 2b0f707..a982786 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -113,7 +113,6 @@
private boolean mIsThumbDetached;
private final boolean mCanThumbDetach;
private boolean mIgnoreDragGesture;
- private boolean mIsRecyclerViewFirstChildInParent = true;
private long mDownTimeStampMillis;
// This is the offset from the top of the scrollbar when the user first starts touching. To
@@ -438,9 +437,7 @@
return false;
}
getHitRect(sTempRect);
- if (mIsRecyclerViewFirstChildInParent) {
- sTempRect.top += mRv.getScrollBarTop();
- }
+ sTempRect.top += mRv.getScrollBarTop();
if (outOffset != null) {
outOffset.set(sTempRect.left, sTempRect.top);
}
@@ -453,8 +450,4 @@
// alpha is so low, it does not matter.
return false;
}
-
- public void setIsRecyclerViewFirstChildInParent(boolean isRecyclerViewFirstChildInParent) {
- mIsRecyclerViewFirstChildInParent = isRecyclerViewFirstChildInParent;
- }
}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index bb4638a..68532ca 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -36,7 +36,6 @@
import android.widget.TableRow;
import android.widget.TextView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem;
@@ -70,7 +69,6 @@
private static final long EDUCATION_TIP_DELAY_MS = 300;
private ItemInfo mOriginalItemInfo;
- private final int mMaxTableHeight;
private int mMaxHorizontalSpan = DEFAULT_MAX_HORIZONTAL_SPANS;
private final int mWidgetCellHorizontalPadding;
@@ -110,10 +108,6 @@
public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setWillNotDraw(false);
- DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
- // Set the max table height to 2 / 3 of the grid height so that the bottom picker won't
- // take over the entire view vertically.
- mMaxTableHeight = deviceProfile.inv.numRows * 2 / 3 * deviceProfile.cellHeightPx;
if (!hasSeenEducationTip()) {
addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
}
@@ -162,13 +156,9 @@
setTranslationShift(mTranslationShift);
- // Ensure the scroll view height is not larger than mMaxTableHeight, which is a value
- // smaller than the entire screen height.
ScrollView widgetsTableScrollView = findViewById(R.id.widgets_table_scroll_view);
- if (widgetsTableScrollView.getMeasuredHeight() > mMaxTableHeight) {
- ViewGroup.LayoutParams layoutParams = widgetsTableScrollView.getLayoutParams();
- layoutParams.height = mMaxTableHeight;
- widgetsTableScrollView.setLayoutParams(layoutParams);
+ TableLayout widgetsTable = findViewById(R.id.widgets_table);
+ if (widgetsTable.getMeasuredHeight() > widgetsTableScrollView.getMeasuredHeight()) {
findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 09f0299..9e12f6f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -198,7 +198,6 @@
.setOnClickListener((View view) -> mViewPager.snapToPage(0));
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(1));
- fastScroller.setIsRecyclerViewFirstChildInParent(false);
mAdapters.get(AdapterHolder.WORK).setup(findViewById(R.id.work_widgets_list_view));
} else {
mViewPager = null;
@@ -334,13 +333,18 @@
setContentViewChildHorizontalMargin(mSearchScrollController.mContainer,
contentHorizontalMarginInPx);
if (mViewPager == null) {
- setContentViewChildHorizontalMargin(
+ setContentViewChildHorizontalPadding(
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView,
contentHorizontalMarginInPx);
} else {
- setContentViewChildHorizontalMargin(mViewPager, contentHorizontalMarginInPx);
+ setContentViewChildHorizontalPadding(
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView,
+ contentHorizontalMarginInPx);
+ setContentViewChildHorizontalPadding(
+ mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView,
+ contentHorizontalMarginInPx);
}
- setContentViewChildHorizontalMargin(
+ setContentViewChildHorizontalPadding(
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView,
contentHorizontalMarginInPx);
}
@@ -352,6 +356,11 @@
layoutParams.setMarginEnd(horizontalMarginInPx);
}
+ private static void setContentViewChildHorizontalPadding(View view, int horizontalPaddingInPx) {
+ view.setPadding(horizontalPaddingInPx, view.getPaddingTop(), horizontalPaddingInPx,
+ view.getPaddingBottom());
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
doMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index cd0c7f2..1a6ce8c 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -153,6 +153,8 @@
public static String dumpHprofData() {
String result;
if (sDumpWasGenerated) {
+ Log.d("b/195319692", "dump has already been generated by another test",
+ new Exception());
result = "dump has already been generated by another test";
} else {
try {
@@ -167,6 +169,7 @@
"am dumpheap " + device.getLauncherPackageName() + " " + fileName);
}
sDumpWasGenerated = true;
+ Log.d("b/195319692", "sDumpWasGenerated := true", new Exception());
result = "memory dump filename: " + fileName;
} catch (Throwable e) {
Log.e(TAG, "dumpHprofData failed", e);