Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-v2-dev
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bf9059f..a48e2c0 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -103,16 +103,10 @@
android:clearTaskOnLaunch="true"
android:exported="false"/>
- <!--
- Activity for gesture nav onboarding.
- It's protected by android.permission.REBOOT to ensure that only system apps can start it
- (setup wizard already has this permission)
- -->
<activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
- android:permission="android.permission.REBOOT"
android:exported="true">
<intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d08949b..deeaaa6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -42,6 +42,7 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
+import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELED;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
@@ -230,7 +231,6 @@
// Used to control launcher components throughout the swipe gesture.
private AnimatorControllerWithResistance mLauncherTransitionController;
- private boolean mHasEndedLauncherTransition;
private AnimationFactory mAnimationFactory = (t) -> { };
@@ -379,6 +379,17 @@
activity.runOnceOnStart(this::onLauncherStart);
}
+ // Set up a entire animation lifecycle callback to notify the current recents view when
+ // the animation is canceled
+ mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> {
+ ThumbnailData snapshot = mGestureState.getRecentsAnimationCanceledSnapshot();
+ if (snapshot != null) {
+ RecentsModel.INSTANCE.get(mContext).onTaskSnapshotChanged(
+ mRecentsView.getRunningTaskId(), snapshot);
+ mRecentsView.onRecentsAnimationComplete();
+ }
+ });
+
setupRecentsViewUi();
linkRecentsViewScroll();
@@ -604,11 +615,11 @@
/**
* We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
- * (it has its own animation) or if we explicitly ended the controller already.
+ * (it has its own animation).
* @return Whether we can create the launcher controller or update its progress.
*/
private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
- return mGestureState.getEndTarget() != HOME && !mHasEndedLauncherTransition;
+ return mGestureState.getEndTarget() != HOME;
}
@Override
@@ -672,6 +683,9 @@
mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
|| (quickswitchThresholdPassed && centermostTaskFlags != 0));
mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
+ // Provide a hint to WM the direction that we will be settling in case the animation
+ // needs to be canceled
+ mRecentsAnimationController.setWillFinishToHome(swipeUpThresholdPassed);
if (swipeUpThresholdPassed) {
mActivity.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
@@ -1423,8 +1437,6 @@
}
private void endLauncherTransitionController() {
- mHasEndedLauncherTransition = true;
-
if (mLauncherTransitionController != null) {
// End the animation, but stay at the same visual progress.
mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
@@ -1471,9 +1483,6 @@
final boolean refreshView = !LIVE_TILE.get() /* refreshView */;
boolean finishTransitionPosted = false;
if (mRecentsAnimationController != null) {
- if (LIVE_TILE.get()) {
- mRecentsAnimationController.getController().setWillFinishToHome(true);
- }
// Update the screenshot of the task
if (mTaskSnapshot == null) {
UI_HELPER_EXECUTOR.execute(() -> {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 6ad7f55..a302a07 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -142,6 +142,8 @@
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
+ private RecentsAnimationController mRecentsAnimationController;
+ private ThumbnailData mRecentsAnimationCanceledSnapshot;
/** The time when the swipe up gesture is triggered. */
private long mSwipeUpStartTimeMs;
@@ -351,13 +353,20 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
+ mRecentsAnimationController = controller;
mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
}
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ mRecentsAnimationCanceledSnapshot = thumbnailData;
mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
+ if (mRecentsAnimationCanceledSnapshot != null) {
+ // Clean up the screenshot to finalize the recents animation cancel
+ mRecentsAnimationController.cleanupScreenshot();
+ mRecentsAnimationCanceledSnapshot = null;
+ }
}
@Override
@@ -366,6 +375,14 @@
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
}
+ /**
+ * Returns the canceled animation thumbnail data. This call only returns a value while
+ * STATE_RECENTS_ANIMATION_CANCELED state is being set.
+ */
+ ThumbnailData getRecentsAnimationCanceledSnapshot() {
+ return mRecentsAnimationCanceledSnapshot;
+ }
+
void setSwipeUpStartTimeMs(long uptimeMs) {
mSwipeUpStartTimeMs = uptimeMs;
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 9e69ef9..f343485 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -159,6 +159,14 @@
}
/**
+ * @see IRecentsAnimationController#cleanupScreenshot()
+ */
+ @UiThread
+ public void cleanupScreenshot() {
+ UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+ }
+
+ /**
* @see RecentsAnimationControllerCompat#detachNavigationBarFromApp
*/
@UiThread
@@ -175,6 +183,14 @@
}
/**
+ * @see IRecentsAnimationController#setWillFinishToHome(boolean)
+ */
+ @UiThread
+ public void setWillFinishToHome(boolean willFinishToHome) {
+ UI_HELPER_EXECUTOR.execute(() -> mController.setWillFinishToHome(willFinishToHome));
+ }
+
+ /**
* Sets the final surface transaction on a Task. This is used by Launcher to notify the system
* that animating Activity to PiP has completed and the associated task surface should be
* updated accordingly. This should be called before `finish`
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index cf523d0..bc49133 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -18,6 +18,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
@@ -122,9 +123,9 @@
mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
getSupportFragmentManager().beginTransaction()
- .replace(R.id.gesture_tutorial_fragment_container, mFragment)
- .runOnCommit(() -> mFragment.onAttachedToWindow())
- .commit();
+ .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+ .runOnCommit(() -> mFragment.onAttachedToWindow())
+ .commit();
mCurrentStep++;
}
@@ -141,21 +142,33 @@
private TutorialType[] getTutorialSteps(Bundle extras) {
TutorialType[] defaultSteps = new TutorialType[] {TutorialType.LEFT_EDGE_BACK_NAVIGATION};
+ mCurrentStep = 1;
+ mNumSteps = 1;
if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
return defaultSteps;
}
- String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
+ Object savedSteps = extras.get(KEY_TUTORIAL_STEPS);
int currentStep = extras.getInt(KEY_CURRENT_STEP, -1);
+ String[] savedStepsNames;
- if (tutorialStepNames == null) {
+ if (savedSteps instanceof String) {
+ savedStepsNames = TextUtils.isEmpty((String) savedSteps)
+ ? null : ((String) savedSteps).split(",");
+ } else if (savedSteps instanceof String[]) {
+ savedStepsNames = (String[]) savedSteps;
+ } else {
return defaultSteps;
}
- TutorialType[] tutorialSteps = new TutorialType[tutorialStepNames.length];
- for (int i = 0; i < tutorialStepNames.length; i++) {
- tutorialSteps[i] = TutorialType.valueOf(tutorialStepNames[i]);
+ if (savedStepsNames == null) {
+ return defaultSteps;
+ }
+
+ TutorialType[] tutorialSteps = new TutorialType[savedStepsNames.length];
+ for (int i = 0; i < savedStepsNames.length; i++) {
+ tutorialSteps[i] = TutorialType.valueOf(savedStepsNames[i]);
}
mCurrentStep = Math.max(currentStep, 1);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2ff03ca..3a445c3 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -404,8 +404,6 @@
private final TaskOverlayFactory mTaskOverlayFactory;
- private int mOrientation;
-
protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
@@ -599,7 +597,6 @@
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
mModel = RecentsModel.INSTANCE.get(context);
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
- mOrientation = getResources().getConfiguration().orientation;
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
@@ -1265,9 +1262,7 @@
private void updateOrientationHandler() {
// Handle orientation changes.
- PagedOrientationHandler oldOrientationHandler = mOrientationHandler;
mOrientationHandler = mOrientationState.getOrientationHandler();
-
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl
? View.LAYOUT_DIRECTION_RTL
@@ -1276,12 +1271,7 @@
? View.LAYOUT_DIRECTION_LTR
: View.LAYOUT_DIRECTION_RTL);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
-
- if (!mOrientationHandler.equals(oldOrientationHandler)) {
- // Changed orientations, update controllers so they intercept accordingly.
- mActivity.getDragLayer().recreateControllers();
- }
-
+ mActivity.getDragLayer().recreateControllers();
boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
|| mOrientationState.getRecentsActivityRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
@@ -2708,15 +2698,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (LIVE_TILE.get() && mEnableDrawingLiveTile && newConfig.orientation != mOrientation) {
- switchToScreenshot(
- () -> finishRecentsAnimation(true /* toRecents */, false /* showPip */,
- this::updateRecentsRotation));
- mEnableDrawingLiveTile = false;
- } else {
- updateRecentsRotation();
- }
- mOrientation = newConfig.orientation;
+ updateRecentsRotation();
}
/**
@@ -3484,16 +3466,26 @@
if (onFinishComplete != null) {
onFinishComplete.run();
}
- // After we finish the recents animation, the current task id should be correctly
- // reset so that when the task is launched from Overview later, it goes through the
- // flow of starting a new task instead of finishing recents animation to app. A
- // typical example of this is (1) user swipes up from app to Overview (2) user
- // taps on QSB (3) user goes back to Overview and launch the most recent task.
- setCurrentTask(-1);
- mRecentsAnimationController = null;
+ onRecentsAnimationComplete();
}, sendUserLeaveHint);
}
+ /**
+ * Called when a running recents animation has finished or canceled.
+ */
+ public void onRecentsAnimationComplete() {
+ // At this point, the recents animation is not running and if the animation was canceled
+ // by a display rotation then reset this state to show the screenshot
+ setRunningTaskViewShowScreenshot(true);
+ // After we finish the recents animation, the current task id should be correctly
+ // reset so that when the task is launched from Overview later, it goes through the
+ // flow of starting a new task instead of finishing recents animation to app. A
+ // typical example of this is (1) user swipes up from app to Overview (2) user
+ // taps on QSB (3) user goes back to Overview and launch the most recent task.
+ setCurrentTask(-1);
+ mRecentsAnimationController = null;
+ }
+
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
mDisallowScrollToClearAll = disallowScrollToClearAll;
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4e9a16e..9891ff5 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -41,6 +41,7 @@
<!-- Scalable Grid -->
<dimen name="scalable_grid_left_right_margin">22dp</dimen>
+ <dimen name="scalable_grid_qsb_bottom_margin">42dp</dimen>
<!-- Workspace page indicator -->
<dimen name="workspace_page_indicator_height">24dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3c0cbb6..2eba4ed 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -162,6 +162,9 @@
public final int hotseatBarSidePaddingEndPx;
public final int hotseatQsbHeight;
+ public final float qsbBottomMarginOriginalPx;
+ public int qsbBottomMarginPx;
+
// All apps
public int allAppsOpenVerticalTranslate;
public int allAppsCellHeightPx;
@@ -336,6 +339,10 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
updateHotseatIconSize(pxFromDp(inv.iconSize, mMetrics, 1f));
+ qsbBottomMarginOriginalPx = isScalableGrid
+ ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
+ : 0;
+
overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
overviewTaskIconSizePx =
isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get() ? res.getDimensionPixelSize(
@@ -364,6 +371,8 @@
extraHotseatBottomPadding = Math.round(paddingHotseatBottom * iconScale);
hotseatBarSizePx += extraHotseatBottomPadding;
+
+ qsbBottomMarginPx = Math.round(qsbBottomMarginOriginalPx * iconScale);
} else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
// We increase the hotseat size when there is extra space.
// ie. For a display with a large aspect ratio, we can keep the icons on the workspace
@@ -810,8 +819,13 @@
int freeSpace = isTaskbarPresent
? workspacePadding.bottom
: hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
- return (int) (freeSpace * QSB_CENTER_FACTOR)
+
+ if (isScalableGrid && qsbBottomMarginPx <= freeSpace) {
+ return qsbBottomMarginPx;
+ } else {
+ return (int) (freeSpace * QSB_CENTER_FACTOR)
+ (isTaskbarPresent ? taskbarSize : getInsets().bottom);
+ }
}
/**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 231875c..750de92 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -142,6 +142,7 @@
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.model.ModelUtils;
import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -2348,24 +2349,43 @@
try {
final LauncherAppWidgetProviderInfo appWidgetInfo;
+ String reason = "";
if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
// If the provider is not ready, bind as a pending widget.
appWidgetInfo = null;
+ reason = "the provider not ready.";
} else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
// The widget id is not valid. Try to find the widget based on the provider info.
appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+ if (appWidgetInfo == null) {
+ if (WidgetsModel.GO_DISABLE_WIDGETS) {
+ reason = "widgets are disabled on go device.";
+ } else {
+ reason = "WidgetManagerHelper cannot find a provider from provider info.";
+ }
+ }
} else {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+ if (appWidgetInfo == null) {
+ if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+ reason = "CustomWidgetManager cannot find provider from that widget id .";
+ } else {
+ reason = "AppWidgetManager cannot find provider for that widget id."
+ + " It could be due to AppWidgetService is not available, or the"
+ + " appWidgetId has not been bound to a the provider yet, or you"
+ + " don't have access to that appWidgetId.";
+ }
+ }
}
// If the provider is ready, but the width is not yet restored, try to restore it.
if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
if (appWidgetInfo == null) {
- Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
- + " belongs to component " + item.providerName
- + ", as the provider is null");
+ FileLog.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName + " user " + item.user
+ + ", as the provider is null and " + reason);
getModelWriter().deleteItemFromDatabase(item);
return null;
}