Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-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/res/drawable/task_menu_item_bg.xml b/quickstep/res/drawable/task_menu_item_bg.xml
new file mode 100644
index 0000000..b6a8b90
--- /dev/null
+++ b/quickstep/res/drawable/task_menu_item_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="?android:attr/colorPrimary"/>
+ <corners android:radius="@dimen/task_menu_item_corner_radius"/>
+</shape>
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index 763e45e..3a47e99 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -25,7 +25,7 @@
<TextView
android:id="@+id/task_name"
- android:background="?android:attr/colorPrimary"
+ android:background="@drawable/task_menu_item_bg"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/quickstep/res/layout/task_view_menu_option.xml b/quickstep/res/layout/task_view_menu_option.xml
index 19ca3e3..5978b97 100644
--- a/quickstep/res/layout/task_view_menu_option.xml
+++ b/quickstep/res/layout/task_view_menu_option.xml
@@ -21,7 +21,7 @@
android:orientation="vertical"
android:paddingTop="@dimen/task_card_menu_option_vertical_padding"
android:paddingBottom="@dimen/task_card_menu_option_vertical_padding"
- android:background="?android:attr/colorPrimary"
+ android:background="@drawable/task_menu_item_bg"
android:theme="@style/PopupItem" >
<View
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 60eeaff..28262a8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,6 +24,7 @@
<!-- Task Menu View -->
<dimen name="task_menu_corner_radius">22dp</dimen>
+ <dimen name="task_menu_item_corner_radius">4dp</dimen>
<dimen name="overview_proactive_row_height">48dp</dimen>
<dimen name="overview_proactive_row_bottom_margin">16dp</dimen>
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4d47ef1..7f38923 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;
@@ -378,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();
@@ -671,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);
@@ -1467,9 +1482,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 0ebe13b..53b6675 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -156,6 +156,14 @@
}
/**
+ * @see IRecentsAnimationController#cleanupScreenshot()
+ */
+ @UiThread
+ public void cleanupScreenshot() {
+ UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+ }
+
+ /**
* @see RecentsAnimationControllerCompat#detachNavigationBarFromApp
*/
@UiThread
@@ -172,6 +180,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/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index c9b68df..84f7e83 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -64,6 +64,7 @@
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -303,10 +304,10 @@
private RecentsAnimationDeviceState mDeviceState;
private TaskAnimationManager mTaskAnimationManager;
- private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
- private InputConsumer mConsumer = InputConsumer.NO_OP;
+ private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
+ private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
- private InputConsumer mResetGestureInputConsumer;
+ private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
private GestureState mGestureState = DEFAULT_STATE;
private InputMonitorCompat mInputMonitorCompat;
@@ -635,7 +636,7 @@
// launched while device is locked even after exiting direct boot mode (e.g. camera).
return createDeviceLockedInputConsumer(newGestureState);
} else {
- return mResetGestureInputConsumer;
+ return getDefaultInputConsumer();
}
}
@@ -644,7 +645,7 @@
InputConsumer base = canStartSystemGesture
|| previousGestureState.isRecentsAnimationRunning()
? newBaseConsumer(previousGestureState, newGestureState, event)
- : mResetGestureInputConsumer;
+ : getDefaultInputConsumer();
if (mDeviceState.isGesturalNavMode()) {
handleOrientationSetup(base);
}
@@ -698,7 +699,7 @@
}
} else {
if (mDeviceState.isScreenPinningActive()) {
- base = mResetGestureInputConsumer;
+ base = getDefaultInputConsumer();
}
if (mDeviceState.canTriggerOneHandedAction(event)) {
@@ -740,14 +741,14 @@
return createOverviewInputConsumer(
previousGestureState, gestureState, event, forceOverviewInputConsumer);
} else if (gestureState.getRunningTask() == null) {
- return mResetGestureInputConsumer;
+ return getDefaultInputConsumer();
} else if (previousGestureState.isRunningAnimationToLauncher()
|| gestureState.getActivityInterface().isResumed()
|| forceOverviewInputConsumer) {
return createOverviewInputConsumer(
previousGestureState, gestureState, event, forceOverviewInputConsumer);
} else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
- return mResetGestureInputConsumer;
+ return getDefaultInputConsumer();
} else {
return createOtherActivityInputConsumer(gestureState, event);
}
@@ -775,7 +776,7 @@
return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, mInputMonitorCompat);
} else {
- return mResetGestureInputConsumer;
+ return getDefaultInputConsumer();
}
}
@@ -784,7 +785,7 @@
boolean forceOverviewInputConsumer) {
StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
- return mResetGestureInputConsumer;
+ return getDefaultInputConsumer();
}
if (activity.getRootView().hasWindowFocus()
@@ -813,13 +814,7 @@
}
private void reset() {
- if (mResetGestureInputConsumer != null) {
- mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
- } else {
- // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
- // NO_OP until then (we never want these to be null).
- mConsumer = mUncheckedConsumer = InputConsumer.NO_OP;
- }
+ mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
mGestureState = DEFAULT_STATE;
// By default, use batching of the input events, but check receiver before using in the rare
// case that the monitor was disposed before the swipe settled
@@ -828,6 +823,19 @@
}
}
+ /**
+ * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
+ */
+ private @NonNull InputConsumer getDefaultInputConsumer() {
+ if (mResetGestureInputConsumer != null) {
+ return mResetGestureInputConsumer;
+ } else {
+ // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
+ // NO_OP until then (we never want these to be null).
+ return InputConsumer.NO_OP;
+ }
+ }
+
private void preloadOverview(boolean fromInit) {
if (!mDeviceState.isUserUnlocked()) {
return;
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 000a95d..09387ff 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -394,8 +394,6 @@
private final TaskOverlayFactory mTaskOverlayFactory;
- private int mOrientation;
-
protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
@@ -590,7 +588,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);
@@ -2649,15 +2646,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();
}
/**
@@ -3413,16 +3402,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 bc63c06..270c92e 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 d301787..009e098 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -158,6 +158,9 @@
public final int hotseatBarSidePaddingStartPx;
public final int hotseatBarSidePaddingEndPx;
+ public final float qsbBottomMarginOriginalPx;
+ public int qsbBottomMarginPx;
+
// All apps
public int allAppsOpenVerticalTranslate;
public int allAppsCellHeightPx;
@@ -331,6 +334,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(
@@ -359,6 +366,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
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ff380ce..42b4e89 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -206,8 +206,14 @@
int freeSpace = dp.isTaskbarPresent
? dp.workspacePadding.bottom
: dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
- return (int) (freeSpace * QSB_CENTER_FACTOR)
- + (dp.isTaskbarPresent ? dp.taskbarSize : dp.getInsets().bottom);
+
+ if (dp.isScalableGrid && dp.qsbBottomMarginPx <= freeSpace) {
+ return dp.qsbBottomMarginPx;
+ } else {
+ return (int) (freeSpace * QSB_CENTER_FACTOR) + (dp.isTaskbarPresent
+ ? dp.taskbarSize
+ : dp.getInsets().bottom);
+ }
}
/**
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index d0e69fa..37b950d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IntProperty;
+import android.util.Log;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -35,6 +36,8 @@
import android.widget.TableRow;
import android.widget.TextView;
+import androidx.annotation.GuardedBy;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.LauncherAppState;
@@ -51,6 +54,7 @@
* Bottom sheet for the "Widgets" system shortcut in the long-press popup.
*/
public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
+ private static final String TAG = "WidgetsBottomSheet";
private static final IntProperty<View> PADDING_BOTTOM =
new IntProperty<View>("paddingBottom") {
@@ -128,6 +132,32 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (doMeasure(widthMeasureSpec, heightMeasureSpec)) {
+ boolean hasUpdated = doMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (hasUpdated) {
+ Log.w(TAG, "WidgetsBottomSheet dimension has been updated after a 2nd"
+ + " measurement.");
+ }
+ }
+ }
+
+ /**
+ * Measures the dimension of this view and its children.
+ *
+ * <p>This function takes account of the following during measurement:
+ * <ol>
+ * <li>status bar and system navigation bar insets</li>
+ * <li>
+ * number of spans that can fit in a row. This affects the number of widgets that can
+ * fit in a row.
+ * </li>
+ * </ol>
+ *
+ * @return {@code true} if the width or height of this view or its children have changed after
+ * the measurement. Otherwise, returns {@code false}.
+ */
+ @GuardedBy("MainThread")
+ private boolean doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
int widthUsed;
if (mInsets.bottom > 0) {
@@ -153,7 +183,9 @@
// Ensure the table layout is showing widgets in the right column after measure.
mMaxHorizontalSpan = maxHorizontalSpan;
onWidgetsBound();
+ return true;
}
+ return false;
}
@Override