Merge "Enable input window rotation flag" into sc-v2-dev
diff --git a/go/quickstep/res/values/styles.xml b/go/quickstep/res/values/styles.xml
index 561531b..442c413 100644
--- a/go/quickstep/res/values/styles.xml
+++ b/go/quickstep/res/values/styles.xml
@@ -16,14 +16,14 @@
-->
<resources>
<!-- App themes -->
- <style name="AppTheme" parent="@style/LauncherTheme">
+ <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
<item name="overviewButtonTextColor">@color/go_overview_text_color</item>
<item name="overviewButtonIconColor">@color/go_overview_text_color</item>
<item name="overviewButtonBackgroundColor">@color/go_overview_button_color</item>
<item name="modalDialogBackground">@color/go_modal_dialog_background</item>
</style>
- <style name="AppTheme.Dark" parent="@style/LauncherTheme.Dark">
+ <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
<item name="overviewButtonTextColor">@color/go_overview_text_color_dark</item>
<item name="overviewButtonIconColor">@color/go_overview_text_color_dark</item>
<item name="overviewButtonBackgroundColor">@color/go_overview_button_color_dark</item>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index e1d0574..b4b29aa 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -64,6 +64,7 @@
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ObjectWrapper;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -109,6 +110,7 @@
private OverviewActionsView mActionsView;
private @Nullable TaskbarManager mTaskbarManager;
+ private @Nullable OverviewCommandHelper mOverviewCommandHelper;
private @Nullable LauncherTaskbarUIController mTaskbarUIController;
private final ServiceConnection mTisBinderConnection = new ServiceConnection() {
@Override
@@ -117,6 +119,8 @@
mTaskbarManager.setLauncher(BaseQuickstepLauncher.this);
Log.d(TAG, "TIS service connected");
resetServiceBindRetryState();
+
+ mOverviewCommandHelper = ((TISBinder) iBinder).getOverviewCommandHelper();
}
@Override
@@ -159,6 +163,15 @@
super.onDestroy();
}
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ if (mOverviewCommandHelper != null) {
+ mOverviewCommandHelper.clearPendingCommands();
+ }
+ }
+
public QuickstepTransitionManager getAppTransitionManager() {
return mAppTransitionManager;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 97f46a5..6249e6a 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1174,6 +1174,7 @@
boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent;
boolean appCanEnterPip = !mDeviceState.isPipActive()
&& runningTaskTarget != null
+ && runningTaskTarget.allowEnterPip
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
&& runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
HomeAnimationFactory homeAnimFactory =
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index f29d68a..7fb8e16 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -107,10 +107,9 @@
public RecentsView getVisibleRecentsView() {
RecentsActivity activity = getCreatedActivity();
if (activity != null) {
- RecentsView recentsView = activity.getOverviewPanel();
- if (activity.hasBeenResumed() || (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode()
- && recentsView.getRunningTaskId() == -1)) {
- return recentsView;
+ if (activity.hasBeenResumed()
+ || (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())) {
+ return activity.getOverviewPanel();
}
}
return null;
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index fd44e02..e2f198c 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -173,7 +173,9 @@
@Override
protected void notifyGestureAnimationStartToRecents() {
if (mRunningOverHome) {
- mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
+ if (SysUINavigationMode.getMode(mContext).hasGestures) {
+ mRecentsView.onGestureAnimationStartOnHome(mGestureState.getRunningTask());
+ }
} else {
super.notifyGestureAnimationStartToRecents();
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 5deb75e..d91d5b0 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -189,7 +189,7 @@
launcher != null && launcher.getStateManager().getState().overviewUi
? launcher.getOverviewPanel() : null;
if (recentsView == null || (!launcher.hasBeenResumed()
- && recentsView.getRunningTaskId() == -1)) {
+ && recentsView.getRunningTaskViewId() == -1)) {
// If live tile has ended, return null.
return null;
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 89c2ed8..5d1f908 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -109,6 +109,11 @@
MAIN_EXECUTOR.execute(() -> addCommand(cmd));
}
+ @UiThread
+ public void clearPendingCommands() {
+ mPendingCommands.clear();
+ }
+
private TaskView getNextTask(RecentsView view) {
final TaskView runningTaskView = view.getRunningTaskView();
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0efe666..780032e 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -33,8 +33,11 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseIntArray;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.OverviewComponentObserverProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -129,6 +132,16 @@
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
+ if (TestProtocol.sDebugTracing && defaultHome == null) {
+ Log.d(TestProtocol.THIRD_PARTY_LAUNCHER_NOT_SET, "getHomeActivities returned null");
+ while ((defaultHome =
+ PackageManagerWrapper.getInstance().getHomeActivities(new ArrayList<>()))
+ == null) {
+ SystemClock.sleep(10);
+ }
+ Log.d(TestProtocol.THIRD_PARTY_LAUNCHER_NOT_SET,
+ "getHomeActivities returned non-null: " + defaultHome);
+ }
mIsHomeDisabled = mDeviceState.isHomeDisabled();
mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 95be45a..03e2395 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,8 +22,8 @@
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
import static com.android.launcher3.Utilities.createHomeIntent;
-import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -75,7 +75,6 @@
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.SplitPlaceholderView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -287,7 +286,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mStateManager = new StateManager<>(this, RecentsState.DEFAULT);
+ mStateManager = new StateManager<>(this, RecentsState.BG_LAUNCHER);
mOldConfig = new Configuration(getResources().getConfiguration());
initDeviceProfile();
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 07d221c..b5da097 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -139,7 +139,7 @@
// If the opening task id is not currently visible in overview, then fall back to normal app
// icon launch animation
- TaskView taskView = recentsView.getTaskView(openingTaskId);
+ TaskView taskView = recentsView.getTaskViewByTaskId(openingTaskId);
if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
return null;
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 69217c2..8155244 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -297,6 +297,10 @@
public TaskbarManager getTaskbarManager() {
return mTaskbarManager;
}
+
+ public OverviewCommandHelper getOverviewCommandHelper() {
+ return mOverviewCommandHelper;
+ }
}
private static boolean sConnected = false;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 540a9ca..64a428f 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -95,7 +95,7 @@
TaskViewSimulator taskViewSimulator) {
super.onPrepareGestureEndAnimation(animatorSet, endTarget, taskViewSimulator);
if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
- TaskView tv = getTaskView(mHomeTaskInfo.taskId);
+ TaskView tv = getTaskViewByTaskId(mHomeTaskInfo.taskId);
if (tv != null) {
PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150,
false /* dismissingForSplitSelection*/);
@@ -117,8 +117,9 @@
}
@Override
- public void setCurrentTask(int runningTaskId) {
- super.setCurrentTask(runningTaskId);
+ public void setCurrentTask(int runningTaskViewId) {
+ super.setCurrentTask(runningTaskViewId);
+ int runningTaskId = getTaskIdsForRunningTaskView()[0];
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId != runningTaskId) {
mHomeTaskInfo = null;
setRunningTaskHidden(false);
@@ -128,7 +129,7 @@
@Nullable
@Override
protected TaskView getHomeTaskView() {
- return mHomeTaskInfo != null ? getTaskView(mHomeTaskInfo.taskId) : null;
+ return mHomeTaskInfo != null ? getTaskViewByTaskId(mHomeTaskInfo.taskId) : null;
}
@Override
@@ -148,11 +149,12 @@
// When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
- if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId && !tasks.isEmpty()) {
+ int runningTaskId = getTaskIdsForRunningTaskView()[0];
+ if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId && !tasks.isEmpty()) {
// Check if the task list has running task
boolean found = false;
for (Task t : tasks) {
- if (t.key.id == mRunningTaskId) {
+ if (t.key.id == runningTaskId) {
found = true;
break;
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index d06d87c..917b58a 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -42,8 +42,8 @@
private static final int FLAG_OVERVIEW_UI = BaseState.getFlag(7);
public static final RecentsState DEFAULT = new RecentsState(0,
- FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM
- | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
+ FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID
+ | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
public static final RecentsState MODAL_TASK = new ModalState(1,
FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
| FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_OVERVIEW_UI);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index d00db2d..b077ca6 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -398,6 +398,9 @@
private final InvariantDeviceProfile mIdp;
+ /**
+ * Getting views should be done via {@link #getTaskViewFromPool()}
+ */
private final ViewPool<TaskView> mTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
@@ -441,7 +444,7 @@
}
// Remove the task immediately from the task list
- TaskView taskView = getTaskView(taskId);
+ TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
removeView(taskView);
}
@@ -463,7 +466,7 @@
return;
}
- TaskView taskView = getTaskView(taskId);
+ TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView == null) {
return;
}
@@ -496,10 +499,16 @@
private int mTaskListChangeId = -1;
// Only valid until the launcher state changes to NORMAL
- protected int mRunningTaskId = -1;
+ /**
+ * ID for the current running TaskView view, unique amongst TaskView instances. ID's are set
+ * through {@link #getTaskViewFromPool()} and incremented by {@link #mTaskViewIdCount}
+ */
+ protected int mRunningTaskViewId = -1;
+ private int mTaskViewIdCount;
+ private final int[] INVALID_TASK_IDS = new int[]{-1, -1};
protected boolean mRunningTaskTileHidden;
private Task mTmpRunningTask;
- protected int mFocusedTaskId = -1;
+ protected int mFocusedTaskViewId = -1;
private boolean mTaskIconScaledDown = false;
private boolean mRunningTaskShowScreenshot = false;
@@ -734,7 +743,7 @@
@Override
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
if (mHandleTaskStackChanges) {
- TaskView taskView = getTaskView(taskId);
+ TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
Task task = taskView.getTask();
taskView.getThumbnail().setThumbnail(task, thumbnailData);
@@ -764,7 +773,7 @@
* @param refreshNow Refresh immediately if it's true.
*/
public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) {
- TaskView taskView = getTaskView(taskId);
+ TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView != null) {
taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow);
}
@@ -838,7 +847,8 @@
if (child instanceof TaskView && child != mSplitHiddenTaskView
&& child != mMovingTaskView) {
TaskView taskView = (TaskView) child;
- mHasVisibleTaskData.delete(taskView.getTaskId());
+ mHasVisibleTaskData.delete(taskView.getTaskIds()[0]);
+ taskView.setTaskViewId(-1);
mTaskViewPool.recycle(taskView);
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
}
@@ -878,7 +888,8 @@
}
public void launchSideTaskInLiveTileModeForRestartedApp(int taskId) {
- if (mRunningTaskId != -1 && mRunningTaskId == taskId) {
+ int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
+ if (mRunningTaskViewId != -1 && mRunningTaskViewId == runningTaskViewId) {
RemoteAnimationTargets targets = getLiveTileParams().getTargetSet();
if (targets != null && targets.findTask(taskId) != null) {
launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
@@ -890,7 +901,7 @@
public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps,
RemoteAnimationTargetCompat[] wallpaper, RemoteAnimationTargetCompat[] nonApps) {
AnimatorSet anim = new AnimatorSet();
- TaskView taskView = getTaskView(taskId);
+ TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView == null || !isTaskViewVisible(taskView)) {
// TODO: Refine this animation.
SurfaceTransactionApplier surfaceApplier =
@@ -967,10 +978,11 @@
== getPagedOrientationHandler().getPrimaryScroll(this);
}
- public TaskView getTaskView(int taskId) {
+ public TaskView getTaskViewByTaskId(int taskId) {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = getTaskViewAt(i);
- if (taskView.getTaskId() == taskId) {
+ int[] taskIds = taskView.getTaskIds();
+ if (taskIds[0] == taskId || taskIds[1] == taskId) {
return taskView;
}
}
@@ -1187,7 +1199,7 @@
unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
TaskView ignoreResetTaskView =
- mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
+ mIgnoreResetTaskId == -1 ? null : getTaskViewByTaskId(mIgnoreResetTaskId);
final int requiredTaskCount = tasks.size();
if (getTaskViewCount() != requiredTaskCount) {
@@ -1195,7 +1207,7 @@
removeView(mClearAllButton);
}
for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
- addView(mTaskViewPool.getView());
+ addView(getTaskViewFromPool());
}
while (getTaskViewCount() > requiredTaskCount) {
removeView(getChildAt(getChildCount() - 1));
@@ -1205,6 +1217,12 @@
}
}
+ // Save running task ID if it exists before rebinding all taskViews, otherwise the task from
+ // the runningTaskView currently bound could get assigned to another TaskView
+ // TODO set these type to array and check all taskIDs? Maybe we can get away w/ only one
+ int runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId)[0];
+ int focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId)[0];
+
// Rebind and reset all task views
for (int i = requiredTaskCount - 1; i >= 0; i--) {
final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
@@ -1213,28 +1231,40 @@
taskView.bind(task, mOrientationState);
}
+ // Keep same previous focused task
+ TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
// If the list changed, maybe the focused task doesn't exist anymore
- if (getFocusedTaskView() == null && getTaskViewCount() > 0) {
- mFocusedTaskId = getTaskViewAt(0).getTaskId();
+ if (newFocusedTaskView == null && getTaskViewCount() > 0) {
+ newFocusedTaskView = getTaskViewAt(0);
}
+ mFocusedTaskViewId = newFocusedTaskView != null ?
+ newFocusedTaskView.getTaskViewId() : -1;
updateTaskSize();
+ TaskView newRunningTaskView = null;
+ if (runningTaskId != -1) {
+ // Update mRunningTaskViewId to be the new TaskView that was assigned by binding
+ // the full list of tasks to taskViews
+ newRunningTaskView = getTaskViewByTaskId(runningTaskId);
+ mRunningTaskViewId = newRunningTaskView.getTaskViewId();
+ }
+
if (mNextPage == INVALID_PAGE) {
// Set the current page to the running task, but not if settling on new task.
- TaskView runningTaskView = getRunningTaskView();
- if (runningTaskView != null) {
- setCurrentPage(indexOfChild(runningTaskView));
+ if (runningTaskId != -1) {
+ setCurrentPage(indexOfChild(newRunningTaskView));
} else if (getTaskViewCount() > 0) {
setCurrentPage(indexOfChild(getTaskViewAt(0)));
}
} else if (currentTaskId != -1) {
- currentTaskView = getTaskView(currentTaskId);
+ currentTaskView = getTaskViewByTaskId(currentTaskId);
if (currentTaskView != null) {
setCurrentPage(indexOfChild(currentTaskView));
}
}
- if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreResetTaskView) {
+ if (mIgnoreResetTaskId != -1 &&
+ getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
// If the taskView mapping is changing, do not preserve the visuals. Since we are
// mostly preserving the first task, and new taskViews are added to the end, it should
// generally map to the same task.
@@ -1278,7 +1308,7 @@
public void resetTaskVisuals() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
TaskView taskView = getTaskViewAt(i);
- if (mIgnoreResetTaskId != taskView.getTaskId()) {
+ if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
taskView.resetViewTransforms();
taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
taskView.setStableAlpha(mContentAlpha);
@@ -1632,7 +1662,7 @@
private void unloadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
if (mHasVisibleTaskData.valueAt(i)) {
- TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
+ TaskView taskView = getTaskViewByTaskId(mHasVisibleTaskData.keyAt(i));
if (taskView != null) {
taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
}
@@ -1647,7 +1677,7 @@
// they want to updated their thumbnail state
for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
if (mHasVisibleTaskData.valueAt(i)) {
- TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
+ TaskView taskView = getTaskViewByTaskId(mHasVisibleTaskData.keyAt(i));
if (taskView != null) {
// Poke the view again, which will trigger it to load high res if the state
// is enabled
@@ -1668,7 +1698,7 @@
setCurrentTask(-1);
mIgnoreResetTaskId = -1;
mTaskListChangeId = -1;
- mFocusedTaskId = getTaskViewCount() > 0 ? getTaskViewAt(0).getTaskId() : -1;
+ mFocusedTaskViewId = getTaskViewCount() > 0 ? getTaskViewAt(0).getTaskViewId() : -1;
if (mRecentsAnimationController != null) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
@@ -1693,20 +1723,49 @@
});
}
- public int getRunningTaskId() {
- return mRunningTaskId;
+ public int getRunningTaskViewId() {
+ return mRunningTaskViewId;
+ }
+
+ protected int[] getTaskIdsForRunningTaskView() {
+ return getTaskIdsForTaskViewId(mRunningTaskViewId);
+ }
+
+ private int[] getTaskIdsForTaskViewId(int taskViewId) {
+ // For now 2 distinct task IDs is max for split screen
+ TaskView runningTaskView = getTaskViewFromTaskViewId(taskViewId);
+ if (runningTaskView == null) {
+ return INVALID_TASK_IDS;
+ }
+
+ return runningTaskView.getTaskIds();
}
public @Nullable TaskView getRunningTaskView() {
- return getTaskView(mRunningTaskId);
- }
-
- public int getRunningTaskIndex() {
- return getTaskIndexForId(mRunningTaskId);
+ return getTaskViewFromTaskViewId(mRunningTaskViewId);
}
public @Nullable TaskView getFocusedTaskView() {
- return getTaskView(mFocusedTaskId);
+ return getTaskViewFromTaskViewId(mFocusedTaskViewId);
+ }
+
+ private TaskView getTaskViewFromTaskViewId(int taskViewId) {
+ if (taskViewId == -1) {
+ return null;
+ }
+
+ for (int i = 0; i < getTaskViewCount(); i++) {
+ TaskView taskView = getTaskViewAt(i);
+ if (taskView.getTaskViewId() == taskViewId) {
+ return taskView;
+ }
+ }
+ return null;
+ }
+
+ public int getRunningTaskIndex() {
+ TaskView taskView = getRunningTaskView();
+ return taskView == null ? -1 : indexOfChild(taskView);
}
protected @Nullable TaskView getHomeTaskView() {
@@ -1714,11 +1773,27 @@
}
/**
+ * Handle the edge case where Recents could increment task count very high over long
+ * period of device usage. Probably will never happen, but meh.
+ */
+ private TaskView getTaskViewFromPool() {
+ TaskView taskView = mTaskViewPool.getView();
+ taskView.setTaskViewId(mTaskViewIdCount);
+ if (mTaskViewIdCount == Integer.MAX_VALUE) {
+ mTaskViewIdCount = 0;
+ } else {
+ mTaskViewIdCount++;
+ }
+
+ return taskView;
+ }
+
+ /**
* Get the index of the task view whose id matches {@param taskId}.
* @return -1 if there is no task view for the task id, else the index of the task view.
*/
public int getTaskIndexForId(int taskId) {
- TaskView tv = getTaskView(taskId);
+ TaskView tv = getTaskViewByTaskId(taskId);
return tv == null ? -1 : indexOfChild(tv);
}
@@ -1858,7 +1933,7 @@
* Returns true if we should add a stub taskView for the running task id
*/
protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
- return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
+ return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
}
/**
@@ -1868,10 +1943,12 @@
* is called. Also scrolls the view to this task.
*/
public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
+ int runningTaskViewId = -1;
if (shouldAddStubTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
- final TaskView taskView = mTaskViewPool.getView();
+ final TaskView taskView = getTaskViewFromPool();
+ runningTaskViewId = taskView.getTaskViewId();
addView(taskView, mTaskViewStartIndex);
if (wasEmpty) {
addView(mClearAllButton);
@@ -1886,12 +1963,13 @@
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
+ } else if (getTaskViewByTaskId(runningTaskInfo.taskId) != null) {
+ runningTaskViewId = getTaskViewByTaskId(runningTaskInfo.taskId).getTaskViewId();
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
- int runningTaskId = runningTaskInfo == null ? -1 : runningTaskInfo.taskId;
- setCurrentTask(runningTaskId);
- mFocusedTaskId = runningTaskId;
+ setCurrentTask(runningTaskViewId);
+ mFocusedTaskViewId = runningTaskViewId;
setCurrentPage(getRunningTaskIndex());
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
@@ -1905,22 +1983,27 @@
/**
* Sets the running task id, cleaning up the old running task if necessary.
*/
- public void setCurrentTask(int runningTaskId) {
- if (mRunningTaskId == runningTaskId) {
+ public void setCurrentTask(int runningTaskViewId) {
+ if (mRunningTaskViewId == runningTaskViewId) {
return;
}
- if (mRunningTaskId != -1) {
+ if (mRunningTaskViewId != -1) {
// Reset the state on the old running task view
setTaskIconScaledDown(false);
setRunningTaskViewShowScreenshot(true);
setRunningTaskHidden(false);
}
- mRunningTaskId = runningTaskId;
+ mRunningTaskViewId = runningTaskViewId;
+ }
+
+ private int getTaskViewIdFromTaskId(int taskId) {
+ TaskView taskView = getTaskViewByTaskId(taskId);
+ return taskView != null ? taskView.getTaskViewId() : -1;
}
/**
- * Hides the tile associated with {@link #mRunningTaskId}
+ * Hides the tile associated with {@link #mRunningTaskViewId}
*/
public void setRunningTaskHidden(boolean isHidden) {
mRunningTaskTileHidden = isHidden;
@@ -2001,7 +2084,7 @@
float bottomAccumulatedTranslationX = 0;
// Contains whether the child index is in top or bottom of grid (for non-focused task)
- // Different from mTopRowIdSet, which contains the taskId of what task is in top row
+ // Different from mTopRowIdSet, which contains the taskViewId of what task is in top row
IntSet topSet = new IntSet();
IntSet bottomSet = new IntSet();
@@ -2053,8 +2136,8 @@
// calculate the distance focused task need to shift.
focusedTaskShift += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
}
- int taskId = taskView.getTaskId();
- boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
+ int taskViewId = taskView.getTaskViewId();
+ boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskViewId)
: topRowWidth <= bottomRowWidth;
if (isTopRow) {
if (homeTaskView != null && nextFocusedTaskView == null) {
@@ -2064,7 +2147,7 @@
topRowWidth += taskWidthAndSpacing;
}
topSet.add(i);
- mTopRowIdSet.add(taskId);
+ mTopRowIdSet.add(taskViewId);
taskView.setGridTranslationY(mTaskGridVerticalDiff);
@@ -2189,13 +2272,13 @@
if (taskView1 == null || taskView2 == null) {
return false;
}
- int taskId1 = taskView1.getTaskId();
- int taskId2 = taskView2.getTaskId();
- if (taskId1 == mFocusedTaskId || taskId2 == mFocusedTaskId) {
+ int taskViewId1 = taskView1.getTaskViewId();
+ int taskViewId2 = taskView2.getTaskViewId();
+ if (taskViewId1 == mFocusedTaskViewId || taskViewId2 == mFocusedTaskViewId) {
return false;
}
- return (mTopRowIdSet.contains(taskId1) && mTopRowIdSet.contains(taskId2)) || (
- !mTopRowIdSet.contains(taskId1) && !mTopRowIdSet.contains(taskId2));
+ return (mTopRowIdSet.contains(taskViewId1) && mTopRowIdSet.contains(taskViewId2)) || (
+ !mTopRowIdSet.contains(taskViewId1) && !mTopRowIdSet.contains(taskViewId2));
}
/**
@@ -2342,7 +2425,8 @@
boolean showAsGrid = showAsGrid();
int taskCount = getTaskViewCount();
int dismissedIndex = indexOfChild(dismissedTaskView);
- int dismissedTaskId = dismissedTaskView.getTaskId();
+ int dismissedTaskId = dismissedTaskView.getTaskIds()[0];
+ int dismissedTaskViewId = dismissedTaskView.getTaskViewId();
// Grid specific properties.
boolean isFocusedTaskDismissed = false;
@@ -2359,7 +2443,7 @@
if (showAsGrid) {
dismissedTaskWidth = dismissedTaskView.getLayoutParams().width + mPageSpacing;
- isFocusedTaskDismissed = dismissedTaskId == mFocusedTaskId;
+ isFocusedTaskDismissed = dismissedTaskViewId == mFocusedTaskViewId;
if (isFocusedTaskDismissed) {
nextFocusedTaskFromTop =
mTopRowIdSet.size() > 0 && mTopRowIdSet.size() >= (taskCount - 1) / 2f;
@@ -2369,7 +2453,7 @@
if (taskView == dismissedTaskView) {
continue;
}
- boolean isTopRow = mTopRowIdSet.contains(taskView.getTaskId());
+ boolean isTopRow = mTopRowIdSet.contains(taskView.getTaskViewId());
if ((nextFocusedTaskFromTop && isTopRow
|| (!nextFocusedTaskFromTop && !isTopRow))) {
nextFocusedTaskView = taskView;
@@ -2548,7 +2632,7 @@
mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
}
removeViewInLayout(dismissedTaskView);
- mTopRowIdSet.remove(dismissedTaskId);
+ mTopRowIdSet.remove(dismissedTaskViewId);
if (taskCount == 1) {
removeViewInLayout(mClearAllButton);
@@ -2556,8 +2640,8 @@
} else {
// Update focus task and its size.
if (finalNextFocusedTaskView != null) {
- mFocusedTaskId = finalNextFocusedTaskView.getTaskId();
- mTopRowIdSet.remove(mFocusedTaskId);
+ mFocusedTaskViewId = finalNextFocusedTaskView.getTaskViewId();
+ mTopRowIdSet.remove(mFocusedTaskViewId);
finalNextFocusedTaskView.animateIconScaleAndDimIntoView();
}
updateTaskSize(true);
@@ -2676,7 +2760,7 @@
@UiThread
private void dismissTask(int taskId) {
- TaskView taskView = getTaskView(taskId);
+ TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView == null) {
return;
}
@@ -2755,9 +2839,12 @@
}
alpha = Utilities.boundToRange(alpha, 0, 1);
mContentAlpha = alpha;
+ int runningTaskId = getTaskIdsForRunningTaskView()[0];
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
TaskView child = getTaskViewAt(i);
- if (!mRunningTaskTileHidden || child.getTaskId() != mRunningTaskId) {
+ int[] childTaskIds = child.getTaskIds();
+ if (!mRunningTaskTileHidden ||
+ (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
child.setStableAlpha(alpha);
}
}
@@ -2916,8 +3003,8 @@
float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
int count = getChildCount();
- TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
- ? null : getTaskView(mRunningTaskId);
+ TaskView runningTask = mRunningTaskViewId == -1 || !mRunningTaskTileHidden
+ ? null : getRunningTaskView();
int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
int modalMidpoint = getCurrentPage();
@@ -3884,8 +3971,9 @@
}
return;
}
- switchToScreenshot(mRunningTaskId == -1 ? null
- : mRecentsAnimationController.screenshotTask(mRunningTaskId), onFinishRunnable);
+ int runningTaskId = getTaskIdsForRunningTaskView()[0];
+ switchToScreenshot(mRunningTaskViewId == -1 ? null
+ : mRecentsAnimationController.screenshotTask(runningTaskId), onFinishRunnable);
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 5608fc7..ec08280 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -366,6 +366,9 @@
private float mModalness = 0;
private float mStableAlpha = 1;
+ private int mTaskViewId = -1;
+ private final int[] mTaskIdContainer = new int[]{-1, -1};
+
private boolean mShowScreenshot;
// The current background requests to load the task thumbnail and icon
@@ -401,6 +404,14 @@
setOutlineProvider(mOutlineProvider);
}
+ public void setTaskViewId(int id) {
+ this.mTaskViewId = id;
+ }
+
+ public int getTaskViewId() {
+ return mTaskViewId;
+ }
+
/**
* Builds proto for logging
*/
@@ -506,6 +517,7 @@
public void bind(Task task, RecentsOrientedState orientedState) {
cancelPendingLoadTasks();
mTask = task;
+ mTaskIdContainer[0] = mTask.key.id;
mSnapshotView.bind(task);
setOrientationState(orientedState);
}
@@ -514,8 +526,12 @@
return mTask;
}
- public int getTaskId() {
- return mTask != null && mTask.key != null ? mTask.key.id : -1;
+ /**
+ * @return integer array of two elements to be size consistent with max number of tasks possible
+ * index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
+ */
+ public int[] getTaskIds() {
+ return mTaskIdContainer;
}
public TaskThumbnailView getThumbnail() {
@@ -598,7 +614,8 @@
ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);
if (ActivityManagerWrapper.getInstance()
.startActivityFromRecents(mTask.key, opts.options)) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRecentsView().getRunningTaskId() != -1) {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
+ getRecentsView().getRunningTaskViewId() != -1) {
// Return a fresh callback in the live tile case, so that it's not accidentally
// triggered by QuickstepTransitionManager.AppLaunchAnimationRunner.
RunnableList callbackList = new RunnableList();
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index dc73a9a..ca47de3 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -82,6 +82,6 @@
RecentsView recentsView = launcher.getOverviewPanel();
return recentsView.getSizeStrategy().isInLiveTileMode()
- && recentsView.getRunningTaskId() != -1;
+ && recentsView.getRunningTaskViewId() != -1;
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index fef9304..3e84a76 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -16,7 +16,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.quickstep.views.DigitalWellBeingToast;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -33,7 +32,6 @@
resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
@Test
- @ScreenRecord //b/193440212
public void testToast() throws Exception {
startAppFast(CALCULATOR_PACKAGE);
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 20b4715..e4f5a19 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -99,6 +99,7 @@
mDevice = UiDevice.getInstance(instrumentation);
mDevice.setOrientationNatural();
mLauncher = new LauncherInstrumentation();
+ mLauncher.enableDebugTracing();
// b/143488140
//mLauncher.enableCheckEventsForSuccessfulGestures();
@@ -214,7 +215,7 @@
// b/143488140
//@NavigationModeSwitch
@Test
- @ScreenRecord // b/194484556
+ @ScreenRecord // b/187080582
public void testOverview() {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ea69b94..164e755 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -93,7 +93,6 @@
@Test
@PortraitLandscape
- @ScreenRecord //b/193440212
public void testOverview() throws Exception {
startTestAppsWithCheck();
// mLauncher.pressHome() also tests an important case of pressing home while in background.
@@ -159,6 +158,7 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
+ @ScreenRecord //b/193125090
public void testOverviewActions() throws Exception {
// Experimenting for b/165029151:
final Overview overview = mLauncher.pressHome().switchToOverview();
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index de091c5..89895e5 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -16,36 +16,12 @@
<com.android.launcher3.shortcuts.DeepShortcutView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="@dimen/bg_popup_item_height"
android:elevation="@dimen/deep_shortcuts_elevation"
android:background="@drawable/middle_item_primary"
android:theme="@style/PopupItem" >
- <com.android.launcher3.BubbleTextView
- style="@style/BaseIconUnBounded"
- android:id="@+id/bubble_text"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="start|center_vertical"
- android:textAlignment="viewStart"
- android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
- android:paddingEnd="@dimen/popup_padding_end"
- android:textSize="14sp"
- android:minLines="1"
- android:maxLines="2"
- android:ellipsize="end"
- android:textColor="?android:attr/textColorPrimary"
- launcher:iconDisplay="shortcut_popup"
- launcher:layoutHorizontal="true"
- android:focusable="false" />
-
- <View
- android:id="@+id/icon"
- android:layout_width="@dimen/system_shortcut_icon_size"
- android:layout_height="@dimen/system_shortcut_icon_size"
- android:layout_marginStart="@dimen/system_shortcut_margin_start"
- android:layout_gravity="start|center_vertical"
- android:backgroundTint="?android:attr/textColorPrimary"/>
+ <include layout="@layout/system_shortcut_content" />
</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_content.xml b/res/layout/system_shortcut_content.xml
new file mode 100644
index 0000000..8b39202
--- /dev/null
+++ b/res/layout/system_shortcut_content.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+ <com.android.launcher3.BubbleTextView
+ style="@style/BaseIconUnBounded"
+ android:id="@+id/bubble_text"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="start|center_vertical"
+ android:textAlignment="viewStart"
+ android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
+ android:paddingEnd="@dimen/popup_padding_end"
+ android:textSize="14sp"
+ android:minLines="1"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:textColor="?android:attr/textColorPrimary"
+ launcher:iconDisplay="shortcut_popup"
+ launcher:layoutHorizontal="true"
+ android:focusable="false" />
+
+ <View
+ android:id="@+id/icon"
+ android:layout_width="@dimen/system_shortcut_icon_size"
+ android:layout_height="@dimen/system_shortcut_icon_size"
+ android:layout_marginStart="@dimen/system_shortcut_margin_start"
+ android:layout_gravity="start|center_vertical"
+ android:backgroundTint="?android:attr/textColorPrimary"/>
+</merge>
\ No newline at end of file
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index b5b8b62..2b8b9fa 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -122,7 +122,7 @@
<string name="allow_rotation_title" msgid="7728578836261442095">"السماح بتدوير الشاشة الرئيسية"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"عند تدوير الهاتف"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"نقاط الإشعارات"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"مفعّل"</string>
+ <string name="notification_dots_desc_on" msgid="1679848116452218908">"مفعّلة"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"غير مفعّل"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"يلزم تمكين الوصول إلى الإشعارات"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"لعرض نقاط الإشعارات، يجب تفعيل إشعارات التطبيق في <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index d3558ec..673a446 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -105,7 +105,7 @@
<string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item atau lebih"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
<string name="styles_wallpaper_button_text" msgid="8216961355289236794">"Wallpaper & gaya"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Setelan layar utama"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Setelan Layar utama"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan Layar utama diputar"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Saat ponsel diputar"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 85d7db6..2415542 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -75,7 +75,7 @@
<string name="all_apps_button_label" msgid="8130441508702294465">"Список приложений"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Открыть список личных приложений"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"Открыть список приложений для работы"</string>
- <string name="remove_drop_target_label" msgid="7812859488053230776">"Убрать"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Удалить"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Удалить"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"О приложении"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Установить"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 010d15b..838d0ec 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -29,7 +29,8 @@
<dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
<dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
- <dimen name="two_panel_home_side_padding">18dp</dimen>
+ <dimen name="two_panels_home_side_padding_landscape">36dp</dimen>
+ <dimen name="two_panels_home_side_padding_portrait">9dp</dimen>
<!-- Hotseat -->
<dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
deleted file mode 100644
index 56ce215..0000000
--- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package com.android.launcher3.model;
-
-
-import static android.database.DatabaseUtils.queryNumEntries;
-
-import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
-import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
-import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
-import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
-import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
-import static com.android.launcher3.util.LauncherModelHelper.NO__ICON;
-import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.util.LauncherModelHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-/**
- * Unit tests for {@link GridBackupTable}
- */
-@RunWith(RobolectricTestRunner.class)
-public class GridBackupTableTest {
-
- private static final int BACKUP_ITEM_COUNT = 12;
-
- private LauncherModelHelper mModelHelper;
- private Context mContext;
- private SQLiteDatabase mDb;
-
- @Before
- public void setUp() {
- mModelHelper = new LauncherModelHelper();
- mContext = RuntimeEnvironment.application;
- mDb = mModelHelper.provider.getDb();
-
- setupGridData();
- }
-
- private void setupGridData() {
- mModelHelper.createGrid(new int[][][]{{
- { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
- { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
- { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
- { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON},
- }});
- assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
- }
-
- @Test
- public void backupTableCreated() {
- GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
- assertFalse(backupTable.backupOrRestoreAsNeeded());
- Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
- assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
- // One extra entry for properties
- assertEquals(BACKUP_ITEM_COUNT + 1, queryNumEntries(mDb, BACKUP_TABLE_NAME));
- }
-
- @Test
- public void backupTableRestored() {
- assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
- Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
- // Delete entries
- mDb.delete(TABLE_NAME, null, null);
- assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
-
- GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
- assertTrue(backupTable.backupOrRestoreAsNeeded());
-
- // Items have been restored
- assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
-
- Point outSize = new Point();
- assertEquals(4, backupTable.getRestoreHotseatAndGridSize(outSize));
- assertEquals(4, outSize.x);
- assertEquals(4, outSize.y);
- }
-
- @Test
- public void backupTableRemovedOnAdd() {
- assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
- Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
- assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
- mModelHelper.addItem(1, 2, DESKTOP, 1, 1);
- assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
- }
-
- @Test
- public void backupTableRemovedOnDelete() {
- assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
- Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
- assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
- mContext.getContentResolver().delete(Favorites.CONTENT_URI, null, null);
- assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
- }
-
- @Test
- public void backupTableRetainedOnUpdate() {
- assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
- Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
-
- assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
-
- ContentValues values = new ContentValues();
- values.put(Favorites.RANK, 4);
- // Something was updated
- assertTrue(mContext.getContentResolver()
- .update(Favorites.CONTENT_URI, values, null, null) > 0);
-
- // Backup table remains
- assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
deleted file mode 100644
index d544a0b..0000000
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ /dev/null
@@ -1,373 +0,0 @@
-package com.android.launcher3.model;
-
-import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds;
-import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
-import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
-import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
-import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.LauncherModelHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-
-/**
- * Unit tests for {@link GridSizeMigrationTask}
- */
-@RunWith(RobolectricTestRunner.class)
-public class GridSizeMigrationTaskTest {
-
- private LauncherModelHelper mModelHelper;
- private Context mContext;
- private SQLiteDatabase mDb;
-
- private HashSet<String> mValidPackages;
- private InvariantDeviceProfile mIdp;
-
- @Before
- public void setUp() {
- mModelHelper = new LauncherModelHelper();
- mContext = RuntimeEnvironment.application;
- mDb = mModelHelper.provider.getDb();
-
- mValidPackages = new HashSet<>();
- mValidPackages.add(TEST_PACKAGE);
- mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
- }
-
- @Test
- public void testHotseatMigration_apps_dropped() throws Exception {
- int[] hotseatItems = {
- mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0),
- mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
- -1,
- mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
- mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0),
- };
-
- mIdp.numDatabaseHotseatIcons = 3;
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
- .migrateHotseat();
- // First item is dropped as it has the least weight.
- verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
- }
-
- @Test
- public void testHotseatMigration_shortcuts_dropped() throws Exception {
- int[] hotseatItems = {
- mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0),
- mModelHelper.addItem(30, 1, HOTSEAT, 0, 0),
- -1,
- mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
- mModelHelper.addItem(10, 4, HOTSEAT, 0, 0),
- };
-
- mIdp.numDatabaseHotseatIcons = 3;
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
- .migrateHotseat();
- // First item is dropped as it has the least weight.
- verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
- }
-
- private void verifyHotseat(int... sortedIds) {
- int screenId = 0;
- int total = 0;
-
- for (int id : sortedIds) {
- Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
- new String[]{LauncherSettings.Favorites._ID},
- "container=-101 and screen=" + screenId, null, null, null);
-
- if (id == -1) {
- assertEquals(0, c.getCount());
- } else {
- assertEquals(1, c.getCount());
- c.moveToNext();
- assertEquals(id, c.getLong(0));
- total ++;
- }
- c.close();
-
- screenId++;
- }
-
- // Verify that not other entry exist in the DB.
- Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
- new String[]{LauncherSettings.Favorites._ID},
- "container=-101", null, null, null);
- assertEquals(total, c.getCount());
- c.close();
- }
-
- @Test
- public void testWorkspace_empty_row_column_removed() throws Exception {
- int[][][] ids = mModelHelper.createGrid(new int[][][]{{
- { 0, 0, -1, 1},
- { 3, 1, -1, 4},
- { -1, -1, -1, -1},
- { 5, 2, -1, 6},
- }});
-
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
- new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
- // Column 2 and row 2 got removed.
- verifyWorkspace(new int[][][] {{
- {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
- {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
- {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
- }});
- }
-
- @Test
- public void testWorkspace_new_screen_created() throws Exception {
- int[][][] ids = mModelHelper.createGrid(new int[][][]{{
- { 0, 0, 0, 1},
- { 3, 1, 0, 4},
- { -1, -1, -1, -1},
- { 5, 2, -1, 6},
- }});
-
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
- new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
- // Items in the second column get moved to new screen
- verifyWorkspace(new int[][][] {{
- {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
- {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
- {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
- }, {
- {ids[0][0][2], ids[0][1][2], -1},
- }});
- }
-
- @Test
- public void testWorkspace_items_merged_in_next_screen() throws Exception {
- int[][][] ids = mModelHelper.createGrid(new int[][][]{{
- { 0, 0, 0, 1},
- { 3, 1, 0, 4},
- { -1, -1, -1, -1},
- { 5, 2, -1, 6},
- },{
- { 0, 0, -1, 1},
- { 3, 1, -1, 4},
- }});
-
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
- new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
- // Items in the second column of the first screen should get placed on the 3rd
- // row of the second screen
- verifyWorkspace(new int[][][] {{
- {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
- {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
- {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
- }, {
- {ids[1][0][0], ids[1][0][1], ids[1][0][3]},
- {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
- {ids[0][0][2], ids[0][1][2], -1},
- }});
- }
-
- @Test
- public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
- // First screen has 2 mItems that need to be moved, but second screen has only one
- // empty space after migration (top-left corner)
- int[][][] ids = mModelHelper.createGrid(new int[][][]{{
- { 0, 0, 0, 1},
- { 3, 1, 0, 4},
- { -1, -1, -1, -1},
- { 5, 2, -1, 6},
- },{
- { -1, 0, -1, 1},
- { 3, 1, -1, 4},
- { -1, -1, -1, -1},
- { 5, 2, -1, 6},
- }});
-
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
- new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
- // Items in the second column of the first screen should get placed on a new screen.
- verifyWorkspace(new int[][][] {{
- {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
- {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
- {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
- }, {
- { -1, ids[1][0][1], ids[1][0][3]},
- {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
- {ids[1][3][0], ids[1][3][1], ids[1][3][3]},
- }, {
- {ids[0][0][2], ids[0][1][2], -1},
- }});
- }
-
- @Test
- public void testWorkspace_first_row_blocked() throws Exception {
- if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
- return;
- }
- // The first screen has one item on the 4th column which needs moving, as the first row
- // will be kept empty.
- int[][][] ids = mModelHelper.createGrid(new int[][][]{{
- { -1, -1, -1, -1},
- { 3, 1, 7, 0},
- { 8, 7, 7, -1},
- { 5, 2, 7, -1},
- }}, 0);
-
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
- new Point(4, 4), new Point(3, 4)).migrateWorkspace();
-
- // Items in the second column of the first screen should get placed on a new screen.
- verifyWorkspace(new int[][][] {{
- { -1, -1, -1},
- {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
- {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
- {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
- }, {
- {ids[0][1][3]},
- }});
- }
-
- @Test
- public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
- if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
- return;
- }
- // Items will get moved to the next screen to keep the first screen empty.
- int[][][] ids = mModelHelper.createGrid(new int[][][]{{
- { -1, -1, -1, -1},
- { 0, 1, 0, 0},
- { 8, 7, 7, -1},
- { 5, 6, 7, -1},
- }}, 0);
-
- new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
- new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
- // Items in the second column of the first screen should get placed on a new screen.
- verifyWorkspace(new int[][][] {{
- { -1, -1, -1},
- {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
- {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
- }, {
- {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
- {ids[0][1][3]},
- }});
- }
-
- /**
- * Verifies that the workspace mItems are arranged in the provided order.
- * @param ids A 3d array where the first dimension represents the screen, and the rest two
- * represent the workspace grid.
- */
- private void verifyWorkspace(int[][][] ids) {
- IntArray allScreens = getWorkspaceScreenIds(mDb, LauncherSettings.Favorites.TABLE_NAME);
- assertEquals(ids.length, allScreens.size());
- int total = 0;
-
- for (int i = 0; i < ids.length; i++) {
- int screenId = allScreens.get(i);
- for (int y = 0; y < ids[i].length; y++) {
- for (int x = 0; x < ids[i][y].length; x++) {
- int id = ids[i][y][x];
-
- Cursor c = mContext.getContentResolver().query(
- LauncherSettings.Favorites.CONTENT_URI,
- new String[]{LauncherSettings.Favorites._ID},
- "container=-100 and screen=" + screenId +
- " and cellX=" + x + " and cellY=" + y, null, null, null);
- if (id == -1) {
- assertEquals(0, c.getCount());
- } else {
- assertEquals(1, c.getCount());
- c.moveToNext();
- assertEquals(String.format("Failed to verify item at %d %d, %d", i, y, x),
- id, c.getLong(0));
- total++;
- }
- c.close();
- }
- }
- }
-
- // Verify that not other entry exist in the DB.
- Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
- new String[]{LauncherSettings.Favorites._ID},
- "container=-100", null, null, null);
- assertEquals(total, c.getCount());
- c.close();
- }
-
- @Test
- public void testMultiStepMigration_small_to_large() throws Exception {
- MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier();
- verifier.migrate(new Point(3, 3), new Point(5, 5));
- verifier.assertCompleted();
- }
-
- @Test
- public void testMultiStepMigration_large_to_small() throws Exception {
- MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
- 5, 5, 4, 4,
- 4, 4, 3, 4
- );
- verifier.migrate(new Point(5, 5), new Point(3, 4));
- verifier.assertCompleted();
- }
-
- @Test
- public void testMultiStepMigration_zig_zag() throws Exception {
- MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
- 5, 7, 4, 7,
- 4, 7, 3, 7
- );
- verifier.migrate(new Point(5, 5), new Point(3, 7));
- verifier.assertCompleted();
- }
-
- private static class MultiStepMigrationTaskVerifier extends MultiStepMigrationTask {
-
- private final LinkedList<Point> mPoints;
-
- public MultiStepMigrationTaskVerifier(int... points) {
- super(null, null, null, false);
-
- mPoints = new LinkedList<>();
- for (int i = 0; i < points.length; i += 2) {
- mPoints.add(new Point(points[i], points[i + 1]));
- }
- }
-
- @Override
- protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
- assertEquals(sourceSize, mPoints.poll());
- assertEquals(nextSize, mPoints.poll());
- return false;
- }
-
- public void assertCompleted() {
- assertTrue(mPoints.isEmpty());
- }
- }
-}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 740bf5a..d2d00c8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -94,6 +94,8 @@
private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f;
private static final float TALLER_DEVICE_ASPECT_RATIO_THRESHOLD = 2.15f;
+ private static final float TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP = 252;
+ private static final float TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP = 268;
// To evenly space the icons, increase the left/right margins for tablets in portrait mode.
private static final int PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER = 4;
@@ -257,7 +259,8 @@
final Resources res = context.getResources();
hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
- isTaskbarPresent = isTablet && FeatureFlags.ENABLE_TASKBAR.get();
+ isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
+ && FeatureFlags.ENABLE_TASKBAR.get();
if (isTaskbarPresent) {
// Taskbar will be added later, but provides bottom insets that we should subtract
// from availableHeightPx.
@@ -302,8 +305,10 @@
: res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
if (isTwoPanels) {
- cellLayoutPaddingLeftRightPx =
- res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
+ cellLayoutPaddingLeftRightPx = res.getDimensionPixelSize(
+ isLandscape
+ ? R.dimen.two_panels_home_side_padding_landscape
+ : R.dimen.two_panels_home_side_padding_portrait);
cellLayoutBottomPaddingPx = 0;
} else if (isLandscape) {
cellLayoutPaddingLeftRightPx = 0;
@@ -407,11 +412,17 @@
} else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
// We increase the hotseat size when there is extra space.
- if (Float.compare(aspectRatio, TALLER_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0) {
- // For taller devices, we will take a third of the extra space from each row,
+ if (Float.compare(aspectRatio, TALLER_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0
+ && extraSpace >= Utilities.dpToPx(TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP)) {
+ // For taller devices, we will take a piece of the extra space from each row,
// and add it to the space above and below the hotseat.
+
+ // For devices with more extra space, we take a larger piece from each cell.
+ int piece = extraSpace < Utilities.dpToPx(TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP)
+ ? 5 : 3;
+
int extraSpace = ((getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2)
- * inv.numRows) / 3;
+ * inv.numRows) / piece;
int halfExtraSpace = extraSpace / 2;
hotseatBarTopPaddingPx += halfExtraSpace;
@@ -752,8 +763,14 @@
// Since we are only concerned with the overall padding, layout direction does
// not matter.
Point padding = getTotalWorkspacePadding();
- result.x = calculateCellWidth(availableWidthPx - padding.x
- - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns);
+ // availableWidthPx is the screen width of the device. In 2 panels mode, each panel should
+ // only have half of the screen width. In addition, there is only cellLayoutPadding in the
+ // left side of the left panel and the right side of the right panel. There is no
+ // cellLayoutPadding in the middle.
+ int screenWidthPx = isTwoPanels
+ ? availableWidthPx / 2 - padding.x - cellLayoutPaddingLeftRightPx
+ : availableWidthPx - padding.x - cellLayoutPaddingLeftRightPx * 2;
+ result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacingPx, inv.numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y
- cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
return result;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 07c2113..8249887 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -80,6 +80,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -2870,13 +2871,26 @@
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up");
}
- OptionsPopupView.showDefaultOptions(this, -1, -1);
+ showDefaultOptions(-1, -1);
}
return true;
}
return super.onKeyUp(keyCode, event);
}
+ /**
+ * Shows the default options popup
+ */
+ public void showDefaultOptions(float x, float y) {
+ float halfSize = getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
+ if (x < 0 || y < 0) {
+ x = mDragLayer.getWidth() / 2;
+ y = mDragLayer.getHeight() / 2;
+ }
+ RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
+ OptionsPopupView.show(this, target, OptionsPopupView.getOptions(this), false);
+ }
+
@Override
protected void collectStateHandlers(List<StateHandler> out) {
out.add(getAllAppsController());
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a8ed6bc..95a8d81 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
@@ -433,32 +432,26 @@
return null;
}
case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
- if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
- Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- prepForMigration(
- InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
- Favorites.TMP_TABLE,
- () -> mOpenHelper,
- () -> DatabaseHelper.createDatabaseHelper(
- getContext(), true /* forMigration */)));
- return result;
- }
- return null;
+ Bundle result = new Bundle();
+ result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+ prepForMigration(
+ InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
+ Favorites.TMP_TABLE,
+ () -> mOpenHelper,
+ () -> DatabaseHelper.createDatabaseHelper(
+ getContext(), true /* forMigration */)));
+ return result;
}
case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
- if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
- Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- prepForMigration(
- arg /* dbFile */,
- Favorites.PREVIEW_TABLE_NAME,
- () -> DatabaseHelper.createDatabaseHelper(
- getContext(), arg, true /* forMigration */),
- () -> mOpenHelper));
- return result;
- }
- return null;
+ Bundle result = new Bundle();
+ result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+ prepForMigration(
+ arg /* dbFile */,
+ Favorites.PREVIEW_TABLE_NAME,
+ () -> DatabaseHelper.createDatabaseHelper(
+ getContext(), arg, true /* forMigration */),
+ () -> mOpenHelper));
+ return result;
}
case LauncherSettings.Settings.METHOD_SWITCH_DATABASE: {
if (TextUtils.equals(arg, mOpenHelper.getDatabaseName())) return null;
@@ -654,8 +647,7 @@
static DatabaseHelper createDatabaseHelper(Context context, String dbName,
boolean forMigration) {
if (dbName == null) {
- dbName = MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
- context).dbFile : LauncherFiles.LAUNCHER_DB;
+ dbName = InvariantDeviceProfile.INSTANCE.get(context).dbFile;
}
DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName, forMigration);
// Table creation sometimes fails silently, which leads to a crash loop.
@@ -666,10 +658,6 @@
// This operation is a no-op if the table already exists.
databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
}
- if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
- databaseHelper.mBackupTableExists = tableExists(
- databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
- }
databaseHelper.mHotseatRestoreTableExists = tableExists(
databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
@@ -851,11 +839,7 @@
case 25:
convertShortcutsToLauncherActivities(db);
case 26:
- // QSB was moved to the grid. Clear the first row on screen 0.
- if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
- !LauncherDbUtils.prepareScreenZeroToHostQsb(mContext, db)) {
- break;
- }
+ // QSB was moved to the grid. Ignore overlapping items
case 27: {
// Update the favorites table so that the screen ids are ordered based on
// workspace page rank.
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 3f7a3ad..eb3f94c 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -238,10 +238,6 @@
return getChildAt(index);
}
- protected int indexToPage(int index) {
- return index;
- }
-
/**
* Updates the scroll of the current page immediately to its final scroll position. We use this
* in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
@@ -322,18 +318,51 @@
*/
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
public IntSet getVisiblePageIndices() {
- IntSet visiblePageIndices = new IntSet();
+ return getPageIndices(mCurrentPage);
+ }
+
+ /**
+ * In case the panelCount is 1 this just returns the same page index in an IntSet.
+ * But in cases where the panelCount > 1 this will return all the page indices that belong
+ * together, i.e. on the Workspace they are next to each other and shown at the same time.
+ */
+ private IntSet getPageIndices(int pageIndex) {
+ // we want to make sure the pageIndex is the leftmost page
+ pageIndex = getLeftmostVisiblePageForIndex(pageIndex);
+
+ IntSet pageIndices = new IntSet();
int panelCount = getPanelCount();
int pageCount = getPageCount();
-
- // If a device goes from one panel to two panel (i.e. unfolding a foldable device) while
- // an odd indexed page is the current page, then the new leftmost visible page will be
- // different from the old mCurrentPage.
- int currentPage = getLeftmostVisiblePageForIndex(mCurrentPage);
- for (int page = currentPage; page < currentPage + panelCount && page < pageCount; page++) {
- visiblePageIndices.add(page);
+ for (int page = pageIndex; page < pageIndex + panelCount && page < pageCount; page++) {
+ pageIndices.add(page);
}
- return visiblePageIndices;
+ return pageIndices;
+ }
+
+ /**
+ * Returns an IntSet with the indices of the neighbour pages that are in the focus direction.
+ */
+ private IntSet getNeighbourPageIndices(int focus) {
+ int panelCount = getPanelCount();
+ // getNextPage is more reliable than getCurrentPage
+ int currentPage = getNextPage();
+
+ int nextPage;
+ if (focus == View.FOCUS_LEFT) {
+ nextPage = currentPage - panelCount;
+ } else if (focus == View.FOCUS_RIGHT) {
+ nextPage = currentPage + panelCount;
+ } else {
+ // no neighbours to other directions
+ return new IntSet();
+ }
+ nextPage = validateNewPage(nextPage);
+ if (nextPage == currentPage) {
+ // We reached the end of the pages
+ return new IntSet();
+ }
+
+ return getPageIndices(nextPage);
}
/**
@@ -352,7 +381,14 @@
* Returns true if the view is on one of the current pages, false otherwise.
*/
public boolean isVisible(View child) {
- return getLeftmostVisiblePageForIndex(indexOfChild(child)) == mCurrentPage;
+ return isVisible(indexOfChild(child));
+ }
+
+ /**
+ * Returns true if the page with the given index is currently visible, false otherwise.
+ */
+ private boolean isVisible(int pageIndex) {
+ return getLeftmostVisiblePageForIndex(pageIndex) == mCurrentPage;
}
/**
@@ -817,8 +853,8 @@
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
- int page = indexToPage(indexOfChild(child));
- if (page != mCurrentPage || !mScroller.isFinished()) {
+ int page = indexOfChild(child);
+ if (!isVisible(page) || !mScroller.isFinished()) {
if (immediate) {
setCurrentPage(page);
} else {
@@ -857,21 +893,25 @@
direction = View.FOCUS_LEFT;
}
}
- if (direction == View.FOCUS_LEFT) {
- if (getCurrentPage() > 0) {
- int nextPage = validateNewPage(getCurrentPage() - 1);
- snapToPage(nextPage);
- getChildAt(nextPage).requestFocus(direction);
- return true;
- }
- } else if (direction == View.FOCUS_RIGHT) {
- if (getCurrentPage() < getPageCount() - 1) {
- int nextPage = validateNewPage(getCurrentPage() + 1);
- snapToPage(nextPage);
- getChildAt(nextPage).requestFocus(direction);
- return true;
+
+ int currentPage = getNextPage();
+ int closestNeighbourIndex = -1;
+ int closestNeighbourDistance = Integer.MAX_VALUE;
+ // Find the closest neighbour page
+ for (int neighbourPageIndex : getNeighbourPageIndices(direction)) {
+ int distance = Math.abs(neighbourPageIndex - currentPage);
+ if (closestNeighbourDistance > distance) {
+ closestNeighbourDistance = distance;
+ closestNeighbourIndex = neighbourPageIndex;
}
}
+ if (closestNeighbourIndex != -1) {
+ View page = getPageAt(closestNeighbourIndex);
+ snapToPage(closestNeighbourIndex);
+ page.requestFocus(direction);
+ return true;
+ }
+
return false;
}
@@ -881,28 +921,12 @@
return;
}
- // Add the current page's views as focusable and the next possible page's too. If the
- // last focus change action was left then the left neighbour's views will be added, and
- // if it was right then the right neighbour's views will be added.
- // Unfortunately mCurrentPage can be outdated if there were multiple control actions in a
- // short period of time, but mNextPage is up to date because it is always updated by
- // method snapToPage.
- int nextPage = getNextPage();
- // XXX-RTL: This will be fixed in a future CL
- if (nextPage >= 0 && nextPage < getPageCount()) {
- getPageAt(nextPage).addFocusables(views, direction, focusableMode);
- }
- if (direction == View.FOCUS_LEFT) {
- if (nextPage > 0) {
- nextPage = validateNewPage(nextPage - 1);
- getPageAt(nextPage).addFocusables(views, direction, focusableMode);
- }
- } else if (direction == View.FOCUS_RIGHT) {
- if (nextPage < getPageCount() - 1) {
- nextPage = validateNewPage(nextPage + 1);
- getPageAt(nextPage).addFocusables(views, direction, focusableMode);
- }
- }
+ // nextPage is more reliable when multiple control movements have been done in a short
+ // period of time
+ getPageIndices(getNextPage())
+ .addAll(getNeighbourPageIndices(direction))
+ .forEach(pageIndex ->
+ getPageAt(pageIndex).addFocusables(views, direction, focusableMode));
}
/**
@@ -1482,8 +1506,8 @@
setCurrentPage(nextPage);
}
- int page = indexToPage(indexOfChild(child));
- if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
+ int page = indexOfChild(child);
+ if (page >= 0 && !isVisible(page) && !isInTouchMode()) {
snapToPage(page);
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5200456..83ca08d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -325,10 +325,13 @@
setPageSpacing(Math.max(maxInsets, maxPadding));
}
+ updateWorkspaceScreensPadding();
+ }
+
+ private void updateWorkspaceScreensPadding() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
int paddingBottom = grid.cellLayoutBottomPaddingPx;
- int twoPanelLandscapeSidePadding = paddingLeftRight * 2;
- int twoPanelPortraitSidePadding = paddingLeftRight / 2;
int panelCount = getPanelCount();
for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
@@ -336,13 +339,11 @@
int paddingRight = paddingLeftRight;
if (panelCount > 1) {
if (i % panelCount == 0) { // left side panel
- paddingLeft = grid.isLandscape ? twoPanelLandscapeSidePadding
- : twoPanelPortraitSidePadding;
+ paddingLeft = paddingLeftRight;
paddingRight = 0;
} else if (i % panelCount == panelCount - 1) { // right side panel
paddingLeft = 0;
- paddingRight = grid.isLandscape ? twoPanelLandscapeSidePadding
- : twoPanelPortraitSidePadding;
+ paddingRight = paddingLeftRight;
} else { // middle panel
paddingLeft = 0;
paddingRight = 0;
@@ -625,10 +626,6 @@
// created CellLayout.
CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
- DeviceProfile grid = mLauncher.getDeviceProfile();
- int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
- int paddingBottom = grid.cellLayoutBottomPaddingPx;
- newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(insertIndex, screenId);
@@ -637,6 +634,7 @@
mLauncher.getStateManager().getState(), newScreen, insertIndex);
updatePageScrollValues();
+ updateWorkspaceScreensPadding();
return newScreen;
}
@@ -1779,7 +1777,7 @@
boolean droppedOnOriginalCell = false;
- int snapScreen = -1;
+ boolean snappedToNewPage = false;
boolean resizeOnDrop = false;
Runnable onCompleteRunnable = null;
if (d.dragSource != this || mDragInfo == null) {
@@ -1861,11 +1859,14 @@
}
if (foundCell) {
- if (getScreenIdForPageIndex(mCurrentPage) != screenId && !hasMovedIntoHotseat) {
- snapScreen = getPageIndexForScreenId(screenId);
+ int targetScreenIndex = getPageIndexForScreenId(screenId);
+ int snapScreen = getLeftmostVisiblePageForIndex(targetScreenIndex);
+ // On large screen devices two pages can be shown at the same time, and snap
+ // isn't needed if the source and target screens appear at the same time
+ if (snapScreen != mCurrentPage && !hasMovedIntoHotseat) {
snapToPage(snapScreen);
+ snappedToNewPage = true;
}
-
final ItemInfo info = (ItemInfo) cell.getTag();
if (hasMovedLayouts) {
// Reparent the view
@@ -1957,7 +1958,7 @@
ANIMATE_INTO_POSITION_AND_DISAPPEAR;
animateWidgetDrop(info, parent, d.dragView, null, animationType, cell, false);
} else {
- int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
+ int duration = snappedToNewPage ? ADJACENT_SCREEN_DROP_DURATION : -1;
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
this);
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 9faac5b..0fb5e77 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -188,7 +188,8 @@
private boolean itemSupportsAccessibleDrag(ItemInfo item) {
if (item instanceof WorkspaceItemInfo) {
// Support the action unless the item is in a context menu.
- return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION;
+ return (item.screenId >= 0 || item.screenId == Workspace.LEFT_PANEL_ID)
+ && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION;
}
return (item instanceof LauncherAppWidgetInfo)
|| (item instanceof FolderInfo);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 9b71918..da701a8 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -144,9 +144,6 @@
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
- public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
- "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm");
-
public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
"ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");
@@ -258,6 +255,10 @@
"WIDGETS_IN_LAUNCHER_PREVIEW", true,
"Enables widgets in Launcher preview for the Wallpaper app.");
+ public static final BooleanFlag QUICK_WALLPAPER_PICKER = getDebugFlag(
+ "QUICK_WALLPAPER_PICKER", false,
+ "Shows quick wallpaper picker in long-press menu");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index d0464a4..c7448dc 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,7 +16,6 @@
package com.android.launcher3.graphics;
-import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -47,7 +46,6 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDelegate;
@@ -198,16 +196,10 @@
@WorkerThread
private boolean doGridMigrationIfNecessary() {
- boolean needsToMigrate =
- MULTI_DB_GRID_MIRATION_ALGO.get()
- ? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)
- : GridSizeMigrationTask.needsToMigrate(mContext, mIdp);
- if (!needsToMigrate) {
+ if (!GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)) {
return false;
}
- return MULTI_DB_GRID_MIRATION_ALGO.get()
- ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp)
- : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp);
+ return GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp);
}
@UiThread
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index acfc339..51cbf4b 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -23,14 +23,12 @@
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
import android.os.Process;
import android.util.Log;
import androidx.annotation.IntDef;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.pm.UserCache;
/**
@@ -85,49 +83,6 @@
}
/**
- * Create a backup from current workspace layout if one isn't created already (Note backup
- * created this way is always sanitized). Otherwise restore from the backup instead.
- */
- public boolean backupOrRestoreAsNeeded() {
- // Check if backup table exists
- if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
- if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
- .getBoolean(Settings.EXTRA_VALUE, false)) {
- // No need to copy if empty DB was created.
- return false;
- }
- doBackup(UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
- Process.myUserHandle()), 0);
- return false;
- }
- return restoreIfBackupExists(Favorites.TABLE_NAME);
- }
-
- public boolean restoreToPreviewIfBackupExists() {
- if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
- return false;
- }
-
- return restoreIfBackupExists(Favorites.PREVIEW_TABLE_NAME);
- }
-
- private boolean restoreIfBackupExists(String toTableName) {
- if (loadDBProperties() != STATE_SANITIZED) {
- return false;
- }
- long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
- Process.myUserHandle());
- copyTable(mDb, BACKUP_TABLE_NAME, toTableName, userSerial);
- Log.d(TAG, "Backup table found");
- return true;
- }
-
- public int getRestoreHotseatAndGridSize(Point outGridSize) {
- outGridSize.set(mRestoredGridX, mRestoredGridY);
- return mRestoredHotseatSize;
- }
-
- /**
* Creates a new table and populates with copy of Favorites.TABLE_NAME
*/
public void createCustomBackupTable(String tableName) {
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
deleted file mode 100644
index 7b3e509..0000000
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ /dev/null
@@ -1,1098 +0,0 @@
-package com.android.launcher3.model;
-
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
-import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_WORKSPACE_SIZE;
-import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
-import static com.android.launcher3.Utilities.getPointString;
-import static com.android.launcher3.Utilities.parsePoint;
-import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
-
-import android.content.ComponentName;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.SparseArray;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.pm.InstallSessionHelper;
-import com.android.launcher3.provider.LauncherDbUtils;
-import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
-import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSparseArrayMap;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.WidgetManagerHelper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-
-/**
- * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
- * result of restoring from a larger device or device density change.
- */
-public class GridSizeMigrationTask {
-
- private static final String TAG = "GridSizeMigrationTask";
- private static final boolean DEBUG = false;
-
- // These are carefully selected weights for various item types (Math.random?), to allow for
- // the least absurd migration experience.
- private static final float WT_SHORTCUT = 1;
- private static final float WT_APPLICATION = 0.8f;
- private static final float WT_WIDGET_MIN = 2;
- private static final float WT_WIDGET_FACTOR = 0.6f;
- private static final float WT_FOLDER_FACTOR = 0.5f;
-
- protected final SQLiteDatabase mDb;
- protected final Context mContext;
-
- protected final IntArray mEntryToRemove = new IntArray();
- protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
-
- private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
- private final HashSet<String> mValidPackages;
- private final String mTableName;
-
- private final int mSrcX, mSrcY;
- private final int mTrgX, mTrgY;
- private final boolean mShouldRemoveX, mShouldRemoveY;
-
- private final int mSrcHotseatSize;
- private final int mDestHotseatSize;
-
- protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
- HashSet<String> validPackages, boolean usePreviewTable, Point sourceSize,
- Point targetSize) {
- mContext = context;
- mDb = db;
- mValidPackages = validPackages;
- mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
-
- mSrcX = sourceSize.x;
- mSrcY = sourceSize.y;
-
- mTrgX = targetSize.x;
- mTrgY = targetSize.y;
-
- mShouldRemoveX = mTrgX < mSrcX;
- mShouldRemoveY = mTrgY < mSrcY;
-
- // Non-used variables
- mSrcHotseatSize = mDestHotseatSize = -1;
- }
-
- protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
- HashSet<String> validPackages, boolean usePreviewTable, int srcHotseatSize,
- int destHotseatSize) {
- mContext = context;
- mDb = db;
- mValidPackages = validPackages;
- mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
-
- mSrcHotseatSize = srcHotseatSize;
-
- mDestHotseatSize = destHotseatSize;
-
- // Non-used variables
- mSrcX = mSrcY = mTrgX = mTrgY = -1;
- mShouldRemoveX = mShouldRemoveY = false;
- }
-
- /**
- * Applied all the pending DB operations
- *
- * @return true if any DB operation was commited.
- */
- private boolean applyOperations() throws Exception {
- // Update items
- int updateCount = mUpdateOperations.size();
- for (int i = 0; i < updateCount; i++) {
- mDb.update(mTableName, mUpdateOperations.valueAt(i),
- "_id=" + mUpdateOperations.keyAt(i), null);
- }
-
- if (!mEntryToRemove.isEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
- }
- mDb.delete(mTableName, Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove),
- null);
- }
-
- return updateCount > 0 || !mEntryToRemove.isEmpty();
- }
-
- /**
- * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them
- * in the order in the new hotseat while keeping an empty space for all-apps. If the number of
- * entries is more than what can fit in the new hotseat, we drop the entries with least weight.
- * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION}
- * & {@see #WT_FOLDER_FACTOR}.
- *
- * @return true if any DB change was made
- */
- protected boolean migrateHotseat() throws Exception {
- ArrayList<DbEntry> items = loadHotseatEntries();
- while (items.size() > mDestHotseatSize) {
- // Pick the center item by default.
- DbEntry toRemove = items.get(items.size() / 2);
-
- // Find the item with least weight.
- for (DbEntry entry : items) {
- if (entry.weight < toRemove.weight) {
- toRemove = entry;
- }
- }
-
- mEntryToRemove.add(toRemove.id);
- items.remove(toRemove);
- }
-
- // Update screen IDS
- int newScreenId = 0;
- for (DbEntry entry : items) {
- if (entry.screenId != newScreenId) {
- entry.screenId = newScreenId;
-
- // These values does not affect the item position, but we should set them
- // to something other than -1.
- entry.cellX = newScreenId;
- entry.cellY = 0;
-
- update(entry);
- }
-
- newScreenId++;
- }
-
- return applyOperations();
- }
-
- @VisibleForTesting
- static IntArray getWorkspaceScreenIds(SQLiteDatabase db, String tableName) {
- return LauncherDbUtils.queryIntArray(db, tableName, Favorites.SCREEN,
- Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
- Favorites.SCREEN, Favorites.SCREEN);
- }
-
- /**
- * @return true if any DB change was made
- */
- protected boolean migrateWorkspace() throws Exception {
- IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName);
- if (allScreens.isEmpty()) {
- throw new Exception("Unable to get workspace screens");
- }
-
- for (int i = 0; i < allScreens.size(); i++) {
- int screenId = allScreens.get(i);
- if (DEBUG) {
- Log.d(TAG, "Migrating " + screenId);
- }
- migrateScreen(screenId);
- }
-
- if (!mCarryOver.isEmpty()) {
- IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
- for (DbEntry e : mCarryOver) {
- itemMap.put(e.id, e);
- }
-
- do {
- // Some items are still remaining. Try adding a few new screens.
-
- // At every iteration, make sure that at least one item is removed from
- // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
- // break the loop and abort migration by throwing an exception.
- OptimalPlacementSolution placement = new OptimalPlacementSolution(
- new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
- placement.find();
- if (placement.finalPlacedItems.size() > 0) {
- int newScreenId = LauncherSettings.Settings.call(
- mContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(EXTRA_VALUE);
- for (DbEntry item : placement.finalPlacedItems) {
- if (!mCarryOver.remove(itemMap.get(item.id))) {
- throw new Exception("Unable to find matching items");
- }
- item.screenId = newScreenId;
- update(item);
- }
- } else {
- throw new Exception("None of the items can be placed on an empty screen");
- }
-
- } while (!mCarryOver.isEmpty());
- }
- return applyOperations();
- }
-
- /**
- * Migrate a particular screen id.
- * Strategy:
- * 1) For all possible combinations of row and column, pick the one which causes the least
- * data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
- * 2) Maintain a list of all lost items before this screen, and add any new item lost from
- * this screen to that list as well.
- * 3) If all those items from the above list can be placed on this screen, place them
- * (otherwise they are placed on a new screen).
- */
- protected void migrateScreen(int screenId) {
- // If we are migrating the first screen, do not touch the first row.
- int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID)
- ? 1 : 0;
-
- ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
-
- int removedCol = Integer.MAX_VALUE;
- int removedRow = Integer.MAX_VALUE;
-
- // removeWt represents the cost function for loss of items during migration, and moveWt
- // represents the cost function for repositioning the items. moveWt is only considered if
- // removeWt is same for two different configurations.
- // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
- // cost.
- float removeWt = Float.MAX_VALUE;
- float moveWt = Float.MAX_VALUE;
- float[] outLoss = new float[2];
- ArrayList<DbEntry> finalItems = null;
-
- // Try removing all possible combinations
- for (int x = 0; x < mSrcX; x++) {
- // Try removing the rows first from bottom. This keeps the workspace
- // nicely aligned with hotseat.
- for (int y = mSrcY - 1; y >= startY; y--) {
- // Use a deep copy when trying out a particular combination as it can change
- // the underlying object.
- ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items),
- outLoss);
-
- if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1]
- < moveWt))) {
- removeWt = outLoss[0];
- moveWt = outLoss[1];
- removedCol = mShouldRemoveX ? x : removedCol;
- removedRow = mShouldRemoveY ? y : removedRow;
- finalItems = itemsOnScreen;
- }
-
- // No need to loop over all rows, if a row removal is not needed.
- if (!mShouldRemoveY) {
- break;
- }
- }
-
- if (!mShouldRemoveX) {
- break;
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
- removedRow, removedCol, screenId));
- }
-
- IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
- for (DbEntry e : deepCopy(items)) {
- itemMap.put(e.id, e);
- }
-
- for (DbEntry item : finalItems) {
- DbEntry org = itemMap.get(item.id);
- itemMap.remove(item.id);
-
- // Check if update is required
- if (!item.columnsSame(org)) {
- update(item);
- }
- }
-
- // The remaining items in {@link #itemMap} are those which didn't get placed.
- for (DbEntry item : itemMap) {
- mCarryOver.add(item);
- }
-
- if (!mCarryOver.isEmpty() && removeWt == 0) {
- // No new items were removed in this step. Try placing all the items on this screen.
- GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
- occupied.markCells(0, 0, mTrgX, startY, true);
- for (DbEntry item : finalItems) {
- occupied.markCells(item, true);
- }
-
- OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
- deepCopy(mCarryOver), startY, true);
- placement.find();
- if (placement.lowestWeightLoss == 0) {
- // All items got placed
-
- for (DbEntry item : placement.finalPlacedItems) {
- item.screenId = screenId;
- update(item);
- }
-
- mCarryOver.clear();
- }
- }
- }
-
- /**
- * Updates an item in the DB.
- */
- protected void update(DbEntry item) {
- ContentValues values = new ContentValues();
- item.addToContentValues(values);
- mUpdateOperations.put(item.id, values);
- }
-
- /**
- * Tries the remove the provided row and column.
- *
- * @param items all the items on the screen under operation
- * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
- * with the overall item movement.
- */
- private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
- ArrayList<DbEntry> items, float[] outLoss) {
- GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
- occupied.markCells(0, 0, mTrgX, startY, true);
-
- col = mShouldRemoveX ? col : Integer.MAX_VALUE;
- row = mShouldRemoveY ? row : Integer.MAX_VALUE;
-
- ArrayList<DbEntry> finalItems = new ArrayList<>();
- ArrayList<DbEntry> removedItems = new ArrayList<>();
-
- for (DbEntry item : items) {
- if ((item.cellX <= col && (item.spanX + item.cellX) > col)
- || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
- removedItems.add(item);
- if (item.cellX >= col) item.cellX--;
- if (item.cellY >= row) item.cellY--;
- } else {
- if (item.cellX > col) item.cellX--;
- if (item.cellY > row) item.cellY--;
- finalItems.add(item);
- occupied.markCells(item, true);
- }
- }
-
- OptimalPlacementSolution placement =
- new OptimalPlacementSolution(occupied, removedItems, startY);
- placement.find();
- finalItems.addAll(placement.finalPlacedItems);
- outLoss[0] = placement.lowestWeightLoss;
- outLoss[1] = placement.lowestMoveCost;
- return finalItems;
- }
-
- private class OptimalPlacementSolution {
- private final ArrayList<DbEntry> itemsToPlace;
- private final GridOccupancy occupied;
-
- // If set to true, item movement are not considered in move cost, leading to a more
- // linear placement.
- private final boolean ignoreMove;
-
- // The first row in the grid from where the placement should start.
- private final int startY;
-
- float lowestWeightLoss = Float.MAX_VALUE;
- float lowestMoveCost = Float.MAX_VALUE;
- ArrayList<DbEntry> finalPlacedItems;
-
- public OptimalPlacementSolution(
- GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) {
- this(occupied, itemsToPlace, startY, false);
- }
-
- public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
- int startY, boolean ignoreMove) {
- this.occupied = occupied;
- this.itemsToPlace = itemsToPlace;
- this.ignoreMove = ignoreMove;
- this.startY = startY;
-
- // Sort the items such that larger widgets appear first followed by 1x1 items
- Collections.sort(this.itemsToPlace);
- }
-
- public void find() {
- find(0, 0, 0, new ArrayList<DbEntry>());
- }
-
- /**
- * Recursively finds a placement for the provided items.
- *
- * @param index the position in {@link #itemsToPlace} to start looking at.
- * @param weightLoss total weight loss upto this point
- * @param moveCost total move cost upto this point
- * @param itemsPlaced all the items already placed upto this point
- */
- public void find(int index, float weightLoss, float moveCost,
- ArrayList<DbEntry> itemsPlaced) {
- if ((weightLoss >= lowestWeightLoss) ||
- ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
- // Abort, as we already have a better solution.
- return;
-
- } else if (index >= itemsToPlace.size()) {
- // End loop.
- lowestWeightLoss = weightLoss;
- lowestMoveCost = moveCost;
-
- // Keep a deep copy of current configuration as it can change during recursion.
- finalPlacedItems = deepCopy(itemsPlaced);
- return;
- }
-
- DbEntry me = itemsToPlace.get(index);
- int myX = me.cellX;
- int myY = me.cellY;
-
- // List of items to pass over if this item was placed.
- ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
- itemsIncludingMe.addAll(itemsPlaced);
- itemsIncludingMe.add(me);
-
- if (me.spanX > 1 || me.spanY > 1) {
- // If the current item is a widget (and it greater than 1x1), try to place it at
- // all possible positions. This is because a widget placed at one position can
- // affect the placement of a different widget.
- int myW = me.spanX;
- int myH = me.spanY;
-
- for (int y = startY; y < mTrgY; y++) {
- for (int x = 0; x < mTrgX; x++) {
- float newMoveCost = moveCost;
- if (x != myX) {
- me.cellX = x;
- newMoveCost++;
- }
- if (y != myY) {
- me.cellY = y;
- newMoveCost++;
- }
- if (ignoreMove) {
- newMoveCost = moveCost;
- }
-
- if (occupied.isRegionVacant(x, y, myW, myH)) {
- // place at this position and continue search.
- occupied.markCells(me, true);
- find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
- occupied.markCells(me, false);
- }
-
- // Try resizing horizontally
- if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
- me.spanX--;
- occupied.markCells(me, true);
- // 1 extra move cost
- find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
- occupied.markCells(me, false);
- me.spanX++;
- }
-
- // Try resizing vertically
- if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
- me.spanY--;
- occupied.markCells(me, true);
- // 1 extra move cost
- find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
- occupied.markCells(me, false);
- me.spanY++;
- }
-
- // Try resizing horizontally & vertically
- if (myH > me.minSpanY && myW > me.minSpanX &&
- occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
- me.spanX--;
- me.spanY--;
- occupied.markCells(me, true);
- // 2 extra move cost
- find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
- occupied.markCells(me, false);
- me.spanX++;
- me.spanY++;
- }
- me.cellX = myX;
- me.cellY = myY;
- }
- }
-
- // Finally also try a solution when this item is not included. Trying it in the end
- // causes it to get skipped in most cases due to higher weight loss, and prevents
- // unnecessary deep copies of various configurations.
- find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
- } else {
- // Since this is a 1x1 item and all the following items are also 1x1, just place
- // it at 'the most appropriate position' and hope for the best.
- // The most appropriate position: one with lease straight line distance
- int newDistance = Integer.MAX_VALUE;
- int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
-
- for (int y = startY; y < mTrgY; y++) {
- for (int x = 0; x < mTrgX; x++) {
- if (!occupied.cells[x][y]) {
- int dist = ignoreMove ? 0 :
- ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY
- - y));
- if (dist < newDistance) {
- newX = x;
- newY = y;
- newDistance = dist;
- }
- }
- }
- }
-
- if (newX < mTrgX && newY < mTrgY) {
- float newMoveCost = moveCost;
- if (newX != myX) {
- me.cellX = newX;
- newMoveCost++;
- }
- if (newY != myY) {
- me.cellY = newY;
- newMoveCost++;
- }
- if (ignoreMove) {
- newMoveCost = moveCost;
- }
- occupied.markCells(me, true);
- find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
- occupied.markCells(me, false);
- me.cellX = myX;
- me.cellY = myY;
-
- // Try to find a solution without this item, only if
- // 1) there was at least one space, i.e., we were able to place this item
- // 2) if the next item has the same weight (all items are already sorted), as
- // if it has lower weight, that solution will automatically get discarded.
- // 3) ignoreMove false otherwise, move cost is ignored and the weight will
- // anyway be same.
- if (index + 1 < itemsToPlace.size()
- && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
- find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
- }
- } else {
- // No more space. Jump to the end.
- for (int i = index + 1; i < itemsToPlace.size(); i++) {
- weightLoss += itemsToPlace.get(i).weight;
- }
- find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
- }
- }
- }
- }
-
- private ArrayList<DbEntry> loadHotseatEntries() {
- Cursor c = queryWorkspace(
- new String[]{
- Favorites._ID, // 0
- Favorites.ITEM_TYPE, // 1
- Favorites.INTENT, // 2
- Favorites.SCREEN}, // 3
- Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT);
-
- final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
- final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
- final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
- final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN);
-
- ArrayList<DbEntry> entries = new ArrayList<>();
- while (c.moveToNext()) {
- DbEntry entry = new DbEntry();
- entry.id = c.getInt(indexId);
- entry.itemType = c.getInt(indexItemType);
- entry.screenId = c.getInt(indexScreen);
-
- if (entry.screenId >= mSrcHotseatSize) {
- mEntryToRemove.add(entry.id);
- continue;
- }
-
- try {
- // calculate weight
- switch (entry.itemType) {
- case Favorites.ITEM_TYPE_SHORTCUT:
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case Favorites.ITEM_TYPE_APPLICATION: {
- verifyIntent(c.getString(indexIntent));
- entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
- WT_APPLICATION : WT_SHORTCUT;
- break;
- }
- case Favorites.ITEM_TYPE_FOLDER: {
- int total = getFolderItemsCount(entry.id);
- if (total == 0) {
- throw new Exception("Folder is empty");
- }
- entry.weight = WT_FOLDER_FACTOR * total;
- break;
- }
- default:
- throw new Exception("Invalid item type");
- }
- } catch (Exception e) {
- if (DEBUG) {
- Log.d(TAG, "Removing item " + entry.id, e);
- }
- mEntryToRemove.add(entry.id);
- continue;
- }
- entries.add(entry);
- }
- c.close();
- return entries;
- }
-
-
- /**
- * Loads entries for a particular screen id.
- */
- protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
- Cursor c = queryWorkspace(
- new String[]{
- Favorites._ID, // 0
- Favorites.ITEM_TYPE, // 1
- Favorites.CELLX, // 2
- Favorites.CELLY, // 3
- Favorites.SPANX, // 4
- Favorites.SPANY, // 5
- Favorites.INTENT, // 6
- Favorites.APPWIDGET_PROVIDER, // 7
- Favorites.APPWIDGET_ID}, // 8
- Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
- + " AND " + Favorites.SCREEN + " = " + screen);
-
- final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
- final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
- final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
- final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
- final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
- final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
- final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
- final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
- final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
-
- ArrayList<DbEntry> entries = new ArrayList<>();
- WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(mContext);
- while (c.moveToNext()) {
- DbEntry entry = new DbEntry();
- entry.id = c.getInt(indexId);
- entry.itemType = c.getInt(indexItemType);
- entry.cellX = c.getInt(indexCellX);
- entry.cellY = c.getInt(indexCellY);
- entry.spanX = c.getInt(indexSpanX);
- entry.spanY = c.getInt(indexSpanY);
- entry.screenId = screen;
-
- try {
- // calculate weight
- switch (entry.itemType) {
- case Favorites.ITEM_TYPE_SHORTCUT:
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case Favorites.ITEM_TYPE_APPLICATION: {
- verifyIntent(c.getString(indexIntent));
- entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
- WT_APPLICATION : WT_SHORTCUT;
- break;
- }
- case Favorites.ITEM_TYPE_APPWIDGET: {
- String provider = c.getString(indexAppWidgetProvider);
- ComponentName cn = ComponentName.unflattenFromString(provider);
- verifyPackage(cn.getPackageName());
- entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
- * entry.spanX * entry.spanY);
-
- int widgetId = c.getInt(indexAppWidgetId);
- LauncherAppWidgetProviderInfo pInfo =
- widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
- Point spans = null;
- if (pInfo != null) {
- spans = pInfo.getMinSpans();
- }
- if (spans != null) {
- entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
- entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
- } else {
- // Assume that the widget be resized down to 2x2
- entry.minSpanX = entry.minSpanY = 2;
- }
-
- if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
- throw new Exception("Widget can't be resized down to fit the grid");
- }
- break;
- }
- case Favorites.ITEM_TYPE_FOLDER: {
- int total = getFolderItemsCount(entry.id);
- if (total == 0) {
- throw new Exception("Folder is empty");
- }
- entry.weight = WT_FOLDER_FACTOR * total;
- break;
- }
- default:
- throw new Exception("Invalid item type");
- }
- } catch (Exception e) {
- if (DEBUG) {
- Log.d(TAG, "Removing item " + entry.id, e);
- }
- mEntryToRemove.add(entry.id);
- continue;
- }
- entries.add(entry);
- }
- c.close();
- return entries;
- }
-
- /**
- * @return the number of valid items in the folder.
- */
- private int getFolderItemsCount(int folderId) {
- Cursor c = queryWorkspace(
- new String[]{Favorites._ID, Favorites.INTENT},
- Favorites.CONTAINER + " = " + folderId);
-
- int total = 0;
- while (c.moveToNext()) {
- try {
- verifyIntent(c.getString(1));
- total++;
- } catch (Exception e) {
- mEntryToRemove.add(c.getInt(0));
- }
- }
- c.close();
- return total;
- }
-
- protected Cursor queryWorkspace(String[] columns, String where) {
- return mDb.query(mTableName, columns, where, null, null, null, null);
- }
-
- /**
- * Verifies if the intent should be restored.
- */
- private void verifyIntent(String intentStr) throws Exception {
- Intent intent = Intent.parseUri(intentStr, 0);
- if (intent.getComponent() != null) {
- verifyPackage(intent.getComponent().getPackageName());
- } else if (intent.getPackage() != null) {
- // Only verify package if the component was null.
- verifyPackage(intent.getPackage());
- }
- }
-
- /**
- * Verifies if the package should be restored
- */
- private void verifyPackage(String packageName) throws Exception {
- if (!mValidPackages.contains(packageName)) {
- throw new Exception("Package not available");
- }
- }
-
- protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
-
- public float weight;
-
- public DbEntry() {
- }
-
- public DbEntry copy() {
- DbEntry entry = new DbEntry();
- entry.copyFrom(this);
- entry.weight = weight;
- entry.minSpanX = minSpanX;
- entry.minSpanY = minSpanY;
- return entry;
- }
-
- /**
- * Comparator such that larger widgets come first, followed by all 1x1 items
- * based on their weights.
- */
- @Override
- public int compareTo(DbEntry another) {
- if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
- if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
- return another.spanY * another.spanX - spanX * spanY;
- } else {
- return -1;
- }
- } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
- return 1;
- } else {
- // Place higher weight before lower weight.
- return Float.compare(another.weight, weight);
- }
- }
-
- public boolean columnsSame(DbEntry org) {
- return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
- org.spanY == spanY && org.screenId == screenId;
- }
-
- public void addToContentValues(ContentValues values) {
- values.put(Favorites.SCREEN, screenId);
- values.put(Favorites.CELLX, cellX);
- values.put(Favorites.CELLY, cellY);
- values.put(Favorites.SPANX, spanX);
- values.put(Favorites.SPANY, spanY);
- }
- }
-
- private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
- ArrayList<DbEntry> dup = new ArrayList<>(src.size());
- for (DbEntry e : src) {
- dup.add(e.copy());
- }
- return dup;
- }
-
- public static void markForMigration(
- Context context, int gridX, int gridY, int hotseatSize) {
- Utilities.getPrefs(context).edit()
- .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(gridX, gridY))
- .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, hotseatSize)
- .apply();
- }
-
- /**
- * Check given a new IDP, if migration is necessary.
- */
- public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
- SharedPreferences prefs = Utilities.getPrefs(context);
- String gridSizeString = getPointString(idp.numColumns, idp.numRows);
-
- return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
- || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
- }
-
- /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
- public static boolean migrateGridIfNeeded(Context context) {
- if (context instanceof LauncherPreviewRenderer.PreviewContext) {
- return true;
- }
- return migrateGridIfNeeded(context, null);
- }
-
- /**
- * Run the migration algorithm if needed. For preview, we provide the intended idp because it
- * has not been changed. If idp is null, we read it from the context, for actual grid migration.
- *
- * @return false if the migration failed.
- */
- public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
- boolean migrateForPreview = idp != null;
- if (!migrateForPreview) {
- idp = LauncherAppState.getIDP(context);
- }
-
- if (!needsToMigrate(context, idp)) {
- return true;
- }
-
- SharedPreferences prefs = Utilities.getPrefs(context);
- String gridSizeString = getPointString(idp.numColumns, idp.numRows);
- long migrationStartTime = SystemClock.elapsedRealtime();
- try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
- context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
- .getBinder(Settings.EXTRA_VALUE)) {
-
- int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
- idp.numDatabaseHotseatIcons);
- Point sourceSize = parsePoint(prefs.getString(
- KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
-
- boolean dbChanged = false;
- if (migrateForPreview) {
- copyTable(transaction.getDb(), Favorites.TABLE_NAME, transaction.getDb(),
- Favorites.PREVIEW_TABLE_NAME, context);
- }
-
- GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
- srcHotseatCount, sourceSize.x, sourceSize.y);
- if (migrateForPreview ? backupTable.restoreToPreviewIfBackupExists()
- : backupTable.backupOrRestoreAsNeeded()) {
- dbChanged = true;
- srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
- }
-
- HashSet<String> validPackages = getValidPackages(context);
- // Hotseat.
- if (srcHotseatCount != idp.numDatabaseHotseatIcons
- && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
- migrateForPreview, srcHotseatCount,
- idp.numDatabaseHotseatIcons).migrateHotseat()) {
- dbChanged = true;
- }
-
- // Grid size
- Point targetSize = new Point(idp.numColumns, idp.numRows);
- if (new MultiStepMigrationTask(validPackages, context, transaction.getDb(),
- migrateForPreview).migrate(sourceSize, targetSize)) {
- dbChanged = true;
- }
-
- if (dbChanged) {
- // Make sure we haven't removed everything.
- final Cursor c = context.getContentResolver().query(
- migrateForPreview ? Favorites.PREVIEW_CONTENT_URI : Favorites.CONTENT_URI,
- null, null, null, null);
- boolean hasData = c.moveToNext();
- c.close();
- if (!hasData) {
- throw new Exception("Removed every thing during grid resize");
- }
- }
-
- transaction.commit();
- if (!migrateForPreview) {
- Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
- }
- return true;
- } catch (Exception e) {
- Log.e(TAG, "Error during preview grid migration", e);
-
- return false;
- } finally {
- Log.v(TAG, "Preview workspace migration completed in "
- + (SystemClock.elapsedRealtime() - migrationStartTime));
-
- if (!migrateForPreview) {
- // Save current configuration, so that the migration does not run again.
- prefs.edit()
- .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
- .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
- .apply();
- }
- }
- }
-
- protected static HashSet<String> getValidPackages(Context context) {
- // Initialize list of valid packages. This contain all the packages which are already on
- // the device and packages which are being installed. Any item which doesn't belong to
- // this set is removed.
- // Since the loader removes such items anyway, removing these items here doesn't cause
- // any extra data loss and gives us more free space on the grid for better migration.
- HashSet<String> validPackages = new HashSet<>();
- for (PackageInfo info : context.getPackageManager()
- .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
- validPackages.add(info.packageName);
- }
- InstallSessionHelper.INSTANCE.get(context)
- .getActiveSessions().keySet()
- .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
- return validPackages;
- }
-
- /**
- * Removes any broken item from the hotseat.
- *
- * @return a map with occupied hotseat position set to non-null value.
- */
- public static IntSparseArrayMap<Object> removeBrokenHotseatItems(Context context)
- throws Exception {
- try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
- context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
- .getBinder(Settings.EXTRA_VALUE)) {
- GridSizeMigrationTask task = new GridSizeMigrationTask(
- context, transaction.getDb(), getValidPackages(context),
- false /* usePreviewTable */, Integer.MAX_VALUE, Integer.MAX_VALUE);
-
- // Load all the valid entries
- ArrayList<DbEntry> items = task.loadHotseatEntries();
- // Delete any entry marked for deletion by above load.
- task.applyOperations();
- IntSparseArrayMap<Object> positions = new IntSparseArrayMap<>();
- for (DbEntry item : items) {
- positions.put(item.screenId, item);
- }
- transaction.commit();
- return positions;
- }
- }
-
- /**
- * Task to run grid migration in multiple steps when the size difference is more than 1.
- */
- protected static class MultiStepMigrationTask {
- private final HashSet<String> mValidPackages;
- private final Context mContext;
- private final SQLiteDatabase mDb;
- private final boolean mUsePreviewTable;
-
- public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
- SQLiteDatabase db, boolean usePreviewTable) {
- mValidPackages = validPackages;
- mContext = context;
- mDb = db;
- mUsePreviewTable = usePreviewTable;
- }
-
- public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
- boolean dbChanged = false;
- if (!targetSize.equals(sourceSize)) {
- if (sourceSize.x < targetSize.x) {
- // Source is smaller that target, just expand the grid without actual migration.
- sourceSize.x = targetSize.x;
- }
- if (sourceSize.y < targetSize.y) {
- // Source is smaller that target, just expand the grid without actual migration.
- sourceSize.y = targetSize.y;
- }
-
- // Migrate the workspace grid, such that the points differ by max 1 in x and y
- // each on every step.
- while (!targetSize.equals(sourceSize)) {
- // Get the next size, such that the points differ by max 1 in x and y each
- Point nextSize = new Point(sourceSize);
- if (targetSize.x < nextSize.x) {
- nextSize.x--;
- }
- if (targetSize.y < nextSize.y) {
- nextSize.y--;
- }
- if (runStepTask(sourceSize, nextSize)) {
- dbChanged = true;
- }
- sourceSize.set(nextSize.x, nextSize.y);
- }
- }
- return dbChanged;
- }
-
- protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
- return new GridSizeMigrationTask(mContext, mDb, mValidPackages, mUsePreviewTable,
- sourceSize, nextSize).migrateWorkspace();
- }
- }
-}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 43f9be5..1378a1a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -17,7 +17,6 @@
package com.android.launcher3.model;
import static com.android.launcher3.WorkspaceLayoutManager.LEFT_PANEL_ID;
-import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -80,7 +79,6 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -334,16 +332,7 @@
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false;
- try {
- ImportDataTask.performImportIfPossible(context);
- } catch (Exception e) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get()
- ? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context)
- : !GridSizeMigrationTask.migrateGridIfNeeded(context))) {
+ if (!GridSizeMigrationTaskV2.migrateGridIfNeeded(context)) {
// Migration failed. Clear workspace.
clearDb = true;
}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
deleted file mode 100644
index c9af2fe..0000000
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * Copyright (C) 2016 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.provider;
-
-import static com.android.launcher3.Utilities.getDevicePrefs;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Process;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.SparseBooleanArray;
-
-import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
-import com.android.launcher3.DefaultLayoutParser;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSparseArrayMap;
-import com.android.launcher3.util.PackageManagerHelper;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.HashSet;
-
-/**
- * Utility class to import data from another Launcher which is based on Launcher3 schema.
- */
-public class ImportDataTask {
-
- public static final String KEY_DATA_IMPORT_SRC_PKG = "data_import_src_pkg";
- public static final String KEY_DATA_IMPORT_SRC_AUTHORITY = "data_import_src_authority";
-
- private static final String TAG = "ImportDataTask";
- private static final int MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION = 6;
- // Insert items progressively to avoid OOM exception when loading icons.
- private static final int BATCH_INSERT_SIZE = 15;
-
- private final Context mContext;
-
- private final Uri mOtherFavoritesUri;
-
- private int mHotseatSize;
- private int mMaxGridSizeX;
- private int mMaxGridSizeY;
-
- private ImportDataTask(Context context, String sourceAuthority) {
- mContext = context;
- mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME);
- }
-
- public boolean importWorkspace() throws Exception {
- FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
-
- mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0;
- importWorkspaceItems();
- GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
-
- // Create empty DB flag.
- LauncherSettings.Settings.call(mContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
- return true;
- }
-
- /**
- * 1) Imports all the workspace entries from the source provider.
- * 2) For home screen entries, maps the screen id based on {@param screenIdMap}
- * 3) In the end fills any holes in hotseat with items from default hotseat layout.
- */
- private void importWorkspaceItems() throws Exception {
- String profileId = Long.toString(UserCache.INSTANCE.get(mContext)
- .getSerialNumberForUser(Process.myUserHandle()));
-
- boolean createEmptyRowOnFirstScreen;
- if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
- try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
- // get items on the first row of the first screen (min screen id)
- "profileId = ? AND container = -100 AND cellY = 0 AND screen = " +
- "(SELECT MIN(screen) FROM favorites WHERE container = -100)",
- new String[]{profileId},
- null)) {
- // First row of first screen is not empty
- createEmptyRowOnFirstScreen = c.moveToNext();
- }
- } else {
- createEmptyRowOnFirstScreen = false;
- }
-
- ArrayList<ContentProviderOperation> insertOperations = new ArrayList<>(BATCH_INSERT_SIZE);
-
- // Set of package names present in hotseat
- final HashSet<String> hotseatTargetApps = new HashSet<>();
- int maxId = 0;
-
- // Number of imported items on workspace and hotseat
- int totalItemsOnWorkspace = 0;
-
- try (Cursor c = mContext.getContentResolver()
- .query(mOtherFavoritesUri, null,
- // Only migrate the primary user
- Favorites.PROFILE_ID + " = ?", new String[]{profileId},
- // Get the items sorted by container, so that the folders are loaded
- // before the corresponding items.
- Favorites.CONTAINER + " , " + Favorites.SCREEN)) {
-
- // various columns we expect to exist.
- final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
- final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
- final int titleIndex = c.getColumnIndexOrThrow(Favorites.TITLE);
- final int containerIndex = c.getColumnIndexOrThrow(Favorites.CONTAINER);
- final int itemTypeIndex = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
- final int widgetProviderIndex = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
- final int screenIndex = c.getColumnIndexOrThrow(Favorites.SCREEN);
- final int cellXIndex = c.getColumnIndexOrThrow(Favorites.CELLX);
- final int cellYIndex = c.getColumnIndexOrThrow(Favorites.CELLY);
- final int spanXIndex = c.getColumnIndexOrThrow(Favorites.SPANX);
- final int spanYIndex = c.getColumnIndexOrThrow(Favorites.SPANY);
- final int rankIndex = c.getColumnIndexOrThrow(Favorites.RANK);
- final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
- final int iconPackageIndex = c.getColumnIndexOrThrow(Favorites.ICON_PACKAGE);
- final int iconResourceIndex = c.getColumnIndexOrThrow(Favorites.ICON_RESOURCE);
-
- SparseBooleanArray mValidFolders = new SparseBooleanArray();
- ContentValues values = new ContentValues();
-
- Integer firstScreenId = null;
- while (c.moveToNext()) {
- values.clear();
- int id = c.getInt(idIndex);
- maxId = Math.max(maxId, id);
- int type = c.getInt(itemTypeIndex);
- int container = c.getInt(containerIndex);
-
- int screen = c.getInt(screenIndex);
-
- int cellX = c.getInt(cellXIndex);
- int cellY = c.getInt(cellYIndex);
- int spanX = c.getInt(spanXIndex);
- int spanY = c.getInt(spanYIndex);
-
- switch (container) {
- case Favorites.CONTAINER_DESKTOP: {
- if (screen < Workspace.FIRST_SCREEN_ID) {
- FileLog.d(TAG, String.format(
- "Skipping item %d, type %d not on a valid screen %d",
- id, type, screen));
- continue;
- }
- if (firstScreenId == null) {
- firstScreenId = screen;
- }
- // Reset the screen to 0-index value
- if (createEmptyRowOnFirstScreen && firstScreenId.equals(screen)) {
- // Shift items by 1.
- cellY++;
- // Change the screen id to first screen
- screen = Workspace.FIRST_SCREEN_ID;
- }
-
- mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX);
- mMaxGridSizeY = Math.max(mMaxGridSizeY, cellY + spanY);
- break;
- }
- case Favorites.CONTAINER_HOTSEAT: {
- mHotseatSize = Math.max(mHotseatSize, screen + 1);
- break;
- }
- default:
- if (!mValidFolders.get(container)) {
- FileLog.d(TAG, String.format("Skipping item %d, type %d not in a valid folder %d", id, type, container));
- continue;
- }
- }
-
- Intent intent = null;
- switch (type) {
- case Favorites.ITEM_TYPE_FOLDER: {
- mValidFolders.put(id, true);
- // Use a empty intent to indicate a folder.
- intent = new Intent();
- break;
- }
- case Favorites.ITEM_TYPE_APPWIDGET: {
- values.put(Favorites.RESTORED,
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
- LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
- values.put(Favorites.APPWIDGET_PROVIDER, c.getString(widgetProviderIndex));
- break;
- }
- case Favorites.ITEM_TYPE_SHORTCUT:
- case Favorites.ITEM_TYPE_APPLICATION: {
- intent = Intent.parseUri(c.getString(intentIndex), 0);
- if (PackageManagerHelper.isLauncherAppTarget(intent)) {
- type = Favorites.ITEM_TYPE_APPLICATION;
- } else {
- values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
- values.put(Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
- }
- values.put(Favorites.ICON, c.getBlob(iconIndex));
- values.put(Favorites.INTENT, intent.toUri(0));
- values.put(Favorites.RANK, c.getInt(rankIndex));
-
- values.put(Favorites.RESTORED, 1);
- break;
- }
- default:
- FileLog.d(TAG, String.format("Skipping item %d, not a valid type %d", id, type));
- continue;
- }
-
- if (container == Favorites.CONTAINER_HOTSEAT) {
- if (intent == null) {
- FileLog.d(TAG, String.format("Skipping item %d, null intent on hotseat", id));
- continue;
- }
- if (intent.getComponent() != null) {
- intent.setPackage(intent.getComponent().getPackageName());
- }
- hotseatTargetApps.add(getPackage(intent));
- }
-
- values.put(Favorites._ID, id);
- values.put(Favorites.ITEM_TYPE, type);
- values.put(Favorites.CONTAINER, container);
- values.put(Favorites.SCREEN, screen);
- values.put(Favorites.CELLX, cellX);
- values.put(Favorites.CELLY, cellY);
- values.put(Favorites.SPANX, spanX);
- values.put(Favorites.SPANY, spanY);
- values.put(Favorites.TITLE, c.getString(titleIndex));
- insertOperations.add(ContentProviderOperation
- .newInsert(Favorites.CONTENT_URI).withValues(values).build());
- if (container < 0) {
- totalItemsOnWorkspace++;
- }
-
- if (insertOperations.size() >= BATCH_INSERT_SIZE) {
- mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
- insertOperations);
- insertOperations.clear();
- }
- }
- }
- FileLog.d(TAG, totalItemsOnWorkspace + " items imported from external source");
- if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) {
- throw new Exception("Insufficient data");
- }
- if (!insertOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
- insertOperations);
- insertOperations.clear();
- }
-
- IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask
- .removeBrokenHotseatItems(mContext);
- int myHotseatCount = LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons;
- if (hotseatItems.size() < myHotseatCount) {
- // Insufficient hotseat items. Add a few more.
- HotseatParserCallback parserCallback = new HotseatParserCallback(
- hotseatTargetApps, hotseatItems, insertOperations, maxId + 1, myHotseatCount);
- new HotseatLayoutParser(mContext,
- parserCallback).loadLayout(null, new IntArray());
- mHotseatSize = hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
-
- if (!insertOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
- insertOperations);
- }
- }
- }
-
- private static String getPackage(Intent intent) {
- return intent.getComponent() != null ? intent.getComponent().getPackageName()
- : intent.getPackage();
- }
-
- /**
- * Performs data import if possible.
- * @return true on successful data import, false if it was not available
- * @throws Exception if the import failed
- */
- public static boolean performImportIfPossible(Context context) throws Exception {
- SharedPreferences devicePrefs = getDevicePrefs(context);
- String sourcePackage = devicePrefs.getString(KEY_DATA_IMPORT_SRC_PKG, "");
- String sourceAuthority = devicePrefs.getString(KEY_DATA_IMPORT_SRC_AUTHORITY, "");
-
- if (TextUtils.isEmpty(sourcePackage) || TextUtils.isEmpty(sourceAuthority)) {
- return false;
- }
-
- // Synchronously clear the migration flags. This ensures that we do not try migration
- // again and thus prevents potential crash loops due to migration failure.
- devicePrefs.edit().remove(KEY_DATA_IMPORT_SRC_PKG).remove(KEY_DATA_IMPORT_SRC_AUTHORITY).commit();
-
- if (!Settings.call(context.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
- .getBoolean(Settings.EXTRA_VALUE, false)) {
- // Only migration if a new DB was created.
- return false;
- }
-
- for (ProviderInfo info : context.getPackageManager().queryContentProviders(
- null, context.getApplicationInfo().uid, 0)) {
-
- if (sourcePackage.equals(info.packageName)) {
- if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- // Only migrate if the source launcher is also on system image.
- return false;
- }
-
- // Wait until we found a provider with matching authority.
- if (sourceAuthority.equals(info.authority)) {
- if (TextUtils.isEmpty(info.readPermission) ||
- context.checkPermission(info.readPermission, Process.myPid(),
- Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
- // All checks passed, run the import task.
- return new ImportDataTask(context, sourceAuthority).importWorkspace();
- }
- }
- }
- }
- return false;
- }
-
- /**
- * Extension of {@link DefaultLayoutParser} which only allows icons and shortcuts.
- */
- private static class HotseatLayoutParser extends DefaultLayoutParser {
- public HotseatLayoutParser(Context context, LayoutParserCallback callback) {
- super(context, null, callback, context.getResources(),
- LauncherAppState.getIDP(context).defaultLayoutId);
- }
-
- @Override
- protected ArrayMap<String, TagParser> getLayoutElementsMap() {
- // Only allow shortcut parsers
- ArrayMap<String, TagParser> parsers = new ArrayMap<>();
- parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
- parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
- parsers.put(TAG_RESOLVE, new ResolveParser());
- return parsers;
- }
- }
-
- /**
- * {@link LayoutParserCallback} which adds items in empty hotseat spots.
- */
- private static class HotseatParserCallback implements LayoutParserCallback {
- private final HashSet<String> mExistingApps;
- private final IntSparseArrayMap<Object> mExistingItems;
- private final ArrayList<ContentProviderOperation> mOutOps;
- private final int mRequiredSize;
- private int mStartItemId;
-
- HotseatParserCallback(
- HashSet<String> existingApps, IntSparseArrayMap<Object> existingItems,
- ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
- mExistingApps = existingApps;
- mExistingItems = existingItems;
- mOutOps = outOps;
- mRequiredSize = requiredSize;
- mStartItemId = startItemId;
- }
-
- @Override
- public int generateNewItemId() {
- return mStartItemId++;
- }
-
- @Override
- public int insertAndCheck(SQLiteDatabase db, ContentValues values) {
- if (mExistingItems.size() >= mRequiredSize) {
- // No need to add more items.
- return 0;
- }
- if (!Integer.valueOf(Favorites.CONTAINER_HOTSEAT)
- .equals(values.getAsInteger(Favorites.CONTAINER))) {
- // Ignore items which are not for hotseat.
- return 0;
- }
-
- Intent intent;
- try {
- intent = Intent.parseUri(values.getAsString(Favorites.INTENT), 0);
- } catch (URISyntaxException e) {
- return 0;
- }
- String pkg = getPackage(intent);
- if (pkg == null || mExistingApps.contains(pkg)) {
- // The item does not target an app or is already in hotseat.
- return 0;
- }
- mExistingApps.add(pkg);
-
- // find next vacant spot.
- int screen = 0;
- while (mExistingItems.get(screen) != null) {
- screen++;
- }
- mExistingItems.put(screen, intent);
- values.put(Favorites.SCREEN, screen);
- mOutOps.add(ContentProviderOperation.newInsert(Favorites.CONTENT_URI).withValues(values).build());
- return 0;
- }
- }
-}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 7e05a5a..6855bb1 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -16,84 +16,21 @@
package com.android.launcher3.provider;
-import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.Process;
-import android.util.Log;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.IntArray;
-import java.util.Locale;
-
/**
* A set of utility methods for Launcher DB used for DB updates and migration.
*/
public class LauncherDbUtils {
- private static final String TAG = "LauncherDbUtils";
-
- /**
- * Makes the first screen as screen 0 (if screen 0 already exists,
- * renames it to some other number).
- * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear
- * the first row. The items in the first screen are moved and resized but the carry-forward
- * items are simply deleted.
- */
- public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
- try (SQLiteTransaction t = new SQLiteTransaction(db)) {
- // Get the first screen
- final int firstScreenId;
- try (Cursor c = db.rawQuery(String.format(Locale.ENGLISH,
- "SELECT MIN(%1$s) from %2$s where %3$s = %4$d",
- Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
- Favorites.CONTAINER_DESKTOP), null)) {
-
- if (!c.moveToNext()) {
- // No update needed
- t.commit();
- return true;
- }
-
- firstScreenId = c.getInt(0);
- }
-
- if (firstScreenId != 0) {
- // Rename the first screen to 0.
- renameScreen(db, firstScreenId, 0);
- }
-
- // Check if the first row is empty
- if (DatabaseUtils.queryNumEntries(db, Favorites.TABLE_NAME,
- "container = -100 and screen = 0 and cellY = 0") == 0) {
- // First row is empty, no need to migrate.
- t.commit();
- return true;
- }
-
- new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db)
- .migrateScreen0();
- t.commit();
- return true;
- } catch (Exception e) {
- Log.e(TAG, "Failed to update workspace size", e);
- return false;
- }
- }
-
- private static void renameScreen(SQLiteDatabase db, int oldScreen, int newScreen) {
- String[] whereParams = new String[] { Integer.toString(oldScreen) };
- ContentValues values = new ContentValues();
- values.put(Favorites.SCREEN, newScreen);
- db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
- }
-
public static IntArray queryIntArray(SQLiteDatabase db, String tableName, String columnName,
String selection, String groupBy, String orderBy) {
IntArray out = new IntArray();
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
deleted file mode 100644
index c0111b9..0000000
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016 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.provider;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.util.IntSparseArrayMap;
-
-import java.util.ArrayList;
-
-/**
- * An extension of {@link GridSizeMigrationTask} which migrates only one screen and
- * deletes all carry-forward items.
- */
-public class LossyScreenMigrationTask extends GridSizeMigrationTask {
-
- private final IntSparseArrayMap<DbEntry> mOriginalItems;
- private final IntSparseArrayMap<DbEntry> mUpdates;
-
- protected LossyScreenMigrationTask(
- Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
- // Decrease the rows count by 1
- super(context, db, getValidPackages(context), false /* usePreviewTable */,
- new Point(idp.numColumns, idp.numRows + 1),
- new Point(idp.numColumns, idp.numRows));
-
- mOriginalItems = new IntSparseArrayMap<>();
- mUpdates = new IntSparseArrayMap<>();
- }
-
- @Override
- protected void update(DbEntry item) {
- mUpdates.put(item.id, item.copy());
- }
-
- @Override
- protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
- ArrayList<DbEntry> result = super.loadWorkspaceEntries(screen);
- for (DbEntry entry : result) {
- mOriginalItems.put(entry.id, entry.copy());
-
- // Shift all items by 1 in y direction and mark them for update.
- entry.cellY++;
- mUpdates.put(entry.id, entry.copy());
- }
-
- return result;
- }
-
- public void migrateScreen0() {
- migrateScreen(Workspace.FIRST_SCREEN_ID);
-
- ContentValues tempValues = new ContentValues();
- for (DbEntry update : mUpdates) {
- DbEntry org = mOriginalItems.get(update.id);
-
- if (org.cellX != update.cellX || org.cellY != update.cellY
- || org.spanX != update.spanX || org.spanY != update.spanY) {
- tempValues.clear();
- update.addToContentValues(tempValues);
- mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?",
- new String[] {Integer.toString(update.id)});
- }
- }
-
- // Delete any carry over items as we are only migration a single screen.
- for (DbEntry entry : mCarryOver) {
- mEntryToRemove.add(entry.id);
- }
-
- if (!mEntryToRemove.isEmpty()) {
- mDb.delete(Favorites.TABLE_NAME,
- Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), null);
- }
- }
-}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 944a41f..5106992 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -102,8 +102,11 @@
return getUIProperty(Bundle::putParcelable, a -> {
WindowInsets insets = a.getWindow()
.getDecorView().getRootWindowInsets();
- return Insets.max(
- insets.getSystemGestureInsets(), insets.getSystemWindowInsets());
+ return Insets.subtract(
+ Insets.max(
+ insets.getSystemGestureInsets(),
+ insets.getSystemWindowInsets()),
+ Insets.of(0, 0, 0, mDeviceProfile.nonOverlappingTaskbarInset));
}, this::getCurrentActivity);
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 65bec25..75c0d03 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -112,4 +112,5 @@
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String WORK_PROFILE_REMOVED = "b/159671700";
public static final String FALLBACK_ACTIVITY_NO_SET = "b/181019015";
+ public static final String THIRD_PARTY_LAUNCHER_NOT_SET = "b/187080582";
}
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 4fa658e..20d2ad3 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -41,7 +41,6 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.views.OptionsPopupView;
/**
* Helper class to handle touch on empty space in workspace and show options popup on long press
@@ -175,7 +174,7 @@
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
- OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
+ mLauncher.showDefaultOptions(mTouchDownPoint.x, mTouchDownPoint.y);
} else {
cancelLongPress();
}
diff --git a/src/com/android/launcher3/util/IntSet.java b/src/com/android/launcher3/util/IntSet.java
index e5b4f59..4fd06fe 100644
--- a/src/com/android/launcher3/util/IntSet.java
+++ b/src/com/android/launcher3/util/IntSet.java
@@ -37,6 +37,15 @@
}
/**
+ * Appends the specified IntSet's values to the set if they does not exist, then returns the
+ * original set that now also contains the new values.
+ */
+ public IntSet addAll(IntSet other) {
+ other.forEach(this::add);
+ return this;
+ }
+
+ /**
* Removes the specified value from the set if it exist.
*/
public void remove(int value) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index ecdd206..257b18f 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -176,16 +176,6 @@
return launcher.findViewById(R.id.popup_container);
}
- public static void showDefaultOptions(Launcher launcher, float x, float y) {
- float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
- if (x < 0 || y < 0) {
- x = launcher.getDragLayer().getWidth() / 2;
- y = launcher.getDragLayer().getHeight() / 2;
- }
- RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
- show(launcher, target, getOptions(launcher), false);
- }
-
/**
* Returns the list of supported actions
*/
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index e36ea90..be83f9a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -402,7 +402,10 @@
private boolean updateMaxSpansPerRow() {
if (getMeasuredWidth() == 0) return false;
- int maxHorizontalSpans = computeMaxHorizontalSpans(mContent,
+ View content = mHasWorkProfile
+ ? mViewPager
+ : mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+ int maxHorizontalSpans = computeMaxHorizontalSpans(content,
mWidgetSheetContentHorizontalPadding);
if (mMaxSpansPerRow != maxHorizontalSpans) {
mMaxSpansPerRow = maxHorizontalSpans;
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index bf4eba0..dcb6dc1 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -153,6 +153,8 @@
public static String dumpHprofData() {
String result;
if (sDumpWasGenerated) {
+ Log.d("b/195319692", "dump has already been generated by another test",
+ new Exception());
result = "dump has already been generated by another test";
} else {
try {
@@ -167,6 +169,7 @@
"am dumpheap " + device.getLauncherPackageName() + " " + fileName);
}
sDumpWasGenerated = true;
+ Log.d("b/195319692", "sDumpWasGenerated := true", new Exception());
result = "memory dump filename: " + fileName;
} catch (Throwable e) {
Log.e(TAG, "dumpHprofData failed", e);
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index dad4f2b..2e8048a 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -27,6 +27,7 @@
import com.android.launcher3.tapl.Widget;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -80,6 +81,7 @@
*/
@Test
@PortraitLandscape
+ @ScreenRecord //b/195263971
public void testDragCustomShortcut() throws Throwable {
clearHomescreen();
mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 641e53a..a5694fc 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -87,7 +87,6 @@
}
@Test
- @ScreenRecord //b/192005114
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
Intent command = RequestPinItemActivity.getCommandIntent(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 93c921e..49934f7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -728,18 +728,23 @@
// otherwise waitForIdle may return immediately in case when there was a big enough
// pause in accessibility events prior to pressing Home.
final String action;
- final boolean launcherWasVisible = isLauncherVisible();
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
checkForAnomaly();
final Point displaySize = getRealDisplaySize();
+ boolean gestureStartFromLauncher = isTablet()
+ ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
+ : isLauncherVisible();
+ GestureScope gestureScope = gestureStartFromLauncher
+ ? GestureScope.INSIDE_TO_OUTSIDE
+ : GestureScope.OUTSIDE_WITH_PILFER;
if (hasLauncherObject(CONTEXT_MENU_RES_ID)) {
linearGesture(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
- false, GestureScope.INSIDE_TO_OUTSIDE);
+ false, gestureScope);
try (LauncherInstrumentation.Closable c1 = addContextLayer(
"Swiped up from context menu to home")) {
waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
@@ -760,9 +765,7 @@
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL,
- launcherWasVisible || isTablet()
- ? GestureScope.INSIDE_TO_OUTSIDE
- : GestureScope.OUTSIDE_WITH_PILFER);
+ gestureScope);
}
} else {
log("Hierarchy before clicking home:");
@@ -1115,9 +1118,9 @@
"swiping");
}
- private int getBottomGestureSize() {
- return ResourceUtils.getNavbarSize(
- ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
+ int getBottomGestureSize() {
+ return Math.max(getTargetInsets().bottom, ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources())) + 1;
}
int getBottomGestureMarginInContainer(UiObject2 container) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 2acf7b4..db2e250 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -34,7 +34,6 @@
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
-import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
import java.util.regex.Pattern;
@@ -72,8 +71,7 @@
mLauncher.addContextLayer("want to switch from workspace to all apps")) {
verifyActiveContainer();
final int deviceHeight = mLauncher.getDevice().getDisplayHeight();
- final int bottomGestureMargin = ResourceUtils.getNavbarSize(
- ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources());
+ final int bottomGestureMargin = mLauncher.getBottomGestureSize();
final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
final int swipeHeight = mLauncher.getTestInfo(