Merge "Import translations. DO NOT MERGE ANYWHERE" into udc-dev
diff --git a/Android.bp b/Android.bp
index e730c9d..a7edf2a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -79,7 +79,7 @@
"androidx.test.uiautomator_uiautomator",
"androidx.preference_preference",
"SystemUISharedLib",
- "SystemUIAnimationLib",
+ "animationlib",
"launcher-testing-shared",
],
srcs: [
@@ -243,7 +243,7 @@
"lottie",
"SystemUISharedLib",
"SystemUI-statsd",
- "SystemUIAnimationLib",
+ "animationlib",
],
manifest: "quickstep/AndroidManifest.xml",
min_sdk_version: "current",
@@ -305,7 +305,7 @@
"SystemUISharedLib",
"Launcher3CommonDepsLib",
"QuickstepResLib",
- "SystemUIAnimationLib",
+ "animationlib",
],
manifest: "quickstep/AndroidManifest.xml",
platform_apis: true,
diff --git a/res/drawable/taskbar_divider_bg.xml b/quickstep/res/drawable/taskbar_divider_bg.xml
similarity index 85%
rename from res/drawable/taskbar_divider_bg.xml
rename to quickstep/res/drawable/taskbar_divider_bg.xml
index a8c2ae7..52e230d 100644
--- a/res/drawable/taskbar_divider_bg.xml
+++ b/quickstep/res/drawable/taskbar_divider_bg.xml
@@ -14,8 +14,7 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle" >
- <solid android:color="?androidprv:attr/colorSurfaceVariant"/>
+ <solid android:color="@color/taskbar_divider_background"/>
<corners android:radius="1dp" />
</shape>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
index 7dda73f..7f65e41 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltip.kt
@@ -26,15 +26,15 @@
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
+import com.android.app.animation.Interpolators.STANDARD
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.R
import com.android.launcher3.anim.AnimatorListeners
import com.android.launcher3.popup.RoundedArrowDrawable
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
-import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
-import com.android.systemui.animation.Interpolators.STANDARD
private const val ENTER_DURATION_MS = 300L
private const val EXIT_DURATION_MS = 150L
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 59b5e74..ed78e2d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -15,13 +15,13 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_MASK;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index f099e06..bc1a2c8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -515,6 +515,14 @@
}
/**
+ * Returns the taskbar divider in the taskbar.
+ */
+ @Nullable
+ public View getTaskbarDividerView() {
+ return mTaskbarDivider;
+ }
+
+ /**
* Returns the QSB in the taskbar.
*/
public View getQsb() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index ec3d1bc..a7e2daa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -459,12 +459,14 @@
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView();
+ boolean isTaskbarDividerView = child == mTaskbarView.getTaskbarDividerView();
if (!mIsHotseatIconOnTopWhenAligned) {
// When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController
// plays iconAlignment to 1 really fast, therefore moving the fading towards the end
// to avoid icons disappearing rather than fading out visually.
setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
- } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())) {
+ } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())
+ || (isTaskbarDividerView && FeatureFlags.ENABLE_TASKBAR_PINNING.get())) {
if (!isToHome
&& mIsHotseatIconOnTopWhenAligned
&& mControllers.taskbarStashController.isStashed()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 549c50b..65f449c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -1063,7 +1063,8 @@
}
@Override
- public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+ public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
+ int workspaceItemCount, boolean isBindSync) {
pendingTasks.add(() -> {
// This is added in pending task as we need to wait for views to be positioned
// correctly before registering them for the animation.
@@ -1073,7 +1074,7 @@
mLauncherUnfoldAnimationController.updateRegisteredViewsIfNeeded();
}
});
- super.onInitialBindComplete(boundPages, pendingTasks);
+ super.onInitialBindComplete(boundPages, pendingTasks, workspaceItemCount, isBindSync);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 36e78fb..39543b0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -95,7 +95,11 @@
i -> MAIN_EXECUTOR.execute(() ->
sHolders.forEach(h -> h.mAppWidgetRemovedCallback.accept(i))),
() -> MAIN_EXECUTOR.execute(() ->
- sHolders.forEach(h -> h.mProviderChangedListeners.forEach(
+ sHolders.forEach(h ->
+ // Listeners might remove themselves from the list during the
+ // iteration. Creating a copy of the list to avoid exceptions
+ // for concurrent modification.
+ new ArrayList<>(h.mProviderChangedListeners).forEach(
ProviderChangedListener::notifyWidgetProvidersChanged))),
UI_HELPER_EXECUTOR.getLooper());
if (!WidgetsModel.GO_DISABLE_WIDGETS) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
index 89aba90..e1ce9b1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
@@ -359,17 +359,6 @@
return true;
});
sandboxCategory.addPreference(launchOverviewTutorialPreference);
- Preference launchAssistantTutorialPreference = new Preference(context);
- launchAssistantTutorialPreference.setKey("launchAssistantTutorial");
- launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
- launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
- launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent
- .putExtra("use_tutorial_menu", false)
- .putExtra("tutorial_steps", new String[] {"ASSISTANT"}));
- return true;
- });
- sandboxCategory.addPreference(launchAssistantTutorialPreference);
Preference launchSandboxModeTutorialPreference = new Preference(context);
launchSandboxModeTutorialPreference.setKey("launchSandboxMode");
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index cd98e7a..64165b6 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -61,14 +61,6 @@
mTarget = activity.getDragLayer();
mTarget.getLocationOnScreen(mLocationOnScreen);
-
- // When Overview is launched via meta+tab or swipe up from an app,
- // the touch mode somehow is not changed to false by the Android framework.
- // The subsequent key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched to
- // focused views, while focus can only be requested in
- // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
- // here we launch overview with live tile.
- mActivity.getRootView().getViewRootImpl().touchModeChanged(false);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
deleted file mode 100644
index 40c600f..0000000
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 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.quickstep.interaction;
-
-import android.graphics.PointF;
-
-import com.android.launcher3.R;
-import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
-import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
-
-/** A {@link TutorialController} for the Assistant tutorial. */
-final class AssistantGestureTutorialController extends TutorialController {
-
- AssistantGestureTutorialController(AssistantGestureTutorialFragment fragment,
- TutorialType tutorialType) {
- super(fragment, tutorialType);
- }
-
- @Override
- public void onBackGestureAttempted(BackGestureResult result) {
- switch (mTutorialType) {
- case ASSISTANT:
- switch (result) {
- case BACK_COMPLETED_FROM_LEFT:
- case BACK_COMPLETED_FROM_RIGHT:
- case BACK_CANCELLED_FROM_LEFT:
- case BACK_CANCELLED_FROM_RIGHT:
- showFeedback(R.string.assistant_gesture_feedback_swipe_too_far_from_corner);
- break;
- }
- break;
- case ASSISTANT_COMPLETE:
- if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
- || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
- mTutorialFragment.close();
- }
- break;
- }
- }
-
-
- @Override
- public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
- switch (mTutorialType) {
- case ASSISTANT:
- switch (result) {
- case HOME_GESTURE_COMPLETED:
- case OVERVIEW_GESTURE_COMPLETED:
- case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
- case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
- case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
- case HOME_OR_OVERVIEW_CANCELLED:
- showFeedback(R.string.assistant_gesture_feedback_swipe_too_far_from_corner);
- break;
- case ASSISTANT_COMPLETED:
- showRippleEffect(null);
- showFeedback(R.string.assistant_gesture_tutorial_playground_subtitle);
- break;
- case ASSISTANT_NOT_STARTED_BAD_ANGLE:
- showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
- break;
- case ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT:
- showFeedback(R.string.assistant_gesture_feedback_swipe_not_long_enough);
- break;
- }
- break;
- case ASSISTANT_COMPLETE:
- if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
- mTutorialFragment.close();
- }
- break;
- }
- }
-
- @Override
- public void setAssistantProgress(float progress) {
- // TODO: Create an animation.
- }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
deleted file mode 100644
index 90a1c36..0000000
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2020 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.quickstep.interaction;
-
-import android.view.MotionEvent;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.logging.StatsLogManager;
-import com.android.quickstep.interaction.TutorialController.TutorialType;
-
-/** Shows the Home gesture interactive tutorial. */
-public class AssistantGestureTutorialFragment extends TutorialFragment {
-
- public AssistantGestureTutorialFragment(boolean fromTutorialMenu) {
- super(fromTutorialMenu);
- }
-
- @Override
- TutorialController createController(TutorialType type) {
- return new AssistantGestureTutorialController(this, type);
- }
-
- @Override
- Class<? extends TutorialController> getControllerClass() {
- return AssistantGestureTutorialController.class;
- }
-
- @Override
- public boolean onTouch(View view, MotionEvent motionEvent) {
- if (motionEvent.getAction() == MotionEvent.ACTION_DOWN && mTutorialController != null) {
- mTutorialController.setRippleHotspot(motionEvent.getX(), motionEvent.getY());
- }
- return super.onTouch(view, motionEvent);
- }
-
- @Override
- void logTutorialStepShown(@NonNull StatsLogManager statsLogManager) {
- // No-Op: tutorial step not currently shown to users
- }
-
- @Override
- void logTutorialStepCompleted(@NonNull StatsLogManager statsLogManager) {
- // No-Op: tutorial step not currently shown to users
- }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index e1f0924..ab76fb4 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -205,9 +205,6 @@
case HOME_GESTURE_COMPLETED:
case OVERVIEW_GESTURE_COMPLETED:
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
- case ASSISTANT_COMPLETED:
- case ASSISTANT_NOT_STARTED_BAD_ANGLE:
- case ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT:
default:
showFeedback(R.string.back_gesture_feedback_swipe_in_nav_bar);
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 57874d9..6cee690 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -15,11 +15,7 @@
*/
package com.android.quickstep.interaction;
-import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
-import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
-import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_GESTURE_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_NOT_STARTED_TOO_FAR_FROM_EDGE;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_CANCELLED;
@@ -27,24 +23,16 @@
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.RectF;
-import android.os.SystemClock;
-import android.view.Display;
-import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
-import android.view.ViewConfiguration;
import androidx.annotation.Nullable;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
@@ -52,46 +40,25 @@
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
-import com.android.systemui.shared.system.QuickStepContract;
-/** Utility class to handle Home and Assistant gestures. */
+/** Utility class to handle Home gesture. */
public class NavBarGestureHandler implements OnTouchListener,
TriggerSwipeUpTouchTracker.OnSwipeUpListener, MotionPauseDetector.OnMotionPauseListener {
private static final String LOG_TAG = "NavBarGestureHandler";
- private static final long RETRACT_GESTURE_ANIMATION_DURATION_MS = 300;
-
private final Context mContext;
private final Point mDisplaySize = new Point();
private final TriggerSwipeUpTouchTracker mSwipeUpTouchTracker;
private final int mBottomGestureHeight;
- private final GestureDetector mAssistantGestureDetector;
- private final int mAssistantAngleThreshold;
- private final RectF mAssistantLeftRegion = new RectF();
- private final RectF mAssistantRightRegion = new RectF();
- private final float mAssistantDragDistThreshold;
- private final float mAssistantFlingDistThreshold;
- private final long mAssistantTimeThreshold;
- private final float mAssistantSquaredSlop;
- private final PointF mAssistantStartDragPos = new PointF();
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private final MotionPauseDetector mMotionPauseDetector;
- private boolean mTouchCameFromAssistantCorner;
private boolean mTouchCameFromNavBar;
- private boolean mPassedAssistantSlop;
- private boolean mAssistantGestureActive;
- private boolean mLaunchedAssistant;
- private long mAssistantDragStartTime;
- private float mAssistantDistance;
- private float mAssistantTimeFraction;
- private float mAssistantLastProgress;
@Nullable
private NavBarGestureAttemptCallback mGestureCallback;
NavBarGestureHandler(Context context) {
mContext = context;
- final Display display = mContext.getDisplay();
DisplayController.Info displayInfo = DisplayController.INSTANCE.get(mContext).getInfo();
final int displayRotation = displayInfo.rotation;
Point currentSize = displayInfo.currentSize;
@@ -105,27 +72,6 @@
final Resources resources = context.getResources();
mBottomGestureHeight =
ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, resources);
- mAssistantDragDistThreshold =
- resources.getDimension(R.dimen.gestures_assistant_drag_threshold);
- mAssistantFlingDistThreshold =
- resources.getDimension(R.dimen.gestures_assistant_fling_threshold);
- mAssistantTimeThreshold =
- resources.getInteger(R.integer.assistant_gesture_min_time_threshold);
- mAssistantAngleThreshold =
- resources.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
-
- mAssistantGestureDetector = new GestureDetector(context, new AssistantGestureListener());
- int assistantWidth = resources.getDimensionPixelSize(R.dimen.gestures_assistant_width);
- final float assistantHeight = Math.max(mBottomGestureHeight,
- QuickStepContract.getWindowCornerRadius(context));
- mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mDisplaySize.y;
- mAssistantLeftRegion.top = mAssistantRightRegion.top = mDisplaySize.y - assistantHeight;
- mAssistantLeftRegion.left = 0;
- mAssistantLeftRegion.right = assistantWidth;
- mAssistantRightRegion.right = mDisplaySize.x;
- mAssistantRightRegion.left = mDisplaySize.x - assistantWidth;
- float slop = ViewConfiguration.get(context).getScaledTouchSlop();
- mAssistantSquaredSlop = slop * slop;
}
void registerNavBarGestureAttemptCallback(NavBarGestureAttemptCallback callback) {
@@ -138,7 +84,7 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
- if (mGestureCallback == null || mAssistantGestureActive) {
+ if (mGestureCallback == null) {
return;
}
if (mTouchCameFromNavBar) {
@@ -153,7 +99,7 @@
@Override
public void onSwipeUpCancelled() {
- if (mGestureCallback != null && !mAssistantGestureActive) {
+ if (mGestureCallback != null) {
mGestureCallback.onNavBarGestureAttempted(HOME_OR_OVERVIEW_CANCELLED, new PointF());
}
}
@@ -166,52 +112,16 @@
case MotionEvent.ACTION_DOWN:
mDownPos.set(event.getX(), event.getY());
mLastPos.set(mDownPos);
- mTouchCameFromAssistantCorner =
- mAssistantLeftRegion.contains(event.getX(), event.getY())
- || mAssistantRightRegion.contains(event.getX(), event.getY());
- mAssistantGestureActive = mTouchCameFromAssistantCorner;
- mTouchCameFromNavBar = !mTouchCameFromAssistantCorner
- && mDownPos.y >= mDisplaySize.y - mBottomGestureHeight;
+ mTouchCameFromNavBar = mDownPos.y >= mDisplaySize.y - mBottomGestureHeight;
if (!mTouchCameFromNavBar && mGestureCallback != null) {
mGestureCallback.setNavBarGestureProgress(null);
}
- mLaunchedAssistant = false;
mSwipeUpTouchTracker.init();
mMotionPauseDetector.clear();
mMotionPauseDetector.setOnMotionPauseListener(this);
break;
case MotionEvent.ACTION_MOVE:
mLastPos.set(event.getX(), event.getY());
- if (!mAssistantGestureActive) {
- break;
- }
-
- if (!mPassedAssistantSlop) {
- // Normal gesture, ensure we pass the slop before we start tracking the gesture
- if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
- > mAssistantSquaredSlop) {
-
- mPassedAssistantSlop = true;
- mAssistantStartDragPos.set(mLastPos.x, mLastPos.y);
- mAssistantDragStartTime = SystemClock.uptimeMillis();
-
- mAssistantGestureActive = isValidAssistantGestureAngle(
- mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y);
- if (!mAssistantGestureActive && mGestureCallback != null) {
- mGestureCallback.onNavBarGestureAttempted(
- ASSISTANT_NOT_STARTED_BAD_ANGLE, new PointF());
- }
- }
- } else {
- // Movement
- mAssistantDistance = (float) Math.hypot(mLastPos.x - mAssistantStartDragPos.x,
- mLastPos.y - mAssistantStartDragPos.y);
- if (mAssistantDistance >= 0) {
- final long diff = SystemClock.uptimeMillis() - mAssistantDragStartTime;
- mAssistantTimeFraction = Math.min(diff * 1f / mAssistantTimeThreshold, 1);
- updateAssistantProgress();
- }
- }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
@@ -222,35 +132,19 @@
intercepted = true;
break;
}
- if (mAssistantGestureActive && !mLaunchedAssistant && mGestureCallback != null) {
- mGestureCallback.onNavBarGestureAttempted(
- ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT, new PointF());
- ValueAnimator animator = ValueAnimator.ofFloat(mAssistantLastProgress, 0)
- .setDuration(RETRACT_GESTURE_ANIMATION_DURATION_MS);
- animator.addUpdateListener(valueAnimator -> {
- float progress = (float) valueAnimator.getAnimatedValue();
- mGestureCallback.setAssistantProgress(progress);
- });
- animator.setInterpolator(Interpolators.DEACCEL_2);
- animator.start();
- }
- mPassedAssistantSlop = false;
break;
}
if (mTouchCameFromNavBar && mGestureCallback != null) {
mGestureCallback.setNavBarGestureProgress(event.getY() - mDownPos.y);
}
mSwipeUpTouchTracker.onMotionEvent(event);
- mAssistantGestureDetector.onTouchEvent(event);
mMotionPauseDetector.addPosition(event);
mMotionPauseDetector.setDisallowPause(mLastPos.y >= mDisplaySize.y - mBottomGestureHeight);
return intercepted;
}
boolean onInterceptTouch(MotionEvent event) {
- return mAssistantLeftRegion.contains(event.getX(), event.getY())
- || mAssistantRightRegion.contains(event.getX(), event.getY())
- || event.getY() >= mDisplaySize.y - mBottomGestureHeight;
+ return event.getY() >= mDisplaySize.y - mBottomGestureHeight;
}
@Override
@@ -263,39 +157,6 @@
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
- /**
- * Determine if angle is larger than threshold for assistant detection
- */
- private boolean isValidAssistantGestureAngle(float deltaX, float deltaY) {
- float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
-
- // normalize so that angle is measured clockwise from horizontal in the bottom right corner
- // and counterclockwise from horizontal in the bottom left corner
- angle = angle > 90 ? 180 - angle : angle;
- return (angle > mAssistantAngleThreshold && angle < 90);
- }
-
- private void updateAssistantProgress() {
- if (!mLaunchedAssistant) {
- mAssistantLastProgress =
- Math.min(mAssistantDistance * 1f / mAssistantDragDistThreshold, 1)
- * mAssistantTimeFraction;
- if (mAssistantDistance >= mAssistantDragDistThreshold && mAssistantTimeFraction >= 1) {
- startAssistant(new PointF());
- } else if (mGestureCallback != null) {
- mGestureCallback.setAssistantProgress(mAssistantLastProgress);
- }
- }
- }
-
- private void startAssistant(PointF velocity) {
- if (mGestureCallback != null) {
- mGestureCallback.onNavBarGestureAttempted(ASSISTANT_COMPLETED, velocity);
- }
- VibratorWrapper.INSTANCE.get(mContext).vibrate(VibratorWrapper.EFFECT_CLICK);
- mLaunchedAssistant = true;
- }
-
enum NavBarGestureResult {
UNKNOWN,
HOME_GESTURE_COMPLETED,
@@ -304,9 +165,6 @@
OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE,
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, // Side swipe on nav bar.
HOME_OR_OVERVIEW_CANCELLED,
- ASSISTANT_COMPLETED,
- ASSISTANT_NOT_STARTED_BAD_ANGLE,
- ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT,
}
/** Callback to let the UI react to attempted nav bar gestures. */
@@ -319,27 +177,5 @@
/** Indicates how far a touch originating in the nav bar has moved from the nav bar. */
default void setNavBarGestureProgress(@Nullable Float displacement) {}
-
- /** Indicates the progress of an Assistant gesture. */
- default void setAssistantProgress(float progress) {}
- }
-
- private class AssistantGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if (!mLaunchedAssistant && mTouchCameFromAssistantCorner) {
- PointF velocity = new PointF(velocityX, velocityY);
- if (!isValidAssistantGestureAngle(velocityX, -velocityY)) {
- if (mGestureCallback != null) {
- mGestureCallback.onNavBarGestureAttempted(ASSISTANT_NOT_STARTED_BAD_ANGLE,
- velocity);
- }
- } else if (mAssistantDistance >= mAssistantFlingDistThreshold) {
- mAssistantLastProgress = 1;
- startAssistant(velocity);
- }
- }
- return true;
- }
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 160e431..dfbcf4d 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -111,6 +111,7 @@
case BACK_COMPLETED_FROM_RIGHT:
case BACK_CANCELLED_FROM_LEFT:
case BACK_CANCELLED_FROM_RIGHT:
+ case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
break;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
index 19b7933..f0bd4f9 100644
--- a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
@@ -49,10 +49,6 @@
@Override
public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
switch (result) {
- case ASSISTANT_COMPLETED:
- showRippleEffect(null);
- showFeedback(R.string.sandbox_mode_assistant_gesture_feedback_successful);
- break;
case HOME_GESTURE_COMPLETED:
animateFakeTaskViewHome(finalVelocity, () -> {
showFeedback(R.string.sandbox_mode_home_gesture_feedback_successful);
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 3faa7e4..25de605 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -117,9 +117,6 @@
case OVERVIEW_NAVIGATION:
case OVERVIEW_NAVIGATION_COMPLETE:
return new OverviewGestureTutorialFragment(fromTutorialMenu);
- case ASSISTANT:
- case ASSISTANT_COMPLETE:
- return new AssistantGestureTutorialFragment(fromTutorialMenu);
case SANDBOX_MODE:
return new SandboxModeTutorialFragment(fromTutorialMenu);
default:
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 2ae512a..e8cadab 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -23,7 +23,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
import org.junit.After;
@@ -61,10 +60,6 @@
}
@Test
- // TODO (b/270201357): When this test is proven stable, remove this TestStabilityRule and
- // introduce into presubmit as well.
- @TestStabilityRule.Stability(
- flavors = TestStabilityRule.LOCAL | TestStabilityRule.PLATFORM_POSTSUBMIT)
@PortraitLandscape
@TaskbarModeSwitch
public void testSplitAppFromHomeWithItself() throws Exception {
diff --git a/res/drawable/taskbar_divider_bg.xml b/res/color-night-v31/taskbar_divider_background.xml
similarity index 69%
copy from res/drawable/taskbar_divider_bg.xml
copy to res/color-night-v31/taskbar_divider_background.xml
index a8c2ae7..1981eec 100644
--- a/res/drawable/taskbar_divider_bg.xml
+++ b/res/color-night-v31/taskbar_divider_background.xml
@@ -13,9 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle" >
- <solid android:color="?androidprv:attr/colorSurfaceVariant"/>
- <corners android:radius="1dp" />
-</shape>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#797869" android:lStar="60" />
+</selector>
diff --git a/res/drawable/taskbar_divider_bg.xml b/res/color-v31/taskbar_divider_background.xml
similarity index 69%
copy from res/drawable/taskbar_divider_bg.xml
copy to res/color-v31/taskbar_divider_background.xml
index a8c2ae7..487e791 100644
--- a/res/drawable/taskbar_divider_bg.xml
+++ b/res/color-v31/taskbar_divider_background.xml
@@ -13,9 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle" >
- <solid android:color="?androidprv:attr/colorSurfaceVariant"/>
- <corners android:radius="1dp" />
-</shape>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#797869" android:lStar="50" />
+</selector>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index f041ffb..5fbd48c 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -16,8 +16,8 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.DatabaseHelper;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.UserCache;
@@ -52,7 +52,7 @@
* Updates the app widgets whose id has changed during the restore process.
*/
@WorkerThread
- public static void restoreAppWidgetIds(Context context, DatabaseHelper helper,
+ public static void restoreAppWidgetIds(Context context, ModelDbController controller,
int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
if (WidgetsModel.GO_DISABLE_WIDGETS) {
Log.e(TAG, "Skipping widget ID remap as widgets not supported");
@@ -92,12 +92,12 @@
final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
int result = new ContentWriter(context,
- new ContentWriter.CommitParams(helper, where, args))
+ new ContentWriter.CommitParams(controller, where, args))
.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
.put(LauncherSettings.Favorites.RESTORED, state)
.commit();
if (result == 0) {
- Cursor cursor = helper.getWritableDatabase().query(
+ Cursor cursor = controller.getDb().query(
Favorites.TABLE_NAME,
new String[] {Favorites.APPWIDGET_ID},
"appWidgetId=?", new String[] { oldWidgetId }, null, null, null);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 0988769..5163ede 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -85,6 +85,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
import java.util.Stack;
public class CellLayout extends ViewGroup {
@@ -891,7 +892,7 @@
*
* @param result Array of 2 ints to hold the x and y coordinate of the point
*/
- void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
+ public void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
cellToRect(cellX, cellY, spanX, spanY, mTempRect);
result[0] = mTempRect.centerX();
result[1] = mTempRect.centerY();
@@ -2340,7 +2341,16 @@
}
Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
Rect r1 = new Rect();
- for (View child: solution.map.keySet()) {
+ // The views need to be sorted so that the results are deterministic on the views positions
+ // and not by the views hash which is "random".
+ // The views are sorted twice, once for the X position and a second time for the Y position
+ // to ensure same order everytime.
+ Comparator comparator = Comparator.comparing(view ->
+ ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX())
+ .thenComparing(view ->
+ ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY());
+ List<View> views = solution.map.keySet().stream().sorted(comparator).toList();
+ for (View child : views) {
if (child == ignoreView) continue;
CellAndSpan c = solution.map.get(child);
CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4dfd918..86a5f9f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -56,6 +56,14 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC;
+import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC;
+import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.COLD;
+import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.COLD_DEVICE_REBOOTING;
+import static com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType.WARM;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
@@ -64,7 +72,6 @@
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import android.animation.Animator;
@@ -111,6 +118,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
@@ -157,6 +165,7 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StartupLatencyLogger;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ItemInstallQueue;
@@ -190,6 +199,7 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
@@ -293,6 +303,8 @@
public static final String ON_RESUME_EVT = "Launcher.onResume";
public static final String ON_NEW_INTENT_EVT = "Launcher.onNewIntent";
+ private static boolean sIsNewProcess = true;
+
private StateManager<LauncherState> mStateManager;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
@@ -404,12 +416,22 @@
private StringCache mStringCache;
private BaseSearchConfig mBaseSearchConfig;
-
+ private StartupLatencyLogger mStartupLatencyLogger;
private CellPosMapper mCellPosMapper = CellPosMapper.DEFAULT;
@Override
@TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
+ mStartupLatencyLogger = createStartupLatencyLogger(
+ sIsNewProcess
+ ? LockedUserState.get(this).isUserUnlockedAtLauncherStartup()
+ ? COLD
+ : COLD_DEVICE_REBOOTING
+ : WARM);
+ sIsNewProcess = false;
+ mStartupLatencyLogger
+ .logStart(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
+ .logStart(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
// Only use a hard-coded cookie since we only want to trace this once.
if (Utilities.ATLEAST_S) {
Trace.beginAsyncSection(
@@ -518,6 +540,7 @@
}
}
+ mStartupLatencyLogger.logWorkspaceLoadStartTime();
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
// If we are not binding synchronously, pause drawing until initial bind complete,
@@ -557,6 +580,17 @@
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
setTitle(R.string.home_screen);
+ mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
+ }
+
+ /**
+ * Create {@link StartupLatencyLogger} that only collects launcher startup latency metrics
+ * without sending them anywhere. Child class can override this method to create logger
+ * that overrides {@link StartupLatencyLogger#log()} to report those metrics.
+ */
+ protected StartupLatencyLogger createStartupLatencyLogger(
+ StatsLogManager.StatsLatencyLogger.LatencyType latencyType) {
+ return new StartupLatencyLogger(latencyType);
}
/**
@@ -1290,7 +1324,10 @@
* Finds all the views we need and configure them properly.
*/
protected void setupViews() {
+ mStartupLatencyLogger.logStart(LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION);
inflateRootView(R.layout.launcher);
+ mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION);
+
mDragLayer = findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
@@ -2695,7 +2732,8 @@
@Override
@TargetApi(Build.VERSION_CODES.S)
- public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+ public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
+ int workspaceItemCount, boolean isBindSync) {
mSynchronouslyBoundPages = boundPages;
mPagesToBindSynchronously = new IntSet();
@@ -2719,6 +2757,26 @@
Trace.endAsyncSection(DISPLAY_WORKSPACE_TRACE_METHOD_NAME,
DISPLAY_WORKSPACE_TRACE_COOKIE);
}
+ mStartupLatencyLogger
+ .logCardinality(workspaceItemCount)
+ .logEnd(isBindSync
+ ? LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+ : LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC);
+ // In the first rootview's onDraw after onInitialBindComplete(), log end of startup latency.
+ getRootView().getViewTreeObserver().addOnDrawListener(
+ new ViewTreeObserver.OnDrawListener() {
+
+ @Override
+ public void onDraw() {
+ mStartupLatencyLogger
+ .logEnd(LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION)
+ .log()
+ .reset();
+ MAIN_EXECUTOR.getHandler().postAtFrontOfQueue(
+ () -> getRootView().getViewTreeObserver()
+ .removeOnDrawListener(this));
+ }
+ });
}
/**
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 6798667d..d3e94e1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -417,7 +417,7 @@
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
- launcherBinder.bindWorkspace(bindAllCallbacks);
+ launcherBinder.bindWorkspace(bindAllCallbacks, /* isBindSync= */ true);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
launcherBinder.bindAllApps();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 0df4bd4..9abec50 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -263,18 +263,6 @@
getModelDbController().refreshHotseatRestoreTable();
return null;
}
- case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
- Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().updateCurrentOpenHelper(arg /* dbFile */));
- return result;
- }
- case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
- Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().prepareForPreview(arg /* dbFile */));
- return result;
- }
}
return null;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index b65e96b..7fda326 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -148,11 +148,6 @@
public static final String HYBRID_HOTSEAT_BACKUP_TABLE = "hotseat_restore_backup";
/**
- * Temporary table used specifically for grid migrations during wallpaper preview
- */
- public static final String PREVIEW_TABLE_NAME = "favorites_preview";
-
- /**
* Temporary table used specifically for multi-db grid migrations
*/
public static final String TMP_TABLE = "favorites_tmp";
@@ -164,18 +159,6 @@
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
- * The content:// style URL for "favorites_preview" table
- */
- public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
-
- /**
- * The content:// style URL for "favorites_tmp" table
- */
- public static final Uri TMP_CONTENT_URI = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + TMP_TABLE);
-
- /**
* The content:// style URL for a given row, identified by its id.
*
* @param id The row id.
@@ -376,10 +359,6 @@
public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table";
- public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
-
- public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
-
public static final String EXTRA_VALUE = "value";
public static final String EXTRA_DB_NAME = "db_name";
@@ -393,11 +372,8 @@
}
public static Bundle call(ContentResolver cr, String method, String arg) {
- return call(cr, method, arg, null /* extras */);
+ return cr.call(CONTENT_URI, method, arg, null);
}
- public static Bundle call(ContentResolver cr, String method, String arg, Bundle extras) {
- return cr.call(CONTENT_URI, method, arg, extras);
- }
}
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 372e9bf..8f0b8ec 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,6 +16,7 @@
package com.android.launcher3.graphics;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -52,6 +53,8 @@
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.GridSizeMigrationUtil;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
@@ -145,7 +148,9 @@
final String query = LauncherSettings.Favorites.ITEM_TYPE + " = "
+ LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
- try (Cursor c = context.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ ModelDbController mainController =
+ LauncherAppState.getInstance(mContext).getModel().getModelDbController();
+ try (Cursor c = mainController.query(TABLE_NAME,
new String[] {
LauncherSettings.Favorites.APPWIDGET_ID,
LauncherSettings.Favorites.SPANX,
@@ -190,8 +195,6 @@
@WorkerThread
private void loadModelData() {
- final boolean migrated = doGridMigrationIfNecessary();
-
final Context inflationContext;
if (mWallpaperColors != null) {
// Create a themed context, without affecting the main application context
@@ -209,8 +212,20 @@
Themes.getActivityThemeRes(mContext));
}
- if (migrated) {
+ if (GridSizeMigrationUtil.needsToMigrate(inflationContext, mIdp)) {
+ // Start the migration
PreviewContext previewContext = new PreviewContext(inflationContext, mIdp);
+ // Copy existing data to preview DB
+ LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
+ .getModel().getModelDbController().getDb(),
+ TABLE_NAME,
+ LauncherAppState.getInstance(previewContext)
+ .getModel().getModelDbController().getDb(),
+ TABLE_NAME,
+ mContext);
+ LauncherAppState.getInstance(previewContext)
+ .getModel().getModelDbController().clearEmptyDbFlag();
+
new LoaderTask(
LauncherAppState.getInstance(previewContext),
/* bgAllAppsList= */ null,
@@ -229,8 +244,7 @@
query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+ Workspace.SECOND_SCREEN_ID;
}
- loadWorkspaceForPreviewSurfaceRenderer(new ArrayList<>(),
- LauncherSettings.Favorites.PREVIEW_CONTENT_URI, query);
+ loadWorkspace(new ArrayList<>(), query, null);
final SparseArray<Size> spanInfo =
getLoadedLauncherWidgetInfo(previewContext.getBaseContext());
@@ -253,14 +267,6 @@
}
}
- @WorkerThread
- private boolean doGridMigrationIfNecessary() {
- if (!GridSizeMigrationUtil.needsToMigrate(mContext, mIdp)) {
- return false;
- }
- return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, mIdp);
- }
-
@UiThread
private void renderView(Context inflationContext, BgDataModel dataModel,
Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
diff --git a/src/com/android/launcher3/logging/StartupLatencyLogger.kt b/src/com/android/launcher3/logging/StartupLatencyLogger.kt
new file mode 100644
index 0000000..435f798
--- /dev/null
+++ b/src/com/android/launcher3/logging/StartupLatencyLogger.kt
@@ -0,0 +1,203 @@
+package com.android.launcher3.logging
+
+import android.os.SystemClock
+import android.util.Log
+import android.util.SparseLongArray
+import androidx.annotation.MainThread
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.contains
+import androidx.core.util.isEmpty
+import com.android.launcher3.BuildConfig
+import com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent
+import com.android.launcher3.logging.StatsLogManager.StatsLatencyLogger.LatencyType
+import com.android.launcher3.util.Preconditions
+
+/** Logger for logging Launcher activity's startup latency. */
+open class StartupLatencyLogger(val latencyType: LatencyType) {
+
+ companion object {
+ const val TAG = "LauncherStartupLatencyLogger"
+ const val UNSET_INT = -1
+ const val UNSET_LONG = -1L
+ }
+
+ @VisibleForTesting val startTimeByEvent = SparseLongArray()
+ @VisibleForTesting val endTimeByEvent = SparseLongArray()
+
+ @VisibleForTesting var cardinality: Int = UNSET_INT
+ @VisibleForTesting var workspaceLoadStartTime: Long = UNSET_LONG
+
+ private var isInTest = false
+
+ /** Subclass can override this method to handle collected latency metrics. */
+ @MainThread
+ open fun log(): StartupLatencyLogger {
+ return this
+ }
+
+ @MainThread
+ fun logWorkspaceLoadStartTime() = logWorkspaceLoadStartTime(SystemClock.elapsedRealtime())
+
+ @VisibleForTesting
+ @MainThread
+ fun logWorkspaceLoadStartTime(startTimeMs: Long): StartupLatencyLogger {
+ Preconditions.assertUIThread()
+ workspaceLoadStartTime = startTimeMs
+ return this
+ }
+
+ /**
+ * Log size of workspace. Larger number of workspace items (icons, folders, widgets) means
+ * longer latency to initialize workspace.
+ */
+ @MainThread
+ fun logCardinality(cardinality: Int): StartupLatencyLogger {
+ Preconditions.assertUIThread()
+ this.cardinality = cardinality
+ return this
+ }
+
+ @MainThread
+ fun logStart(event: LauncherLatencyEvent) = logStart(event, SystemClock.elapsedRealtime())
+
+ @MainThread
+ fun logStart(event: LauncherLatencyEvent, startTimeMs: Long): StartupLatencyLogger {
+ // In unit test no looper is attached to current thread
+ Preconditions.assertUIThread()
+ if (validateLoggingEventAtStart(event)) {
+ startTimeByEvent.put(event.id, startTimeMs)
+ }
+ return this
+ }
+
+ @MainThread
+ fun logEnd(event: LauncherLatencyEvent) = logEnd(event, SystemClock.elapsedRealtime())
+
+ @MainThread
+ fun logEnd(event: LauncherLatencyEvent, endTimeMs: Long): StartupLatencyLogger {
+ // In unit test no looper is attached to current thread
+ Preconditions.assertUIThread()
+ maybeLogStartOfWorkspaceLoadTime(event)
+ if (validateLoggingEventAtEnd(event)) {
+ endTimeByEvent.put(event.id, endTimeMs)
+ }
+
+ return this
+ }
+
+ @MainThread
+ fun reset() {
+ // In unit test no looper is attached to current thread
+ Preconditions.assertUIThread()
+ startTimeByEvent.clear()
+ endTimeByEvent.clear()
+ cardinality = UNSET_INT
+ workspaceLoadStartTime = UNSET_LONG
+ }
+
+ @MainThread
+ private fun maybeLogStartOfWorkspaceLoadTime(event: LauncherLatencyEvent) {
+ if (workspaceLoadStartTime == UNSET_LONG) {
+ return
+ }
+ if (
+ event == LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC ||
+ event == LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC
+ ) {
+ logStart(event, workspaceLoadStartTime)
+ workspaceLoadStartTime = UNSET_LONG
+ }
+ }
+
+ /** @return true if we can log start of [LauncherLatencyEvent] and vice versa. */
+ @MainThread
+ private fun validateLoggingEventAtStart(event: LauncherLatencyEvent): Boolean {
+ if (!BuildConfig.IS_STUDIO_BUILD && !isInTest) {
+ return true
+ }
+ if (startTimeByEvent.contains(event.id)) {
+ Log.e(TAG, String.format("Cannot restart same %s event", event.name))
+ return false
+ } else if (
+ startTimeByEvent.isEmpty() &&
+ event != LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION
+ ) {
+ Log.e(
+ TAG,
+ String.format(
+ "The first log start event must be %s.",
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.name
+ )
+ )
+ return false
+ } else if (
+ event == LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC &&
+ startTimeByEvent.get(
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC.id
+ ) != 0L
+ ) {
+ Log.e(
+ TAG,
+ String.format(
+ "Cannot start %s event after %s starts",
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC.name,
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC.name
+ )
+ )
+ return false
+ } else if (
+ event == LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC &&
+ startTimeByEvent.get(
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC.id
+ ) != 0L
+ ) {
+ Log.e(
+ TAG,
+ String.format(
+ "Cannot start %s event after %s starts",
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC.name,
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC.name
+ )
+ )
+ return false
+ }
+
+ return true
+ }
+
+ /** @return true if we can log end of [LauncherLatencyEvent] and vice versa. */
+ @MainThread
+ private fun validateLoggingEventAtEnd(event: LauncherLatencyEvent): Boolean {
+ if (!BuildConfig.IS_STUDIO_BUILD && !isInTest) {
+ return true
+ }
+ if (!startTimeByEvent.contains(event.id)) {
+ Log.e(TAG, String.format("Cannot end %s event before starting it", event.name))
+ return false
+ } else if (endTimeByEvent.contains(event.id)) {
+ Log.e(TAG, String.format("Cannot end same %s event again", event.name))
+ return false
+ } else if (
+ event != LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION &&
+ endTimeByEvent.contains(
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.id
+ )
+ ) {
+ Log.e(
+ TAG,
+ String.format(
+ "Cannot end %s event after %s",
+ event.name,
+ LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.name
+ )
+ )
+ return false
+ }
+ return true
+ }
+
+ @VisibleForTesting
+ fun setIsInTest() {
+ isInTest = true
+ }
+}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 5b4a02b..8197b73 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -657,6 +657,39 @@
}
}
+ /** Launcher's latency events. */
+ public enum LauncherLatencyEvent implements EventEnum {
+ // Details of below 6 events with prefix of "LAUNCHER_LATENCY_STARTUP_" are discussed in
+ // go/launcher-startup-latency
+ @UiEvent(doc = "The total duration of launcher startup latency.")
+ LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION(1362),
+
+ @UiEvent(doc = "The duration of launcher activity's onCreate().")
+ LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE(1363),
+
+ @UiEvent(doc =
+ "The duration to inflate launcher root view in launcher activity's onCreate().")
+ LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION(1364),
+
+ @UiEvent(doc = "The duration of synchronous loading workspace")
+ LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC(1366),
+
+ @UiEvent(doc = "The duration of asynchronous loading workspace")
+ LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC(1367),
+ ;
+
+ private final int mId;
+
+ LauncherLatencyEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
/**
* Launcher specific ranking related events.
*/
@@ -807,7 +840,10 @@
CONTROLLED(7),
CACHED(8),
// example: device is rebooting via power key or shell command `adb reboot`
- COLD_DEVICE_REBOOTING(9);
+ COLD_DEVICE_REBOOTING(9),
+ // Tracking warm startup latency:
+ // https://developer.android.com/topic/performance/vitals/launch-time#warm
+ WARM(10);
private final int mId;
LatencyType(int id) {
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index c946e2c..ba9eb20 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -78,14 +78,14 @@
/**
* Binds all loaded data to actual views on the main thread.
*/
- public void bindWorkspace(boolean incrementBindId) {
+ public void bindWorkspace(boolean incrementBindId, boolean isBindSync) {
if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) {
DisjointWorkspaceBinder workspaceBinder =
initWorkspaceBinder(incrementBindId, mBgDataModel.collectWorkspaceScreens());
- workspaceBinder.bindCurrentWorkspacePages();
+ workspaceBinder.bindCurrentWorkspacePages(isBindSync);
workspaceBinder.bindOtherWorkspacePages();
} else {
- bindWorkspaceAllAtOnce(incrementBindId);
+ bindWorkspaceAllAtOnce(incrementBindId, isBindSync);
}
}
@@ -108,13 +108,13 @@
}
}
- private void bindWorkspaceAllAtOnce(boolean incrementBindId) {
+ private void bindWorkspaceAllAtOnce(boolean incrementBindId, boolean isBindSync) {
// Save a copy of all the bg-thread collections
ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
final IntArray orderedScreenIds = new IntArray();
ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
-
+ final int workspaceItemCount;
synchronized (mBgDataModel) {
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
@@ -124,11 +124,13 @@
mBgDataModel.lastBindId++;
}
mMyBindingId = mBgDataModel.lastBindId;
+ workspaceItemCount = mBgDataModel.itemsIdMap.size();
}
for (Callbacks cb : mCallbacksList) {
new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
+ workspaceItems, appWidgets, extraItems, orderedScreenIds)
+ .bind(isBindSync, workspaceItemCount);
}
}
@@ -246,7 +248,7 @@
mOrderedScreenIds = orderedScreenIds;
}
- private void bind() {
+ private void bind(boolean isBindSync, int workspaceItemCount) {
final IntSet currentScreenIds =
mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds);
Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks);
@@ -297,7 +299,8 @@
executeCallbacksTask(
c -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- c.onInitialBindComplete(currentScreenIds, pendingTasks);
+ c.onInitialBindComplete(
+ currentScreenIds, pendingTasks, workspaceItemCount, isBindSync);
}, mUiExecutor);
mCallbacks.bindStringCache(mBgDataModel.stringCache.clone());
@@ -361,18 +364,19 @@
* loaded, it will bind all workspace items immediately, and bindOtherWorkspacePages() will
* not bind any items.
*/
- protected void bindCurrentWorkspacePages() {
+ protected void bindCurrentWorkspacePages(boolean isBindSync) {
// Save a copy of all the bg-thread collections
ArrayList<ItemInfo> workspaceItems;
ArrayList<LauncherAppWidgetInfo> appWidgets;
ArrayList<FixedContainerItems> fciList = new ArrayList<>();
-
+ final int workspaceItemCount;
synchronized (mBgDataModel) {
workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems);
appWidgets = new ArrayList<>(mBgDataModel.appWidgets);
if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mBgDataModel.extraItems.forEach(fciList::add);
}
+ workspaceItemCount = mBgDataModel.itemsIdMap.size();
}
workspaceItems.forEach(it -> mBoundItemIds.add(it.id));
@@ -395,10 +399,10 @@
bindWorkspaceItems(workspaceItems);
bindAppWidgets(appWidgets);
-
executeCallbacksTask(c -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- c.onInitialBindComplete(mCurrentScreenIds, new RunnableList());
+ c.onInitialBindComplete(
+ mCurrentScreenIds, new RunnableList(), workspaceItemCount, isBindSync);
}, mUiExecutor);
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 0e3b06c..0861e9d 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -484,7 +484,9 @@
default void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { }
default void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
- default void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+ /** Called when workspace has been bound. */
+ default void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
+ int workspaceItemCount, boolean isBindSync) {
pendingTasks.executeAllAndDestroy();
}
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index dc5fcf7..ecf5f67 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
-import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import android.content.ContentValues;
import android.content.Context;
@@ -36,9 +36,6 @@
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherFiles;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
@@ -58,6 +55,7 @@
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Locale;
+import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
/**
@@ -76,45 +74,23 @@
private static final boolean LOGD = false;
private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
- public static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
private final Context mContext;
- private final boolean mForMigration;
+ private final ToLongFunction<UserHandle> mUserSerialProvider;
+ private final Runnable mOnEmptyDbCreateCallback;
+
private int mMaxItemId = -1;
public boolean mHotseatRestoreTableExists;
- public static DatabaseHelper createDatabaseHelper(Context context, boolean forMigration) {
- return createDatabaseHelper(context, null, forMigration);
- }
-
- public static DatabaseHelper createDatabaseHelper(Context context, String dbName,
- boolean forMigration) {
- if (dbName == null) {
- dbName = InvariantDeviceProfile.INSTANCE.get(context).dbFile;
- }
- DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName, forMigration);
- // Table creation sometimes fails silently, which leads to a crash loop.
- // This way, we will try to create a table every time after crash, so the device
- // would eventually be able to recover.
- if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
- Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
- // This operation is a no-op if the table already exists.
- databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
- }
- databaseHelper.mHotseatRestoreTableExists = tableExists(
- databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
-
- databaseHelper.initIds();
- return databaseHelper;
- }
-
/**
* Constructor used in tests and for restore.
*/
- public DatabaseHelper(Context context, String dbName, boolean forMigration) {
+ public DatabaseHelper(Context context, String dbName,
+ ToLongFunction<UserHandle> userSerialProvider, Runnable onEmptyDbCreateCallback) {
super(context, dbName, SCHEMA_VERSION);
mContext = context;
- mForMigration = forMigration;
+ mUserSerialProvider = userSerialProvider;
+ mOnEmptyDbCreateCallback = onEmptyDbCreateCallback;
}
protected void initIds() {
@@ -131,13 +107,11 @@
mMaxItemId = 1;
- addFavoritesTable(db, false);
+ addTableToDb(db, getDefaultUserSerial(), false /* optional */);
// Fresh and clean launcher DB.
mMaxItemId = initializeMaxItemId(db);
- if (!mForMigration) {
- onEmptyDbCreated();
- }
+ mOnEmptyDbCreateCallback.run();
}
public void onAddOrDeleteOp(SQLiteDatabase db) {
@@ -147,38 +121,8 @@
}
}
- /**
- * Re-composite given key in respect to database. If the current db is
- * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
- * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
- * string will be "EMPTY_DATABASE_CREATED@minimal.db".
- */
- public String getKey(final String key) {
- if (TextUtils.equals(getDatabaseName(), LauncherFiles.LAUNCHER_DB)) {
- return key;
- }
- return key + "@" + getDatabaseName();
- }
-
- /**
- * Overridden in tests.
- */
- protected void onEmptyDbCreated() {
- // Set the flag for empty DB
- LauncherPrefs.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true)
- .commit();
- }
-
- public long getSerialNumberForUser(UserHandle user) {
- return UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user);
- }
-
- public long getDefaultUserSerial() {
- return getSerialNumberForUser(Process.myUserHandle());
- }
-
- private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
- Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
+ private long getDefaultUserSerial() {
+ return mUserSerialProvider.applyAsLong(Process.myUserHandle());
}
@Override
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index eded5ea..9a6cde6 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -16,6 +16,9 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
+import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.content.ComponentName;
@@ -34,16 +37,15 @@
import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
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.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -89,81 +91,38 @@
return needsToMigrate;
}
- /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
- public static boolean migrateGridIfNeeded(Context context) {
- if (context instanceof LauncherPreviewRenderer.PreviewContext) {
- return true;
- }
- return migrateGridIfNeeded(context, null);
- }
-
/**
- * When migrating the grid for preview, we copy the table
- * {@link LauncherSettings.Favorites#TABLE_NAME} into
- * {@link LauncherSettings.Favorites#PREVIEW_TABLE_NAME}, run grid size migration from the
- * former to the later, then use the later table for preview.
- *
- * Similarly when doing the actual grid migration, the former grid option's table
- * {@link LauncherSettings.Favorites#TABLE_NAME} is copied into the new grid option's
- * {@link LauncherSettings.Favorites#TMP_TABLE}, we then run the grid size migration algorithm
+ * When migrating the grid, we copy the table
+ * {@link LauncherSettings.Favorites#TABLE_NAME} from {@code source} into
+ * {@link LauncherSettings.Favorites#TMP_TABLE}, run the grid size migration algorithm
* to migrate the later to the former, and load the workspace from the default
* {@link LauncherSettings.Favorites#TABLE_NAME}.
*
* @return false if the migration failed.
*/
- public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
- boolean migrateForPreview = idp != null;
- if (!migrateForPreview) {
- idp = LauncherAppState.getIDP(context);
- }
+ public static boolean migrateGridIfNeeded(
+ @NonNull Context context,
+ @NonNull InvariantDeviceProfile idp,
+ @NonNull DatabaseHelper target,
+ @NonNull SQLiteDatabase source) {
DeviceGridState srcDeviceState = new DeviceGridState(context);
DeviceGridState destDeviceState = new DeviceGridState(idp);
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
+ copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
HashSet<String> validPackages = getValidPackages(context);
-
- if (migrateForPreview) {
- if (!LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW,
- destDeviceState.getDbFile()).getBoolean(
- LauncherSettings.Settings.EXTRA_VALUE)) {
- return false;
- }
- } else if (!LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER,
- destDeviceState.getDbFile()).getBoolean(
- LauncherSettings.Settings.EXTRA_VALUE)) {
- return false;
- }
-
long migrationStartTime = System.currentTimeMillis();
- try (SQLiteTransaction t = (SQLiteTransaction) LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
- LauncherSettings.Settings.EXTRA_VALUE)) {
-
- DbReader srcReader = new DbReader(t.getDb(),
- migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
- : LauncherSettings.Favorites.TMP_TABLE,
- context, validPackages);
- DbReader destReader = new DbReader(t.getDb(),
- migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
- : LauncherSettings.Favorites.TABLE_NAME,
- context, validPackages);
+ try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
+ DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context, validPackages);
+ DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context, validPackages);
Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
- migrate(context, t.getDb(), srcReader, destReader, destDeviceState.getNumHotseat(),
+ migrate(target, srcReader, destReader, destDeviceState.getNumHotseat(),
targetSize, srcDeviceState, destDeviceState);
-
- if (!migrateForPreview) {
- dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
- }
-
+ dropTable(t.getDb(), TMP_TABLE);
t.commit();
return true;
} catch (Exception e) {
@@ -174,7 +133,7 @@
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
- if (!migrateForPreview) {
+ if (!(context instanceof SandboxContext)) {
// Save current configuration, so that the migration does not run again.
destDeviceState.writeToPrefs(context);
}
@@ -182,7 +141,7 @@
}
public static boolean migrate(
- @NonNull final Context context, @NonNull final SQLiteDatabase db,
+ @NonNull DatabaseHelper helper,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
final int destHotseatSize, @NonNull final Point targetSize,
@NonNull final DeviceGridState srcDeviceState,
@@ -234,8 +193,8 @@
Collections.sort(workspaceToBeAdded);
// Migrate hotseat
- solveHotseatPlacement(db, srcReader,
- destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded);
+ solveHotseatPlacement(helper, destHotseatSize,
+ srcReader, destReader, dstHotseatItems, hotseatToBeAdded);
// Migrate workspace.
// First we create a collection of the screens
@@ -255,8 +214,8 @@
if (DEBUG) {
Log.d(TAG, "Migrating " + screenId);
}
- solveGridPlacement(db, srcReader,
- destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false);
+ solveGridPlacement(helper, srcReader,
+ destReader, screenId, trgX, trgY, workspaceToBeAdded, false);
if (workspaceToBeAdded.isEmpty()) {
break;
}
@@ -266,8 +225,8 @@
// any of the screens, in this case we add them to new screens until all of them are placed.
int screenId = destReader.mLastScreenId + 1;
while (!workspaceToBeAdded.isEmpty()) {
- solveGridPlacement(db, srcReader,
- destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
+ solveGridPlacement(helper, srcReader,
+ destReader, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
screenId++;
}
@@ -298,33 +257,33 @@
});
}
- private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
+ private static void insertEntryInDb(DatabaseHelper helper, DbEntry entry,
String srcTableName, String destTableName) {
- int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
+ int id = copyEntryAndUpdate(helper, entry, srcTableName, destTableName);
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
for (Set<Integer> itemIds : entry.mFolderItems.values()) {
for (int itemId : itemIds) {
- copyEntryAndUpdate(db, context, itemId, id, srcTableName, destTableName);
+ copyEntryAndUpdate(helper, itemId, id, srcTableName, destTableName);
}
}
}
}
- private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ private static int copyEntryAndUpdate(DatabaseHelper helper,
DbEntry entry, String srcTableName, String destTableName) {
- return copyEntryAndUpdate(db, context, entry, -1, -1, srcTableName, destTableName);
+ return copyEntryAndUpdate(helper, entry, -1, -1, srcTableName, destTableName);
}
- private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ private static int copyEntryAndUpdate(DatabaseHelper helper,
int id, int folderId, String srcTableName, String destTableName) {
- return copyEntryAndUpdate(db, context, null, id, folderId, srcTableName, destTableName);
+ return copyEntryAndUpdate(helper, null, id, folderId, srcTableName, destTableName);
}
- private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
- DbEntry entry, int id, int folderId, String srcTableName, String destTableName) {
+ private static int copyEntryAndUpdate(DatabaseHelper helper, DbEntry entry,
+ int id, int folderId, String srcTableName, String destTableName) {
int newId = -1;
- Cursor c = db.query(srcTableName, null,
+ Cursor c = helper.getWritableDatabase().query(srcTableName, null,
LauncherSettings.Favorites._ID + " = '" + (entry != null ? entry.id : id) + "'",
null, null, null, null);
while (c.moveToNext()) {
@@ -335,11 +294,9 @@
} else {
values.put(LauncherSettings.Favorites.CONTAINER, folderId);
}
- newId = LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
- LauncherSettings.Settings.EXTRA_VALUE);
+ newId = helper.generateNewItemId();
values.put(LauncherSettings.Favorites._ID, newId);
- db.insert(destTableName, null, values);
+ helper.getWritableDatabase().insert(destTableName, null, values);
}
c.close();
return newId;
@@ -367,9 +324,9 @@
return validPackages;
}
- private static void solveGridPlacement(@NonNull final SQLiteDatabase db,
+ private static void solveGridPlacement(@NonNull final DatabaseHelper helper,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
- @NonNull final Context context, final int screenId, final int trgX, final int trgY,
+ final int screenId, final int trgX, final int trgY,
@NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
final Point trg = new Point(trgX, trgY);
@@ -391,7 +348,7 @@
continue;
}
if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
- insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
+ insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
iterator.remove();
}
}
@@ -428,9 +385,9 @@
return false;
}
- private static void solveHotseatPlacement(@NonNull final SQLiteDatabase db,
+ private static void solveHotseatPlacement(
+ @NonNull final DatabaseHelper helper, final int hotseatSize,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
- @NonNull final Context context, final int hotseatSize,
@NonNull final List<DbEntry> placedHotseatItems,
@NonNull final List<DbEntry> itemsToPlace) {
@@ -447,7 +404,7 @@
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
- insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
+ insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
occupied[entry.screenId] = true;
}
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index a5dccc1..2054d93 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,16 +16,16 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
-import android.net.Uri;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -66,9 +66,7 @@
private final LongSparseArray<UserHandle> allUsers;
private final LauncherAppState mApp;
- private final Uri mContentUri;
private final Context mContext;
- private final PackageManager mPM;
private final IconCache mIconCache;
private final InvariantDeviceProfile mIDP;
@@ -108,17 +106,14 @@
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app,
- UserManagerState userManagerState) {
+ public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState) {
super(cursor);
mApp = app;
allUsers = userManagerState.allUsers;
- mContentUri = contentUri;
mContext = app.getContext();
mIconCache = app.getIconCache();
mIDP = app.getInvariantDeviceProfile();
- mPM = mContext.getPackageManager();
// Init column indices
mIconIndex = getColumnIndexOrThrow(Favorites.ICON);
@@ -390,7 +385,7 @@
*/
public ContentWriter updater() {
return new ContentWriter(mContext, new ContentWriter.CommitParams(
- mApp.getModel().getModelDbController().getDatabaseHelper(),
+ mApp.getModel().getModelDbController(),
BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
}
@@ -409,8 +404,8 @@
public boolean commitDeleted() {
if (mItemsToRemove.size() > 0) {
// Remove dead items
- mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery(
- Favorites._ID, mItemsToRemove), null);
+ mApp.getModel().getModelDbController().delete(TABLE_NAME,
+ Utilities.createDbSelectionQuery(Favorites._ID, mItemsToRemove), null);
return true;
}
return false;
@@ -435,9 +430,8 @@
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(Favorites.RESTORED, 0);
- mContext.getContentResolver().update(mContentUri, values,
- Utilities.createDbSelectionQuery(
- Favorites._ID, mRestoredRows), null);
+ mApp.getModel().getModelDbController().update(TABLE_NAME, values,
+ Utilities.createDbSelectionQuery(Favorites._ID, mRestoredRows), null);
}
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 481cc6e..d4eded5 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
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;
@@ -41,7 +42,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Point;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Trace;
import android.os.UserHandle;
@@ -50,7 +50,6 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
-import android.util.TimingLogger;
import androidx.annotation.Nullable;
@@ -200,25 +199,10 @@
}
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
- TimingLogger timingLogger = new TimingLogger(TAG, "run");
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
- Trace.beginSection("LoadWorkspace");
- try {
- loadWorkspace(allShortcuts, memoryLogger);
- } finally {
- Trace.endSection();
- }
- logASplit(timingLogger, "loadWorkspace");
-
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- verifyNotStopped();
- mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
- mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- mModelDelegate.markActive();
- logASplit(timingLogger, "workspaceDelegateItems");
- }
+ loadWorkspace(allShortcuts, "", memoryLogger);
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
// sanitizeData should not be invoked if the workspace is loaded from a db different
@@ -228,21 +212,21 @@
verifyNotStopped();
sanitizeFolders(mItemsDeleted);
sanitizeWidgetsShortcutsAndPackages();
- logASplit(timingLogger, "sanitizeData");
+ logASplit("sanitizeData");
}
verifyNotStopped();
- mLauncherBinder.bindWorkspace(true /* incrementBindId */);
- logASplit(timingLogger, "bindWorkspace");
+ mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
+ logASplit("bindWorkspace");
mModelDelegate.workspaceLoadComplete();
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
- logASplit(timingLogger, "sendFirstScreenActiveInstallsBroadcast");
+ logASplit("sendFirstScreenActiveInstallsBroadcast");
// Take a break
waitForIdle();
- logASplit(timingLogger, "step 1 complete");
+ logASplit("step 1 complete");
verifyNotStopped();
// second step
@@ -253,16 +237,16 @@
} finally {
Trace.endSection();
}
- logASplit(timingLogger, "loadAllApps");
+ logASplit("loadAllApps");
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- logASplit(timingLogger, "allAppsDelegateItems");
+ logASplit("allAppsDelegateItems");
}
verifyNotStopped();
mLauncherBinder.bindAllApps();
- logASplit(timingLogger, "bindAllApps");
+ logASplit("bindAllApps");
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
@@ -270,75 +254,73 @@
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
- logASplit(timingLogger, "update icon cache");
+ logASplit("update icon cache");
verifyNotStopped();
- logASplit(timingLogger, "save shortcuts in icon cache");
+ logASplit("save shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
- logASplit(timingLogger, "step 2 complete");
+ logASplit("step 2 complete");
verifyNotStopped();
// third step
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
- logASplit(timingLogger, "loadDeepShortcuts");
+ logASplit("loadDeepShortcuts");
verifyNotStopped();
mLauncherBinder.bindDeepShortcuts();
- logASplit(timingLogger, "bindDeepShortcuts");
+ logASplit("bindDeepShortcuts");
verifyNotStopped();
- logASplit(timingLogger, "save deep shortcuts in icon cache");
+ logASplit("save deep shortcuts in icon cache");
updateHandler.updateIcons(allDeepShortcuts,
new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
waitForIdle();
- logASplit(timingLogger, "step 3 complete");
+ logASplit("step 3 complete");
verifyNotStopped();
// fourth step
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
- logASplit(timingLogger, "load widgets");
+ logASplit("load widgets");
verifyNotStopped();
mLauncherBinder.bindWidgets();
- logASplit(timingLogger, "bindWidgets");
+ logASplit("bindWidgets");
verifyNotStopped();
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
- logASplit(timingLogger, "otherDelegateItems");
+ logASplit("otherDelegateItems");
verifyNotStopped();
}
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
- logASplit(timingLogger, "save widgets in icon cache");
+ logASplit("save widgets in icon cache");
// fifth step
loadFolderNames();
verifyNotStopped();
updateHandler.finish();
- logASplit(timingLogger, "finish icon update");
+ logASplit("finish icon update");
mModelDelegate.modelLoadComplete();
transaction.commit();
memoryLogger.clearLogs();
} catch (CancellationException e) {
// Loader stopped, ignore
- logASplit(timingLogger, "Cancelled");
+ logASplit("Cancelled");
} catch (Exception e) {
memoryLogger.printLogs();
throw e;
- } finally {
- timingLogger.dumpToLog();
}
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -348,25 +330,29 @@
this.notify();
}
- private void loadWorkspace(
- List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger memoryLogger) {
- loadWorkspace(allDeepShortcuts, Favorites.CONTENT_URI,
- null /* selection */, memoryLogger);
- }
+ protected void loadWorkspace(
+ List<ShortcutInfo> allDeepShortcuts,
+ String selection,
+ LoaderMemoryLogger memoryLogger) {
+ Trace.beginSection("LoadWorkspace");
+ try {
+ loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger);
+ } finally {
+ Trace.endSection();
+ }
+ logASplit("loadWorkspace");
- protected void loadWorkspaceForPreviewSurfaceRenderer(
- List<ShortcutInfo> allDeepShortcuts, Uri contentUri, String selection) {
- loadWorkspace(allDeepShortcuts, contentUri, selection, null);
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
+ verifyNotStopped();
mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
mModelDelegate.markActive();
+ logASplit("workspaceDelegateItems");
}
}
- protected void loadWorkspace(
+ private void loadWorkspaceImpl(
List<ShortcutInfo> allDeepShortcuts,
- Uri contentUri,
String selection,
@Nullable LoaderMemoryLogger memoryLogger) {
final Context context = mApp.getContext();
@@ -377,7 +363,7 @@
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false;
- if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
+ if (!mApp.getModel().getModelDbController().migrateGridIfNeeded()) {
// Migration failed. Clear workspace.
clearDb = true;
}
@@ -402,8 +388,9 @@
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mShortcutKeyToPinnedShortcuts = new HashMap<>();
+ ModelDbController dbController = mApp.getModel().getModelDbController();
final LoaderCursor c = new LoaderCursor(
- contentResolver.query(contentUri, null, selection, null, null), contentUri,
+ dbController.query(TABLE_NAME, null, selection, null, null),
mApp, mUserManagerState);
final Bundle extras = c.getExtras();
mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME);
@@ -1112,12 +1099,9 @@
FileLog.d(TAG, widgetDimension.toString());
}
- private static void logASplit(@Nullable TimingLogger timingLogger, String label) {
- if (timingLogger != null) {
- timingLogger.addSplit(label);
- if (DEBUG) {
- Log.d(TAG, label);
- }
+ private static void logASplit(String label) {
+ if (DEBUG) {
+ Log.d(TAG, label);
}
}
}
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 97bce8c..1274c08 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -19,11 +19,10 @@
import static android.util.Base64.NO_WRAP;
import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
+import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
-import static com.android.launcher3.model.DatabaseHelper.EMPTY_DATABASE_CREATED;
-import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import android.app.blob.BlobHandle;
@@ -31,7 +30,6 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
@@ -43,6 +41,7 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
@@ -54,18 +53,22 @@
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.AutoInstallsLayout.SourceResources;
+import com.android.launcher3.ConstantItem;
import com.android.launcher3.DefaultLayoutParser;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.util.Partner;
import com.android.launcher3.widget.LauncherWidgetHolder;
@@ -73,7 +76,6 @@
import java.io.InputStream;
import java.io.StringReader;
-import java.util.function.Supplier;
/**
* Utility class which maintains an instance of Launcher database and provides utility methods
@@ -82,6 +84,8 @@
public class ModelDbController {
private static final String TAG = "LauncherProvider";
+ private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
+
protected DatabaseHelper mOpenHelper;
private final Context mContext;
@@ -92,26 +96,36 @@
private synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
- mOpenHelper = DatabaseHelper.createDatabaseHelper(
- mContext, false /* forMigration */);
-
- RestoreDbTask.restoreIfNeeded(mContext, mOpenHelper);
+ mOpenHelper = createDatabaseHelper(false /* forMigration */);
+ RestoreDbTask.restoreIfNeeded(mContext, this);
}
}
- private synchronized boolean prepForMigration(String dbFile, String targetTableName,
- Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
- if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
- Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile);
- return false;
- }
+ protected DatabaseHelper createDatabaseHelper(boolean forMigration) {
+ boolean isSandbox = mContext instanceof SandboxContext;
+ String dbName = isSandbox ? null : InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
- final DatabaseHelper helper = src.get();
- mOpenHelper = dst.get();
- copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
- mOpenHelper.getWritableDatabase(), targetTableName, mContext);
- helper.close();
- return true;
+ // Set the flag for empty DB
+ Runnable onEmptyDbCreateCallback = forMigration ? () -> { }
+ : () -> LauncherPrefs.get(mContext).putSync(getEmptyDbCreatedKey(dbName).to(true));
+
+ DatabaseHelper databaseHelper = new DatabaseHelper(mContext, dbName,
+ this::getSerialNumberForUser, onEmptyDbCreateCallback);
+ // Table creation sometimes fails silently, which leads to a crash loop.
+ // This way, we will try to create a table every time after crash, so the device
+ // would eventually be able to recover.
+ if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
+ Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
+ // This operation is a no-op if the table already exists.
+ addTableToDb(databaseHelper.getWritableDatabase(),
+ getSerialNumberForUser(Process.myUserHandle()),
+ true /* optional */);
+ }
+ databaseHelper.mHotseatRestoreTableExists = tableExists(
+ databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
+
+ databaseHelper.initIds();
+ return databaseHelper;
}
/**
@@ -267,42 +281,38 @@
}
/**
- * Updates the current DB and copies all the existing data to the temp table
- * @param dbFile name of the target db file name
+ * Migrates the DB if needed, and returns false if the migration failed
+ * and DB needs to be cleared.
*/
- @WorkerThread
- public boolean updateCurrentOpenHelper(String dbFile) {
- createDbIfNotExists();
- return prepForMigration(
- dbFile,
- Favorites.TMP_TABLE,
- () -> mOpenHelper,
- () -> DatabaseHelper.createDatabaseHelper(
- mContext, true /* forMigration */));
+ public boolean migrateGridIfNeeded() {
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
+ if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) {
+ return true;
+ }
+ String targetDbName = new DeviceGridState(idp).getDbFile();
+ if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) {
+ // DB is same, ignore
+ return true;
+ }
+ DatabaseHelper oldHelper = mOpenHelper;
+ mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
+ : createDatabaseHelper(true /* forMigration */);
+ try {
+ return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, idp, mOpenHelper,
+ oldHelper.getWritableDatabase());
+ } finally {
+ if (mOpenHelper != oldHelper) {
+ oldHelper.close();
+ }
+ }
}
/**
- * Returns the current DatabaseHelper.
- * Only for tests
+ * Returns the underlying model database
*/
- @WorkerThread
- public DatabaseHelper getDatabaseHelper() {
+ public SQLiteDatabase getDb() {
createDbIfNotExists();
- return mOpenHelper;
- }
-
- /**
- * Prepares the DB for preview by copying all existing data to preview table
- */
- @WorkerThread
- public boolean prepareForPreview(String dbFile) {
- createDbIfNotExists();
- return prepForMigration(
- dbFile,
- Favorites.PREVIEW_TABLE_NAME,
- () -> DatabaseHelper.createDatabaseHelper(
- mContext, dbFile, true /* forMigration */),
- () -> mOpenHelper);
+ return mOpenHelper.getWritableDatabase();
}
private void onAddOrDeleteOp(SQLiteDatabase db) {
@@ -345,8 +355,7 @@
}
private void clearFlagEmptyDbCreated() {
- LauncherPrefs.getPrefs(mContext).edit()
- .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit();
+ LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()));
}
/**
@@ -359,9 +368,8 @@
@WorkerThread
public synchronized void loadDefaultFavoritesIfNecessary() {
createDbIfNotExists();
- SharedPreferences sp = LauncherPrefs.getPrefs(mContext);
- if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
+ if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()))) {
Log.d(TAG, "loading default workspace");
LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
@@ -479,4 +487,27 @@
return new DefaultLayoutParser(mContext, widgetHolder,
mOpenHelper, mContext.getResources(), defaultLayout);
}
+
+ /**
+ * Re-composite given key in respect to database. If the current db is
+ * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
+ * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
+ * string will be "EMPTY_DATABASE_CREATED@minimal.db".
+ */
+ private ConstantItem<Boolean> getEmptyDbCreatedKey(String dbName) {
+ if (mContext instanceof SandboxContext) {
+ return LauncherPrefs.nonRestorableItem(EMPTY_DATABASE_CREATED,
+ false /* default value */, false /* boot aware */);
+ }
+ String key = TextUtils.equals(dbName, LauncherFiles.LAUNCHER_DB)
+ ? EMPTY_DATABASE_CREATED : EMPTY_DATABASE_CREATED + "@" + dbName;
+ return LauncherPrefs.backedUpItem(key, false /* default value */, false /* boot aware */);
+ }
+
+ /**
+ * Returns the serial number for the provided user
+ */
+ public long getSerialNumberForUser(UserHandle user) {
+ return UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user);
+ }
}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 48969fc..c718dcc 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -101,7 +101,7 @@
UserManagerState ums = new UserManagerState();
ums.init(UserCache.INSTANCE.get(context),
context.getSystemService(UserManager.class));
- LoaderCursor lc = new LoaderCursor(c, null, LauncherAppState.getInstance(context), ums);
+ LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums);
IntSet deletedShortcuts = new IntSet();
while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index ac72164..a6e064a 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,6 +16,8 @@
package com.android.launcher3.provider;
+import static android.os.Process.myUserHandle;
+
import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
@@ -47,8 +49,8 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.DatabaseHelper;
import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -83,12 +85,12 @@
/**
* Tries to restore the backup DB if needed
*/
- public static void restoreIfNeeded(Context context, DatabaseHelper helper) {
+ public static void restoreIfNeeded(Context context, ModelDbController dbController) {
if (!isPending(context)) {
return;
}
- if (!performRestore(context, helper)) {
- helper.createEmptyDB(helper.getWritableDatabase());
+ if (!performRestore(context, dbController)) {
+ dbController.createEmptyDB();
}
// Obtain InvariantDeviceProfile first before setting pending to false, so
@@ -102,12 +104,12 @@
idp.reinitializeAfterRestore(context);
}
- private static boolean performRestore(Context context, DatabaseHelper helper) {
- SQLiteDatabase db = helper.getWritableDatabase();
+ private static boolean performRestore(Context context, ModelDbController controller) {
+ SQLiteDatabase db = controller.getDb();
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, helper, db, new BackupManager(context));
- task.restoreAppWidgetIdsIfExists(context, helper);
+ task.sanitizeDB(context, controller, db, new BackupManager(context));
+ task.restoreAppWidgetIdsIfExists(context, controller);
t.commit();
return true;
} catch (Exception e) {
@@ -129,10 +131,10 @@
* @return number of items deleted.
*/
@VisibleForTesting
- protected int sanitizeDB(Context context, DatabaseHelper helper, SQLiteDatabase db,
+ protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db,
BackupManager backupManager) throws Exception {
// Primary user ids
- long myProfileId = helper.getDefaultUserSerial();
+ long myProfileId = controller.getSerialNumberForUser(myUserHandle());
long oldProfileId = getDefaultProfileId(db);
LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId);
LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size()
@@ -144,7 +146,7 @@
long oldManagedProfileId = oldManagedProfileIds.keyAt(i);
UserHandle user = getUserForAncestralSerialNumber(backupManager, oldManagedProfileId);
if (user != null) {
- long newManagedProfileId = helper.getSerialNumberForUser(user);
+ long newManagedProfileId = controller.getSerialNumberForUser(user);
profileMapping.put(oldManagedProfileId, newManagedProfileId);
}
}
@@ -213,7 +215,7 @@
}
// Override shortcuts
- maybeOverrideShortcuts(context, helper, db, myProfileId);
+ maybeOverrideShortcuts(context, controller, db, myProfileId);
return itemsDeleted;
}
@@ -321,11 +323,11 @@
.putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
}
- private void restoreAppWidgetIdsIfExists(Context context, DatabaseHelper helper) {
+ private void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
LauncherPrefs lp = LauncherPrefs.get(context);
if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
- AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, helper,
+ AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, controller,
IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
host);
@@ -343,7 +345,7 @@
APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
}
- protected static void maybeOverrideShortcuts(Context context, DatabaseHelper helper,
+ protected static void maybeOverrideShortcuts(Context context, ModelDbController controller,
SQLiteDatabase db, long currentUser) {
Map<String, LauncherActivityInfo> activityOverrides = ApiWrapper.getActivityOverrides(
context);
@@ -367,7 +369,7 @@
if (override != null) {
ContentValues values = new ContentValues();
values.put(Favorites.PROFILE_ID,
- helper.getSerialNumberForUser(override.getUser()));
+ controller.getSerialNumberForUser(override.getUser()));
values.put(Favorites.INTENT, AppInfo.makeLaunchIntent(override).toUri(0));
db.update(Favorites.TABLE_NAME, values, String.format("%s=?", Favorites._ID),
new String[]{String.valueOf(c.getInt(idIndex))});
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index e509235..7c5ef4d 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -26,7 +26,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.model.DatabaseHelper;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.pm.UserCache;
/**
@@ -106,7 +106,7 @@
public int commit() {
if (mCommitParams != null) {
- mCommitParams.mDatabaseHelper.getWritableDatabase().update(
+ mCommitParams.mDbController.update(
Favorites.TABLE_NAME, getValues(mContext),
mCommitParams.mWhere, mCommitParams.mSelectionArgs);
}
@@ -115,12 +115,12 @@
public static final class CommitParams {
- final DatabaseHelper mDatabaseHelper;
+ final ModelDbController mDbController;
final String mWhere;
final String[] mSelectionArgs;
- public CommitParams(DatabaseHelper helper, String where, String[] selectionArgs) {
- mDatabaseHelper = helper;
+ public CommitParams(ModelDbController controller, String where, String[] selectionArgs) {
+ mDbController = controller;
mWhere = where;
mSelectionArgs = selectionArgs;
}
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
index f5e13d2..1231604 100644
--- a/src/com/android/launcher3/util/LockedUserState.kt
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -7,6 +7,7 @@
import androidx.annotation.VisibleForTesting
class LockedUserState(private val mContext: Context) : SafeCloseable {
+ val isUserUnlockedAtLauncherStartup: Boolean
var isUserUnlocked: Boolean
private set
private val mUserUnlockedActions: RunnableList = RunnableList()
@@ -20,10 +21,17 @@
}
init {
+ // 1) when user reboots devices, launcher process starts at lock screen and both
+ // isUserUnlocked and isUserUnlockedAtLauncherStartup are init as false. After user unlocks
+ // screen, isUserUnlocked will be updated to true via Intent.ACTION_USER_UNLOCKED,
+ // yet isUserUnlockedAtLauncherStartup will remains as false.
+ // 2) when launcher process restarts after user has unlocked screen, both variable are
+ // init as true and will not change.
isUserUnlocked =
mContext
.getSystemService(UserManager::class.java)!!
.isUserUnlocked(Process.myUserHandle())
+ isUserUnlockedAtLauncherStartup = isUserUnlocked
if (isUserUnlocked) {
notifyUserUnlocked()
} else {
diff --git a/tests/assets/ReorderAlgorithmUnitTest/reorder_algorithm_test_cases b/tests/assets/ReorderAlgorithmUnitTest/reorder_algorithm_test_cases
new file mode 100644
index 0000000..6f92d6c
--- /dev/null
+++ b/tests/assets/ReorderAlgorithmUnitTest/reorder_algorithm_test_cases
@@ -0,0 +1,1804 @@
+###################################################################################################
+# This file contains test case composed of the following tags:
+# * # (coments): Lines starting with this character would be ignored.
+# * arguments: is set of words separated by spaces that can later be parsed
+# * board: represent a workspace, the first line is the dimensions of the board width x height (wxh)
+# There are different characters on the board that represent different things:
+# * x: The x character represents spaces that would be ignored, for example it can be used in
+# the first row if we don't know how wide the smartspace is.
+# * i: Represents an icon on the workspace, none in particular just an icon
+# * [a-z]: Represents a widget and it can be any number or character
+# except any other already in use. The whole continuos are of the same character is the
+# area of the widget.
+# * [A-Z]: Represents a folder and number of icons in the folder is represented by the order of
+# letter in the alphabet, A=2, B=3, C=4 ... etc.
+#
+# The "arguments:" tag represents where you want to add an item, the format is:
+# "x y spanX spanY minSpanX minSpanY type<widget/icon/folder> ifSolutionIsPossible<valid/invalid>"
+#
+# Test are parsed by CellLayoutTestCaseReader.java and boards are parsed by CellLayoutBoard.java
+###################################################################################################
+# Test 0
+board: 7x10
+-----de
+-----de
+-----di
+-----ii
+-----ii
+-----ci
+-----ci
+-----ii
+aaaaaai
+iibbbbi
+# This represents where you want to add an item, the format is "x y spanX spanY minSpanX minSpanY type<widget/icon/folder> ifSolutionIsPossible<valid/invalid>"
+arguments: 1 3 5 4 4 1 widget valid
+board: 7x10
+-----cb
+-----cb
+-----ci
+izzzzzi
+izzzzzi
+azzzzzi
+azzzzzi
+-----ii
+ddddddi
+iieeeei
+# Test 1
+board: 9x10
+aaaaaaaai
+i-----hhj
+c-----hhj
+c-----hhi
+c-----iii
+i-----iii
+beddddddi
+befffffii
+iifffffii
+iiggggggi
+arguments: 3 3 4 6 1 2 widget valid
+board: 9x10
+ggggggggi
+i-zzzzffe
+h-zzzzffe
+h-zzzzffi
+h-zzzziii
+i-zzzziii
+dcbbbbbbi
+dcaaaaaii
+iiaaaaaii
+iijjjjjji
+# Test 2
+board: 7x6
+aiii--f
+acci--f
+addi--f
+iebi--i
+iebiiii
+iiiiiii
+arguments: 1 1 1 1 1 1 icon valid
+board: 7x6
+aiii--f
+azeei-f
+abbi--f
+icdi--i
+icdiiii
+iiiiiii
+# Test 3
+board: 4x10
+-ibg
+-ibg
+-ibi
+-idf
+-cdf
+-cdi
+-cei
+-cei
+-iii
+aaai
+arguments: 2 1 1 5 1 1 widget valid
+board: 4x10
+if-c
+ifzc
+ifzi
+igzb
+egzb
+egzi
+e-di
+e-di
+-iii
+aaai
+# Test 4
+board: 8x8
+i---c---
+a---c---
+a---i---
+ibbbi---
+--------
+--------
+----i---
+ii--i---
+arguments: 1 1 3 6 1 4 widget valid
+board: 8x8
+i---c---
+azzzc---
+azzzi---
+izzzbbbi
+-zzz----
+-zzz----
+-zzzi---
+ii--i---
+# Test 5
+board: 10x8
+aaaaaaaaai
+bbbbbcciii
+---------f
+---------f
+---------f
+---------i
+iiddddddii
+iieeiiiiii
+arguments: 2 5 7 1 3 1 widget valid
+board: 10x8
+bbbbbbbbbi
+eeeeecciii
+---------a
+---------a
+---------a
+--zzzzzzzi
+iiddddddii
+iiffiiiiii
+# Test 6
+board: 8x7
+-------i
+bbcaaaah
+bbcaaaah
+bbcaaaai
+bbcddddi
+bbieeeii
+iiiffggi
+arguments: 5 1 1 2 1 1 icon valid
+board: 8x7
+-----z-i
+bbfaaaad
+bbfaaaad
+bbfaaaai
+bbfhhhhi
+bbigggii
+iiieecci
+# Test 7
+board: 10x3
+ii-------i
+----------
+-ii----iii
+arguments: 1 0 7 1 2 1 widget valid
+board: 10x3
+izzzzzzzii
+----------
+-ii----iii
+# Test 8
+board: 8x9
+aaaaaiii
+ibbbbbii
+-----ffi
+-----eei
+-----hhi
+-----ggi
+-----jji
+iiccccii
+iidddddi
+arguments: 2 2 5 2 3 1 widget valid
+board: 8x9
+fffffiii
+ihhhhhii
+bbzzzzzi
+jjzzzzzi
+-----ggi
+-----cci
+-----aai
+iiddddii
+iieeeeei
+# Test 9
+board: 4x8
+--ci
+--cd
+--cd
+--ci
+--ii
+aaai
+aaai
+bbbi
+arguments: 1 4 2 1 2 1 widget valid
+board: 4x8
+--bi
+--bc
+--bc
+--bi
+izzi
+aaai
+aaai
+dddi
+# Test 10
+board: 8x3
+i----cci
+iaaaaaai
+iiibbbbi
+arguments: 2 1 2 1 1 1 widget valid
+board: 8x3
+i-zz-cci
+ibbbbbbi
+iiiaaaai
+# Test 11
+board: 5x10
+-----
+-----
+-----
+-----
+-----
+-----
+-----
+-----
+i---i
+iaaai
+arguments: 0 3 3 6 1 2 widget valid
+board: 5x10
+-----
+-----
+-----
+zzz--
+zzz--
+zzz--
+zzz--
+zzz--
+zzzii
+iaaai
+# Test 12
+board: 6x10
+aad-ii
+aad-ii
+aai-eg
+aac-eg
+aac-ei
+aac-ef
+aai-ef
+aai-ei
+aai-ii
+bbiiii
+arguments: 1 5 1 4 1 3 widget valid
+board: 6x10
+ccb-ii
+ccb-ii
+cci-fa
+cce-fa
+cce-fi
+ccezfg
+ccizfg
+ccizfi
+ccizii
+ddiiii
+# Test 13
+board: 10x10
+iaaaaaaiii
+i-------ii
+i-------ii
+i-------ei
+i-------ei
+b-------ei
+b-------ei
+b-------ii
+biccccccci
+iiddddddii
+arguments: 0 2 6 1 3 1 widget valid
+board: 10x10
+iaaaaaaiii
+i-------ii
+zzzzzzi-ii
+i-------ci
+i-------ci
+e-------ci
+e-------ci
+e-------ii
+eibbbbbbbi
+iiddddddii
+# Test 14
+board: 3x6
+--a
+--a
+--a
+--a
+--i
+iii
+arguments: 0 2 1 3 1 3 widget valid
+board: 3x6
+--a
+--a
+z-a
+z-a
+z-i
+iii
+# Test 15
+board: 6x7
+------
+-i---i
+-i---c
+ia---c
+ia---i
+ia---i
+iibbbi
+arguments: 2 0 2 3 2 1 widget valid
+board: 6x7
+--zz--
+-izz-i
+-izz-b
+ia---b
+ia---i
+ia---i
+iiccci
+# Test 16
+board: 8x6
+bbbiiddi
+icccccci
+aaaggiii
+eeeeeffi
+ii----ii
+iiihhhhi
+arguments: 3 3 2 1 1 1 widget valid
+board: 8x6
+ccciiffi
+ieeeeeei
+ggghhiii
+aaaaaddi
+ii-zz-ii
+iiibbbbi
+# Test 17
+board: 4x9
+---i
+---i
+---d
+---d
+---i
+---i
+ibbi
+iaai
+icci
+arguments: 0 2 1 6 1 1 widget valid
+board: 4x9
+i--i
+i--i
+z--d
+z--d
+z--i
+z--i
+zaai
+zbbi
+icci
+# Test 18
+board: 7x5
+iaaaaai
+----iii
+----iii
+iiibbbi
+iiiccii
+arguments: 2 0 4 1 3 1 widget valid
+board: 7x5
+ibbbbbi
+zzzziii
+----iii
+iiiccci
+iiiaaii
+# Test 19
+board: 6x4
+aaaaii
+bbi--i
+cci--i
+iiiddi
+arguments: 4 0 1 2 1 1 widget valid
+board: 6x4
+cccczi
+aai-zi
+bbi-ii
+iiiddi
+# Test 20
+board: 8x5
+bbiaeeei
+bbiaiffi
+cciddddi
+----hhii
+iigggggi
+arguments: 0 1 1 3 1 1 widget valid
+board: 8x5
+bbieccci
+bbieiddi
+z-iffffi
+zaa-ggii
+iihhhhhi
+# Test 21
+board: 5x4
+i--ii
+a--ii
+aicci
+iibbi
+arguments: 1 2 2 1 1 1 widget valid
+board: 5x4
+iaaii
+bi-ii
+bzz-i
+iicci
+# Test 22
+board: 4x10
+---i
+---d
+---d
+---d
+---i
+aaai
+aaai
+aaai
+bbbi
+ccci
+arguments: 2 0 1 1 1 1 icon valid
+board: 4x10
+--zi
+---d
+---d
+---d
+---i
+bbbi
+bbbi
+bbbi
+ccci
+aaai
+# Test 23
+board: 10x10
+----------
+----------
+----------
+----------
+----------
+----------
+----------
+----------
+----------
+--aaai---i
+arguments: 0 0 8 5 5 4 widget valid
+board: 10x10
+zzzzzzzz--
+zzzzzzzz--
+zzzzzzzz--
+zzzzzzzz--
+zzzzzzzz--
+----------
+----------
+----------
+----------
+--aaai---i
+# Test 24
+board: 4x9
+---b
+---b
+---b
+---b
+---b
+---c
+---c
+---i
+iaai
+arguments: 2 0 1 2 1 1 widget valid
+board: 4x9
+--zc
+--zc
+---c
+---c
+---c
+---a
+---a
+---i
+ibbi
+# Test 25
+board: 4x5
+-cci
+-bbi
+-bbi
+-iii
+iaai
+arguments: 0 1 1 1 1 1 icon valid
+board: 4x5
+-bbi
+zaai
+-aai
+-iii
+icci
+# Test 26
+board: 7x3
+a-----i
+a-----i
+ibbbbbi
+arguments: 0 0 2 1 1 1 widget valid
+board: 7x3
+zza---i
+--a---i
+ibbbbbi
+# Test 27
+board: 8x6
+i-----di
+------di
+------ii
+--bbbbii
+i-iiccci
+i--iiaai
+arguments: 5 1 2 3 1 1 widget valid
+board: 8x6
+i---d--i
+----dzzi
+----izzi
+aaaaizzi
+i-iiccci
+i--iibbi
+# Test 28
+board: 4x7
+---i
+---i
+---a
+---a
+---a
+---i
+iiii
+arguments: 1 2 1 3 1 3 widget valid
+board: 4x7
+---i
+---i
+-z-a
+-z-a
+-z-a
+---i
+iiii
+# Test 29
+board: 5x10
+ii--b
+ii--b
+ai--i
+ai--i
+iiiii
+-----
+-----
+-----
+-----
+-----
+arguments: 1 2 2 5 1 4 widget valid
+board: 5x10
+ii--a
+ii--a
+bzz-i
+bzz-i
+izzii
+-zz--
+-zz--
+-i---
+-i---
+-ii--
+# Test 30
+board: 10x4
+a--------i
+a--------i
+i--------i
+ibbbbbicci
+arguments: 7 1 1 2 1 2 widget valid
+board: 10x4
+b--------i
+b------z-i
+i------z-i
+iaaaaaicci
+# Test 31
+board: 5x3
+a--ii
+a--ii
+iiiii
+arguments: 1 0 3 1 2 1 widget valid
+board: 5x3
+azzzi
+a-iii
+iiiii
+# Test 32
+board: 5x9
+-----
+-----
+-----
+-----
+----i
+----b
+----b
+----i
+aaaii
+arguments: 1 0 2 7 2 1 widget valid
+board: 5x9
+-zz--
+-zz--
+-zz--
+-zz--
+-zz-i
+-zz-a
+-zz-a
+----i
+bbbii
+# Test 33
+board: 4x8
+icci
+aaai
+aaai
+iiii
+ibbi
+---i
+---i
+dddi
+arguments: 2 1 1 6 1 5 icon invalid
+board: 4x8
+----
+----
+----
+----
+----
+----
+----
+----
+# Test 34
+board: 7x3
+------i
+------i
+iaaiiii
+arguments: 0 1 4 1 1 1 widget valid
+board: 7x3
+------i
+zzzz--i
+iaaiiii
+# Test 35
+board: 5x4
+ii-bi
+ai-bi
+ai-ii
+iiiii
+arguments: 1 1 2 1 2 1 widget valid
+board: 5x4
+iiiai
+bzzai
+bi-ii
+iiiii
+# Test 36
+board: 9x7
+iii--iiii
+aai--bbbi
+aai--bbbi
+aai--ccii
+iiiiiiiii
+---------
+---------
+arguments: 1 1 6 2 4 2 widget valid
+board: 9x7
+iii--iiii
+-zzzzzz-i
+-zzzzzz-i
+bbi--aaai
+bbiiiaaai
+bbi--cci-
+iii--iii-
+# Test 37
+board: 6x9
+aai--i
+-iii-i
+iiiiii
+-i----
+-ibbbi
+-ibbbi
+-iccci
+-i---i
+iidddi
+arguments: 2 2 3 1 2 1 widget valid
+board: 6x9
+ddi--i
+-iii-i
+iizzzi
+-iiii-
+-iaaai
+-iaaai
+-iccci
+-i---i
+iibbbi
+# Test 38
+board: 10x6
+----------
+----------
+----------
+----------
+----------
+----------
+arguments: 0 0 6 4 1 2 widget valid
+board: 10x6
+zzzzzz----
+zzzzzz----
+zzzzzz----
+zzzzzz----
+----------
+----------
+# Test 39
+board: 3x9
+-bc
+-bc
+-bd
+-bd
+-bi
+-bi
+-ii
+aai
+iii
+arguments: 0 1 1 6 1 4 widget valid
+board: 3x9
+-bd
+zbd
+zbc
+zbc
+zbi
+zbi
+zii
+aai
+iii
+# Test 40
+board: 3x3
+--i
+--i
+aai
+arguments: 1 0 1 1 1 1 icon valid
+board: 3x3
+-zi
+--i
+aai
+# Test 41
+board: 6x3
+-----i
+------
+--i---
+arguments: 0 1 4 1 3 1 widget valid
+board: 6x3
+-----i
+zzzz--
+--i---
+# Test 42
+board: 5x5
+iaaai
+---ii
+iibbi
+iicci
+iiiii
+arguments: 1 0 1 2 1 2 icon invalid
+board: 5x5
+-----
+-----
+-----
+-----
+-----
+# Test 43
+board: 3x6
+--i
+--i
+--b
+--b
+--i
+aai
+arguments: 1 2 1 3 1 3 widget valid
+board: 3x6
+--i
+--i
+-za
+-za
+-zi
+bbi
+# Test 44
+board: 4x8
+---a
+---a
+---a
+---a
+---a
+---i
+iiii
+iiii
+arguments: 2 2 1 3 1 3 widget valid
+board: 4x8
+---a
+---a
+--za
+--za
+--za
+---i
+iiii
+iiii
+# Test 45
+board: 9x9
+i-----kki
+i-----kki
+a-----kki
+a-----kki
+i-----lli
+iicccibbj
+idiffibbj
+ideeiibbi
+iigggihhi
+arguments: 2 1 5 3 4 2 widget valid
+board: 9x9
+i-----lli
+izzzzzlli
+fzzzzzlli
+fzzzzzlli
+i-----jji
+iihhhikke
+igiccikke
+igbbiikki
+iiaaaiddi
+# Test 46
+board: 9x5
+ab---iggi
+ab---iffi
+ab---eeei
+aicccccci
+iiiidddii
+arguments: 1 0 2 3 1 3 widget valid
+board: 9x5
+bzze-iffi
+bzze-iaai
+bzze-dddi
+bicccccci
+iiiigggii
+# Test 47
+board: 5x6
+ai--b
+ai--b
+ai--i
+ai--i
+iiiii
+iiiii
+arguments: 2 4 2 1 2 1 widget valid
+board: 5x6
+bi--a
+bi--a
+bi--i
+biiii
+iizzi
+iiiii
+# Test 48
+board: 5x4
+-----
+-i---
+----i
+-iiii
+arguments: 1 0 3 2 2 1 widget valid
+board: 5x4
+-zzz-
+-zzzi
+----i
+-iiii
+# Test 49
+board: 6x3
+i---ii
+i---ii
+iiaaai
+arguments: 2 0 1 1 1 1 icon valid
+board: 6x3
+i-z-ii
+i---ii
+iiaaai
+# Test 50
+board: 8x9
+--------
+--------
+--------
+--------
+--------
+--------
+--------
+--------
+--------
+arguments: 1 1 6 7 3 1 widget valid
+board: 8x9
+--------
+-zzzzzz-
+-zzzzzz-
+-zzzzzz-
+-zzzzzz-
+-zzzzzz-
+-zzzzzz-
+-zzzzzz-
+--------
+# Test 51
+board: 3x6
+--b
+--b
+--b
+--i
+iii
+aai
+arguments: 0 3 1 2 1 2 widget valid
+board: 3x6
+--a
+--a
+i-a
+z-i
+zii
+bbi
+# Test 52
+board: 5x8
+a--df
+a--df
+a--di
+a--ei
+i--ei
+i--ii
+ibbbi
+iccci
+arguments: 1 3 1 3 1 2 widget valid
+board: 5x8
+e--cb
+e--cb
+e--ci
+ez-di
+iz-di
+iz-ii
+iaaai
+ifffi
+# Test 53
+board: 8x3
+aaaai--i
+iccci--i
+bbbiiiii
+arguments: 1 1 3 1 3 1 icon invalid
+board: 8x3
+--------
+--------
+--------
+# Test 54
+board: 8x9
+------di
+------di
+------di
+------ii
+iaaaaaai
+baaaaaai
+baaaaaai
+baaaaaai
+iiccccci
+arguments: 3 3 2 1 1 1 widget valid
+board: 8x9
+------ai
+------ai
+------ai
+---zz-ii
+icccccci
+bcccccci
+bcccccci
+bcccccci
+iidddddi
+# Test 55
+board: 4x8
+ibbi
+iaai
+iaai
+caai
+ciii
+iddi
+--ii
+ieei
+arguments: 1 4 1 1 1 1 icon valid
+board: 4x8
+iaai
+icci
+icci
+ecci
+ezii
+ibbi
+-iii
+iddi
+# Test 56
+board: 6x9
+-----i
+------
+-i---i
+i----i
+-----i
+--icci
+-i-bbi
+-i-iii
+iiaaai
+arguments: 1 0 4 2 4 2 widget valid
+board: 6x9
+-zzzzi
+-zzzz-
+-i---i
+i----i
+-----i
+--icci
+-i-bbi
+-i-iii
+iiaaai
+# Test 57
+board: 4x9
+aaai
+ibbi
+c-ii
+c-ei
+c-ei
+c-ii
+c-ii
+ciii
+iddi
+arguments: 1 6 2 2 2 2 widget valid
+board: 4x9
+eeei
+icci
+d-ii
+dibi
+dibi
+diii
+dzzi
+dzzi
+iaai
+# Test 58
+board: 3x6
+aai
+iii
+--b
+--b
+--i
+iii
+arguments: 0 1 1 1 1 1 icon valid
+board: 3x6
+aai
+zii
+i-b
+--b
+--i
+iii
+# Test 59
+board: 9x6
+iiiiii--i
+iaaeii--g
+baaeii--g
+baaiii--i
+icciiiiii
+iddiiiffi
+arguments: 3 3 5 1 4 1 widget valid
+board: 9x6
+iiiiii--i
+ibbdiii-a
+ebbdiiiia
+ebbzzzzzi
+icciiiiii
+iffiiiggi
+# Test 60
+board: 5x8
+iiiii
+ii-ii
+ai-ii
+ai-ii
+ai-ii
+aibbi
+iicci
+iiiii
+arguments: 2 0 2 2 1 2 widget valid
+board: 5x8
+iizzi
+iizzi
+biiii
+biiii
+biiii
+bicci
+iiaai
+iiiii
+# Test 61
+board: 7x10
+-------
+-------
+-------
+-------
+-------
+-------
+-------
+-------
+-------
+-------
+arguments: 2 0 3 8 2 7 widget valid
+board: 7x10
+--zzz--
+--zzz--
+--zzz--
+--zzz--
+--zzz--
+--zzz--
+--zzz--
+--zzz--
+-------
+-------
+# Test 62
+board: 8x8
+aaiiiiii
+iiibbbbi
+dddi---i
+iiii---i
+ccci---h
+ccci---h
+eeei---i
+fffigggi
+arguments: 1 4 4 2 1 1 widget valid
+board: 8x8
+eeiiiiii
+iiiaaaai
+fffigggi
+iiiigggi
+-zzzzi-c
+-zzzz--c
+hhhii--i
+dddibbbi
+# Test 63
+board: 8x5
+ibbii--d
+iiiii--d
+aaaii--d
+aaaii--i
+cccciiii
+arguments: 3 2 2 1 2 1 widget valid
+board: 8x5
+ibbii--d
+iiiii--d
+ccczziid
+cccii--i
+aaaaiiii
+# Test 64
+board: 9x5
+iiac--iii
+iiac--ffi
+biac--eei
+biai--iii
+iiiiddddi
+arguments: 3 1 2 3 2 1 widget valid
+board: 9x5
+iif--eiii
+iifzzebbi
+cifzzeaai
+cifzziiii
+iiiiddddi
+# Test 65
+board: 6x7
+aaaiii
+aaaiii
+ibbcci
+i----f
+d----f
+d----i
+iieeei
+arguments: 2 1 1 5 1 4 widget valid
+board: 6x7
+aaaiii
+aaaiii
+i-zbbi
+i-zeec
+d-z--c
+d-z--i
+iifffi
+# Test 66
+board: 10x10
+aaaaaaaaii
+i---ijjjni
+i---ijjjni
+b---kjjjni
+b---kjjjii
+b---klliii
+b---immmmi
+bcccccccci
+ieeiddiiii
+iffiigghhi
+arguments: 2 4 3 4 3 1 widget valid
+board: 10x10
+aaaaaaaaii
+i---innnci
+i---innnci
+hzzzknnnci
+hzzzknnnii
+hzzzkeeiii
+hzzziffffi
+hddddddddi
+ijjilliiii
+iggiimmbbi
+# Test 67
+board: 3x8
+--i
+--i
+--i
+bai
+bai
+iai
+iai
+iii
+arguments: 0 0 1 2 1 1 widget valid
+board: 3x8
+z-i
+z-i
+--i
+bai
+bai
+iai
+iai
+iii
+# Test 68
+board: 6x5
+i---ii
+ibcaei
+ibcaei
+ibiaii
+iiiddi
+arguments: 4 1 1 3 1 1 icon valid
+board: 6x5
+i--zii
+icadbi
+icadbi
+icidii
+iiieei
+# Test 69
+board: 3x7
+a-i
+a-b
+a-b
+a-b
+a-i
+i-i
+iii
+arguments: 0 0 1 4 1 1 widget valid
+board: 3x7
+zbi
+zba
+zba
+zba
+-bi
+i-i
+iii
+# Test 70
+board: 5x10
+---ii
+---ii
+---ii
+aaaai
+-----
+-----
+-----
+-----
+-----
+-----
+arguments: 0 5 3 4 3 1 widget valid
+board: 5x10
+---ii
+---ii
+---ii
+aaaai
+-----
+zzz--
+zzz--
+zzz--
+zzz--
+-----
+# Test 71
+board: 8x9
+-----i-i
+--------
+--------
+--------
+--------
+--------
+--------
+--------
+--------
+arguments: 2 3 5 5 1 4 widget valid
+board: 8x9
+-----i-i
+--------
+--------
+--zzzzz-
+--zzzzz-
+--zzzzz-
+--zzzzz-
+--zzzzz-
+--------
+# Test 72
+board: 4x6
+i--d
+a--d
+a--i
+acbi
+acbi
+iiii
+arguments: 0 2 1 3 1 2 widget valid
+board: 4x6
+iz-b
+cz-b
+cz-i
+cadi
+cadi
+iiii
+# Test 73
+board: 3x7
+--c
+--c
+--c
+--i
+bai
+bai
+iii
+arguments: 1 0 1 2 1 1 widget valid
+board: 3x7
+-zb
+-zb
+--b
+--i
+cai
+cai
+iii
+# Test 74
+board: 4x7
+---i
+---i
+---b
+---b
+---b
+---i
+iaai
+arguments: 2 2 1 3 1 1 widget valid
+board: 4x7
+---i
+---i
+--zb
+--zb
+--zb
+---i
+iaai
+# Test 75
+board: 4x3
+a--i
+a--i
+ibbi
+arguments: 1 0 1 1 1 1 icon valid
+board: 4x3
+bz-i
+b--i
+iaai
+# Test 76
+board: 10x5
+--ai------
+--aii----i
+--iii----i
+--iii----i
+iiiiiibbbi
+arguments: 5 2 4 2 3 2 widget valid
+board: 10x5
+--bi------
+--bii----i
+--iiizzzzi
+--iiizzzzi
+iiiiiiaaai
+# Test 77
+board: 8x10
+-------i
+-------e
+-------e
+-------e
+-------e
+-------e
+-------i
+ccibbbii
+iaaaaaai
+iiiddddi
+arguments: 4 2 3 5 3 1 widget valid
+board: 8x10
+-------i
+-------c
+----zzzc
+----zzzc
+----zzzc
+----zzzc
+----zzzi
+aaibbbii
+iddddddi
+iiieeeei
+# Test 78
+board: 3x9
+aai
+i-i
+iii
+--i
+---
+--i
+b-i
+bii
+iii
+arguments: 1 1 1 7 1 6 widget valid
+board: 3x9
+aai
+izi
+izi
+izi
+-z-
+izi
+bzi
+bzi
+iii
+# Test 79
+board: 6x5
+aaii-i
+--i---
+--i--i
+iii--i
+iiiiii
+arguments: 2 3 3 1 3 1 widget valid
+board: 6x5
+aaii-i
+--i---
+--ii-i
+iizzzi
+iiiiii
+# Test 80
+board: 6x4
+------
+------
+----ii
+------
+arguments: 0 0 4 2 3 1 widget valid
+board: 6x4
+zzzz--
+zzzz--
+----ii
+------
+# Test 81
+board: 5x6
+aaaai
+i--ii
+b--ii
+b--ii
+b--ii
+iiiii
+arguments: 2 3 2 1 1 1 widget valid
+board: 5x6
+aaaai
+i--ii
+b--ii
+bizzi
+b--ii
+iiiii
+# Test 82
+board: 6x8
+aaaaii
+bbbbbi
+icciii
+-----i
+-----i
+ddfffi
+ddgggi
+eeeeei
+arguments: 1 3 2 2 1 1 widget valid
+board: 6x8
+bbbbii
+gggggi
+icciii
+-zz--i
+-zz--i
+eedddi
+eefffi
+aaaaai
+# Test 83
+board: 8x10
+iiaaaaai
+------cd
+------cd
+------ci
+------ii
+iiiiibbi
+--------
+--------
+--------
+--------
+arguments: 2 0 5 3 5 1 widget valid
+board: 8x10
+iizzzzzi
+--zzzzza
+--zzzzza
+--dddddi
+------bi
+iiiii-bi
+------b-
+------i-
+-----cc-
+--------
+# Test 84
+board: 6x6
+---aii
+---aii
+---bbi
+---iii
+--iiii
+i----i
+arguments: 2 0 3 4 1 3 widget valid
+board: 6x6
+bizzzi
+bizzzi
+aazzzi
+iizzzi
+--iiii
+i----i
+# Test 85
+board: 4x7
+iaai
+ii-i
+ii-i
+ii-i
+iiii
+----
+---i
+arguments: 2 2 1 4 1 2 widget valid
+board: 4x7
+iaai
+ii-i
+iizi
+iizi
+iizi
+--z-
+--ii
+# Test 86
+board: 4x9
+----
+a--i
+a--i
+i--i
+i--c
+i--c
+b--i
+b--i
+iiii
+arguments: 0 0 2 7 2 7 widget valid
+board: 4x9
+zz--
+zzbi
+zzbi
+zzii
+zzia
+zzia
+zzci
+--ci
+iiii
+# Test 87
+board: 5x6
+-----
+-i--i
+-----
+-----
+-----
+-----
+arguments: 0 0 3 3 1 2 widget valid
+board: 5x6
+zzz--
+zzzii
+zzz--
+-----
+-----
+-----
+# Test 88
+board: 5x5
+ii--a
+ii--a
+-i--i
+-i-ii
+ii--i
+arguments: 0 1 2 3 2 1 widget valid
+board: 5x5
+iiiia
+zz-ia
+zz-ii
+zz-ii
+ii--i
+# Test 89
+board: 5x10
+ccddi
+bbbii
+eeeei
+iajji
+faiii
+fgggi
+fgggi
+ihhhi
+----i
+iikki
+arguments: 1 3 1 4 1 2 icon invalid
+board: 5x10
+-----
+-----
+-----
+-----
+-----
+-----
+-----
+-----
+-----
+-----
+# Test 90
+board: 3x6
+--i
+--b
+--b
+--b
+--i
+aai
+arguments: 1 0 1 2 1 1 widget valid
+board: 3x6
+-zi
+-za
+--a
+--a
+--i
+bbi
+# Test 91
+board: 4x8
+---i
+---c
+---c
+---c
+---c
+---i
+aaai
+bbbi
+arguments: 1 1 1 2 1 2 widget valid
+board: 4x8
+---i
+-z-c
+-z-c
+---c
+---c
+---i
+bbbi
+aaai
+# Test 92
+board: 5x7
+----c
+----c
+----c
+----c
+----i
+aaaai
+ibbii
+arguments: 1 1 2 4 1 2 widget valid
+board: 5x7
+----b
+-zz-b
+-zz-b
+-zz-b
+-zz-i
+aaaai
+iccii
+# Test 93
+board: 5x4
+i--ii
+i--ii
+i--ii
+iiiii
+arguments: 2 1 1 2 1 2 widget valid
+board: 5x4
+i--ii
+i-zii
+i-zii
+iiiii
+# Test 94
+board: 10x5
+aabc---iii
+aabc---eei
+aabi---ffi
+aaii---ggi
+iiiiiddddi
+arguments: 4 1 3 3 1 3 widget valid
+board: 10x5
+ggfe---iii
+ggfezzzddi
+ggfizzzbbi
+ggiizzzaai
+iiiiicccci
+# Test 95
+board: 9x5
+baffi---i
+baeei---i
+caiii---i
+ciddi---i
+iiiiiggii
+arguments: 0 0 4 3 3 1 widget valid
+board: 9x5
+ezzzdccii
+ezzzdggii
+fzzzdiiii
+fibbi---i
+iiiiiaaii
+# Test 96
+board: 7x7
+-------
+--i---i
+-------
+-------
+------i
+-------
+------i
+arguments: 0 1 5 2 3 1 widget valid
+board: 7x7
+-------
+zzzzzii
+zzzzz--
+-------
+------i
+-------
+------i
+# Test 97
+board: 10x4
+i-----ccii
+a-----ccii
+a-----iddi
+iibbbiiiii
+arguments: 7 2 1 1 1 1 icon valid
+board: 10x4
+i-----ccii
+d-----ccii
+d---ibbz-i
+iiaaaiiiii
+# Test 98
+board: 7x9
+--i-iii
+ai-icci
+ai-iiii
+iibbbbi
+ii-iiii
+-i-ii-i
+-i-ii-i
+ii-iiii
+ii----i
+arguments: 1 6 3 2 3 2 widget valid
+board: 7x9
+--i-iii
+bi-iaai
+bi-iiii
+iicccci
+ii-iiii
+-iiii-i
+izzzi-i
+izzziii
+iiii--i
+# Test 99
+board: 4x10
+aaii
+iiii
+---c
+---c
+---c
+---d
+---d
+---i
+ibbi
+iiii
+arguments: 0 0 2 6 1 2 widget valid
+board: 4x10
+zzii
+zzii
+zz-c
+zz-c
+zz-c
+zz-b
+aa-b
+ii-i
+iddi
+iiii
+# Test 100
+board: 8x4
+iittt---
+yyyy---i
+yyyyi--i
+yyyy---i
+# This represents where you want to add an item, the format is "x y spanX spanY minSpanX minSpanY type<widget/icon/folder> ifSolutionIsPossible<valid/invalid>"
+arguments: 5 1 2 3 1 1 widget valid
+board: 8x4
+iittt---
+yyyy-hhi
+yyyyihhi
+yyyy-hhi
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
index cf96f04..3c2b49a 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutBoard.java
@@ -171,6 +171,8 @@
}
}
+ private HashSet<Character> mUsedWidgetTypes = new HashSet<>();
+
static final int INFINITE = 99999;
char[][] mWidget = new char[30][30];
@@ -182,6 +184,8 @@
WidgetRect mMain = null;
+ int mWidth, mHeight;
+
CellLayoutBoard() {
for (int x = 0; x < mWidget.length; x++) {
for (int y = 0; y < mWidget[0].length; y++) {
@@ -190,6 +194,17 @@
}
}
+ CellLayoutBoard(int width, int height) {
+ mWidget = new char[width][height];
+ this.mWidth = width;
+ this.mHeight = height;
+ for (int x = 0; x < mWidget.length; x++) {
+ for (int y = 0; y < mWidget[0].length; y++) {
+ mWidget[x][y] = CellType.EMPTY;
+ }
+ }
+ }
+
public List<WidgetRect> getWidgets() {
return mWidgetsRects;
}
@@ -256,6 +271,16 @@
}).collect(Collectors.toList());
}
+ private char getNextWidgetType() {
+ for (char type = 'a'; type <= 'z'; type++) {
+ if (type == 'i') continue;
+ if (mUsedWidgetTypes.contains(type)) continue;
+ mUsedWidgetTypes.add(type);
+ return type;
+ }
+ return 'z';
+ }
+
public void addWidget(int x, int y, int spanX, int spanY, char type) {
Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y);
removeOverlappingItems(rect);
@@ -268,6 +293,10 @@
}
}
+ public void addWidget(int x, int y, int spanX, int spanY) {
+ addWidget(x, y, spanX, spanY, getNextWidgetType());
+ }
+
public void addIcon(int x, int y) {
Point iconCoord = new Point(x, y);
removeOverlappingItems(iconCoord);
@@ -367,6 +396,8 @@
}
}
}
+ board.mHeight = lines.length;
+ board.mWidth = lines[0].length();
board.mWidgetsRects = getRects(board.mWidget);
board.mWidgetsRects.forEach(widgetRect -> {
if (widgetRect.mType == CellType.MAIN_WIDGET) {
@@ -380,6 +411,11 @@
public String toString(int maxX, int maxY) {
StringBuilder s = new StringBuilder();
+ s.append("board: ");
+ s.append(maxX);
+ s.append("x");
+ s.append(maxY);
+ s.append("\n");
maxX = Math.min(maxX, mWidget.length);
maxY = Math.min(maxY, mWidget[0].length);
for (int y = 0; y < maxY; y++) {
@@ -391,6 +427,11 @@
return s.toString();
}
+ @Override
+ public String toString() {
+ return toString(mWidth, mHeight);
+ }
+
public static List<CellLayoutBoard> boardListFromString(String boardsStr) {
String[] lines = boardsStr.split("\n");
ArrayList<String> individualBoards = new ArrayList<>();
@@ -410,4 +451,12 @@
}
return boards;
}
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
}
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
index e3d07a9..0d2f252 100644
--- a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
+++ b/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
@@ -24,12 +24,12 @@
import com.android.launcher3.views.DoubleShadowBubbleTextView;
import java.util.ArrayList;
+import java.util.List;
public class CellLayoutTestUtils {
public static ArrayList<CellLayoutBoard> workspaceToBoards(Launcher launcher) {
ArrayList<CellLayoutBoard> boards = new ArrayList<>();
- int widgetCount = 0;
for (CellLayout cellLayout : launcher.getWorkspace().mWorkspaceScreens) {
int count = cellLayout.getShortcutsAndWidgets().getChildCount();
@@ -52,11 +52,29 @@
} else {
// is widget
board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
- params.cellVSpan, (char) ('a' + widgetCount));
- widgetCount++;
+ params.cellVSpan);
}
}
}
return boards;
}
+
+ public static CellLayoutBoard viewsToBoard(List<View> views, int width, int height) {
+ CellLayoutBoard board = new CellLayoutBoard();
+ board.mWidth = width;
+ board.mHeight = height;
+
+ for (View callView : views) {
+ CellLayoutLayoutParams params = (CellLayoutLayoutParams) callView.getLayoutParams();
+ // is icon
+ if (callView instanceof DoubleShadowBubbleTextView) {
+ board.addIcon(params.getCellX(), params.getCellY());
+ } else {
+ // is widget
+ board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
+ params.cellVSpan);
+ }
+ }
+ return board;
+ }
}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
new file mode 100644
index 0000000..e3de500
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2023 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.celllayout;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.views.DoubleShadowBubbleTextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ReorderAlgorithmUnitTest {
+ private Context mApplicationContext;
+
+ private int mPrevNumColumns, mPrevNumRows;
+
+ @Test
+ public void testAllCases() throws IOException {
+ List<ReorderAlgorithmUnitTestCase> testCases = getTestCases(
+ "ReorderAlgorithmUnitTest/reorder_algorithm_test_cases");
+ mApplicationContext = new ActivityContextWrapper(getApplicationContext());
+ List<Integer> failingCases = new ArrayList<>();
+ for (int i = 0; i < testCases.size(); i++) {
+ try {
+ evaluateTestCase(testCases.get(i));
+ } catch (AssertionError e) {
+ e.printStackTrace();
+ failingCases.add(i);
+ }
+ }
+ assertEquals("Some test cases failed " + Arrays.toString(failingCases.toArray()), 0,
+ failingCases.size());
+ }
+
+ private void addViewInCellLayout(CellLayout cellLayout, int cellX, int cellY, int spanX,
+ int spanY, boolean isWidget) {
+ View cell = isWidget ? new View(mApplicationContext) : new DoubleShadowBubbleTextView(
+ mApplicationContext);
+ cell.setLayoutParams(new CellLayoutLayoutParams(cellX, cellY, spanX, spanY));
+ cellLayout.addViewToCellLayout(cell, -1, cell.getId(),
+ (CellLayoutLayoutParams) cell.getLayoutParams(), true);
+ }
+
+ public CellLayout createCellLayout(int width, int height) {
+ Context c = mApplicationContext;
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
+ // modify the device profile.
+ dp.inv.numColumns = width;
+ dp.inv.numRows = height;
+
+ CellLayout cl = new CellLayout(getWrappedContext(c, dp));
+ // I put a very large number for width and height so that all the items can fit, it doesn't
+ // need to be exact, just bigger than the sum of cell border
+ cl.measure(View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY));
+
+ cl.measure(View.MeasureSpec.makeMeasureSpec(cl.getDesiredWidth(), View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(cl.getDesiredHeight(), View.MeasureSpec.EXACTLY));
+ return cl;
+ }
+
+ private Context getWrappedContext(Context context, DeviceProfile dp) {
+ return new ActivityContextWrapper(context) {
+ public DeviceProfile getDeviceProfile() {
+ return dp;
+ }
+ };
+ }
+
+ public CellLayout.ItemConfiguration solve(CellLayoutBoard board, int x, int y, int spanX,
+ int spanY, int minSpanX, int minSpanY) {
+ CellLayout cl = createCellLayout(board.getWidth(), board.getHeight());
+
+ // The views have to be sorted or the result can vary
+ board.getIcons()
+ .stream()
+ .map(CellLayoutBoard.IconPoint::getCoord)
+ .sorted(Comparator.comparing(p -> ((Point) p).x).thenComparing(p -> ((Point) p).y))
+ .forEach(p -> addViewInCellLayout(cl, p.x, p.y, 1, 1, false));
+ board.getWidgets().stream()
+ .sorted(Comparator.comparing(CellLayoutBoard.WidgetRect::getCellX)
+ .thenComparing(CellLayoutBoard.WidgetRect::getCellY))
+ .forEach(widget -> addViewInCellLayout(cl, widget.getCellX(), widget.getCellY(),
+ widget.getSpanX(), widget.getSpanY(), true));
+
+ int[] testCaseXYinPixels = new int[2];
+ cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels);
+ CellLayout.ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
+ testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY,
+ null);
+ if (solution == null) {
+ solution = new CellLayout.ItemConfiguration();
+ solution.isSolution = false;
+ }
+ return solution;
+ }
+
+ public CellLayoutBoard boardFromSolution(CellLayout.ItemConfiguration solution, int width,
+ int height) {
+ // Update the views with solution value
+ solution.map.forEach((key, val) -> key.setLayoutParams(
+ new CellLayoutLayoutParams(val.cellX, val.cellY, val.spanX, val.spanY)));
+ CellLayoutBoard board = CellLayoutTestUtils.viewsToBoard(
+ new ArrayList<>(solution.map.keySet()), width, height);
+ board.addWidget(solution.cellX, solution.cellY, solution.spanX, solution.spanY,
+ 'z');
+ return board;
+ }
+
+ public void evaluateTestCase(ReorderAlgorithmUnitTestCase testCase) {
+ CellLayout.ItemConfiguration solution = solve(testCase.startBoard, testCase.x,
+ testCase.y, testCase.spanX, testCase.spanY, testCase.minSpanX,
+ testCase.minSpanY);
+ assertEquals("should be a valid solution", solution.isSolution,
+ testCase.isValidSolution);
+ if (testCase.isValidSolution) {
+ CellLayoutBoard finishBoard = boardFromSolution(solution,
+ testCase.startBoard.getWidth(), testCase.startBoard.getHeight());
+ assertTrue("End result and test case result board doesn't match ",
+ finishBoard.compareTo(testCase.endBoard) == 0);
+ }
+ }
+
+ @Before
+ public void storePreviousValues() {
+ Context c = new ActivityContextWrapper(getApplicationContext());
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
+ mPrevNumColumns = dp.inv.numColumns;
+ mPrevNumRows = dp.inv.numColumns;
+ }
+
+ @After
+ public void restorePreviousValues() {
+ Context c = new ActivityContextWrapper(getApplicationContext());
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(c).getDeviceProfile(c).copy(c);
+ dp.inv.numColumns = mPrevNumColumns;
+ dp.inv.numRows = mPrevNumRows;
+ }
+
+ @SuppressWarnings("UnusedMethod")
+ /**
+ * Utility function used to generate all the test cases
+ */
+ private ReorderAlgorithmUnitTestCase generateRandomTestCase() {
+ ReorderAlgorithmUnitTestCase testCase = new ReorderAlgorithmUnitTestCase();
+
+ int width = getRandom(3, 8);
+ int height = getRandom(3, 8);
+
+ int targetWidth = getRandom(1, width - 2);
+ int targetHeight = getRandom(1, height - 2);
+
+ int minTargetWidth = getRandom(1, targetWidth);
+ int minTargetHeight = getRandom(1, targetHeight);
+
+ int x = getRandom(0, width - targetWidth);
+ int y = getRandom(0, height - targetHeight);
+
+ CellLayoutBoard board = generateBoard(new CellLayoutBoard(width, height),
+ new Rect(0, 0, width, height), targetWidth * targetHeight);
+
+ CellLayout.ItemConfiguration solution = solve(board, x, y, targetWidth, targetHeight,
+ minTargetWidth, minTargetHeight);
+
+ CellLayoutBoard finishBoard = solution.isSolution ? boardFromSolution(solution,
+ board.getWidth(), board.getHeight()) : new CellLayoutBoard(board.getWidth(),
+ board.getHeight());
+
+
+ testCase.startBoard = board;
+ testCase.endBoard = finishBoard;
+ testCase.isValidSolution = solution.isSolution;
+ testCase.x = x;
+ testCase.y = y;
+ testCase.spanX = targetWidth;
+ testCase.spanY = targetHeight;
+ testCase.minSpanX = minTargetWidth;
+ testCase.minSpanY = minTargetHeight;
+ testCase.type = solution.area() == 1 ? "icon" : "widget";
+
+ return testCase;
+ }
+
+ private int getRandom(int start, int end) {
+ int random = end == 0 ? 0 : new Random().nextInt(end);
+ return start + random;
+ }
+
+ private CellLayoutBoard generateBoard(CellLayoutBoard board, Rect area,
+ int emptySpaces) {
+ if (area.height() * area.width() <= 0) return board;
+
+ int width = getRandom(1, area.width() - 1);
+ int height = getRandom(1, area.height() - 1);
+
+ int x = area.left + getRandom(0, area.width() - width);
+ int y = area.top + getRandom(0, area.height() - height);
+
+ if (emptySpaces > 0) {
+ emptySpaces -= width * height;
+ } else if (width * height > 1) {
+ board.addWidget(x, y, width, height);
+ } else {
+ board.addIcon(x, y);
+ }
+
+ generateBoard(board,
+ new Rect(area.left, area.top, area.right, y), emptySpaces);
+ generateBoard(board,
+ new Rect(area.left, y, x, area.bottom), emptySpaces);
+ generateBoard(board,
+ new Rect(x, y + height, area.right, area.bottom), emptySpaces);
+ generateBoard(board,
+ new Rect(x + width, y, area.right, y + height), emptySpaces);
+
+ return board;
+ }
+
+ private static List<ReorderAlgorithmUnitTestCase> getTestCases(String testPath)
+ throws IOException {
+ List<ReorderAlgorithmUnitTestCase> cases = new ArrayList<>();
+ Iterator<CellLayoutTestCaseReader.TestSection> iterableSection =
+ CellLayoutTestCaseReader.readFromFile(testPath).parse().iterator();
+ while (iterableSection.hasNext()) {
+ cases.add(ReorderAlgorithmUnitTestCase.readNextCase(iterableSection));
+ }
+ return cases;
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
new file mode 100644
index 0000000..4274130
--- /dev/null
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2023 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.celllayout;
+
+import java.util.Iterator;
+
+/**
+ * Represents a test case for {@code ReorderAlgorithmUnitTest}. The test cases are generated from
+ * text, an example of a test is the following:
+ *
+ * board: 10x8
+ * aaaaaaaaai
+ * bbbbbcciii
+ * ---------f
+ * ---------f
+ * ---------f
+ * ---------i
+ * iiddddddii
+ * iieeiiiiii
+ * arguments: 2 5 7 1 3 1 widget valid
+ * board: 10x8
+ * bbbbbbbbbi
+ * eeeeecciii
+ * ---------a
+ * ---------a
+ * ---------a
+ * --zzzzzzzi
+ * iiddddddii
+ * iiffiiiiii
+ *
+ *
+ * This represents a Workspace boards and a dragged widget that wants to be dropped on the
+ * workspace. The endBoard represents the result from such drag
+ * The first board is the startBoard, the arguments are as follow: cellX, cellY, widget spanX,
+ * widget spanY, minimum spanX, minimum spanX, type of object being drag (icon, widget, folder ),
+ * if the resulting board is a valid solution or not reorder was found.
+ *
+ * For more information on how to read the board please go to the text file
+ * reorder_algorithm_test_cases
+ */
+public class ReorderAlgorithmUnitTestCase {
+
+ CellLayoutBoard startBoard;
+
+ int x, y, spanX, spanY, minSpanX, minSpanY;
+ String type;
+ boolean isValidSolution;
+ CellLayoutBoard endBoard;
+
+ public static ReorderAlgorithmUnitTestCase readNextCase(
+ Iterator<CellLayoutTestCaseReader.TestSection> sections) {
+ ReorderAlgorithmUnitTestCase testCase = new ReorderAlgorithmUnitTestCase();
+ CellLayoutTestCaseReader.Board startBoard =
+ (CellLayoutTestCaseReader.Board) sections.next();
+ testCase.startBoard = CellLayoutBoard.boardFromString(startBoard.board);
+ CellLayoutTestCaseReader.Arguments arguments =
+ (CellLayoutTestCaseReader.Arguments) sections.next();
+ testCase.x = Integer.parseInt(arguments.arguments[0]);
+ testCase.y = Integer.parseInt(arguments.arguments[1]);
+ testCase.spanX = Integer.parseInt(arguments.arguments[2]);
+ testCase.spanY = Integer.parseInt(arguments.arguments[3]);
+ testCase.minSpanX = Integer.parseInt(arguments.arguments[4]);
+ testCase.minSpanY = Integer.parseInt(arguments.arguments[5]);
+ testCase.type = arguments.arguments[6];
+ testCase.isValidSolution = arguments.arguments[7].compareTo("valid") == 0;
+
+ CellLayoutTestCaseReader.Board endBoard = (CellLayoutTestCaseReader.Board) sections.next();
+ testCase.endBoard = CellLayoutBoard.boardFromString(endBoard.board);
+ return testCase;
+ }
+
+ public CellLayoutBoard getStartBoard() {
+ return startBoard;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ public int getSpanX() {
+ return spanX;
+ }
+
+ public void setSpanX(int spanX) {
+ this.spanX = spanX;
+ }
+
+ public int getSpanY() {
+ return spanY;
+ }
+
+ public void setSpanY(int spanY) {
+ this.spanY = spanY;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public boolean isValidSolution() {
+ return isValidSolution;
+ }
+
+ public void setValidSolution(boolean validSolution) {
+ isValidSolution = validSolution;
+ }
+
+ public CellLayoutBoard getEndBoard() {
+ return endBoard;
+ }
+
+ public void setEndBoard(CellLayoutBoard endBoard) {
+ this.endBoard = endBoard;
+ }
+
+ @Override
+ public String toString() {
+ String valid = isValidSolution ? "valid" : "invalid";
+ return startBoard + "arguments: " + x + " " + y + " " + spanX + " " + spanY + " " + minSpanX
+ + " " + minSpanY + " " + type + " " + valid + "\n" + endBoard;
+ }
+}
diff --git a/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt b/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt
new file mode 100644
index 0000000..fffa6d7
--- /dev/null
+++ b/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt
@@ -0,0 +1,368 @@
+package com.android.launcher3.logging
+
+import androidx.core.util.isEmpty
+import androidx.test.annotation.UiThreadTest
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Unit test for [StartupLatencyLogger]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StartupLatencyLoggerTest {
+
+ private val underTest: StartupLatencyLogger =
+ StartupLatencyLogger(StatsLogManager.StatsLatencyLogger.LatencyType.COLD)
+
+ @Before
+ fun setup() {
+ underTest.setIsInTest()
+ }
+
+ @Test
+ @UiThreadTest
+ fun logTotalDurationStart() {
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ val startTime =
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.id
+ )
+ assertThat(startTime).isEqualTo(100)
+ assertThat(underTest.endTimeByEvent.isEmpty()).isTrue()
+ }
+
+ @Test
+ @UiThreadTest
+ fun logTotalDurationEnd() {
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ underTest.logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ val endTime =
+ underTest.endTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.id
+ )
+ assertThat(endTime).isEqualTo(100)
+ }
+
+ @Test
+ @UiThreadTest
+ fun logStartOfOtherEvents_withoutLogStartOfTotalDuration_noOp() {
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE,
+ 100
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION,
+ 101
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC,
+ 102
+ )
+
+ assertThat(underTest.startTimeByEvent.isEmpty()).isTrue()
+ }
+
+ @Test
+ @UiThreadTest
+ fun logStartOfOtherEvents_afterLogStartOfTotalDuration_logged() {
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE,
+ 100
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION,
+ 101
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC,
+ 102
+ )
+
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(4)
+ }
+
+ @Test
+ @UiThreadTest
+ fun logDuplicatedStartEvent_2ndEvent_notLogged() {
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 101
+ )
+
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(1)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.id
+ )
+ )
+ .isEqualTo(100)
+ }
+
+ @Test
+ @UiThreadTest
+ fun loadStartOfWorkspace_thenEndWithAsync_logAsyncStart() {
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+ .logWorkspaceLoadStartTime(111)
+
+ underTest.logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC,
+ 120
+ )
+
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(2)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC
+ .id
+ )
+ )
+ .isEqualTo(111)
+ }
+
+ @Test
+ @UiThreadTest
+ fun loadStartOfWorkspace_thenEndWithSync_logSyncStart() {
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+ .logWorkspaceLoadStartTime(111)
+
+ underTest.logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC,
+ 120
+ )
+
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(2)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+ .id
+ )
+ )
+ .isEqualTo(111)
+ }
+
+ @Test
+ @UiThreadTest
+ fun loadStartOfWorkspaceLoadSync_thenAsync_asyncNotLogged() {
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC,
+ 110
+ )
+
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC,
+ 111
+ )
+
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(2)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+ .id
+ )
+ )
+ .isEqualTo(110)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC
+ .id
+ )
+ )
+ .isEqualTo(0)
+ }
+
+ @Test
+ @UiThreadTest
+ fun loadStartOfWorkspaceLoadAsync_thenSync_syncNotLogged() {
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC,
+ 111
+ )
+
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC,
+ 112
+ )
+
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(2)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC
+ .id
+ )
+ )
+ .isEqualTo(111)
+ assertThat(
+ underTest.startTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+ .id
+ )
+ )
+ .isEqualTo(0)
+ }
+
+ @Test
+ @UiThreadTest
+ fun logEndOfEvent_withoutStartEvent_notLogged() {
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ underTest.logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC,
+ 120
+ )
+
+ assertThat(underTest.endTimeByEvent.size()).isEqualTo(0)
+ assertThat(
+ underTest.endTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+ .id
+ )
+ )
+ .isEqualTo(0)
+ }
+
+ @Test
+ @UiThreadTest
+ fun logEndOfEvent_afterEndOfTotalDuration_notLogged() {
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+ .logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 120
+ )
+
+ underTest.logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC,
+ 121
+ )
+
+ assertThat(underTest.endTimeByEvent.size()).isEqualTo(1)
+ assertThat(
+ underTest.endTimeByEvent.get(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+ .id
+ )
+ )
+ .isEqualTo(0)
+ }
+
+ @Test
+ @UiThreadTest
+ fun logCardinality_setCardinality() {
+ underTest.logCardinality(-1)
+ underTest.logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+
+ underTest.logCardinality(235)
+
+ assertThat(underTest.cardinality).isEqualTo(235)
+ }
+
+ @Test
+ @UiThreadTest
+ fun reset_clearState() {
+ underTest
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 100
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE,
+ 100
+ )
+ .logStart(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION,
+ 110
+ )
+ .logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION,
+ 115
+ )
+ .logWorkspaceLoadStartTime(120)
+ .logCardinality(235)
+ .logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE,
+ 100
+ )
+ .logEnd(
+ StatsLogManager.LauncherLatencyEvent
+ .LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_ASYNC,
+ 140
+ )
+ .logEnd(
+ StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+ 160
+ )
+ assertThat(underTest.startTimeByEvent.size()).isEqualTo(4)
+ assertThat(underTest.endTimeByEvent.size()).isEqualTo(4)
+ assertThat(underTest.cardinality).isEqualTo(235)
+
+ underTest.reset()
+
+ assertThat(underTest.startTimeByEvent.isEmpty()).isTrue()
+ assertThat(underTest.endTimeByEvent.isEmpty()).isTrue()
+ assertThat(underTest.cardinality).isEqualTo(StartupLatencyLogger.UNSET_INT)
+ assertThat(underTest.workspaceLoadStartTime).isEqualTo(StartupLatencyLogger.UNSET_LONG)
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index 0a1a9ba..cea95e5 100644
--- a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -39,6 +39,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
+import com.android.launcher3.pm.UserCache;
import org.junit.Before;
import org.junit.Test;
@@ -222,7 +223,9 @@
private class MyDatabaseHelper extends DatabaseHelper {
MyDatabaseHelper() {
- super(mContext, DB_FILE, false);
+ super(mContext, DB_FILE,
+ UserCache.INSTANCE.get(mContext)::getSerialNumberForUser,
+ () -> { });
}
@Override
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index f24f0da..63dbaa7 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.model
+import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.Cursor
@@ -23,6 +24,7 @@
import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
@@ -31,10 +33,7 @@
import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.LauncherDbUtils
-import com.android.launcher3.util.LauncherModelHelper
-import com.android.launcher3.util.LauncherModelHelper.*
import com.google.common.truth.Truth.assertThat
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,11 +42,12 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class GridSizeMigrationUtilTest {
- private lateinit var modelHelper: LauncherModelHelper
+
private lateinit var context: Context
- private lateinit var db: SQLiteDatabase
private lateinit var validPackages: Set<String>
private lateinit var idp: InvariantDeviceProfile
+ private lateinit var dbHelper: DatabaseHelper
+ private lateinit var db: SQLiteDatabase
private val testPackage1 = "com.android.launcher3.validpackage1"
private val testPackage2 = "com.android.launcher3.validpackage2"
private val testPackage3 = "com.android.launcher3.validpackage3"
@@ -61,13 +61,17 @@
@Before
fun setUp() {
- modelHelper = LauncherModelHelper()
- context = modelHelper.sandboxContext
- db = modelHelper.provider.db
+ context = InstrumentationRegistry.getInstrumentation().targetContext
+ dbHelper =
+ DatabaseHelper(
+ context,
+ null,
+ UserCache.INSTANCE.get(context)::getSerialNumberForUser
+ ) {}
+ db = dbHelper.writableDatabase
validPackages =
setOf(
- TEST_PACKAGE,
testPackage1,
testPackage2,
testPackage3,
@@ -86,11 +90,6 @@
addTableToDb(db, userSerial, false, TMP_TABLE)
}
- @After
- fun tearDown() {
- modelHelper.destroy()
- }
-
/**
* Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is not
* needed anymore
@@ -99,26 +98,26 @@
@Throws(Exception::class)
fun testMigration() {
// Src Hotseat icons
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Src grid icons
// _ _ _ _ _
// _ _ _ _ 5
// _ _ 6 _ 7
// _ _ 8 _ 9
// _ _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage9, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 1, testPackage5, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage6, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 2, testPackage7, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage8, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
// Dest hotseat icons
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Dest grid icons
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage10)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -126,8 +125,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -138,12 +136,13 @@
// Check hotseat items
var c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -168,12 +167,13 @@
// Check workspace items
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -209,30 +209,30 @@
fun testMigrationBackAndForth() {
// Hotseat items in grid A
// 1 2 _ 3 4
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Workspace items in grid A
// _ _ _ _ _
// _ _ _ _ 5
// _ _ 6 _ 7
// _ _ 8 _ _
// _ _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 1, testPackage5, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage6, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 2, testPackage7, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage8, 8, TMP_TABLE)
// Hotseat items in grid B
// 2 _ _ _
- modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Workspace items in grid B
// _ _ _ _
// _ _ _ 10
// _ _ _ _
// _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 3, testPackage10)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -241,8 +241,7 @@
val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
// migrate from A -> B
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
readerGridA,
readerGridB,
idp.numDatabaseHotseatIcons,
@@ -253,12 +252,13 @@
// Check hotseat items in grid B
var c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -272,12 +272,13 @@
// Check workspace items in grid B
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -294,12 +295,11 @@
assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
// add item in B
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 2, testPackage9)
// migrate from B -> A
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
readerGridB,
readerGridA,
5,
@@ -309,12 +309,13 @@
)
// Check hotseat items in grid A
c =
- context.contentResolver.query(
- TMP_CONTENT_URI,
+ db.query(
+ TMP_TABLE,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -328,12 +329,13 @@
// Check workspace items in grid A
c =
- context.contentResolver.query(
- TMP_CONTENT_URI,
+ db.query(
+ TMP_TABLE,
arrayOf(SCREEN, CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -354,12 +356,11 @@
assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
// remove item from B
- modelHelper.deleteItem(7, TMP_TABLE)
+ db.delete(TMP_TABLE, "$_ID=7", null)
// migrate from A -> B
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
readerGridA,
readerGridB,
idp.numDatabaseHotseatIcons,
@@ -370,12 +371,13 @@
// Check hotseat items in grid B
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -389,12 +391,13 @@
// Check workspace items in grid B
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -443,10 +446,28 @@
fun migrateToLargerHotseat() {
val srcHotseatItems =
intArrayOf(
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
- modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ addItem(
+ ITEM_TYPE_APPLICATION,
+ 0,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage1,
+ 1,
+ TMP_TABLE
+ ),
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE),
+ addItem(
+ ITEM_TYPE_APPLICATION,
+ 2,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage3,
+ 3,
+ TMP_TABLE
+ ),
+ addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
)
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
idp.numDatabaseHotseatIcons = 6
@@ -455,8 +476,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -467,12 +487,13 @@
// Check hotseat items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -501,11 +522,11 @@
@Test
fun migrateFromLargerHotseat() {
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -513,8 +534,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -525,12 +545,13 @@
// Check hotseat items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -568,11 +589,11 @@
enableNewMigrationLogic("4,4")
// Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 1, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 2, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 3, 3, testPackage5, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage1, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage2, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 1, testPackage3, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 2, testPackage4, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 3, 3, testPackage5, 9, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 6
@@ -581,8 +602,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -593,12 +613,13 @@
// Get workspace items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(INTENT, SCREEN),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -630,11 +651,11 @@
enableNewMigrationLogic("2,2")
// Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 1, testPackage1, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 1, testPackage2, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 0, 0, testPackage3, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 1, 0, testPackage4, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 0, 0, testPackage5, 9, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 5
@@ -642,8 +663,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -654,12 +674,13 @@
// Get workspace items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(INTENT, SCREEN),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -691,11 +712,11 @@
enableNewMigrationLogic("5,5")
// Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 1, testPackage1, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 1, testPackage2, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 0, 0, testPackage3, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 1, 0, testPackage4, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 0, 0, testPackage5, 9, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -703,8 +724,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -715,12 +735,13 @@
// Get workspace items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(INTENT, SCREEN),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -747,4 +768,48 @@
private fun enableNewMigrationLogic(srcGridSize: String) {
LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize))
}
+
+ private fun addItem(
+ type: Int,
+ screen: Int,
+ container: Int,
+ x: Int,
+ y: Int,
+ packageName: String?
+ ): Int {
+ return addItem(
+ type,
+ screen,
+ container,
+ x,
+ y,
+ packageName,
+ dbHelper.generateNewItemId(),
+ TABLE_NAME
+ )
+ }
+
+ private fun addItem(
+ type: Int,
+ screen: Int,
+ container: Int,
+ x: Int,
+ y: Int,
+ packageName: String?,
+ id: Int,
+ tableName: String
+ ): Int {
+ val values = ContentValues()
+ values.put(_ID, id)
+ values.put(CONTAINER, container)
+ values.put(SCREEN, screen)
+ values.put(CELLX, x)
+ values.put(CELLY, y)
+ values.put(SPANX, 1)
+ values.put(SPANY, 1)
+ values.put(ITEM_TYPE, type)
+ values.put(INTENT, Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0))
+ db.insert(tableName, null, values)
+ return id
+ }
}
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index d192be4..78812c0 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -59,7 +59,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
@@ -102,7 +101,7 @@
});
UserManagerState ums = new UserManagerState();
- mLoaderCursor = new LoaderCursor(mCursor, Favorites.CONTENT_URI, mApp, ums);
+ mLoaderCursor = new LoaderCursor(mCursor, mApp, ums);
ums.allUsers.put(0, Process.myUserHandle());
}
diff --git a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index 42c9f11..b9da16a 100644
--- a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -180,7 +180,8 @@
}
@Override
- public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks) {
+ public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
+ int workspaceItemCount, boolean isBindSync) {
mPageBoundSync = boundPages;
mPendingTasks = pendingTasks;
}
diff --git a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
index 2b6f9ff..54b8489 100644
--- a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
+++ b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
@@ -45,6 +45,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.DatabaseHelper;
import com.android.launcher3.model.DbDowngradeHelper;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.settings.SettingsActivity;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IOUtils;
@@ -165,12 +166,11 @@
private class MyDatabaseHelper extends DatabaseHelper {
MyDatabaseHelper() {
- super(mContext, null, false);
+ super(mContext, null, UserCache.INSTANCE.get(mContext)::getSerialNumberForUser,
+ () -> { });
}
@Override
protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
-
- protected void onEmptyDbCreated() { }
}
}
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 67de1f5..1b1b294 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -46,6 +46,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.DatabaseHelper;
+import com.android.launcher3.model.ModelDbController;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,13 +64,13 @@
@Test
public void testGetProfileId() throws Exception {
- SQLiteDatabase db = new MyDatabaseHelper(23).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(23).getDb();
assertEquals(23, new RestoreDbTask().getDefaultProfileId(db));
}
@Test
public void testMigrateProfileId() throws Exception {
- SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
@@ -89,7 +90,7 @@
@Test
public void testChangeDefaultColumn() throws Exception {
- SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
@@ -120,20 +121,20 @@
long workProfileId = myProfileId + 2;
long workProfileId_old = myProfileId + 3;
- MyDatabaseHelper helper = new MyDatabaseHelper(myProfileId);
- SQLiteDatabase db = helper.getWritableDatabase();
+ MyModelDbController controller = new MyModelDbController(myProfileId);
+ SQLiteDatabase db = controller.getDb();
BackupManager bm = spy(new BackupManager(context));
doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
doReturn(mWorkUser).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
- helper.users.put(workProfileId, mWorkUser);
+ controller.users.put(workProfileId, mWorkUser);
- addIconsBulk(helper, 10, 1, myProfileId_old);
- addIconsBulk(helper, 6, 2, workProfileId_old);
+ addIconsBulk(controller, 10, 1, myProfileId_old);
+ addIconsBulk(controller, 6, 2, workProfileId_old);
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, helper, helper.getWritableDatabase(), bm);
+ task.sanitizeDB(context, controller, controller.getDb(), bm);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -151,20 +152,20 @@
long myProfileId_old = myProfileId + 1;
long workProfileId_old = myProfileId + 3;
- MyDatabaseHelper helper = new MyDatabaseHelper(myProfileId);
- SQLiteDatabase db = helper.getWritableDatabase();
+ MyModelDbController controller = new MyModelDbController(myProfileId);
+ SQLiteDatabase db = controller.getDb();
BackupManager bm = spy(new BackupManager(context));
doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
// Work profile is not migrated
doReturn(null).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
- addIconsBulk(helper, 10, 1, myProfileId_old);
- addIconsBulk(helper, 6, 2, workProfileId_old);
+ addIconsBulk(controller, 10, 1, myProfileId_old);
+ addIconsBulk(controller, 6, 2, workProfileId_old);
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, helper, helper.getWritableDatabase(), bm);
+ task.sanitizeDB(context, controller, controller.getDb(), bm);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -173,12 +174,13 @@
assertEquals(10, getCount(db, "select * from favorites"));
}
- private void addIconsBulk(DatabaseHelper helper, int count, int screen, long profileId) {
+ private void addIconsBulk(MyModelDbController controller,
+ int count, int screen, long profileId) {
int columns = LauncherAppState.getIDP(getInstrumentation().getTargetContext()).numColumns;
String packageName = getInstrumentation().getContext().getPackageName();
for (int i = 0; i < count; i++) {
ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites._ID, helper.generateNewItemId());
+ values.put(LauncherSettings.Favorites._ID, controller.generateNewItemId());
values.put(LauncherSettings.Favorites.CONTAINER, CONTAINER_DESKTOP);
values.put(LauncherSettings.Favorites.SCREEN, screen);
values.put(LauncherSettings.Favorites.CELLX, i % columns);
@@ -189,11 +191,11 @@
values.put(LauncherSettings.Favorites.ITEM_TYPE, ITEM_TYPE_APPLICATION);
values.put(LauncherSettings.Favorites.INTENT,
new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
- helper.getWritableDatabase().insert(TABLE_NAME, null, values);
+
+ controller.insert(TABLE_NAME, values);
}
}
-
@Test
public void testRemoveScreenIdGaps_firstScreenEmpty() {
runRemoveScreenIdGapsTest(
@@ -216,7 +218,7 @@
}
private void runRemoveScreenIdGapsTest(int[] screenIds, int[] expectedScreenIds) {
- SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < screenIds.length; i++) {
ContentValues values = new ContentValues();
@@ -254,14 +256,15 @@
}
}
- private class MyDatabaseHelper extends DatabaseHelper {
+ private class MyModelDbController extends ModelDbController {
- public final LongSparseArray<UserHandle> users;
+ public final LongSparseArray<UserHandle> users = new LongSparseArray<>();
- MyDatabaseHelper(long profileId) {
- super(getInstrumentation().getTargetContext(), null, false);
- users = new LongSparseArray<>();
+ MyModelDbController(long profileId) {
+ super(getInstrumentation().getTargetContext());
users.put(profileId, myUserHandle());
+ mOpenHelper = new DatabaseHelper(getInstrumentation().getTargetContext(), null,
+ this::getSerialNumberForUser, () -> { });
}
@Override
@@ -269,10 +272,5 @@
int index = users.indexOfValue(user);
return index >= 0 ? users.keyAt(index) : -1;
}
-
- @Override
- protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
-
- protected void onEmptyDbCreated() { }
}
}
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index bf31e39..6fca965 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -363,12 +363,6 @@
sandboxContext.getContentResolver().insert(contentUri, values);
}
- public void deleteItem(int itemId, @NonNull final String tableName) {
- final Uri uri = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId);
- sandboxContext.getContentResolver().delete(uri, null, null);
- }
-
/**
* Sets up a mock provider to load the provided layout by default, next time the layout loads
*/
@@ -426,7 +420,7 @@
}
public SQLiteDatabase getDb() {
- return getModelDbController().getDatabaseHelper().getWritableDatabase();
+ return getModelDbController().getDb();
}
}