Merge "Provide the back callback instance when unregistering it from SysUI." into tm-dev
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
index c252fba..c287446 100644
--- a/go/quickstep/src/com/android/launcher3/AppSharing.java
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -77,11 +77,12 @@
return FileProvider.getUriForFile(context, authority, pathFile, displayName);
}
- private SystemShortcut<Launcher> getShortcut(Launcher launcher, ItemInfo info) {
+ private SystemShortcut<Launcher> getShortcut(Launcher launcher, ItemInfo info,
+ View originalView) {
if (TextUtils.isEmpty(mSharingComponent)) {
return null;
}
- return new Share(launcher, info);
+ return new Share(launcher, info, originalView);
}
/**
@@ -104,8 +105,9 @@
private final PopupDataProvider mPopupDataProvider;
private final boolean mSharingEnabledForUser;
- public Share(Launcher target, ItemInfo itemInfo) {
- super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
+ public Share(Launcher target, ItemInfo itemInfo, View originalView) {
+ super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo,
+ originalView);
mPopupDataProvider = target.getPopupDataProvider();
mSharingEnabledForUser = bluetoothSharingEnabled(target);
@@ -200,6 +202,7 @@
/**
* Shortcut factory for generating the Share App button
*/
- public static final SystemShortcut.Factory<Launcher> SHORTCUT_FACTORY = (launcher, itemInfo) ->
- (new AppSharing(launcher)).getShortcut(launcher, itemInfo);
+ public static final SystemShortcut.Factory<Launcher> SHORTCUT_FACTORY =
+ (launcher, itemInfo, originalView) ->
+ (new AppSharing(launcher)).getShortcut(launcher, itemInfo, originalView);
}
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 4e3c02c..2cd48d5 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -15,8 +15,7 @@
*/
-->
<resources>
- <dimen name="overview_actions_top_margin_gesture">19.1dp</dimen>
- <dimen name="overview_actions_bottom_margin_gesture">10dp</dimen>
+ <dimen name="overview_actions_top_margin">12dp</dimen>
<dimen name="overview_grid_side_margin">52dp</dimen>
<dimen name="overview_page_spacing">38dp</dimen>
</resources>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index 223a5e9..5153afa 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -20,8 +20,6 @@
<dimen name="overview_task_margin">12dp</dimen>
<dimen name="overview_task_margin_grid">4dp</dimen>
<dimen name="overview_actions_button_spacing">36dp</dimen>
- <dimen name="overview_actions_top_margin_gesture">19.37dp</dimen>
- <dimen name="overview_actions_bottom_margin_gesture">22dp</dimen>
<dimen name="overview_grid_side_margin">60dp</dimen>
<dimen name="overview_grid_row_spacing">36dp</dimen>
<dimen name="overview_page_spacing">36dp</dimen>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3f08cf3..4210052 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -35,13 +35,12 @@
<dimen name="overview_task_margin">16dp</dimen>
<dimen name="overview_task_margin_grid">0dp</dimen>
<item name="overview_max_scale" format="float" type="dimen">0.7</item>
+ <item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
<!-- Overrideable in overlay that provides the Overview Actions. -->
<dimen name="overview_actions_height">48dp</dimen>
<dimen name="overview_actions_button_spacing">32dp</dimen>
- <dimen name="overview_actions_top_margin_gesture">28dp</dimen>
- <dimen name="overview_actions_bottom_margin_gesture">28dp</dimen>
- <dimen name="overview_actions_margin_three_button">8dp</dimen>
+ <dimen name="overview_actions_top_margin">24dp</dimen>
<dimen name="overview_actions_horizontal_margin">16dp</dimen>
<dimen name="overview_page_spacing">16dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index f80deeb..81b0dd2 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -109,6 +109,8 @@
<string name="back_gesture_intro_title">Swipe to go back</string>
<!-- Introduction subtitle for the Back gesture tutorial. [CHAR LIMIT=200] -->
<string name="back_gesture_intro_subtitle">To go back to the last screen, swipe from the left or right edge to the middle of the screen.</string>
+ <!-- Introduction subtitle for the Back gesture tutorial that will be spoken by screen readers. [CHAR LIMIT=200] -->
+ <string name="back_gesture_spoken_intro_subtitle">To go back to the last screen, swipe with 2 fingers from the left or right edge to the middle of the screen.</string>
<string name="home_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
<!-- Feedback shown during interactive parts of Home gesture tutorial when the Overview gesture is detected. [CHAR LIMIT=100] -->
@@ -123,6 +125,8 @@
<string name="home_gesture_intro_title">Swipe to go home</string>
<!-- Introduction subtitle for the Home gesture tutorial. [CHAR LIMIT=100] -->
<string name="home_gesture_intro_subtitle">Swipe up from the bottom of your screen. This gesture always takes you to the Home screen.</string>
+ <!-- Introduction subtitle for the Home gesture tutorial that will be spoken by screen readers. [CHAR LIMIT=100] -->
+ <string name="home_gesture_spoken_intro_subtitle">Swipe up with 2 fingers from the bottom of the screen. This gesture always takes you to the Home screen.</string>
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] -->
<string name="overview_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
@@ -138,6 +142,8 @@
<string name="overview_gesture_intro_title">Swipe to switch apps</string>
<!-- Introduction subtitle for the Overview gesture tutorial. [CHAR LIMIT=100] -->
<string name="overview_gesture_intro_subtitle">To switch between apps, swipe up from the bottom of your screen, hold, then release.</string>
+ <!-- Introduction subtitle for the Overview gesture tutorial that will be spoken by screen readers. [CHAR LIMIT=100] -->
+ <string name="overview_gesture_spoken_intro_subtitle">To switch between apps, swipe up with 2 fingers from the bottom of your screen, hold, then release.</string>
<!-- Title shown during interactive part of Assistant gesture tutorial. [CHAR LIMIT=30] -->
<string name="assistant_gesture_tutorial_playground_title" translatable="false">Tutorial: Assistant</string>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index fe24c4b..6abcbd5 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -296,7 +296,7 @@
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController());
overviewPanel.init(mActionsView, controller);
- mActionsView.setDp(getDeviceProfile());
+ mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
mAppTransitionManager = new QuickstepTransitionManager(this);
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 0f3474e..9f35401 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1354,7 +1354,7 @@
*/
private RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS,
- RectF closingWindowStartRect) {
+ RectF closingWindowStartRect, float startWindowCornerRadius) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
@@ -1403,7 +1403,7 @@
final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
- windowTargetBounds) {
+ windowTargetBounds, startWindowCornerRadius) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress,
@@ -1421,7 +1421,7 @@
final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
FloatingWidgetView finalFloatingWidget = floatingWidget;
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
- windowTargetBounds) {
+ windowTargetBounds, startWindowCornerRadius) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
final float fallbackBackgroundAlpha =
@@ -1438,7 +1438,8 @@
} else {
// If no floating icon or widget is present, animate the to the default window
// target rect.
- anim.addOnUpdateListener(new SpringAnimRunner(targets, targetRect, windowTargetBounds));
+ anim.addOnUpdateListener(new SpringAnimRunner(
+ targets, targetRect, windowTargetBounds, startWindowCornerRadius));
}
// Use a fixed velocity to start the animation.
@@ -1580,7 +1581,8 @@
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
boolean fromUnlock,
- RectF startRect) {
+ RectF startRect,
+ float startWindowCornerRadius) {
AnimatorSet anim = null;
RectFSpringAnim rectFSpringAnim = null;
@@ -1612,7 +1614,8 @@
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
PointF velocity = new PointF(0, -velocityPxPerS);
rectFSpringAnim = getClosingWindowAnimators(
- anim, appTargets, launcherView, velocity, startRect);
+ anim, appTargets, launcherView, velocity, startRect,
+ startWindowCornerRadius);
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
true /* animateOverviewScrim */, launcherView).getAnimators());
@@ -1711,7 +1714,8 @@
Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
appTargets, wallpaperTargets, mFromUnlock,
- new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx));
+ new RectF(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx),
+ QuickStepContract.getWindowCornerRadius(mLauncher));
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
result.setAnimation(pair.second, mLauncher);
@@ -1874,9 +1878,9 @@
private final Rect mTmpRect = new Rect();
SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect,
- Rect windowTargetBounds) {
+ Rect windowTargetBounds, float startWindowCornerRadius) {
mAppTargets = appTargets;
- mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+ mStartRadius = startWindowCornerRadius;
mEndRadius = Math.max(1, targetRect.width()) / 2f;
mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
mWindowTargetBounds.set(windowTargetBounds);
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 85d9f01..62a8da7 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -409,11 +409,11 @@
@Nullable
@Override
public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
- ItemInfo itemInfo) {
+ ItemInfo itemInfo, View originalView) {
if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
return null;
}
- return new PinPrediction(activity, itemInfo);
+ return new PinPrediction(activity, itemInfo, originalView);
}
private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
@@ -498,9 +498,9 @@
private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
- private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
+ private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_pin, R.string.pin_prediction, target,
- itemInfo);
+ itemInfo, originalView);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index e489cb3..68ed682 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -40,6 +40,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.View;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -193,7 +194,7 @@
@MainThread
private SystemShortcut getShortcutForApp(String packageName, int userId,
- BaseDraggingActivity activity, ItemInfo info) {
+ BaseDraggingActivity activity, ItemInfo info, View originalView) {
Preconditions.assertUIThread();
// Work profile apps are not recognized by digital wellbeing.
if (userId != UserHandle.myUserId()) {
@@ -217,7 +218,7 @@
"getShortcutForApp [" + packageName + "]: action: '" + action.getTitle()
+ "'");
}
- return new RemoteActionShortcut(action, activity, info);
+ return new RemoteActionShortcut(action, activity, info, originalView);
}
}
@@ -378,8 +379,8 @@
* Shortcut factory for generating wellbeing action
*/
public static final SystemShortcut.Factory<BaseDraggingActivity> SHORTCUT_FACTORY =
- (activity, info) -> (info.getTargetComponent() == null) ? null : INSTANCE.get(activity)
- .getShortcutForApp(
+ (activity, info, originalView) -> (info.getTargetComponent() == null) ? null
+ : INSTANCE.get(activity).getShortcutForApp(
info.getTargetComponent().getPackageName(), info.user.getIdentifier(),
- activity, info);
+ activity, info, originalView);
}
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index cc0072e..86310fa 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -34,8 +34,9 @@
static SystemShortcut.Factory<BaseQuickstepLauncher> getSplitSelectShortcutByPosition(
SplitPositionOption position) {
- return (activity, itemInfo) -> new QuickstepSystemShortcut.SplitSelectSystemShortcut(
- activity, itemInfo, position);
+ return (activity, itemInfo, originalView) ->
+ new QuickstepSystemShortcut.SplitSelectSystemShortcut(activity, itemInfo,
+ originalView, position);
}
class SplitSelectSystemShortcut extends SystemShortcut<BaseQuickstepLauncher> {
@@ -43,8 +44,8 @@
private final SplitPositionOption mPosition;
public SplitSelectSystemShortcut(BaseQuickstepLauncher launcher, ItemInfo itemInfo,
- SplitPositionOption position) {
- super(position.iconResId, position.textResId, launcher, itemInfo);
+ View originalView, SplitPositionOption position) {
+ super(position.iconResId, position.textResId, launcher, itemInfo, originalView);
mPosition = position;
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchSessionManager.java b/quickstep/src/com/android/launcher3/search/SearchSessionManager.java
deleted file mode 100644
index da85793..0000000
--- a/quickstep/src/com/android/launcher3/search/SearchSessionManager.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.search;
-
-import android.app.search.SearchSession;
-import android.content.Context;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.R;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-
-/** Manages an all apps search session. */
-public class SearchSessionManager implements ResourceBasedOverride {
-
- /** Entry state for the search session (e.g. from all apps). */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {ZERO_ALLAPPS, ZERO_QSB})
- public @interface ZeroEntryState {}
- public static final int ZERO_ALLAPPS = 1;
- public static final int ZERO_QSB = 2;
-
- /** Creates a {@link SearchSessionManager} instance. */
- public static SearchSessionManager newInstance(Context context) {
- return Overrides.getObject(
- SearchSessionManager.class, context, R.string.search_session_manager_class);
- }
-
- /** The current {@link SearchSession}. */
- @UiThread
- public void setSearchSession(SearchSession session) {}
-
- /** {@code true} if IME is shown. */
- public void setIsImeShown(boolean value) {}
-
- /** Returns {@code true} if IME is enabled. */
- public boolean getIsImeEnabled() {
- return false;
- }
-
- /** The current entry state for search. */
- public @ZeroEntryState int getEntryState() {
- return ZERO_ALLAPPS;
- }
-
- /**
- * When user enters all apps surface via tap on home widget, set the state to
- * {@code #ZERO_QSB}. When user exits, reset to {@code #ZERO_ALLAPPS}
- */
- public void setEntryState(@ZeroEntryState int state) {}
-
- /** This will be called before opening all apps, to prepare zero state suggestions. */
- public void prepareZeroState() {}
-
- /** Apply predicted items for the search zero state. */
- public void setZeroStatePredictedItems(List<ItemInfo> items) {}
-
- /** Returns {@code true} if the session is valid and should be enabled. */
- public boolean isValidSession() {
- return false;
- }
-
- /** Called when the search session is destroyed. */
- public void onDestroy() {}
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 1ccad78..c6dbc87 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -159,7 +159,7 @@
mPopupDataProvider.getNotificationKeysForItem(item),
// TODO (b/198438631): add support for INSTALL shortcut factory
getSystemShortcuts()
- .map(s -> s.getShortcut(context, item))
+ .map(s -> s.getShortcut(context, item, icon))
.filter(Objects::nonNull)
.collect(Collectors.toList()));
container.requestFocus();
@@ -242,7 +242,8 @@
*/
private SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
SplitPositionOption position) {
- return (context, itemInfo) -> new TaskbarSplitShortcut(context, itemInfo, position);
+ return (context, itemInfo, originalView) -> new TaskbarSplitShortcut(context, itemInfo,
+ originalView, position);
}
/**
@@ -253,9 +254,9 @@
private static class TaskbarSplitShortcut extends SystemShortcut<BaseTaskbarContext> {
private final SplitPositionOption mPosition;
- TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo,
+ TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo, View originalView,
SplitPositionOption position) {
- super(position.iconResId, position.textResId, context, itemInfo);
+ super(position.iconResId, position.textResId, context, itemInfo, originalView);
mPosition = position;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f32b315..7c52e80 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -220,8 +220,7 @@
(getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
boolean visible = (state == NORMAL || state == OVERVIEW)
&& (willUserBeActive || isUserActive())
- && !profile.isVerticalBarLayout()
- && profile.isPhone && !profile.isLandscape;
+ && !profile.isVerticalBarLayout();
UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index cd14b3f..3c5f216 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -30,7 +30,8 @@
*/
public class AllAppsState extends LauncherState {
- private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS;
+ private static final int STATE_FLAGS =
+ FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE;
public AllAppsState(int id) {
super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
@@ -88,7 +89,9 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return ALL_APPS_CONTENT | HOTSEAT_ICONS;
+ // Don't add HOTSEAT_ICONS for phones in ALL_APPS state.
+ return launcher.getDeviceProfile().isPhone ? ALL_APPS_CONTENT
+ : ALL_APPS_CONTENT | HOTSEAT_ICONS;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index b90e820..5acce89 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -163,7 +163,11 @@
if (mActivity != activity) {
return;
}
+ if (mTaskAnimationManager != null) {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true);
+ }
mRecentsView = null;
+ mActivity.unregisterActivityLifecycleCallbacks(mLifecycleCallbacks);
mActivity = null;
}
};
@@ -1571,9 +1575,6 @@
private void reset() {
mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
- if (mActivity != null) {
- mActivity.unregisterActivityLifecycleCallbacks(mLifecycleCallbacks);
- }
}
/**
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 9686510..48127c0 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.util.DisplayController.getNavigationMode;
import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
@@ -62,7 +61,6 @@
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.SplitScreenBounds;
-import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -224,7 +222,7 @@
Resources res = context.getResources();
if (dp.isTablet) {
Rect gridRect = new Rect();
- calculateGridSize(context, dp, gridRect);
+ calculateGridSize(dp, gridRect);
PointF taskDimension = getTaskDimension(context, dp);
float scale = gridRect.height() / taskDimension.y;
@@ -238,15 +236,15 @@
int taskMargin = dp.overviewTaskMarginPx;
calculateTaskSizeInternal(context, dp,
dp.overviewTaskThumbnailTopMarginPx,
- getOverviewActionsHeight(context, dp),
+ dp.getOverviewActionsClaimedSpace(),
res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin,
+ Gravity.CENTER,
outRect);
}
}
- private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
- int claimedSpaceAbove, int claimedSpaceBelow, int minimumHorizontalPadding,
- Rect outRect) {
+ private void calculateTaskSizeInternal(Context context, DeviceProfile dp, int claimedSpaceAbove,
+ int claimedSpaceBelow, int minimumHorizontalPadding, int gravity, Rect outRect) {
PointF taskDimension = getTaskDimension(context, dp);
Rect insets = dp.getInsets();
@@ -264,7 +262,7 @@
int outWidth = Math.round(scale * taskDimension.x);
int outHeight = Math.round(scale * taskDimension.y);
- Gravity.apply(Gravity.CENTER, outWidth, outHeight, potentialTaskRect, outRect);
+ Gravity.apply(gravity, outWidth, outHeight, potentialTaskRect, outRect);
}
private static PointF getTaskDimension(Context context, DeviceProfile dp) {
@@ -314,10 +312,10 @@
/**
* Calculates the overview grid size for the provided device configuration.
*/
- public final void calculateGridSize(Context context, DeviceProfile dp, Rect outRect) {
+ public final void calculateGridSize(DeviceProfile dp, Rect outRect) {
Rect insets = dp.getInsets();
int topMargin = dp.overviewTaskThumbnailTopMarginPx;
- int bottomMargin = getOverviewActionsHeight(context, dp);
+ int bottomMargin = dp.getOverviewActionsClaimedSpace();
int sideMargin = dp.overviewGridSideMargin;
outRect.set(0, 0, dp.widthPx, dp.heightPx);
@@ -352,21 +350,17 @@
* Calculates the modal taskView size for the provided device configuration
*/
public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+ calculateTaskSize(context, dp, outRect);
+ float maxScale = context.getResources().getFloat(R.dimen.overview_modal_max_scale);
calculateTaskSizeInternal(
context, dp,
dp.overviewTaskMarginPx,
- getOverviewActionsHeight(context, dp),
- dp.overviewTaskMarginPx,
+ dp.heightPx - outRect.bottom - dp.getInsets().bottom,
+ Math.round((dp.availableWidthPx - outRect.width() * maxScale) / 2),
+ Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
outRect);
}
- /** Gets the space that the overview actions will take, including bottom margin. */
- private int getOverviewActionsHeight(Context context, DeviceProfile dp) {
- return OverviewActionsView.getOverviewActionsBottomMarginPx(getNavigationMode(context), dp)
- + OverviewActionsView.getOverviewActionsTopMarginPx(getNavigationMode(context), dp)
- + dp.overviewActionsHeight;
- }
-
/**
* Called when the gesture ends and the animation starts towards the given target. Used to add
* an optional additional animation with the same duration.
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 4d854b6..dc65b50 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -29,7 +29,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
-import android.util.MathUtils;
import android.util.Pair;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -201,10 +200,10 @@
float followWidth = screenWidth - dX;
// The 'progress width' is the width of the window if it strictly linearly interpolates
// to minimum scale base on progress.
- float progressWidth = MathUtils.lerp(1, MIN_WINDOW_SCALE, progress) * screenWidth;
+ float progressWidth = Utilities.mapRange(progress, 1, MIN_WINDOW_SCALE) * screenWidth;
// The final width is derived from interpolating between the follow with and progress width
// using gesture progress.
- float width = MathUtils.lerp(followWidth, progressWidth, progress);
+ float width = Utilities.mapRange(progress, followWidth, progressWidth);
float height = screenHeight / screenWidth * width;
float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
// Base the window movement in the Y axis on the touch movement in the Y axis.
@@ -227,13 +226,15 @@
return;
}
mCurrentRect.set(
- MathUtils.lerp(mCancelRect.left, mStartRect.left, progress),
- MathUtils.lerp(mCancelRect.top, mStartRect.top, progress),
- MathUtils.lerp(mCancelRect.right, mStartRect.right, progress),
- MathUtils.lerp(mCancelRect.bottom, mStartRect.bottom, progress));
+ Utilities.mapRange(progress, mCancelRect.left, mStartRect.left),
+ Utilities.mapRange(progress, mCancelRect.top, mStartRect.top),
+ Utilities.mapRange(progress, mCancelRect.right, mStartRect.right),
+ Utilities.mapRange(progress, mCancelRect.bottom, mStartRect.bottom));
+ float endCornerRadius = Utilities.mapRange(
+ mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
float cornerRadius = Utilities.mapRange(
- progress, mWindowScaleEndCornerRadius, mWindowScaleStartCornerRadius);
+ progress, endCornerRadius, mWindowScaleStartCornerRadius);
applyTransform(mCurrentRect, cornerRadius);
}
@@ -273,12 +274,15 @@
mLauncher.getStateManager().moveToRestState();
}
+ float cornerRadius = Utilities.mapRange(
+ mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
Pair<RectFSpringAnim, AnimatorSet> pair =
mQuickstepTransitionManager.createWallpaperOpenAnimations(
new RemoteAnimationTargetCompat[]{mBackTarget},
new RemoteAnimationTargetCompat[]{},
false /* fromUnlock */,
- mCurrentRect);
+ mCurrentRect,
+ cornerRadius);
startTransitionAnimations(pair.first, pair.second);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 300f085..3803f03 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -256,7 +256,7 @@
private BaseIconFactory getIconFactory() {
if (mIconFactory == null) {
mIconFactory = new BaseIconFactory(mContext,
- DisplayController.INSTANCE.get(mContext).getInfo().densityDpi,
+ DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(),
mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
}
return mIconFactory;
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 2d1f17c..f53491b 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -118,8 +118,8 @@
* * There aren't at least 2 tasks in overview to show split options for
* * Device is in "Lock task mode"
* * The taskView to show split options for is the focused task AND we haven't started
- * scrolling in overview (if we haven't scrolled, there's a split overview action button so
- * we don't need this menu option)
+ * scrolling in overview (if we haven't scrolled, there's a split overview action button so
+ * we don't need this menu option)
*/
private static void addSplitOptions(List<SystemShortcut> outShortcuts,
BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
@@ -156,13 +156,15 @@
* Subclasses can attach any system listeners in this method, must be paired with
* {@link #removeListeners()}
*/
- public void initListeners() { }
+ public void initListeners() {
+ }
/**
* Subclasses should remove any system listeners in this method, must be paired with
* {@link #initListeners()}
*/
- public void removeListeners() { }
+ public void removeListeners() {
+ }
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
@@ -189,7 +191,7 @@
mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
mThumbnailView = taskThumbnailView;
mImageApi = new ImageActionsApi(
- mApplicationContext, mThumbnailView::getThumbnail);
+ mApplicationContext, mThumbnailView::getThumbnail);
}
protected T getActionsView() {
@@ -263,7 +265,8 @@
/**
* Gets the modal state system shortcut.
*/
- public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo) {
+ public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo,
+ View original) {
return null;
}
@@ -277,9 +280,10 @@
* Gets the system shortcut for the screenshot that will be added to the task menu.
*/
public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
- ItemInfo iteminfo) {
- return new ScreenshotSystemShortcut(activity, iteminfo);
+ ItemInfo iteminfo, View originalView) {
+ return new ScreenshotSystemShortcut(activity, iteminfo, originalView);
}
+
/**
* Gets the task snapshot as it is displayed on the screen.
*
@@ -316,12 +320,18 @@
Toast.LENGTH_LONG).show();
}
+ /** Called when the snapshot has updated its full screen drawing parameters. */
+ public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
+ }
+
private class ScreenshotSystemShortcut extends SystemShortcut {
private final BaseDraggingActivity mActivity;
- ScreenshotSystemShortcut(BaseDraggingActivity activity, ItemInfo itemInfo) {
- super(R.drawable.ic_screenshot, R.string.action_screenshot, activity, itemInfo);
+ ScreenshotSystemShortcut(BaseDraggingActivity activity, ItemInfo itemInfo,
+ View originalView) {
+ super(R.drawable.ic_screenshot, R.string.action_screenshot, activity, itemInfo,
+ originalView);
mActivity = activity;
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index e731b79..e807e26 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -49,7 +49,6 @@
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -78,7 +77,7 @@
TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()),
taskContainer.getA11yNodeId()
);
- return new AppInfo(activity, taskContainer.getItemInfo(), accessibilityInfo);
+ return new AppInfo(activity, taskContainer.getItemInfo(), taskView, accessibilityInfo);
}
@Override
@@ -123,7 +122,7 @@
private final SplitPositionOption mSplitPositionOption;
public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView,
SplitPositionOption option) {
- super(option.iconResId, option.textResId, target, taskView.getItemInfo());
+ super(option.iconResId, option.textResId, target, taskView.getItemInfo(), taskView);
mTaskView = taskView;
mSplitPositionOption = option;
}
@@ -147,7 +146,8 @@
public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
TaskIdAttributeContainer taskContainer, MultiWindowFactory factory,
LauncherEvent launcherEvent) {
- super(iconRes, textRes, activity, taskContainer.getItemInfo());
+ super(iconRes, textRes, activity, taskContainer.getItemInfo(),
+ taskContainer.getTaskView());
mLauncherEvent = launcherEvent;
mHandler = new Handler(Looper.getMainLooper());
mTaskView = taskContainer.getTaskView();
@@ -320,7 +320,7 @@
public PinSystemShortcut(BaseDraggingActivity target,
TaskIdAttributeContainer taskContainer) {
super(R.drawable.ic_pin, R.string.recent_task_option_pin, target,
- taskContainer.getItemInfo());
+ taskContainer.getItemInfo(), taskContainer.getTaskView());
mTaskView = taskContainer.getTaskView();
}
@@ -337,20 +337,23 @@
TaskShortcutFactory INSTALL = (activity, taskContainer) ->
InstantAppResolver.newInstance(activity).isInstantApp(activity,
- taskContainer.getTask().getTopComponent().getPackageName())
- ? new SystemShortcut.Install(activity, taskContainer.getItemInfo()) : null;
+ taskContainer.getTask().getTopComponent().getPackageName())
+ ? new SystemShortcut.Install(activity, taskContainer.getItemInfo(),
+ taskContainer.getTaskView()) : null;
TaskShortcutFactory WELLBEING = (activity, taskContainer) ->
- WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo());
+ WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, taskContainer.getItemInfo(),
+ taskContainer.getTaskView());
TaskShortcutFactory SCREENSHOT = (activity, taskContainer) ->
taskContainer.getThumbnailView().getTaskOverlay()
- .getScreenshotShortcut(activity, taskContainer.getItemInfo());
+ .getScreenshotShortcut(activity, taskContainer.getItemInfo(),
+ taskContainer.getTaskView());
TaskShortcutFactory MODAL = (activity, taskContainer) -> {
if (ENABLE_OVERVIEW_SELECTIONS.get()) {
- return taskContainer.getThumbnailView()
- .getTaskOverlay().getModalStateSystemShortcut(taskContainer.getItemInfo());
+ return taskContainer.getThumbnailView().getTaskOverlay().getModalStateSystemShortcut(
+ taskContainer.getItemInfo(), taskContainer.getTaskView());
}
return null;
};
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 29f2123..6179b81 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -38,7 +38,6 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -501,7 +500,7 @@
}
for (int i = 0; i < nonAppTargets.length; ++i) {
- final SurfaceControl leash = appTargets[i].leash;
+ final SurfaceControl leash = nonAppTargets[i].leash;
if (nonAppTargets[i].windowType == TYPE_DOCK_DIVIDER && leash != null) {
openingTargets.add(leash);
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index c9ee2db..3e68c7f 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -221,6 +221,9 @@
setOverviewStateEnabled(true);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.isFullScreen());
+ if (toState == MODAL_TASK) {
+ setOverviewSelectEnabled(true);
+ }
Log.d(BAD_STATE, "FRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
setFreezeViewVisibility(true);
}
@@ -236,6 +239,9 @@
Log.d(BAD_STATE, "FRV onStateTransitionComplete setFreezeVisibility=false, finalState="
+ finalState);
setFreezeViewVisibility(false);
+ if (finalState != MODAL_TASK) {
+ setOverviewSelectEnabled(false);
+ }
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 9ba5577..35d9f22 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -33,17 +33,22 @@
}
@Override
- public Integer getIntroductionTitle() {
+ public int getIntroductionTitle() {
return R.string.back_gesture_intro_title;
}
@Override
- public Integer getIntroductionSubtitle() {
+ public int getIntroductionSubtitle() {
return R.string.back_gesture_intro_subtitle;
}
@Override
- public Integer getSuccessFeedbackSubtitle() {
+ public int getSpokenIntroductionSubtitle() {
+ return R.string.back_gesture_spoken_intro_subtitle;
+ }
+
+ @Override
+ public int getSuccessFeedbackSubtitle() {
return mTutorialFragment.isAtFinalStep()
? R.string.back_gesture_feedback_complete_without_follow_up
: R.string.back_gesture_feedback_complete_with_overview_follow_up;
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 6254313..f519d50 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -32,17 +32,22 @@
}
@Override
- public Integer getIntroductionTitle() {
+ public int getIntroductionTitle() {
return R.string.home_gesture_intro_title;
}
@Override
- public Integer getIntroductionSubtitle() {
+ public int getIntroductionSubtitle() {
return R.string.home_gesture_intro_subtitle;
}
@Override
- public Integer getSuccessFeedbackSubtitle() {
+ public int getSpokenIntroductionSubtitle() {
+ return R.string.home_gesture_spoken_intro_subtitle;
+ }
+
+ @Override
+ public int getSuccessFeedbackSubtitle() {
return mTutorialFragment.isAtFinalStep()
? R.string.home_gesture_feedback_complete_without_follow_up
: R.string.home_gesture_feedback_complete_with_follow_up;
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 09640c6..6b016ce 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -42,17 +42,22 @@
}
@Override
- public Integer getIntroductionTitle() {
+ public int getIntroductionTitle() {
return R.string.overview_gesture_intro_title;
}
@Override
- public Integer getIntroductionSubtitle() {
+ public int getIntroductionSubtitle() {
return R.string.overview_gesture_intro_subtitle;
}
@Override
- public Integer getSuccessFeedbackSubtitle() {
+ public int getSpokenIntroductionSubtitle() {
+ return R.string.overview_gesture_spoken_intro_subtitle;
+ }
+
+ @Override
+ public int getSuccessFeedbackSubtitle() {
return mTutorialFragment.getNumSteps() > 1 && mTutorialFragment.isAtFinalStep()
? R.string.overview_gesture_feedback_complete_with_follow_up
: R.string.overview_gesture_feedback_complete_without_follow_up;
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 6a8894e..fa7d848 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -62,7 +62,7 @@
abstract class TutorialController implements BackGestureAttemptCallback,
NavBarGestureAttemptCallback {
- private static final String TAG = "TutorialController";
+ private static final String LOG_TAG = "TutorialController";
private static final float FINGER_DOT_VISIBLE_ALPHA = 0.7f;
private static final float FINGER_DOT_SMALL_SCALE = 0.7f;
@@ -217,18 +217,23 @@
}
@StringRes
- public Integer getIntroductionTitle() {
- return null;
+ public int getIntroductionTitle() {
+ return NO_ID;
}
@StringRes
- public Integer getIntroductionSubtitle() {
- return null;
+ public int getIntroductionSubtitle() {
+ return NO_ID;
}
@StringRes
- public Integer getSuccessFeedbackSubtitle() {
- return null;
+ public int getSpokenIntroductionSubtitle() {
+ return NO_ID;
+ }
+
+ @StringRes
+ public int getSuccessFeedbackSubtitle() {
+ return NO_ID;
}
void showFeedback() {
@@ -247,7 +252,16 @@
* Show feedback reflecting a successful gesture attempt.
**/
void showSuccessFeedback() {
- showFeedback(getSuccessFeedbackSubtitle(), true);
+ int successSubtitleResId = getSuccessFeedbackSubtitle();
+ if (successSubtitleResId == NO_ID) {
+ // Allow crash since this should never be reached with a tutorial controller used in
+ // production.
+ Log.e(LOG_TAG,
+ "Cannot show success feedback for tutorial step: " + mTutorialType
+ + ", no success feedback subtitle",
+ new IllegalStateException());
+ }
+ showFeedback(successSubtitleResId, true);
}
/**
@@ -269,6 +283,7 @@
isGestureSuccessful
? R.string.gesture_tutorial_nice : R.string.gesture_tutorial_try_again,
subtitleResId,
+ NO_ID,
isGestureSuccessful,
false);
}
@@ -276,6 +291,7 @@
void showFeedback(
int titleResId,
int subtitleResId,
+ int spokenSubtitleResId,
boolean isGestureSuccessful,
boolean useGestureAnimationDelay) {
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
@@ -287,7 +303,10 @@
mFeedbackTitleView.setText(titleResId);
TextView subtitle =
mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle);
- subtitle.setText(subtitleResId);
+ subtitle.setText(spokenSubtitleResId == NO_ID
+ ? mContext.getText(subtitleResId)
+ : Utilities.wrapForTts(
+ mContext.getText(subtitleResId), mContext.getString(spokenSubtitleResId)));
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
showActionButton();
@@ -580,7 +599,7 @@
packageManager.getApplicationInfo(
PIXEL_TIPS_APP_PACKAGE_NAME, PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG,
+ Log.e(LOG_TAG,
"Could not find app label for package name: "
+ PIXEL_TIPS_APP_PACKAGE_NAME
+ ". Defaulting to 'Pixel Tips.'",
@@ -593,7 +612,7 @@
subtitleTextView.setText(
mContext.getString(R.string.skip_tutorial_dialog_subtitle, tipsAppName));
} else {
- Log.w(TAG, "No subtitle view in the skip tutorial dialog to update.");
+ Log.w(LOG_TAG, "No subtitle view in the skip tutorial dialog to update.");
}
Button cancelButton = (Button) contentView.findViewById(
@@ -602,7 +621,7 @@
cancelButton.setOnClickListener(
v -> tutorialDialog.dismiss());
} else {
- Log.w(TAG, "No cancel button in the skip tutorial dialog to update.");
+ Log.w(LOG_TAG, "No cancel button in the skip tutorial dialog to update.");
}
Button confirmButton = contentView.findViewById(
@@ -613,7 +632,7 @@
tutorialDialog.dismiss();
});
} else {
- Log.w(TAG, "No confirm button in the skip tutorial dialog to update.");
+ Log.w(LOG_TAG, "No confirm button in the skip tutorial dialog to update.");
}
tutorialDialog.getWindow().setBackgroundDrawable(
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 33e800d..1599c8c 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.interaction;
+import static android.view.View.NO_ID;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
@@ -211,13 +213,31 @@
if (isGestureComplete()) {
mTutorialController.showSuccessFeedback();
} else if (!mIntroductionShown) {
- Integer introTileStringResId = mTutorialController.getIntroductionTitle();
- Integer introSubtitleResId = mTutorialController.getIntroductionSubtitle();
- if (introTileStringResId != null && introSubtitleResId != null) {
- mTutorialController.showFeedback(
- introTileStringResId, introSubtitleResId, false, true);
- mIntroductionShown = true;
+ int introTitleResId = mTutorialController.getIntroductionTitle();
+ int introSubtitleResId = mTutorialController.getIntroductionSubtitle();
+ if (introTitleResId == NO_ID) {
+ // Allow crash since this should never be reached with a tutorial controller used in
+ // production.
+ Log.e(LOG_TAG,
+ "Cannot show introduction feedback for tutorial step: " + mTutorialType
+ + ", no introduction feedback title",
+ new IllegalStateException());
}
+ if (introTitleResId == NO_ID) {
+ // Allow crash since this should never be reached with a tutorial controller used in
+ // production.
+ Log.e(LOG_TAG,
+ "Cannot show introduction feedback for tutorial step: " + mTutorialType
+ + ", no introduction feedback subtitle",
+ new IllegalStateException());
+ }
+ mTutorialController.showFeedback(
+ introTitleResId,
+ introSubtitleResId,
+ mTutorialController.getSpokenIntroductionSubtitle(),
+ false,
+ true);
+ mIntroductionShown = true;
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 45aaf35..306ebd7 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -100,6 +100,9 @@
setOverviewStateEnabled(toState.overviewUi);
setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
+ if (toState == OVERVIEW_MODAL_TASK) {
+ setOverviewSelectEnabled(true);
+ }
Log.d(BAD_STATE, "LRV onStateTransitionStart setFreezeVisibility=true, toState=" + toState);
setFreezeViewVisibility(true);
}
@@ -115,6 +118,9 @@
Log.d(BAD_STATE, "LRV onStateTransitionComplete setFreezeVisibility=false, finalState="
+ finalState);
setFreezeViewVisibility(false);
+ if (finalState != OVERVIEW_MODAL_TASK) {
+ setOverviewSelectEnabled(false);
+ }
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 99a2d6f..1c4e497 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,8 +16,6 @@
package com.android.quickstep.views;
-import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
-
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -58,7 +56,6 @@
HIDDEN_NON_ZERO_ROTATION,
HIDDEN_NO_TASKS,
HIDDEN_NO_RECENTS,
- HIDDEN_FOCUSED_SCROLL,
HIDDEN_SPLIT_SCREEN})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsHiddenFlags { }
@@ -66,8 +63,7 @@
public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0;
public static final int HIDDEN_NO_TASKS = 1 << 1;
public static final int HIDDEN_NO_RECENTS = 1 << 2;
- public static final int HIDDEN_FOCUSED_SCROLL = 1 << 3;
- public static final int HIDDEN_SPLIT_SCREEN = 1 << 4;
+ public static final int HIDDEN_SPLIT_SCREEN = 1 << 3;
@IntDef(flag = true, value = {
DISABLED_SCROLLING,
@@ -99,6 +95,7 @@
@Nullable
protected DeviceProfile mDp;
+ private final Rect mTaskSize = new Rect();
public OverviewActionsView(Context context) {
this(context, null);
@@ -202,8 +199,10 @@
* Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
*/
private void updatePadding() {
- boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent &&
- DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS;
+ if (mDp == null) {
+ return;
+ }
+ boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent && !mDp.isGestureMode;
if (alignFor3ButtonTaskbar) {
// Add extra horizontal spacing
int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext());
@@ -225,15 +224,34 @@
LayoutParams actionParams = (LayoutParams) findViewById(
R.id.action_buttons).getLayoutParams();
actionParams.setMargins(
- actionParams.leftMargin, getOverviewActionsTopMarginPx(mode, mDp),
- actionParams.rightMargin, getOverviewActionsBottomMarginPx(mode, mDp));
+ actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
+ actionParams.rightMargin, getBottomMargin());
+ }
+
+ private int getBottomMargin() {
+ if (mDp == null) {
+ return 0;
+ }
+
+ if (mDp.isVerticalBarLayout()) {
+ return mDp.getInsets().bottom;
+ }
+
+ if (!mDp.isGestureMode && mDp.isTaskbarPresent) {
+ return mDp.getOverviewActionsClaimedSpaceBelow();
+ }
+
+ // Align to bottom of task Rect.
+ return mDp.heightPx - mTaskSize.bottom - mDp.overviewActionsTopMarginPx
+ - mDp.overviewActionsHeight;
}
/**
- * Set the device profile for this view to draw with.
+ * Updates device profile and task size for this view to draw with.
*/
- public void setDp(DeviceProfile dp) {
+ public void updateDimension(DeviceProfile dp, Rect taskSize) {
mDp = dp;
+ mTaskSize.set(taskSize);
updateVerticalMargin(DisplayController.getNavigationMode(getContext()));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
@@ -257,45 +275,4 @@
mSplitButton.setVisibility(visible ? VISIBLE : GONE);
findViewById(R.id.action_split_space).setVisibility(visible ? VISIBLE : GONE);
}
-
- /** Get the top margin associated with the action buttons in Overview. */
- public static int getOverviewActionsTopMarginPx(NavigationMode mode, DeviceProfile dp) {
- // In vertical bar, use the smaller task margin for the top regardless of mode
- if (dp.isVerticalBarLayout()) {
- return dp.overviewTaskMarginPx;
- }
-
- if (mode == NavigationMode.THREE_BUTTONS) {
- return dp.overviewActionsMarginThreeButtonPx;
- }
-
- return dp.overviewActionsTopMarginGesturePx;
- }
-
- /** Get the bottom margin associated with the action buttons in Overview. */
- public static int getOverviewActionsBottomMarginPx(NavigationMode mode, DeviceProfile dp) {
- int bottomInset = dp.getInsets().bottom;
-
- if (dp.isVerticalBarLayout()) {
- return bottomInset;
- }
-
- if (mode == NavigationMode.THREE_BUTTONS) {
- int bottomMargin = dp.overviewActionsMarginThreeButtonPx + bottomInset;
- if (dp.isTaskbarPresent) {
- // Align vertically, using taskbar height + mDp.taskbarOffsetY() to estimate where
- // the button nav top is.
- int actionsTop = (dp.heightPx - bottomMargin - bottomInset)
- - dp.overviewActionsHeight;
- int navTop = dp.heightPx - (dp.taskbarSize + dp.getTaskbarOffsetY());
- bottomMargin -=
- navTop - actionsTop + ((dp.taskbarSize - dp.overviewActionsHeight) / 2);
- }
- return bottomMargin;
- }
-
- // There is no bottom inset when taskbar is present, use stashed taskbar as padding instead.
- return dp.overviewActionsBottomMarginGesturePx
- + (dp.isTaskbarPresent ? dp.stashedTaskbarSize : bottomInset);
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 49bf827..71b2cea 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -378,6 +378,8 @@
// OverScroll constants
private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
+ private static final int DEFAULT_ACTIONS_VIEW_ALPHA_ANIMATION_DURATION = 300;
+
private static final int DISMISS_TASK_DURATION = 300;
private static final int ADDITION_TASK_DURATION = 200;
private static final float INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.55f;
@@ -455,6 +457,7 @@
protected boolean mFreezeViewVisibility;
private boolean mOverviewGridEnabled;
private boolean mOverviewFullscreenEnabled;
+ private boolean mOverviewSelectEnabled;
private float mAdjacentPageHorizontalOffset = 0;
protected float mTaskViewsSecondaryTranslation = 0;
@@ -651,6 +654,8 @@
private TaskView mMovingTaskView;
private OverviewActionsView mActionsView;
+ private ObjectAnimator mActionsViewAlphaAnimator;
+ private float mActionsViewAlphaAnimatorFinalValue;
private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
new MultiWindowModeChangedListener() {
@@ -1144,6 +1149,11 @@
return getScrollForPage(taskIndex) == getPagedOrientationHandler().getPrimaryScroll(this);
}
+ private boolean isFocusedTaskInExpectedScrollPosition() {
+ TaskView focusedTask = getFocusedTaskView();
+ return focusedTask != null && isTaskInExpectedScrollPosition(indexOfChild(focusedTask));
+ }
+
/**
* Returns a {@link TaskView} that has taskId matching {@code taskId} or null if no match.
*/
@@ -1190,13 +1200,15 @@
@Override
protected void onPageBeginTransition() {
super.onPageBeginTransition();
- mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
+ if (!mActivity.getDeviceProfile().isTablet) {
+ mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
+ }
}
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
- if (isClearAllHidden()) {
+ if (isClearAllHidden() && !mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
if (getNextPage() > 0) {
@@ -1637,11 +1649,11 @@
// Propagate DeviceProfile change event.
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator().setDp(dp));
- mActionsView.setDp(dp);
mOrientationState.setDeviceProfile(dp);
// Update RecentsView and TaskView's DeviceProfile dependent layout.
updateOrientationHandler();
+ mActionsView.updateDimension(dp, mLastComputedTaskSize);
}
private void updateOrientationHandler() {
@@ -1706,7 +1718,7 @@
dp.widthPx - mInsets.right - mTempRect.right,
dp.heightPx - mInsets.bottom - mTempRect.bottom);
- mSizeStrategy.calculateGridSize(mActivity, mActivity.getDeviceProfile(),
+ mSizeStrategy.calculateGridSize(mActivity.getDeviceProfile(),
mLastComputedGridSize);
mSizeStrategy.calculateGridTaskSize(mActivity, mActivity.getDeviceProfile(),
mLastComputedGridTaskSize, mOrientationHandler);
@@ -1764,7 +1776,8 @@
* Returns the size of task selected to enter modal state.
*/
public Point getSelectedTaskSize() {
- mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect);
+ mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(),
+ mTempRect);
return new Point(mTempRect.width(), mTempRect.height());
}
@@ -1806,16 +1819,24 @@
}
private void updateActionsViewFocusedScroll() {
- boolean hiddenFocusedScroll;
if (showAsGrid()) {
- TaskView focusedTaskView = getFocusedTaskView();
- hiddenFocusedScroll = focusedTaskView == null
- || !isTaskInExpectedScrollPosition(indexOfChild(focusedTaskView));
- } else {
- hiddenFocusedScroll = false;
+ float actionsViewAlphaValue = isFocusedTaskInExpectedScrollPosition() ? 1 : 0;
+ // If animation is already in progress towards the same end value, do not restart.
+ if (mActionsViewAlphaAnimator == null || !mActionsViewAlphaAnimator.isStarted()
+ || (mActionsViewAlphaAnimator.isStarted()
+ && mActionsViewAlphaAnimatorFinalValue != actionsViewAlphaValue)) {
+ animateActionsViewAlpha(actionsViewAlphaValue,
+ DEFAULT_ACTIONS_VIEW_ALPHA_ANIMATION_DURATION);
+ }
}
- mActionsView.updateHiddenFlags(OverviewActionsView.HIDDEN_FOCUSED_SCROLL,
- hiddenFocusedScroll);
+ }
+
+ private void animateActionsViewAlpha(float alphaValue, long duration) {
+ mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(
+ mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, alphaValue);
+ mActionsViewAlphaAnimatorFinalValue = alphaValue;
+ mActionsViewAlphaAnimator.setDuration(duration);
+ mActionsViewAlphaAnimator.start();
}
/**
@@ -2344,10 +2365,9 @@
}
private void animateActionsViewIn() {
- ObjectAnimator anim = ObjectAnimator.ofFloat(
- mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 0, 1);
- anim.setDuration(TaskView.SCALE_ICON_DURATION);
- anim.start();
+ if (!showAsGrid() || isFocusedTaskInExpectedScrollPosition()) {
+ animateActionsViewAlpha(1, TaskView.SCALE_ICON_DURATION);
+ }
}
public void animateUpTaskIconScale() {
@@ -3290,7 +3310,7 @@
// Update various scroll-dependent UI.
dispatchScrollChanged();
updateActionsViewFocusedScroll();
- if (isClearAllHidden()) {
+ if (isClearAllHidden() && !mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING,
false);
}
@@ -3689,10 +3709,7 @@
// Update the pivots such that when the task is scaled, it fills the full page
getTaskSize(mTempRect);
- getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
- mActivity.getDeviceProfile(), mTempPointF);
- setPivotX(mTempPointF.x);
- setPivotY(mTempPointF.y);
+ updatePivots();
setTaskModalness(mTaskModalness);
mLastComputedTaskStartPushOutDistance = null;
mLastComputedTaskEndPushOutDistance = null;
@@ -3704,6 +3721,18 @@
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
+ private void updatePivots() {
+ if (mOverviewSelectEnabled) {
+ setPivotX(mLastComputedTaskSize.centerX());
+ setPivotY(mLastComputedTaskSize.bottom);
+ } else {
+ getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
+ mActivity.getDeviceProfile(), mTempPointF);
+ setPivotX(mTempPointF.x);
+ setPivotY(mTempPointF.y);
+ }
+ }
+
private void updatePageOffsets() {
float offset = mAdjacentPageHorizontalOffset;
float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
@@ -4866,6 +4895,17 @@
}
/**
+ * Update whether RecentsView is in select mode. Should be enabled before transitioning to
+ * select mode, and only disabled after transitioning from select mode.
+ */
+ public void setOverviewSelectEnabled(boolean overviewSelectEnabled) {
+ if (mOverviewSelectEnabled != overviewSelectEnabled) {
+ mOverviewSelectEnabled = overviewSelectEnabled;
+ updatePivots();
+ }
+ }
+
+ /**
* Switch the current running task view to static snapshot mode,
* capturing the snapshot at the same time.
*/
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index bff8651..d8120ff 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -283,6 +283,7 @@
public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
mFullscreenParams = fullscreenParams;
+ getTaskOverlay().setFullscreenParams(fullscreenParams);
invalidate();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 8869ff1..b5971f2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1022,7 +1022,7 @@
}
public float getTaskCornerRadius() {
- return TaskCornerRadius.get(mActivity);
+ return mCurrentFullscreenParams.mCornerRadius;
}
@Override
diff --git a/res/anim-v33/shared_x_axis_activity_close_enter.xml b/res/anim-v33/shared_x_axis_activity_close_enter.xml
new file mode 100644
index 0000000..94ef06c
--- /dev/null
+++ b/res/anim-v33/shared_x_axis_activity_close_enter.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showBackdrop="true">
+
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_decelerate"
+ android:startOffset="100"
+ android:duration="350" />
+
+ <translate
+ android:fromXDelta="-25%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/res/anim-v33/shared_x_axis_activity_close_exit.xml b/res/anim-v33/shared_x_axis_activity_close_exit.xml
new file mode 100644
index 0000000..19eb09e
--- /dev/null
+++ b/res/anim-v33/shared_x_axis_activity_close_exit.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_accelerate"
+ android:startOffset="0"
+ android:duration="100" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="25%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/res/anim-v33/shared_x_axis_activity_open_enter.xml b/res/anim-v33/shared_x_axis_activity_open_enter.xml
new file mode 100644
index 0000000..f699cec
--- /dev/null
+++ b/res/anim-v33/shared_x_axis_activity_open_enter.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showBackdrop="true">
+
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_decelerate"
+ android:startOffset="100"
+ android:duration="350" />
+
+ <translate
+ android:fromXDelta="25%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/res/anim-v33/shared_x_axis_activity_open_exit.xml b/res/anim-v33/shared_x_axis_activity_open_exit.xml
new file mode 100644
index 0000000..85988ec
--- /dev/null
+++ b/res/anim-v33/shared_x_axis_activity_open_exit.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_accelerate"
+ android:startOffset="0"
+ android:duration="100" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="-25%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set>
\ No newline at end of file
diff --git a/res/interpolator/fast_out_extra_slow_in.xml b/res/interpolator/fast_out_extra_slow_in.xml
new file mode 100644
index 0000000..f296a82
--- /dev/null
+++ b/res/interpolator/fast_out_extra_slow_in.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
\ No newline at end of file
diff --git a/res/interpolator/standard_accelerate.xml b/res/interpolator/standard_accelerate.xml
new file mode 100644
index 0000000..394393d
--- /dev/null
+++ b/res/interpolator/standard_accelerate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.3"
+ android:controlY1="0"
+ android:controlX2="1"
+ android:controlY2="1"/>
\ No newline at end of file
diff --git a/res/interpolator/standard_decelerate.xml b/res/interpolator/standard_decelerate.xml
new file mode 100644
index 0000000..579f4f5
--- /dev/null
+++ b/res/interpolator/standard_decelerate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="0"
+ android:controlX2="0"
+ android:controlY2="1"/>
\ No newline at end of file
diff --git a/res/values-v33/style.xml b/res/values-v33/style.xml
new file mode 100644
index 0000000..bd48468
--- /dev/null
+++ b/res/values-v33/style.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2022 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+ <style name="HomeSettings.Theme" parent="@android:style/Theme.DeviceDefault.Settings">
+ <item name="android:listPreferredItemPaddingEnd">16dp</item>
+ <item name="android:listPreferredItemPaddingStart">24dp</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:switchStyle">@style/HomeSettings.SwitchStyle</item>
+ <item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="preferenceTheme">@style/HomeSettings.PreferenceTheme</item>
+ <item name="android:windowAnimationStyle">@style/Animation.SharedBackground</item>
+ </style>
+
+ <style name="Animation.SharedBackground" parent="@android:style/Animation.Activity">
+ <item name="android:activityOpenEnterAnimation">@anim/shared_x_axis_activity_open_enter</item>
+ <item name="android:activityOpenExitAnimation">@anim/shared_x_axis_activity_open_exit</item>
+ <item name="android:activityCloseEnterAnimation">@anim/shared_x_axis_activity_close_enter</item>
+ <item name="android:activityCloseExitAnimation">@anim/shared_x_axis_activity_close_exit</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index dd3e08b..e85969b 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -306,6 +306,17 @@
if not specified -->
<attr name="allAppsBorderSpaceTwoPanelLandscapeVertical" format="float" />
+ <!-- alignment of hotseat to the grid.
+ Not applicable for 3 button mode when taskbar is enabled -->
+ <!-- defaults to numColumns, if not specified -->
+ <attr name="hotseatColumnSpan" format="integer" />
+ <!-- defaults to numColumns, if not specified -->
+ <attr name="hotseatColumnSpanLandscape" format="integer" />
+ <!-- defaults to numColumns, if not specified -->
+ <attr name="hotseatColumnSpanTwoPanelLandscape" format="integer" />
+ <!-- defaults to numColumns, if not specified -->
+ <attr name="hotseatColumnSpanTwoPanelPortrait" format="integer" />
+
<!-- defaults to borderSpaceDps, if not specified -->
<attr name="hotseatBorderSpace" format="float" />
<!-- defaults to hotseatBorderSpace, if not specified -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 52ff3f0..2c3f5ed 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -368,9 +368,7 @@
<dimen name="overview_actions_height">0dp</dimen>
<dimen name="overview_actions_button_spacing">0dp</dimen>
<dimen name="overview_actions_margin_gesture">0dp</dimen>
- <dimen name="overview_actions_top_margin_gesture">0dp</dimen>
- <dimen name="overview_actions_bottom_margin_gesture">0dp</dimen>
- <dimen name="overview_actions_margin_three_button">0dp</dimen>
+ <dimen name="overview_actions_top_margin">0dp</dimen>
<dimen name="overview_grid_side_margin">0dp</dimen>
<dimen name="overview_grid_row_spacing">0dp</dimen>
<dimen name="overview_page_spacing">0dp</dimen>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 07ce598..258da80 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -176,6 +176,7 @@
launcher:allAppsBorderSpaceHorizontal="8"
launcher:allAppsBorderSpaceVertical="16"
launcher:allAppsBorderSpaceLandscape="16"
+ launcher:hotseatColumnSpanLandscape="4"
launcher:hotseatBorderSpace="58"
launcher:hotseatBorderSpaceLandscape="50.4"
launcher:canBeDefault="true" />
diff --git a/res/xml/paddings_6x5.xml b/res/xml/paddings_6x5.xml
index a958ec7..a72f554 100644
--- a/res/xml/paddings_6x5.xml
+++ b/res/xml/paddings_6x5.xml
@@ -32,7 +32,7 @@
</device-padding>
<device-padding
- launcher:maxEmptySpace="80dp">
+ launcher:maxEmptySpace="170dp">
<workspaceTopPadding
launcher:a="0"
launcher:b="20dp"/>
@@ -47,7 +47,7 @@
</device-padding>
<device-padding
- launcher:maxEmptySpace="280dp">
+ launcher:maxEmptySpace="410dp">
<workspaceTopPadding
launcher:a="0"
launcher:b="112dp"/>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 74ec7ee..6302739 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -96,7 +96,6 @@
private static final int MAX_SEARCH_LOOP_COUNT = 20;
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
- private static final float HIGHLIGHT_SCALE = 1.16f;
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@@ -259,6 +258,12 @@
mDotParams.scale = 0f;
mForceHideDot = false;
setBackground(null);
+
+ setTag(null);
+ if (mIconLoadRequest != null) {
+ mIconLoadRequest.cancel();
+ mIconLoadRequest = null;
+ }
}
private void cancelDotScaleAnim() {
@@ -363,8 +368,7 @@
}
}
- public void setBubbleTextHolder(
- BubbleTextHolder bubbleTextHolder) {
+ public void setBubbleTextHolder(BubbleTextHolder bubbleTextHolder) {
mBubbleTextHolder = bubbleTextHolder;
}
@@ -1020,19 +1024,6 @@
getIconBounds(mIconSize, bounds);
}
- private int getIconSizeForDisplay(int display) {
- DeviceProfile grid = mActivity.getDeviceProfile();
- switch (display) {
- case DISPLAY_ALL_APPS:
- return grid.allAppsIconSizePx;
- case DISPLAY_FOLDER:
- return grid.folderChildIconSizePx;
- case DISPLAY_WORKSPACE:
- default:
- return grid.iconSizePx;
- }
- }
-
public void getSourceVisualDragBounds(Rect bounds) {
getIconBounds(mIconSize, bounds);
}
@@ -1045,8 +1036,8 @@
}
private void resetIconScale() {
- if (mIcon instanceof FastBitmapDrawable) {
- ((FastBitmapDrawable) mIcon).resetScale();
+ if (mIcon != null) {
+ mIcon.resetScale();
}
}
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index af85594..4daca8b 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -7,15 +7,19 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Bundle;
+import android.os.Process;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -23,6 +27,7 @@
import java.io.IOException;
import java.net.URISyntaxException;
+import java.util.Collections;
import java.util.List;
/**
@@ -43,6 +48,8 @@
private static final String ATTR_CONTAINER = "container";
private static final String ATTR_SCREEN = "screen";
private static final String ATTR_FOLDER_ITEMS = "folderItems";
+ private static final String ATTR_SHORTCUT_ID = "shortcutId";
+ private static final String ATTR_PACKAGE_NAME = "packageName";
// TODO: Remove support for this broadcast, instead use widget options to send bind time options
private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
@@ -178,7 +185,6 @@
}
}
-
/**
* Shortcut parser which allows any uri and not just web urls.
*/
@@ -189,6 +195,35 @@
}
@Override
+ public int parseAndAdd(XmlPullParser parser) {
+ final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ final String shortcutId = getAttributeValue(parser, ATTR_SHORTCUT_ID);
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(shortcutId)) {
+ return parseAndAddDeepShortcut(shortcutId, packageName);
+ }
+ return super.parseAndAdd(parser);
+ }
+
+ /**
+ * This method parses and adds a deep shortcut.
+ * @return item id if the shortcut is successfully added else -1
+ */
+ private int parseAndAddDeepShortcut(String shortcutId, String packageName) {
+ try {
+ LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
+ launcherApps.pinShortcuts(packageName, Collections.singletonList(shortcutId),
+ Process.myUserHandle());
+ Intent intent = ShortcutKey.makeIntent(shortcutId, packageName);
+ mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_RESTORED_ICON);
+ return addShortcut(null, intent, Favorites.ITEM_TYPE_DEEP_SHORTCUT);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to pin the shortcut for shortcut id = " + shortcutId
+ + " and package name = " + packageName);
+ }
+ return -1;
+ }
+
+ @Override
protected Intent parseIntent(XmlPullParser parser) {
String uri = null;
try {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 33bb0a5..88030ae 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -158,6 +158,7 @@
public final int numShownHotseatIcons;
public int hotseatCellHeightPx;
private final int hotseatExtraVerticalSize;
+ private final boolean areNavButtonsInline;
// In portrait: size = height, in landscape: size = width
public int hotseatBarSizePx;
public int hotseatBarTopPaddingPx;
@@ -195,9 +196,7 @@
public int overviewTaskIconDrawableSizeGridPx;
public int overviewTaskThumbnailTopMarginPx;
public final int overviewActionsHeight;
- public final int overviewActionsMarginThreeButtonPx;
- public final int overviewActionsTopMarginGesturePx;
- public final int overviewActionsBottomMarginGesturePx;
+ public final int overviewActionsTopMarginPx;
public final int overviewActionsButtonSpacing;
public int overviewPageSpacing;
public int overviewRowSpacing;
@@ -360,7 +359,7 @@
// We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB
// might be inline in either orientations, to keep hotseat size consistent across rotation.
- boolean areNavButtonsInline = isTaskbarPresent && !isGestureMode;
+ areNavButtonsInline = isTaskbarPresent && !isGestureMode;
if (areNavButtonsInline && canQsbInline) {
numShownHotseatIcons = inv.numShrunkenHotseatIcons;
} else {
@@ -375,15 +374,14 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
if (isQsbInline) {
hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin);
- qsbWidth = calculateQsbWidth();
} else {
hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize(
R.dimen.dynamic_grid_hotseat_bottom_tall_padding)
: res.getDimensionPixelSize(
R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
- qsbWidth = 0;
}
+
springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
R.dimen.spring_loaded_hotseat_top_margin);
hotseatBarSidePaddingEndPx =
@@ -392,9 +390,7 @@
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
hotseatExtraVerticalSize =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
- hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
- updateHotseatIconSize(
- pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
+ updateHotseatIconSize(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
qsbBottomMarginOriginalPx = isScalableGrid
? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
@@ -408,16 +404,14 @@
overviewTaskIconDrawableSizeGridPx =
res.getDimensionPixelSize(R.dimen.task_thumbnail_icon_drawable_size_grid);
overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
- overviewActionsTopMarginGesturePx = res.getDimensionPixelSize(
- R.dimen.overview_actions_top_margin_gesture);
- overviewActionsBottomMarginGesturePx = res.getDimensionPixelSize(
- R.dimen.overview_actions_bottom_margin_gesture);
+ // In vertical bar, use the smaller task margin for the top regardless of mode.
+ overviewActionsTopMarginPx = isVerticalBarLayout()
+ ? overviewTaskMarginPx
+ : res.getDimensionPixelSize(R.dimen.overview_actions_top_margin);
overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
overviewActionsButtonSpacing = res.getDimensionPixelSize(
R.dimen.overview_actions_button_spacing);
overviewActionsHeight = res.getDimensionPixelSize(R.dimen.overview_actions_height);
- overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
- R.dimen.overview_actions_margin_three_button);
// Grid task's top margin is only overviewTaskIconSizePx + overviewTaskMarginGridPx, but
// overviewTaskThumbnailTopMarginPx is applied to all TaskThumbnailView, so exclude the
// extra margin when calculating row spacing.
@@ -487,6 +481,10 @@
cellLayoutPadding);
updateWorkspacePadding();
+ // Hotseat and QSB width depends on updated cellSize and workspace padding
+ hotseatBorderSpace = calculateHotseatBorderSpace();
+ qsbWidth = calculateQsbWidth();
+
flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
R.dimen.drag_flingToDeleteMinVelocity);
@@ -497,14 +495,26 @@
new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
}
+ /**
+ * QSB width is always calculated because when in 3 button nav the width doesn't follow the
+ * width of the hotseat.
+ */
private int calculateQsbWidth() {
- int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+ if (isQsbInline) {
+ int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
+ return getIconToIconWidthForColumns(columns)
+ - iconSizePx * numShownHotseatIcons
+ - hotseatBorderSpace * numShownHotseatIcons;
+ } else {
+ int columns = inv.hotseatColumnSpan[mTypeIndex];
+ return getIconToIconWidthForColumns(columns);
+ }
+ }
- return cellWidthPx * columns
- + cellLayoutBorderSpacePx.x * (columns - 1)
- - (cellWidthPx - iconSizePx) // left and right cell space
- - iconSizePx * numShownHotseatIcons
- - hotseatBorderSpace * numShownHotseatIcons;
+ private int getIconToIconWidthForColumns(int columns) {
+ return columns * getCellSize().x
+ + (columns - 1) * cellLayoutBorderSpacePx.x
+ - (getCellSize().x - iconSizePx); // left and right cell space
}
private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) {
@@ -658,7 +668,7 @@
Point workspacePadding = getTotalWorkspacePadding();
// Check to see if the icons fit within the available height.
- float usedHeight = getCellLayoutHeight();
+ float usedHeight = getCellLayoutHeightSpecification();
final int maxHeight = getWorkspaceHeight(workspacePadding);
float extraHeight = Math.max(0, maxHeight - usedHeight);
float scaleY = maxHeight / usedHeight;
@@ -669,7 +679,8 @@
// We scale to fit the cellWidth and cellHeight in the available space.
// The benefit of scalable grids is that we can get consistent aspect ratios between
// devices.
- float usedWidth = getCellLayoutWidth() + (desiredWorkspaceHorizontalMarginPx * 2);
+ float usedWidth =
+ getCellLayoutWidthSpecification() + (desiredWorkspaceHorizontalMarginPx * 2);
// We do not subtract padding here, as we also scale the workspace padding if needed.
scaleX = availableWidthPx / usedWidth;
shouldScale = true;
@@ -678,19 +689,19 @@
if (shouldScale) {
float scale = Math.min(scaleX, scaleY);
updateIconSize(scale, res);
- extraHeight = Math.max(0, maxHeight - getCellLayoutHeight());
+ extraHeight = Math.max(0, maxHeight - getCellLayoutHeightSpecification());
}
updateAvailableFolderCellDimensions(res);
return Math.round(extraHeight);
}
- private int getCellLayoutHeight() {
+ private int getCellLayoutHeightSpecification() {
return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacePx.y * (inv.numRows - 1))
+ cellLayoutPaddingPx.top + cellLayoutPaddingPx.bottom;
}
- private int getCellLayoutWidth() {
+ private int getCellLayoutWidthSpecification() {
int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
return (cellWidthPx * numColumns) + (cellLayoutBorderSpacePx.x * (numColumns - 1))
+ cellLayoutPaddingPx.left + cellLayoutPaddingPx.right;
@@ -744,13 +755,6 @@
// All apps
updateAllAppsIconSize(scale, res);
- // Hotseat
- hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics, scale);
- if (isQsbInline) {
- qsbWidth = calculateQsbWidth();
- } else {
- qsbWidth = 0;
- }
updateHotseatIconSize(iconSizePx);
// Folder icon
@@ -758,6 +762,23 @@
folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
}
+ /**
+ * Hotseat width spans a certain number of columns on scalable grids.
+ * This method calculates the space between the icons to achieve that width.
+ */
+ private int calculateHotseatBorderSpace() {
+ if (!isScalableGrid) return 0;
+ //TODO(http://b/228998082) remove this when 3 button spaces are fixed
+ if (areNavButtonsInline) {
+ return pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
+ } else {
+ int columns = inv.hotseatColumnSpan[mTypeIndex];
+ float hotseatWidthPx = getIconToIconWidthForColumns(columns);
+ float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
+ return (int) (hotseatWidthPx - hotseatIconsTotalPx) / (numShownHotseatIcons - 1);
+ }
+ }
+
/**
* Updates the iconSize for allApps* variants.
@@ -920,7 +941,7 @@
/**
* Gets the scaled bottom of the workspace in px for the spring-loaded edit state.
*/
- public float getWorkspaceSpringLoadShrunkBottom() {
+ private float getWorkspaceSpringLoadShrunkBottom() {
int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx;
workspaceSpringLoadShrunkBottom =
heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset()
@@ -931,10 +952,30 @@
/**
* Gets the minimum visible amount of the next workspace page when in the spring-loaded state.
*/
- public float getWorkspaceSpringLoadedMinimumNextPageVisible() {
+ private float getWorkspaceSpringLoadedMinimumNextPageVisible() {
return getCellSize().x / 2f;
}
+ /**
+ * Gets the scale of the workspace for the spring-loaded edit state.
+ */
+ public float getWorkspaceSpringLoadScale() {
+ float cellLayoutHeight = availableHeightPx - workspacePadding.top - workspacePadding.bottom;
+ float scale = (getWorkspaceSpringLoadShrunkBottom() - getWorkspaceSpringLoadShrunkTop())
+ / cellLayoutHeight;
+ scale = Math.min(scale, 1f);
+
+ // Reduce scale if next pages would not be visible after scaling the workspace
+ int workspaceWidth = getWorkspaceWidth();
+ float scaledWorkspaceWidth = workspaceWidth * scale;
+ float maxAvailableWidth =
+ workspaceWidth - (2 * getWorkspaceSpringLoadedMinimumNextPageVisible());
+ if (scaledWorkspaceWidth > maxAvailableWidth) {
+ scale *= maxAvailableWidth / scaledWorkspaceWidth;
+ }
+ return scale;
+ }
+
public int getWorkspaceWidth() {
return getWorkspaceWidth(getTotalWorkspacePadding());
}
@@ -1053,6 +1094,13 @@
mHotseatPadding.left -= diff;
mHotseatPadding.right += diff;
}
+ } else if (isScalableGrid) {
+ int sideSpacing = (availableWidthPx - qsbWidth) / 2;
+ mHotseatPadding.set(sideSpacing,
+ hotseatBarTopPaddingPx,
+ sideSpacing,
+ hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
+ + mInsets.bottom);
} else {
// We want the edges of the hotseat to line up with the edges of the workspace, but the
// icons in the hotseat are a different size, and so don't line up perfectly. To account
@@ -1104,6 +1152,24 @@
}
/**
+ * Returns the number of pixels required below OverviewActions excluding insets.
+ */
+ public int getOverviewActionsClaimedSpaceBelow() {
+ if (isTaskbarPresent && !isGestureMode) {
+ // Align vertically to where nav buttons are.
+ return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
+ }
+
+ return 0;
+ }
+
+ /** Gets the space that the overview actions will take, including bottom margin. */
+ public int getOverviewActionsClaimedSpace() {
+ return overviewActionsTopMarginPx + overviewActionsHeight
+ + getOverviewActionsClaimedSpaceBelow();
+ }
+
+ /**
* @return the bounds for which the open folders should be contained within
*/
public Rect getAbsoluteOpenFolderBounds() {
@@ -1271,6 +1337,7 @@
writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin));
writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
+ writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx));
writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx));
@@ -1323,12 +1390,10 @@
overviewTaskIconDrawableSizeGridPx));
writer.println(prefix + pxToDpStr("overviewTaskThumbnailTopMarginPx",
overviewTaskThumbnailTopMarginPx));
- writer.println(prefix + pxToDpStr("overviewActionsMarginThreeButtonPx",
- overviewActionsMarginThreeButtonPx));
- writer.println(prefix + pxToDpStr("overviewActionsTopMarginGesturePx",
- overviewActionsTopMarginGesturePx));
- writer.println(prefix + pxToDpStr("overviewActionsBottomMarginGesturePx",
- overviewActionsBottomMarginGesturePx));
+ writer.println(prefix + pxToDpStr("overviewActionsTopMarginPx",
+ overviewActionsTopMarginPx));
+ writer.println(prefix + pxToDpStr("overviewActionsHeight",
+ overviewActionsHeight));
writer.println(prefix + pxToDpStr("overviewActionsButtonSpacing",
overviewActionsButtonSpacing));
writer.println(prefix + pxToDpStr("overviewPageSpacing", overviewPageSpacing));
@@ -1344,12 +1409,14 @@
prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
workspaceSpringLoadShrunkBottom));
+ writer.println(
+ prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
}
private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) {
Configuration config = new Configuration(c.getResources().getConfiguration());
config.orientation = orientation;
- config.densityDpi = info.densityDpi;
+ config.densityDpi = info.getDensityDpi();
config.smallestScreenWidthDp = (int) info.smallestSizeDp(bounds);
return c.createConfigurationContext(config);
}
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index ec3629d..2e3f26c 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -237,10 +237,8 @@
rightButton.getMeasuredHeight());
} else if (dp.isPhone) {
// Buttons aligned to outer edges of scaled workspace.
- float shrunkTop = dp.getWorkspaceSpringLoadShrunkTop();
- float shrunkBottom = dp.getWorkspaceSpringLoadShrunkBottom();
- float scale =
- (shrunkBottom - shrunkTop) / launcher.getWorkspace().getNormalChildHeight();
+ float scale = dp.getWorkspaceSpringLoadScale();
+
int workspaceWidth = (int) (launcher.getWorkspace().getNormalChildWidth() * scale);
int start = barCenter - (workspaceWidth / 2);
int end = barCenter + (workspaceWidth / 2);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index a9db5ce..76106fc 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -173,17 +173,9 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int width;
- if (mActivity.getDeviceProfile().isQsbInline) {
- width = mActivity.getDeviceProfile().qsbWidth;
- } else {
- MarginLayoutParams qsbParams = (MarginLayoutParams) mQsb.getLayoutParams();
- width = getShortcutsAndWidgets().getMeasuredWidth()
- - qsbParams.getMarginStart()
- - qsbParams.getMarginEnd();
- }
+ int qsbWidth = mActivity.getDeviceProfile().qsbWidth;
- mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ mQsb.measure(MeasureSpec.makeMeasureSpec(qsbWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 36c1797..89b1771 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -125,6 +125,7 @@
public PointF[] borderSpaces;
public float folderBorderSpace;
public float[] hotseatBorderSpaces;
+ public int[] hotseatColumnSpan;
public float[] horizontalMargin;
@@ -356,6 +357,7 @@
numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
+ hotseatColumnSpan = displayOption.hotseatColumnSpan;
numAllAppsColumns = closestProfile.numAllAppsColumns;
numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
@@ -396,7 +398,8 @@
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
float parallaxFactor =
- dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720
+ dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.getDensityDpi())
+ < 720
? 2
: wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight);
defaultWallpaperSize.x =
@@ -585,8 +588,8 @@
}
}
- float width = dpiFromPx(minWidthPx, displayInfo.densityDpi);
- float height = dpiFromPx(minHeightPx, displayInfo.densityDpi);
+ float width = dpiFromPx(minWidthPx, displayInfo.getDensityDpi());
+ float height = dpiFromPx(minHeightPx, displayInfo.getDensityDpi());
// Sort the profiles based on the closeness to the device size
Collections.sort(points, (a, b) ->
@@ -806,7 +809,9 @@
private float folderBorderSpace;
private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
private final float[] horizontalMargin = new float[COUNT_SIZES];
+ //TODO(http://b/228998082) remove this when 3 button spaces are fixed
private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
+ private final int[] hotseatColumnSpan = new int[COUNT_SIZES];
private final float[] iconSizes = new float[COUNT_SIZES];
private final float[] textSizes = new float[COUNT_SIZES];
@@ -1032,6 +1037,17 @@
R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
hotseatBorderSpaces[INDEX_DEFAULT]);
+ hotseatColumnSpan[INDEX_DEFAULT] = a.getInt(
+ R.styleable.ProfileDisplayOption_hotseatColumnSpan, grid.numColumns);
+ hotseatColumnSpan[INDEX_LANDSCAPE] = a.getInt(
+ R.styleable.ProfileDisplayOption_hotseatColumnSpanLandscape, grid.numColumns);
+ hotseatColumnSpan[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt(
+ R.styleable.ProfileDisplayOption_hotseatColumnSpanTwoPanelLandscape,
+ grid.numColumns);
+ hotseatColumnSpan[INDEX_TWO_PANEL_PORTRAIT] = a.getInt(
+ R.styleable.ProfileDisplayOption_hotseatColumnSpanTwoPanelPortrait,
+ grid.numColumns);
+
a.recycle();
}
@@ -1090,6 +1106,7 @@
minCellSize[i].y += p.minCellSize[i].y;
horizontalMargin[i] += p.horizontalMargin[i];
hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
+ hotseatColumnSpan[i] = p.hotseatColumnSpan[i];
allAppsCellSize[i].x += p.allAppsCellSize[i].x;
allAppsCellSize[i].y += p.allAppsCellSize[i].y;
allAppsIconSizes[i] += p.allAppsIconSizes[i];
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index baee49f..c0bcaa3 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -80,6 +80,9 @@
public static final int FLAG_CLOSE_POPUPS = BaseState.getFlag(6);
public static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
+ // Flag indicating that hotseat and its contents are not accessible.
+ public static final int FLAG_HOTSEAT_INACCESSIBLE = BaseState.getFlag(8);
+
public static final float NO_OFFSET = 0;
public static final float NO_SCALE = 1;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8358f2a..7b96838 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -273,6 +273,16 @@
}
/**
+ * Similar to {@link #mapCoordInSelfToDescendant(View descendant, View root, float[] coord)}
+ * but accepts a Rect instead of float[].
+ */
+ public static void mapRectInSelfToDescendant(View descendant, View root, Rect rect) {
+ float[] coords = new float[]{rect.left, rect.top, rect.right, rect.bottom};
+ mapCoordInSelfToDescendant(descendant, root, coords);
+ rect.set((int) coords[0], (int) coords[1], (int) coords[2], (int) coords[3]);
+ }
+
+ /**
* Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
*/
public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index ed01660..78771ce 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -200,6 +200,7 @@
private final int[] mTempXY = new int[2];
private final float[] mTempFXY = new float[2];
+ private final Rect mTempRect = new Rect();
@Thunk float[] mDragViewVisualCenter = new float[2];
private SpringLoadedDragController mSpringLoadedDragController;
@@ -906,7 +907,11 @@
* two panel UI is enabled.
*/
public int getScreenPair(int screenId) {
- if (screenId % 2 == 0) {
+ if (screenId == EXTRA_EMPTY_SCREEN_ID) {
+ return EXTRA_EMPTY_SCREEN_SECOND_ID;
+ } else if (screenId == EXTRA_EMPTY_SCREEN_SECOND_ID) {
+ return EXTRA_EMPTY_SCREEN_ID;
+ } else if (screenId % 2 == 0) {
return screenId + 1;
} else {
return screenId - 1;
@@ -1730,7 +1735,7 @@
// If it's an external drop (e.g. from All Apps), check if it should be accepted
CellLayout dropTargetLayout = mDropToLayout;
if (d.dragSource != this) {
- // Don't accept the drop if we're not over a screen at time of drop
+ // Don't accept the drop if we're not over a valid drop target at time of drop
if (dropTargetLayout == null) {
return false;
}
@@ -2331,17 +2336,6 @@
xy[1] = xy[1] - v.getTop();
}
- boolean isPointInSelfOverHotseat(int x, int y) {
- mTempFXY[0] = x;
- mTempFXY[1] = y;
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
- View hotseat = mLauncher.getHotseat();
- return mTempFXY[0] >= hotseat.getLeft()
- && mTempFXY[0] <= hotseat.getRight()
- && mTempFXY[1] >= hotseat.getTop()
- && mTempFXY[1] <= hotseat.getBottom();
- }
-
/**
* Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
* @param layout either hotseat of a page in workspace
@@ -2379,7 +2373,7 @@
final View child = (mDragInfo == null) ? null : mDragInfo.cell;
if (setDropLayoutForDragObject(d, mDragViewVisualCenter[0], mDragViewVisualCenter[1])) {
- if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
+ if (mDragTargetLayout == null || mLauncher.isHotseatLayout(mDragTargetLayout)) {
mSpringLoadedDragController.cancel();
} else {
mSpringLoadedDragController.setAlarm(mDragTargetLayout);
@@ -2458,52 +2452,25 @@
*/
private boolean setDropLayoutForDragObject(DragObject d, float centerX, float centerY) {
CellLayout layout = null;
- // Test to see if we are over the hotseat first
- if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
- if (isPointInSelfOverHotseat(d.x, d.y)) {
- layout = mLauncher.getHotseat();
+ if (shouldUseHotseatAsDropLayout(d)) {
+ layout = mLauncher.getHotseat();
+ } else if (!isDragObjectOverSmartSpace(d)) {
+ // If the object is over qsb/smartspace, we don't want to highlight anything.
+
+ // Check neighbour pages
+ layout = checkDragObjectIsOverNeighbourPages(d, centerX);
+
+ if (layout == null) {
+ // Check visible pages
+ IntSet visiblePageIndices = getVisiblePageIndices();
+ for (int visiblePageIndex : visiblePageIndices) {
+ layout = verifyInsidePage(visiblePageIndex, d.x, d.y);
+ if (layout != null) break;
+ }
}
}
- // Note, centerX represents the center of the object that is being dragged, visually. d.x
- // represents the location of the finger within the dragged item.
- float touchX;
- float touchY = d.y;
-
- // Go through the pages and check if the dragged item is inside one of them. This block
- // is responsible for determining whether we need to snap to a different screen.
- int nextPage = getNextPage();
- IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1, nextPage
- + (isTwoPanelEnabled() ? 2 : 1));
- for (int pageIndex : pageIndexesToVerify) {
- if (layout != null || isPageInTransition()) {
- break;
- }
-
- // When deciding whether to perform a page switch, we need to consider the most extreme
- // X coordinate between the finger location and the center of the object being dragged.
- // This is either the max or the min of the two depending on whether dragging to the
- // left / right, respectively.
- touchX = ((((pageIndex < nextPage) && !mIsRtl) || pageIndex > nextPage && mIsRtl)
- ? Math.min(d.x, centerX) : Math.max(d.x, centerX));
- layout = verifyInsidePage(pageIndex, touchX, touchY);
- }
-
- // If the dragged item isn't located in one of the pages above, the icon will stay on the
- // current screen. For two panel pick the closest panel on the current screen,
- // on one panel just choose the current page.
- if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
- if (isTwoPanelEnabled()) {
- // When determining which panel to use within a single screen, we always use
- // the centroid of the object rather than the finger.
- touchX = centerX;
- nextPage = getScreenCenter(getScrollX()) > touchX
- ? (mIsRtl ? nextPage + 1 : nextPage) // left side
- : (mIsRtl ? nextPage : nextPage + 1); // right side
- }
- layout = (CellLayout) getChildAt(nextPage);
- }
-
+ // Update the current drop layout if the target changed
if (layout != mDragTargetLayout) {
setCurrentDropLayout(layout);
setCurrentDragOverlappingLayout(layout);
@@ -2512,6 +2479,69 @@
return false;
}
+ private boolean shouldUseHotseatAsDropLayout(DragObject dragObject) {
+ if (mLauncher.getHotseat() == null
+ || mLauncher.getHotseat().getShortcutsAndWidgets() == null
+ || isDragWidget(dragObject)) {
+ return false;
+ }
+ View hotseatShortcuts = mLauncher.getHotseat().getShortcutsAndWidgets();
+ getViewBoundsRelativeToWorkspace(hotseatShortcuts, mTempRect);
+ return mTempRect.contains(dragObject.x, dragObject.y);
+ }
+
+ private boolean isDragObjectOverSmartSpace(DragObject dragObject) {
+ if (mQsb == null) {
+ return false;
+ }
+ getViewBoundsRelativeToWorkspace(mQsb, mTempRect);
+ return mTempRect.contains(dragObject.x, dragObject.y);
+ }
+
+ private CellLayout checkDragObjectIsOverNeighbourPages(DragObject d, float centerX) {
+ if (isPageInTransition()) {
+ return null;
+ }
+
+ // Check the workspace pages whether the object is over any of them
+
+ // Note, centerX represents the center of the object that is being dragged, visually.
+ // d.x represents the location of the finger within the dragged item.
+ float touchX;
+ float touchY = d.y;
+
+ // Go through the pages and check if the dragged item is inside one of them. This block
+ // is responsible for determining whether we need to snap to a different screen.
+ int nextPage = getNextPage();
+ IntSet pageIndexesToVerify = IntSet.wrap(nextPage - 1,
+ nextPage + (isTwoPanelEnabled() ? 2 : 1));
+
+ for (int pageIndex : pageIndexesToVerify) {
+ // When deciding whether to perform a page switch, we need to consider the most
+ // extreme X coordinate between the finger location and the center of the object
+ // being dragged. This is either the max or the min of the two depending on whether
+ // dragging to the left / right, respectively.
+ touchX = (((pageIndex < nextPage) && !mIsRtl) || (pageIndex > nextPage && mIsRtl))
+ ? Math.min(d.x, centerX) : Math.max(d.x, centerX);
+ CellLayout layout = verifyInsidePage(pageIndex, touchX, touchY);
+ if (layout != null) {
+ return layout;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the given view's bounds relative to Workspace
+ */
+ private void getViewBoundsRelativeToWorkspace(View view, Rect outRect) {
+ mLauncher.getDragLayer()
+ .getDescendantRectRelativeToSelf(view, mTempRect);
+ // map draglayer relative bounds to workspace
+ mLauncher.getDragLayer().mapRectInSelfToDescendant(this, mTempRect);
+ outRect.set(mTempRect);
+ }
+
/**
* Returns the child CellLayout if the point is inside the page coordinates, null otherwise.
*/
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 84b95ec..bf56ac0 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -25,6 +25,7 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
+import static com.android.launcher3.LauncherState.FLAG_HOTSEAT_INACCESSIBLE;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -151,6 +152,12 @@
propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
workspacePageIndicatorAlpha, fadeInterpolator);
+ // Update the accessibility flags for hotseat based on launcher state.
+ hotseat.setImportantForAccessibility(
+ state.hasFlag(FLAG_HOTSEAT_INACCESSIBLE)
+ ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+
Interpolator translationInterpolator =
config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 7687fea..2a16210 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -43,10 +43,6 @@
public static final String TAG = "AlphabeticalAppsList";
- private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
- private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
-
- private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
private final WorkAdapterProvider mWorkAdapterProvider;
/**
@@ -196,27 +192,6 @@
return true;
}
- public boolean appendSearchResults(ArrayList<AdapterItem> results) {
- if (hasFilter() && results != null && results.size() > 0) {
- updateSearchAdapterItems(results, mSearchResults.size());
- refreshRecyclerView();
- return true;
- }
- return false;
- }
-
- void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
- for (int i = 0; i < list.size(); i++) {
- AdapterItem adapterItem = list.get(i);
- adapterItem.position = offset + i;
- mAdapterItems.add(adapterItem);
-
- if (adapterItem.isCountedForAccessibility()) {
- mAccessibilityResultsCount++;
- }
- }
- }
-
/**
* Updates internals when the set of apps are updated.
*/
@@ -286,7 +261,6 @@
String lastSectionName = null;
FastScrollSectionInfo lastFastScrollerSectionInfo = null;
int position = 0;
- int appIndex = 0;
// Prepare to update the list of sections, filtered apps, etc.
mAccessibilityResultsCount = 0;
@@ -315,8 +289,7 @@
}
// Create an app item
- AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info,
- appIndex++);
+ AdapterItem appItem = AdapterItem.asApp(position++, info);
if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
lastFastScrollerSectionInfo.fastScrollToItem = appItem;
}
@@ -324,7 +297,16 @@
mAdapterItems.add(appItem);
}
} else {
- updateSearchAdapterItems(mSearchResults, 0);
+ int count = mSearchResults.size();
+ for (int i = 0; i < count; i++) {
+ AdapterItem adapterItem = mSearchResults.get(i);
+ adapterItem.position = i;
+ mAdapterItems.add(adapterItem);
+
+ if (adapterItem.isCountedForAccessibility()) {
+ mAccessibilityResultsCount++;
+ }
+ }
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
// Append the search market item
if (hasNoFilteredResults()) {
@@ -360,34 +342,16 @@
mNumAppRowsInAdapter = rowIndex + 1;
// Pre-calculate all the fast scroller fractions
- switch (mFastScrollDistributionMode) {
- case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION:
- float rowFraction = 1f / mNumAppRowsInAdapter;
- for (FastScrollSectionInfo info : mFastScrollerSections) {
- AdapterItem item = info.fastScrollToItem;
- if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) {
- info.touchFraction = 0f;
- continue;
- }
-
- float subRowFraction =
- item.rowAppIndex * (rowFraction / mNumAppsPerRowAllApps);
- info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
- }
- break;
- case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS:
- float perSectionTouchFraction = 1f / mFastScrollerSections.size();
- float cumulativeTouchFraction = 0f;
- for (FastScrollSectionInfo info : mFastScrollerSections) {
- AdapterItem item = info.fastScrollToItem;
- if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) {
- info.touchFraction = 0f;
- continue;
- }
- info.touchFraction = cumulativeTouchFraction;
- cumulativeTouchFraction += perSectionTouchFraction;
- }
- break;
+ float perSectionTouchFraction = 1f / mFastScrollerSections.size();
+ float cumulativeTouchFraction = 0f;
+ for (FastScrollSectionInfo info : mFastScrollerSections) {
+ AdapterItem item = info.fastScrollToItem;
+ if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) {
+ info.touchFraction = 0f;
+ continue;
+ }
+ info.touchFraction = cumulativeTouchFraction;
+ cumulativeTouchFraction += perSectionTouchFraction;
}
}
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 976284d..8ac2536 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -36,7 +36,6 @@
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.views.ActivityContext;
import java.util.Arrays;
@@ -94,31 +93,21 @@
// The type of this item
public int viewType;
- // The section name of this item. Note that there can be multiple items with different
- // sectionNames in the same section
- public String sectionName = null;
// The row that this item shows up on
public int rowIndex;
// The index of this app in the row
public int rowAppIndex;
// The associated ItemInfoWithIcon for the item
- public ItemInfoWithIcon itemInfo = null;
- // The index of this app not including sections
- public int appIndex = -1;
- // Search section associated to result
- public DecorationInfo decorationInfo = null;
+ public AppInfo itemInfo = null;
/**
* Factory method for AppIcon AdapterItem
*/
- public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
- int appIndex) {
+ public static AdapterItem asApp(int pos, AppInfo appInfo) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_ICON;
item.position = pos;
- item.sectionName = sectionName;
item.itemInfo = appInfo;
- item.appIndex = appIndex;
return item;
}
@@ -267,11 +256,7 @@
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
- if (adapterItem.itemInfo instanceof AppInfo) {
- icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
- } else {
- icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
- }
+ icon.applyFromApplicationInfo(adapterItem.itemInfo);
break;
case VIEW_TYPE_EMPTY_SEARCH:
TextView emptyViewText = (TextView) holder.itemView;
diff --git a/src/com/android/launcher3/allapps/DecorationInfo.java b/src/com/android/launcher3/allapps/DecorationInfo.java
deleted file mode 100644
index 50b250c..0000000
--- a/src/com/android/launcher3/allapps/DecorationInfo.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-public class DecorationInfo {
-}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 893e547..bc2c318 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -177,14 +177,6 @@
}
@Override
- public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
- if (items != null) {
- mApps.appendSearchResults(items);
- notifyResultChanged();
- }
- }
-
- @Override
public void clearSearchResult() {
if (mApps.setSearchResults(null)) {
notifyResultChanged();
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 222c8fe..33d0082 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -85,7 +85,7 @@
for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) {
AppInfo info = apps.get(i);
if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
- AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount);
+ AdapterItem appItem = AdapterItem.asApp(resultCount, info);
result.add(appItem);
resultCount++;
}
diff --git a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
index 72956b0..c0a04b1 100644
--- a/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
+++ b/src/com/android/launcher3/popup/LauncherPopupLiveUpdateHandler.java
@@ -45,8 +45,9 @@
@Override
public void onWidgetsBound() {
- ItemInfo itemInfo = (ItemInfo) mPopupContainerWithArrow.getOriginalIcon().getTag();
- SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mContext, itemInfo);
+ BubbleTextView originalIcon = mPopupContainerWithArrow.getOriginalIcon();
+ SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mContext,
+ (ItemInfo) originalIcon.getTag(), originalIcon);
View widgetsView = getWidgetsView(mPopupContainerWithArrow);
if (widgetsView == null && mPopupContainerWithArrow.getWidgetContainer() != null) {
widgetsView = getWidgetsView(mPopupContainerWithArrow.getWidgetContainer());
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index bd3778a..484b879 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -218,7 +218,7 @@
popupDataProvider.getShortcutCountForItem(item),
popupDataProvider.getNotificationKeysForItem(item),
launcher.getSupportedShortcuts()
- .map(s -> s.getShortcut(launcher, item))
+ .map(s -> s.getShortcut(launcher, item, icon))
.filter(Objects::nonNull)
.collect(Collectors.toList()));
launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 7c393ad..e5e2c35 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -46,8 +46,8 @@
private final RemoteAction mAction;
public RemoteActionShortcut(RemoteAction action,
- BaseDraggingActivity activity, ItemInfo itemInfo) {
- super(0, R.id.action_remote_action_shortcut, activity, itemInfo);
+ BaseDraggingActivity activity, ItemInfo itemInfo, View originalView) {
+ super(0, R.id.action_remote_action_shortcut, activity, itemInfo, originalView);
mAction = action;
}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 08d3779..0e25984c 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -46,18 +46,21 @@
protected final T mTarget;
protected final ItemInfo mItemInfo;
+ protected final View mOriginalView;
/**
* Indicates if it's invokable or not through some disabled UI
*/
private boolean isEnabled = true;
- public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
+ public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo,
+ View originalView) {
mIconResId = iconResId;
mLabelResId = labelResId;
mAccessibilityActionId = labelResId;
mTarget = target;
mItemInfo = itemInfo;
+ mOriginalView = originalView;
}
public SystemShortcut(SystemShortcut<T> other) {
@@ -66,6 +69,7 @@
mAccessibilityActionId = other.mAccessibilityActionId;
mTarget = other.mTarget;
mItemInfo = other.mItemInfo;
+ mOriginalView = other.mOriginalView;
}
/**
@@ -107,10 +111,10 @@
public interface Factory<T extends Context & ActivityContext> {
- @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
+ @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo, View originalView);
}
- public static final Factory<Launcher> WIDGETS = (launcher, itemInfo) -> {
+ public static final Factory<Launcher> WIDGETS = (launcher, itemInfo, originalView) -> {
if (itemInfo.getTargetComponent() == null) return null;
final List<WidgetItem> widgets =
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
@@ -118,12 +122,13 @@
if (widgets.isEmpty()) {
return null;
}
- return new Widgets(launcher, itemInfo);
+ return new Widgets(launcher, itemInfo, originalView);
};
public static class Widgets extends SystemShortcut<Launcher> {
- public Widgets(Launcher target, ItemInfo itemInfo) {
- super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo);
+ public Widgets(Launcher target, ItemInfo itemInfo, View originalView) {
+ super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo,
+ originalView);
}
@Override
@@ -145,9 +150,9 @@
@Nullable
private SplitAccessibilityInfo mSplitA11yInfo;
- public AppInfo(T target, ItemInfo itemInfo) {
+ public AppInfo(T target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
- itemInfo);
+ itemInfo, originalView);
}
/**
@@ -160,8 +165,9 @@
* That way it could directly create the correct node info for any shortcut that supports
* split, but then we'll need custom resIDs for each pair of shortcuts.
*/
- public AppInfo(T target, ItemInfo itemInfo, SplitAccessibilityInfo accessibilityInfo) {
- this(target, itemInfo);
+ public AppInfo(T target, ItemInfo itemInfo, View originalView,
+ SplitAccessibilityInfo accessibilityInfo) {
+ this(target, itemInfo, originalView);
mSplitA11yInfo = accessibilityInfo;
mAccessibilityActionId = accessibilityInfo.nodeId;
}
@@ -203,28 +209,29 @@
}
}
- public static final Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
- boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
- && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
+ public static final Factory<BaseDraggingActivity> INSTALL =
+ (activity, itemInfo, originalView) -> {
+ boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)
+ && ((WorkspaceItemInfo) itemInfo).hasStatusFlag(
WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
- boolean isInstantApp = false;
- if (itemInfo instanceof com.android.launcher3.model.data.AppInfo) {
- com.android.launcher3.model.data.AppInfo
- appInfo = (com.android.launcher3.model.data.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
- }
- boolean enabled = supportsWebUI || isInstantApp;
- if (!enabled) {
- return null;
- }
- return new Install(activity, itemInfo);
+ boolean isInstantApp = false;
+ if (itemInfo instanceof com.android.launcher3.model.data.AppInfo) {
+ com.android.launcher3.model.data.AppInfo
+ appInfo = (com.android.launcher3.model.data.AppInfo) itemInfo;
+ isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
+ }
+ boolean enabled = supportsWebUI || isInstantApp;
+ if (!enabled) {
+ return null;
+ }
+ return new Install(activity, itemInfo, originalView);
};
public static class Install extends SystemShortcut<BaseDraggingActivity> {
- public Install(BaseDraggingActivity target, ItemInfo itemInfo) {
+ public Install(BaseDraggingActivity target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_install_no_shadow, R.string.install_drop_target_label,
- target, itemInfo);
+ target, itemInfo, originalView);
}
@Override
diff --git a/src/com/android/launcher3/search/SearchCallback.java b/src/com/android/launcher3/search/SearchCallback.java
index 5796116..495a303 100644
--- a/src/com/android/launcher3/search/SearchCallback.java
+++ b/src/com/android/launcher3/search/SearchCallback.java
@@ -32,13 +32,6 @@
void onSearchResult(String query, ArrayList<T> items);
/**
- * Called when the search from secondary source is complete.
- *
- * @param items list of search results
- */
- void onAppendSearchResult(String query, ArrayList<T> items);
-
- /**
* Called when the search results should be cleared.
*/
void clearSearchResult();
diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
index e9058c3..a0ed77e 100644
--- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
+++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
@@ -205,8 +205,8 @@
/**
* Returns a system shortcut to pin/unpin a shortcut
*/
- public SystemShortcut getSystemShortcut(ItemInfo info) {
- return new PinUnPinShortcut(mLauncher, info,
+ public SystemShortcut getSystemShortcut(ItemInfo info, View originalView) {
+ return new PinUnPinShortcut(mLauncher, info, originalView,
mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
}
@@ -214,10 +214,11 @@
private final boolean mIsPinned;
- PinUnPinShortcut(SecondaryDisplayLauncher target, ItemInfo info, boolean isPinned) {
+ PinUnPinShortcut(SecondaryDisplayLauncher target, ItemInfo info, View originalView,
+ boolean isPinned) {
super(isPinned ? R.drawable.ic_remove_no_shadow : R.drawable.ic_pin,
isPinned ? R.string.remove_drop_target_label : R.string.action_add_to_workspace,
- target, info);
+ target, info, originalView);
mIsPinned = isPinned;
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 9201006..e906c95 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -193,8 +193,8 @@
container.populateAndShow((BubbleTextView) v,
popupDataProvider.getShortcutCountForItem(item),
Collections.emptyList(),
- Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item),
- APP_INFO.getShortcut(mActivity, item)));
+ Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v),
+ APP_INFO.getShortcut(mActivity, item, v)));
v.getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index 0c6d675..9af68c0 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -57,11 +57,17 @@
}
public static Intent makeIntent(ShortcutInfo si) {
+ return makeIntent(si.getId(), si.getPackage()).setComponent(si.getActivity());
+ }
+
+ /**
+ * Creates an intent for shortcut id and package name.
+ */
+ public static Intent makeIntent(String shortcutId, String packageName) {
return new Intent(Intent.ACTION_MAIN)
.addCategory(INTENT_CATEGORY)
- .setComponent(si.getActivity())
- .setPackage(si.getPackage())
+ .setPackage(packageName)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- .putExtra(EXTRA_SHORTCUT_ID, si.getId());
+ .putExtra(EXTRA_SHORTCUT_ID, shortcutId);
}
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 7e9d56d..e311bc8 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -52,16 +52,7 @@
}
float shrunkTop = grid.getWorkspaceSpringLoadShrunkTop();
- float shrunkBottom = grid.getWorkspaceSpringLoadShrunkBottom();
- float scale = Math.min((shrunkBottom - shrunkTop) / ws.getNormalChildHeight(), 1f);
-
- // Reduce scale if next pages would not be visible after scaling the workspace
- float scaledWorkspaceWidth = ws.getWidth() * scale;
- float maxAvailableWidth =
- ws.getWidth() - (2 * grid.getWorkspaceSpringLoadedMinimumNextPageVisible());
- if (scaledWorkspaceWidth > maxAvailableWidth) {
- scale *= maxAvailableWidth / scaledWorkspaceWidth;
- }
+ float scale = grid.getWorkspaceSpringLoadScale();
float halfHeight = ws.getHeight() / 2;
float myCenter = ws.getTop() + halfHeight;
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 8005181..7c73be5 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -242,7 +242,9 @@
change |= CHANGE_SUPPORTED_BOUNDS;
Point currentS = newInfo.currentSize;
- Point expectedS = oldInfo.mPerDisplayBounds.get(newInfo.displayId).first.size;
+ Pair<CachedDisplayInfo, WindowBounds[]> cachedBounds =
+ oldInfo.mPerDisplayBounds.get(newInfo.displayId);
+ Point expectedS = cachedBounds == null ? null : cachedBounds.first.size;
if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
Log.e("b/198965093",
"Inconsistent number of displays"
@@ -250,10 +252,12 @@
+ "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
+ "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
}
- if ((Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y)
+ if (expectedS != null
+ && (Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y)
|| Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y))
&& display.getState() == Display.STATE_OFF) {
- Log.e("b/198965093", "Display size changed while display is off, ignoring change");
+ Log.e("b/198965093",
+ "Display size changed while display is off, ignoring change");
return;
}
}
@@ -286,7 +290,7 @@
// Configuration property
public final float fontScale;
- public final int densityDpi;
+ private final int densityDpi;
public final NavigationMode navigationMode;
private final PortraitSize mScreenSizeDp;
@@ -353,6 +357,10 @@
public float smallestSizeDp(WindowBounds bounds) {
return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()), densityDpi);
}
+
+ public int getDensityDpi() {
+ return densityDpi;
+ }
}
/**
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 8485371..6978e0c 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -15,17 +15,12 @@
*/
package com.android.launcher3.util;
-import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
@@ -42,7 +37,7 @@
private static final int KEEP_ALIVE = 1;
/** Dedicated executor instances for work depending on other packages. */
- private static final Map<String, ExecutorService> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
+ private static final Map<String, LooperExecutor> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
/**
* An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
@@ -90,11 +85,10 @@
*
* @param packageName Package associated with the executor.
*/
- public static ExecutorService getPackageExecutor(String packageName) {
+ public static LooperExecutor getPackageExecutor(String packageName) {
return PACKAGE_EXECUTORS.computeIfAbsent(
- packageName,
- p -> newSingleThreadExecutor(
- new SimpleThreadFactory(p, THREAD_PRIORITY_BACKGROUND)));
+ packageName, p -> new LooperExecutor(
+ createAndStartNewLooper(p, Process.THREAD_PRIORITY_DEFAULT)));
}
/**
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 5aaa275..61b7fa1 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
-import static com.android.launcher3.ResourceUtils.getDimenByName;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import static com.android.launcher3.util.RotationUtils.deltaRotation;
@@ -157,16 +156,16 @@
int bottomNav = isTablet
? 0
: (config.screenHeightDp > config.screenWidthDp
- ? getDimenByName(NAVBAR_HEIGHT, systemRes, 0)
+ ? getDimenByName(NAVBAR_HEIGHT, systemRes)
: (isGesture
- ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0)
+ ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes)
: 0));
Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
Insets statusBarInsets = oldInsets.getInsets(WindowInsets.Type.statusBars());
- int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0);
+ int statusBarHeight = getDimenByName("status_bar_height", systemRes);
Insets newStatusBarInsets = Insets.of(
statusBarInsets.left,
Math.max(statusBarInsets.top, statusBarHeight),
@@ -222,23 +221,23 @@
boolean isTabletOrGesture = isTablet
|| (Utilities.ATLEAST_R && isGestureNav(context));
- int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0);
+ int statusBarHeight = getDimenByName("status_bar_height", systemRes);
int navBarHeightPortrait, navBarHeightLandscape, navbarWidthLandscape;
navBarHeightPortrait = isTablet
? (mTaskbarDrawnInProcess
? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
- : getDimenByName(NAVBAR_HEIGHT, systemRes, 0);
+ : getDimenByName(NAVBAR_HEIGHT, systemRes);
navBarHeightLandscape = isTablet
? (mTaskbarDrawnInProcess
? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
: (isTabletOrGesture
- ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) : 0);
+ ? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes) : 0);
navbarWidthLandscape = isTabletOrGesture
? 0
- : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes, 0);
+ : getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes);
WindowBounds[] result = new WindowBounds[4];
Point tempSize = new Point();
@@ -274,6 +273,13 @@
return result;
}
+ /**
+ * Wrapper around the utility method for easier emulation
+ */
+ protected int getDimenByName(String resName, Resources res) {
+ return ResourceUtils.getDimenByName(resName, res, 0);
+ }
+
protected boolean isGestureNav(Context context) {
return ResourceUtils.getIntegerByName("config_navBarInteractionMode",
context.getResources(), INVALID_RESOURCE_HANDLE) == 2;
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index f71aa13..4c001fd 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -413,6 +413,14 @@
}
/**
+ * Similar to {@link #mapCoordInSelfToDescendant(View descendant, float[] coord)}
+ * but accepts a Rect instead of float[].
+ */
+ public void mapRectInSelfToDescendant(View descendant, Rect rect) {
+ Utilities.mapRectInSelfToDescendant(descendant, this, rect);
+ }
+
+ /**
* Inverse of {@link #getDescendantCoordRelativeToSelf(View, float[])}.
*/
public void mapCoordInSelfToDescendant(View descendant, float[] coord) {
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
index 2751a52..a15508a 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -95,11 +95,6 @@
}
@Override
- public void onAppendSearchResult(String query, ArrayList<WidgetsListBaseEntry> items) {
- // Not needed.
- }
-
- @Override
public void clearSearchResult() {
// Any existing search session will be cancelled by setting text to empty.
mInput.setText("");
diff --git a/tests/res/raw/devices.json b/tests/res/raw/devices.json
new file mode 100644
index 0000000..a78dd86
--- /dev/null
+++ b/tests/res/raw/devices.json
@@ -0,0 +1,45 @@
+{
+ "pixel6pro": {
+ "width": 1440,
+ "height": 3120,
+ "density": 560,
+ "name": "pixel6pro",
+ "cutout": "0, 130, 0, 0",
+ "grids": [
+ "normal",
+ "reasonable",
+ "practical",
+ "big",
+ "crazy_big"
+ ],
+ "resourceOverrides": {
+ "status_bar_height": 98,
+ "navigation_bar_height_landscape": 56,
+ "navigation_bar_height": 56,
+ "navigation_bar_width": 56
+ }
+ },
+ "test": {
+ "data needs updating": 0
+ },
+ "pixel5": {
+ "width": 1080,
+ "height": 2340,
+ "density": 440,
+ "name": "pixel5",
+ "cutout": "0, 136, 0, 0",
+ "grids": [
+ "normal",
+ "reasonable",
+ "practical",
+ "big",
+ "crazy_big"
+ ],
+ "resourceOverrides": {
+ "status_bar_height": 66,
+ "navigation_bar_height_landscape": 44,
+ "navigation_bar_height": 44,
+ "navigation_bar_width": 44
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index f91f1c4..6d0fcb6 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -64,6 +64,7 @@
windowBounds = WindowBounds(x, y, x, y - 100, 0)
whenever(info.isTablet(any())).thenReturn(false)
+ whenever(info.getDensityDpi()).thenReturn(560)
inv = newScalableInvariantDeviceProfile()
}
@@ -77,6 +78,7 @@
windowBounds = WindowBounds(x, y, x, y - 100, 0)
whenever(info.isTablet(any())).thenReturn(true)
+ whenever(info.getDensityDpi()).thenReturn(320)
inv = newScalableInvariantDeviceProfile()
}
@@ -107,6 +109,7 @@
PointF(16f, 16f)
).toTypedArray()
hotseatBorderSpaces = FloatArray(4) { 16f }
+ hotseatColumnSpan = IntArray(4) { 4 }
iconSize = FloatArray(4) { 56f }
allAppsIconSize = FloatArray(4) { 56f }
iconTextSize = FloatArray(4) { 14f }
diff --git a/tests/src/com/android/launcher3/HotseatSizeTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
similarity index 97%
rename from tests/src/com/android/launcher3/HotseatSizeTest.kt
rename to tests/src/com/android/launcher3/HotseatShownIconsTest.kt
index a44939f..593239d 100644
--- a/tests/src/com/android/launcher3/HotseatSizeTest.kt
+++ b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
@@ -23,15 +23,13 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito.`when` as whenever
/**
* Test for [DeviceProfile]
*/
@SmallTest
@RunWith(AndroidJUnit4::class)
-class HotseatSizeTest : DeviceProfileBaseTest() {
+class HotseatShownIconsTest : DeviceProfileBaseTest() {
@Test
fun hotseat_size_is_normal_for_handhelds() {
diff --git a/tests/src/com/android/launcher3/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt
index e00dca8..905c1e1 100644
--- a/tests/src/com/android/launcher3/InlineQsbTest.kt
+++ b/tests/src/com/android/launcher3/InlineQsbTest.kt
@@ -29,17 +29,16 @@
class InlineQsbTest : DeviceProfileBaseTest() {
@Test
- fun qsbWidth_is_match_parent_for_phones() {
+ fun qsb_is_not_inline_for_phones() {
initializeVarsForPhone()
val dp = newDP()
assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.qsbWidth).isEqualTo(0)
}
@Test
- fun qsbWidth_is_match_parent_for_tablet_portrait() {
+ fun qsb_is_inline_for_tablet_portrait() {
initializeVarsForTablet()
inv = newScalableInvariantDeviceProfile().apply {
inlineQsb = booleanArrayOf(
@@ -62,11 +61,10 @@
)
assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.qsbWidth).isEqualTo(0)
}
@Test
- fun qsbWidth_has_size_for_tablet_landscape() {
+ fun qsb_is_inline_for_tablet_landscape() {
initializeVarsForTablet(isLandscape = true)
inv = newScalableInvariantDeviceProfile().apply {
inlineQsb = booleanArrayOf(
@@ -75,16 +73,17 @@
false,
false
)
+ numColumns = 6
+ numRows = 5
+ numShownHotseatIcons = 6
}
val dp = newDP()
if (dp.hotseatQsbHeight > 0) {
assertThat(dp.isQsbInline).isTrue()
- assertThat(dp.qsbWidth).isGreaterThan(0)
} else { // Launcher3 doesn't have QSB height
assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.qsbWidth).isEqualTo(0)
}
}
@@ -92,14 +91,13 @@
* This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support
*/
@Test
- fun qsbWidth_is_match_parent_for_tablet_landscape_without_inline() {
+ fun qsb_is_not_inline_for_tablet_landscape_without_inline() {
initializeVarsForTablet(isLandscape = true)
useTwoPanels = true
val dp = newDP()
assertThat(dp.isQsbInline).isFalse()
- assertThat(dp.qsbWidth).isEqualTo(0)
}
}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java
new file mode 100644
index 0000000..31468c5
--- /dev/null
+++ b/tests/src/com/android/launcher3/deviceemulator/DisplayEmulator.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.deviceemulator;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.launcher3.deviceemulator.models.DeviceEmulationData;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.util.window.WindowManagerProxy;
+
+import java.util.concurrent.Callable;
+
+
+public class DisplayEmulator {
+ Context mContext;
+ LauncherInstrumentation mLauncher;
+ DisplayEmulator(Context context, LauncherInstrumentation launcher) {
+ mContext = context;
+ mLauncher = launcher;
+ }
+
+ /**
+ * By changing the WindowManagerProxy we can override the window insets information
+ **/
+ private IWindowManager changeWindowManagerInstance(DeviceEmulationData deviceData) {
+ WindowManagerProxy.INSTANCE.initializeForTesting(
+ new TestWindowManagerProxy(mContext, deviceData));
+ return WindowManagerGlobal.getWindowManagerService();
+ }
+
+ public <T> T emulate(DeviceEmulationData device, String grid, Callable<T> runInEmulation)
+ throws Exception {
+ WindowManagerProxy original = WindowManagerProxy.INSTANCE.get(mContext);
+ // Set up emulation
+ final int userId = UserHandle.myUserId();
+ WindowManagerProxy.INSTANCE.initializeForTesting(
+ new TestWindowManagerProxy(mContext, device));
+ IWindowManager wm = changeWindowManagerInstance(device);
+ // Change density twice to force display controller to reset its state
+ wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density / 2, userId);
+ wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, device.density, userId);
+ wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, device.width, device.height);
+ wm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+
+ // Set up grid
+ setGrid(grid);
+ try {
+ return runInEmulation.call();
+ } finally {
+ // Clear emulation
+ WindowManagerProxy.INSTANCE.initializeForTesting(original);
+ UiDevice.getInstance(getInstrumentation()).executeShellCommand("cmd window reset");
+ }
+ }
+
+ private void setGrid(String gridType) {
+ // When the grid changes, the desktop arrangement get stored in SQL and we need to wait to
+ // make sure there is no SQL operations running and get SQL_BUSY error, that's why we need
+ // to call mLauncher.waitForLauncherInitialized();
+ mLauncher.waitForLauncherInitialized();
+ String testProviderAuthority = mContext.getPackageName() + ".grid_control";
+ Uri gridUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(testProviderAuthority)
+ .appendPath("default_grid")
+ .build();
+ ContentValues values = new ContentValues();
+ values.put("name", gridType);
+ mContext.getContentResolver().update(gridUri, values, null, null);
+ }
+}
diff --git a/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java
new file mode 100644
index 0000000..ca2f81e
--- /dev/null
+++ b/tests/src/com/android/launcher3/deviceemulator/TestWindowManagerProxy.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.deviceemulator;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.WindowInsets;
+
+import com.android.launcher3.deviceemulator.models.DeviceEmulationData;
+import com.android.launcher3.util.RotationUtils;
+import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.util.window.CachedDisplayInfo;
+import com.android.launcher3.util.window.WindowManagerProxy;
+
+public class TestWindowManagerProxy extends WindowManagerProxy {
+
+ private final DeviceEmulationData mDevice;
+
+ public TestWindowManagerProxy(Context context, DeviceEmulationData device) {
+ super(true);
+ mDevice = device;
+ }
+
+ @Override
+ public boolean isInternalDisplay(Display display) {
+ return display.getDisplayId() == Display.DEFAULT_DISPLAY;
+ }
+
+ @Override
+ protected int getDimenByName(String resName, Resources res) {
+ Integer mock = mDevice.resourceOverrides.get(resName);
+ return mock != null ? mock : super.getDimenByName(resName, res);
+ }
+
+ @Override
+ public CachedDisplayInfo getDisplayInfo(Context context, Display display) {
+ int rotation = display.getRotation();
+ Point size = new Point(mDevice.width, mDevice.height);
+ RotationUtils.rotateSize(size, rotation);
+ Rect cutout = new Rect(mDevice.cutout);
+ RotationUtils.rotateRect(cutout, rotation);
+ return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutout);
+ }
+
+ @Override
+ public WindowBounds getRealBounds(Context windowContext, Display display,
+ CachedDisplayInfo info) {
+ return estimateInternalDisplayBounds(windowContext)
+ .get(getDisplayId(display)).second[display.getRotation()];
+ }
+
+ @Override
+ public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
+ Rect outInsets) {
+ outInsets.set(getRealBounds(context, context.getDisplay(),
+ getDisplayInfo(context, context.getDisplay())).insets);
+ return oldInsets;
+ }
+}
diff --git a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
new file mode 100644
index 0000000..8d275cc
--- /dev/null
+++ b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.deviceemulator.models;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
+import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
+import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
+import static com.android.launcher3.ResourceUtils.getDimenByName;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.ArrayMap;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.IOUtils;
+import com.android.launcher3.util.IntArray;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Map;
+
+public class DeviceEmulationData {
+
+ public final int width;
+ public final int height;
+ public final int density;
+ public final String name;
+ public final String[] grids;
+ public final Rect cutout;
+ public final Map<String, Integer> resourceOverrides;
+
+ private static final String[] EMULATED_SYSTEM_RESOURCES = new String[]{
+ NAVBAR_HEIGHT,
+ NAVBAR_HEIGHT_LANDSCAPE,
+ NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE,
+ "status_bar_height",
+ };
+
+ public DeviceEmulationData(int width, int height, int density, Rect cutout, String name,
+ String[] grid,
+ Map<String, Integer> resourceOverrides) {
+ this.width = width;
+ this.height = height;
+ this.density = density;
+ this.name = name;
+ this.grids = grid;
+ this.cutout = cutout;
+ this.resourceOverrides = resourceOverrides;
+ }
+
+ public static DeviceEmulationData deviceFromJSON(JSONObject json) throws JSONException {
+ int width = json.getInt("width");
+ int height = json.getInt("height");
+ int density = json.getInt("density");
+ String name = json.getString("name");
+
+ JSONArray gridArray = json.getJSONArray("grids");
+ String[] grids = new String[gridArray.length()];
+ for (int i = 0, count = grids.length; i < count; i++) {
+ grids[i] = gridArray.getString(i);
+ }
+
+ IntArray deviceCutout = IntArray.fromConcatString(json.getString("cutout"));
+ Rect cutout = new Rect(deviceCutout.get(0), deviceCutout.get(1), deviceCutout.get(2),
+ deviceCutout.get(3));
+
+
+ JSONObject resourceOverridesJson = json.getJSONObject("resourceOverrides");
+ Map<String, Integer> resourceOverrides = new ArrayMap<>();
+ for (String key : resourceOverridesJson.keySet()) {
+ resourceOverrides.put(key, resourceOverridesJson.getInt(key));
+ }
+ return new DeviceEmulationData(width, height, density, cutout, name, grids,
+ resourceOverrides);
+ }
+
+ @Override
+ public String toString() {
+ JSONObject json = new JSONObject();
+ try {
+ json.put("width", width);
+ json.put("height", height);
+ json.put("density", density);
+ json.put("name", name);
+ json.put("cutout", IntArray.wrap(
+ cutout.left, cutout.top, cutout.right, cutout.bottom).toConcatString());
+
+ JSONArray gridArray = new JSONArray();
+ Arrays.stream(grids).forEach(gridArray::put);
+ json.put("grids", gridArray);
+
+
+ JSONObject resourceOverrides = new JSONObject();
+ for (Map.Entry<String, Integer> e : this.resourceOverrides.entrySet()) {
+ resourceOverrides.put(e.getKey(), e.getValue());
+ }
+ json.put("resourceOverrides", resourceOverrides);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return json.toString();
+ }
+
+ public static DeviceEmulationData getCurrentDeviceData(Context context) {
+ DisplayController.Info info = DisplayController.INSTANCE.get(context).getInfo();
+ String[] grids = InvariantDeviceProfile.INSTANCE.get(context)
+ .parseAllGridOptions(context).stream()
+ .map(go -> go.name).toArray(String[]::new);
+ String code = Build.MODEL.replaceAll("\\s", "").toLowerCase();
+
+ Map<String, Integer> resourceOverrides = new ArrayMap<>();
+ for (String s : EMULATED_SYSTEM_RESOURCES) {
+ resourceOverrides.put(s, getDimenByName(s, context.getResources(), 0));
+ }
+ return new DeviceEmulationData(info.currentSize.x, info.currentSize.y,
+ info.getDensityDpi(), info.cutout, code, grids, resourceOverrides);
+ }
+
+ public static DeviceEmulationData getDevice(String deviceCode) throws Exception {
+ return DeviceEmulationData.deviceFromJSON(readJSON().getJSONObject(deviceCode));
+ }
+
+ private static JSONObject readJSON() throws Exception {
+ Context context = getInstrumentation().getContext();
+ Resources myRes = context.getResources();
+ int resId = myRes.getIdentifier("devices", "raw", context.getPackageName());
+ try (InputStream is = myRes.openRawResource(resId)) {
+ return new JSONObject(new String(IOUtils.toByteArray(is)));
+ }
+ }
+
+}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 657f213..4c41d7e 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -132,7 +132,9 @@
dumpCommand("logcat -d -s TestRunner", diagFile(description, "FilteredLogcat", "txt"));
// Dump bugreport
- if (launcher.getSystemAnomalyMessage(false, false) != null) {
+ final String systemAnomalyMessage = launcher.getSystemAnomalyMessage(false, false);
+ if (systemAnomalyMessage != null) {
+ Log.d(TAG, "Saving bugreport, system anomaly message: " + systemAnomalyMessage, e);
dumpCommand("bugreportz -s", diagFile(description, "Bugreport", "zip"));
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index fee4490..ae0f6a6 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -389,8 +389,7 @@
// Since the destination can be on another page, we need to drag to the edge first
// until we reach the target page
while (targetDest.x > displayX || targetDest.x < 0) {
- // TODO: b/219919285
- int edgeX = targetDest.x > 0 ? displayX - 1 : 1;
+ int edgeX = targetDest.x > 0 ? displayX : 0;
Point screenEdge = new Point(edgeX, targetDest.y);
Point finalDragStart = dragStart;
executeAndWaitForPageScroll(launcher,