Merge changes from topic "launcher3-gradle-fixes" into ub-launcher3-master
* changes:
Fix Launcher3 gradle issue with src_plugins.
Fix Launcher3 gradle src_shorcuts_overrides issue.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4ac51ab..1a485ed 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
<!--
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
Refer comments around specific entries on how to extend individual components.
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index 0080898..25518af 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -22,7 +22,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index 63820f6..ce66448 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -151,7 +151,7 @@
private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) {
mIconDpi = iconDpi;
mDefaultIcons.clear();
-
+ mIconDb.clear();
mIconDb.close();
mIconDb = new IconDB(mContext, mDbFileName, iconPixelSize);
mCache.clear();
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 6e7534d..8af310c 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 20a83b5..a1cee82 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -80,8 +80,8 @@
import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
/**
@@ -204,7 +204,15 @@
mLauncher.getStateManager().setCurrentAnimation(anim);
Rect windowTargetBounds = getWindowTargetBounds(targetCompats);
- playIconAnimators(anim, v, windowTargetBounds);
+ boolean isAllOpeningTargetTrs = true;
+ for (int i = 0; i < targetCompats.length; i++) {
+ RemoteAnimationTargetCompat target = targetCompats[i];
+ if (target.mode == MODE_OPENING) {
+ isAllOpeningTargetTrs &= target.isTranslucent;
+ }
+ if (!isAllOpeningTargetTrs) break;
+ }
+ playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs);
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
getLauncherContentAnimator(true /* isAppOpening */);
@@ -432,7 +440,8 @@
/**
* Animators for the "floating view" of the view used to launch the target.
*/
- private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds) {
+ private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
+ boolean toggleVisibility) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
mFloatingView = new View(mLauncher);
if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
@@ -485,7 +494,9 @@
// Swap the two views in place.
((ViewGroup) mDragLayer.getParent()).addView(mFloatingView);
- v.setVisibility(View.INVISIBLE);
+ if (toggleVisibility) {
+ v.setVisibility(View.INVISIBLE);
+ }
int[] dragLayerBounds = new int[2];
mDragLayer.getLocationOnScreen(dragLayerBounds);
@@ -580,8 +591,8 @@
MODE_OPENING);
RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets,
MODE_CLOSING);
- SyncRtSurfaceTransactionApplier surfaceApplier = new SyncRtSurfaceTransactionApplier(
- mFloatingView);
+ SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+ new SyncRtSurfaceTransactionApplierCompat(mFloatingView);
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(APP_LAUNCH_DURATION);
@@ -740,8 +751,8 @@
* Animator that controls the transformations of the windows the targets that are closing.
*/
private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
- SyncRtSurfaceTransactionApplier surfaceApplier =
- new SyncRtSurfaceTransactionApplier(mDragLayer);
+ SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+ new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
Matrix matrix = new Matrix();
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
int duration = CLOSING_TRANSITION_DURATION_MS;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
index eaf4183..5afeca7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
@@ -34,7 +34,16 @@
}
@Override
- public void setEnabled(ComponentName component, boolean enabled) {
+ public void setEnabled(ComponentName component) {
+ setState(component, true);
+ }
+
+ @Override
+ public void setDisabled(ComponentName component, int reason) {
+ setState(component, reason == ENABLED);
+ }
+
+ private void setState(ComponentName component, boolean enabled) {
putBoolean(pluginEnabledKey(component), enabled);
}
@@ -44,6 +53,11 @@
}
@Override
+ public int getDisableReason(ComponentName componentName) {
+ return isEnabled(componentName) ? ENABLED : DISABLED_MANUALLY;
+ }
+
+ @Override
public void putBoolean(String key, boolean value) {
mSharedPrefs.edit().putBoolean(key, value).apply();
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index f60572c..4646fd7f 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -45,7 +45,6 @@
import android.os.Looper;
import android.view.View;
-import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -194,7 +193,7 @@
int topMargin = context.getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
int paddingTop = targetRect.rect.top - topMargin - dp.getInsets().top;
- int paddingBottom = dp.availableHeightPx + dp.getInsets().top - targetRect.rect.bottom;
+ int paddingBottom = dp.heightPx - dp.getInsets().bottom - targetRect.rect.bottom;
return FastOverviewState.OVERVIEW_TRANSLATION_FACTOR * (paddingBottom - paddingTop);
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index a3207f5..9bbe57a 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -66,7 +66,7 @@
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.TransactionCompat;
import java.util.ArrayList;
@@ -344,7 +344,7 @@
clipHelper.prepareAnimation(false /* isOpening */);
ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
- .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplier(rootView));
+ .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index b07f8af..da5c4fa 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -289,16 +290,20 @@
}
mPrevPrevProgressDelta = mPrevProgressDelta;
mPrevProgressDelta = progressDelta;
- float scrollDiff = nextPage.getWidth() + mRecentsView.getPageSpacing();
- int scrollDir = mRecentsView.isRtl() ? -1 : 1;
- int linearScrollDiff = (int) (progress * scrollDiff * scrollDir);
- float accelScrollDiff = ACCEL.getInterpolation(progress) * scrollDiff * scrollDir;
+ int startScroll = mRecentsView.getScrollForPage(
+ mRecentsView.indexOfChild(currentPage));
+ int scrollDiff = mRecentsView.getScrollForPage(mRecentsView.indexOfChild(nextPage))
+ - startScroll;
+
+ int linearScrollDiff = (int) (progress * scrollDiff);
currentPage.setZoomScale(1 - DEACCEL_3.getInterpolation(progress)
* TaskView.EDGE_SCALE_DOWN_FACTOR);
- currentPage.setTranslationX(linearScrollDiff + accelScrollDiff);
+ if (!ENABLE_TASK_STABILIZER.get()) {
+ float accelScrollDiff = ACCEL.getInterpolation(progress) * scrollDiff;
+ currentPage.setTranslationX(linearScrollDiff + accelScrollDiff);
+ }
nextPage.setTranslationZ(1);
nextPage.setTranslationY(currentPage.getTranslationY());
- int startScroll = mRecentsView.isRtl() ? mRecentsView.getMaxScrollX() : 0;
mRecentsView.setScrollX(startScroll + linearScrollDiff);
}
return;
@@ -358,9 +363,16 @@
: mStartedFromHome
? QUICK_SCRUB_FROM_HOME_START_DURATION
: QUICK_SCRUB_FROM_APP_START_DURATION;
- int pageToGoTo = mStartedFromHome || mIsQuickSwitch
- ? 0
- : mRecentsView.getNextPage() + 1;
+ final int pageToGoTo;
+ if (mStartedFromHome) {
+ pageToGoTo = 0;
+ } else if (mIsQuickSwitch) {
+ TaskView tv = mRecentsView.getRunningTaskView();
+ pageToGoTo = tv != null ? mRecentsView.indexOfChild(tv)
+ : mRecentsView.getNextPage();
+ } else {
+ pageToGoTo = mRecentsView.getNextPage() + 1;
+ }
goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
QUICK_SCRUB_START_INTERPOLATOR);
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index fec38bf..cedd952 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -16,8 +16,10 @@
package com.android.quickstep;
+import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
+import android.os.Build;
import android.os.Process;
import android.util.SparseBooleanArray;
import com.android.launcher3.MainThreadExecutor;
@@ -36,16 +38,20 @@
/**
* Manages the recent task list from the system, caching it as necessary.
*/
+@TargetApi(Build.VERSION_CODES.P)
public class RecentTasksList extends TaskStackChangeListener {
private final KeyguardManagerCompat mKeyguardManager;
private final MainThreadExecutor mMainThreadExecutor;
private final BackgroundExecutor mBgThreadExecutor;
+ private final TaskListStabilizer mStabilizer = new TaskListStabilizer();
// The list change id, increments as the task list changes in the system
private int mChangeId;
// The last change id when the list was last loaded completely, must be <= the list change id
private int mLastLoadedId;
+ // The last change id was loaded with keysOnly = true
+ private boolean mLastLoadHadKeysOnly;
ArrayList<Task> mTasks = new ArrayList<>();
@@ -57,41 +63,43 @@
}
/**
- * Asynchronously fetches the list of recent tasks.
+ * Fetches the task keys skipping any local cache.
+ */
+ public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
+ // Kick off task loading in the background
+ mBgThreadExecutor.submit(() -> {
+ ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
+ mMainThreadExecutor.execute(() -> callback.accept(tasks));
+ });
+ }
+
+ /**
+ * Asynchronously fetches the list of recent tasks, reusing cached list if available.
*
- * @param numTasks The maximum number of tasks to fetch
* @param loadKeysOnly Whether to load other associated task data, or just the key
* @param callback The callback to receive the list of recent tasks
* @return The change id of the current task list
*/
- public synchronized int getTasks(int numTasks, boolean loadKeysOnly,
- Consumer<ArrayList<Task>> callback) {
+ public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
final int requestLoadId = mChangeId;
- final int numLoadTasks = numTasks > 0
- ? numTasks
- : Integer.MAX_VALUE;
+ Runnable resultCallback = callback == null
+ ? () -> { }
+ : () -> callback.accept(mStabilizer.reorder(mTasks));
- if (mLastLoadedId == mChangeId) {
+ if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
// The list is up to date, callback with the same list
- mMainThreadExecutor.execute(() -> {
- if (callback != null) {
- callback.accept(mTasks);
- }
- });
+ mMainThreadExecutor.execute(resultCallback);
}
// Kick off task loading in the background
mBgThreadExecutor.submit(() -> {
- ArrayList<Task> tasks = loadTasksInBackground(numLoadTasks,
- loadKeysOnly);
+ ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
mMainThreadExecutor.execute(() -> {
mTasks = tasks;
mLastLoadedId = requestLoadId;
-
- if (callback != null) {
- callback.accept(tasks);
- }
+ mLastLoadHadKeysOnly = loadKeysOnly;
+ resultCallback.run();
});
});
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index a62e6ad..75ccba6 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -98,7 +98,7 @@
* @return the request id associated with this call.
*/
public int getTasks(Consumer<ArrayList<Task>> callback) {
- return mTaskList.getTasks(-1, false /* loadKeysOnly */, callback);
+ return mTaskList.getTasks(false /* loadKeysOnly */, callback);
}
/**
@@ -124,7 +124,7 @@
* called on the UI thread.
*/
public void findTaskWithId(int taskId, Consumer<Task.TaskKey> callback) {
- mTaskList.getTasks(-1, true /* loadKeysOnly */, (tasks) -> {
+ mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
for (Task task : tasks) {
if (task.key.id == taskId) {
callback.accept(task.key);
@@ -150,7 +150,7 @@
// Keep the cache up to date with the latest thumbnails
int runningTaskId = RecentsModel.getRunningTaskId();
- mTaskList.getTasks(mThumbnailCache.getCacheSize(), true /* keysOnly */, (tasks) -> {
+ mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) {
if (task.key.id == runningTaskId) {
// Skip the running task, it's not going to have an up-to-date snapshot by the
diff --git a/quickstep/src/com/android/quickstep/TaskListStabilizer.java b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
new file mode 100644
index 0000000..0d23973
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
+
+import android.os.SystemClock;
+import android.util.SparseArray;
+
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+public class TaskListStabilizer {
+
+ private static final long TASK_CACHE_TIMEOUT_MS = 5000;
+
+ private final SparseArray<Task> mTempMap = new SparseArray<>();
+ private final IntArray mTempArray = new IntArray();
+ private final IntSet mTempSet = new IntSet();
+
+ private final IntArray mLastStableOrder = new IntArray();
+ private final IntSet mLastSet = new IntSet();
+ private final IntArray mLastUnstableOrder = new IntArray();
+
+ private long mLastReorderTime;
+
+ public ArrayList<Task> reorder(ArrayList<Task> tasks) {
+ if (!ENABLE_TASK_STABILIZER.get()) {
+ return tasks;
+ }
+
+ // Create task id array
+ int count = tasks.size();
+ mTempArray.clear();
+ mTempSet.clear();
+ mTempMap.clear();
+
+ for (int i = 0; i < count; i++) {
+ Task t = tasks.get(i);
+ mTempMap.put(t.key.id, t);
+
+ mTempSet.add(t.key.id);
+ mTempArray.add(t.key.id);
+ }
+
+ if (mLastSet.equals(mTempSet) && isStabilizationQuickEnough()) {
+ if (mLastStableOrder.equals(mTempArray)) {
+ // Everything is same
+ return tasks;
+ }
+
+ if (!mLastUnstableOrder.equals(mTempArray)) {
+ // Fast reordering, record the current time.
+ mLastUnstableOrder.copyFrom(mTempArray);
+ mLastReorderTime = SystemClock.uptimeMillis();
+ }
+
+ // Reorder the tasks based on the last stable order.
+ ArrayList<Task> sorted = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ sorted.add(mTempMap.get(mLastStableOrder.get(i)));
+ }
+ return sorted;
+ }
+
+ // Cache the data
+ mLastStableOrder.copyFrom(mTempArray);
+ mLastUnstableOrder.copyFrom(mTempArray);
+ mLastSet.copyFrom(mTempSet);
+
+ mLastReorderTime = SystemClock.uptimeMillis();
+
+ return tasks;
+ }
+
+ private boolean isStabilizationQuickEnough() {
+ return (SystemClock.uptimeMillis() - mLastReorderTime) < TASK_CACHE_TIMEOUT_MS;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index cf46b09..c2777e7 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -43,7 +43,7 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import java.util.List;
@@ -146,7 +146,8 @@
public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
- .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplier(v));
+ .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
+
final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index e1ffcf0..e951750 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -27,6 +27,7 @@
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -86,7 +87,7 @@
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.WindowCallbacksCompat;
import java.util.StringJoiner;
@@ -215,7 +216,7 @@
private T mActivity;
private LayoutListener mLayoutListener;
private RecentsView mRecentsView;
- private SyncRtSurfaceTransactionApplier mSyncTransactionApplier;
+ private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
private QuickScrubController mQuickScrubController;
private AnimationFactory mAnimationFactory = (t, i) -> { };
@@ -406,7 +407,7 @@
}
mRecentsView = activity.getOverviewPanel();
- SyncRtSurfaceTransactionApplier.create(mRecentsView, (applier) -> {
+ SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
mSyncTransactionApplier = applier;
});
mQuickScrubController = mRecentsView.getQuickScrubController();
@@ -587,7 +588,7 @@
RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
if (controller != null) {
- SyncRtSurfaceTransactionApplier syncTransactionApplier
+ SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
= Looper.myLooper() == mMainThreadHandler.getLooper()
? mSyncTransactionApplier
: null;
@@ -1028,10 +1029,17 @@
mRecentsView.animateUpRunningTaskIconScale();
if (mQuickScrubController.isQuickSwitch()) {
+ // Adjust the running task so that it is centered and fills the screen.
TaskView runningTask = mRecentsView.getRunningTaskView();
if (runningTask != null) {
- runningTask.setTranslationY(-mActivity.getResources().getDimension(
- R.dimen.task_thumbnail_half_top_margin) * 1f / mRecentsView.getScaleX());
+ float insetHeight = mDp.heightPx - mDp.getInsets().top - mDp.getInsets().bottom;
+ // Usually insetDiff will be 0, unless we allow apps to draw under the insets. In
+ // that case (insetDiff != 0), we need to center in the system-specified available
+ // height rather than launcher's inset height by adding half the insetDiff.
+ float insetDiff = mDp.availableHeightPx - insetHeight;
+ float topMargin = mActivity.getResources().getDimension(
+ R.dimen.task_thumbnail_half_top_margin);
+ runningTask.setTranslationY((insetDiff / 2 - topMargin) / mRecentsView.getScaleX());
}
}
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index d8a3282..431517a 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -46,8 +46,8 @@
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TransactionCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -217,14 +217,14 @@
return mCurrentRectWithInsets;
}
- private void applySurfaceParams(@Nullable SyncRtSurfaceTransactionApplier
+ private void applySurfaceParams(@Nullable SyncRtSurfaceTransactionApplierCompat
syncTransactionApplier, SurfaceParams[] params) {
if (syncTransactionApplier != null) {
syncTransactionApplier.scheduleApply(params);
} else {
TransactionCompat t = new TransactionCompat();
for (SurfaceParams param : params) {
- SyncRtSurfaceTransactionApplier.applyParams(t, param);
+ SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
}
t.setEarlyWakeup();
t.apply();
@@ -353,7 +353,7 @@
public static class TransformParams {
float progress;
- SyncRtSurfaceTransactionApplier syncTransactionApplier;
+ SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
public TransformParams() {
progress = 0;
@@ -364,7 +364,8 @@
return this;
}
- public TransformParams setSyncTransactionApplier(SyncRtSurfaceTransactionApplier applier) {
+ public TransformParams setSyncTransactionApplier(
+ SyncRtSurfaceTransactionApplierCompat applier) {
this.syncTransactionApplier = applier;
return this;
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7842fa4..34b5748 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -60,6 +61,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ListView;
import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
@@ -482,6 +484,11 @@
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task);
}
+ TaskView runningTaskView = getRunningTaskView();
+ if (runningTaskView != null) {
+ setCurrentPage(indexOfChild(runningTaskView));
+ }
+
if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) {
// If the taskView mapping is changing, do not preserve the visuals. Since we are
// mostly preserving the first task, and new taskViews are added to the end, it should
@@ -545,8 +552,8 @@
// Keep this logic in sync with ActivityControlHelper.getTranslationYForQuickScrub.
mTempRect.top -= mTaskTopMargin;
setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
- dp.availableWidthPx + mInsets.left - mTempRect.right,
- dp.availableHeightPx + mInsets.top - mTempRect.bottom);
+ dp.widthPx - mInsets.right - mTempRect.right,
+ dp.heightPx - mInsets.bottom - mTempRect.bottom);
}
protected abstract void getTaskSize(DeviceProfile dp, Rect outRect);
@@ -749,7 +756,8 @@
setRunningTaskIconScaledDown(runningTaskIconScaledDown);
setRunningTaskHidden(runningTaskTileHidden);
- setCurrentPage(0);
+ TaskView tv = getRunningTaskView();
+ setCurrentPage(tv == null ? 0 : indexOfChild(tv));
// Load the tasks (if the loading is already
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseGridChangesTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseGridChangesTestCase.java
new file mode 100644
index 0000000..07834fc
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseGridChangesTestCase.java
@@ -0,0 +1,121 @@
+package com.android.launcher3.model;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.util.TestLauncherProvider;
+
+import org.junit.Before;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowContentResolver;
+import org.robolectric.shadows.ShadowLog;
+
+public abstract class BaseGridChangesTestCase {
+
+
+ public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+
+ public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ public static final int NO__ICON = -1;
+
+ public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
+
+ public Context mContext;
+ public TestLauncherProvider mProvider;
+ public SQLiteDatabase mDb;
+
+ @Before
+ public void setUpBaseCase() {
+ ShadowLog.stream = System.out;
+
+ mContext = RuntimeEnvironment.application;
+ mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
+ ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
+ mDb = mProvider.getDb();
+ }
+
+ /**
+ * Adds a dummy item in the DB.
+ * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
+ * folder (where the type represents the number of items in the folder).
+ */
+ public int addItem(int type, int screen, int container, int x, int y) {
+ int id = LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+ .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites._ID, id);
+ values.put(LauncherSettings.Favorites.CONTAINER, container);
+ values.put(LauncherSettings.Favorites.SCREEN, screen);
+ values.put(LauncherSettings.Favorites.CELLX, x);
+ values.put(LauncherSettings.Favorites.CELLY, y);
+ values.put(LauncherSettings.Favorites.SPANX, 1);
+ values.put(LauncherSettings.Favorites.SPANY, 1);
+
+ if (type == APP_ICON || type == SHORTCUT) {
+ values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
+ values.put(LauncherSettings.Favorites.INTENT,
+ new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
+ } else {
+ values.put(LauncherSettings.Favorites.ITEM_TYPE,
+ LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
+ // Add folder items.
+ for (int i = 0; i < type; i++) {
+ addItem(APP_ICON, 0, id, 0, 0);
+ }
+ }
+
+ mContext.getContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
+ return id;
+ }
+
+ public int[][][] createGrid(int[][][] typeArray) {
+ return createGrid(typeArray, 1);
+ }
+
+ /**
+ * Initializes the DB with dummy elements to represent the provided grid structure.
+ * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
+ * type definitions. The first dimension represents the screens and the next
+ * two represent the workspace grid.
+ * @param startScreen First screen id from where the icons will be added.
+ * @return the same grid representation where each entry is the corresponding item id.
+ */
+ public int[][][] createGrid(int[][][] typeArray, int startScreen) {
+ LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ int[][][] ids = new int[typeArray.length][][];
+
+ for (int i = 0; i < typeArray.length; i++) {
+ // Add screen to DB
+ int screenId = startScreen + i;
+
+ // Keep the screen id counter up to date
+ LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+ ids[i] = new int[typeArray[i].length][];
+ for (int y = 0; y < typeArray[i].length; y++) {
+ ids[i][y] = new int[typeArray[i][y].length];
+ for (int x = 0; x < typeArray[i][y].length; x++) {
+ if (typeArray[i][y][x] < 0) {
+ // Empty cell
+ ids[i][y][x] = -1;
+ } else {
+ ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
+ }
+ }
+ }
+ }
+
+ return ids;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
new file mode 100644
index 0000000..53287a9
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
@@ -0,0 +1,115 @@
+package com.android.launcher3.model;
+
+
+import static android.database.DatabaseUtils.queryNumEntries;
+
+import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentValues;
+import android.graphics.Point;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Unit tests for {@link GridBackupTable}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class GridBackupTableTest extends BaseGridChangesTestCase {
+
+ private static final int BACKUP_ITEM_COUNT = 12;
+
+ @Before
+ public void setupGridData() {
+ createGrid(new int[][][]{{
+ { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
+ { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
+ { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
+ { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON},
+ }});
+ assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
+ }
+
+ @Test
+ public void backupTableCreated() {
+ GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
+ assertFalse(backupTable.backupOrRestoreAsNeeded());
+ Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+ assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+ // One extra entry for properties
+ assertEquals(BACKUP_ITEM_COUNT + 1, queryNumEntries(mDb, BACKUP_TABLE_NAME));
+ }
+
+ @Test
+ public void backupTableRestored() {
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+ // Delete entries
+ mDb.delete(TABLE_NAME, null, null);
+ assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
+
+ GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
+ assertTrue(backupTable.backupOrRestoreAsNeeded());
+
+ // Items have been restored
+ assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
+
+ Point outSize = new Point();
+ assertEquals(4, backupTable.getRestoreHotseatAndGridSize(outSize));
+ assertEquals(4, outSize.x);
+ assertEquals(4, outSize.y);
+ }
+
+ @Test
+ public void backupTableRemovedOnAdd() {
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+ assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+ addItem(1, 2, DESKTOP, 1, 1);
+ assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
+ }
+
+ @Test
+ public void backupTableRemovedOnDelete() {
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+ assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+ mContext.getContentResolver().delete(Favorites.CONTENT_URI, null, null);
+ assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
+ }
+
+ @Test
+ public void backupTableRetainedOnUpdate() {
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+ assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+ ContentValues values = new ContentValues();
+ values.put(Favorites.RANK, 4);
+ // Something was updated
+ assertTrue(mContext.getContentResolver()
+ .update(Favorites.CONTENT_URI, values, null, null) > 0);
+
+ // Backup table remains
+ assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index d4188aa..ce07a27 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -5,29 +5,21 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
import android.database.Cursor;
import android.graphics.Point;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FlagOverrideRule;
import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.TestLauncherProvider;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowContentResolver;
import java.util.HashSet;
import java.util.LinkedList;
@@ -36,47 +28,33 @@
* Unit tests for {@link GridSizeMigrationTask}
*/
@RunWith(RobolectricTestRunner.class)
-public class GridSizeMigrationTaskTest {
-
- private static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
- private static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
- private static final int APPLICATION = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- private static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-
- private static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
+public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
@Rule
public final FlagOverrideRule flags = new FlagOverrideRule();
private HashSet<String> mValidPackages;
private InvariantDeviceProfile mIdp;
- private Context mContext;
- private TestLauncherProvider mProvider;
@Before
public void setUp() {
mValidPackages = new HashSet<>();
mValidPackages.add(TEST_PACKAGE);
mIdp = new InvariantDeviceProfile();
- mContext = RuntimeEnvironment.application;
-
- mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
- ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
}
@Test
public void testHotseatMigration_apps_dropped() throws Exception {
int[] hotseatItems = {
- addItem(APPLICATION, 0, HOTSEAT, 0, 0),
+ addItem(APP_ICON, 0, HOTSEAT, 0, 0),
addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
-1,
addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
- addItem(APPLICATION, 4, HOTSEAT, 0, 0),
+ addItem(APP_ICON, 4, HOTSEAT, 0, 0),
};
mIdp.numHotseatIcons = 3;
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages, 5, 3)
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
.migrateHotseat();
// First item is dropped as it has the least weight.
verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
@@ -85,7 +63,7 @@
@Test
public void testHotseatMigration_shortcuts_dropped() throws Exception {
int[] hotseatItems = {
- addItem(APPLICATION, 0, HOTSEAT, 0, 0),
+ addItem(APP_ICON, 0, HOTSEAT, 0, 0),
addItem(30, 1, HOTSEAT, 0, 0),
-1,
addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
@@ -93,7 +71,7 @@
};
mIdp.numHotseatIcons = 3;
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages, 5, 3)
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
.migrateHotseat();
// First item is dropped as it has the least weight.
verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
@@ -138,7 +116,7 @@
{ 5, 2, -1, 6},
}});
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Column 2 and row 2 got removed.
@@ -158,7 +136,7 @@
{ 5, 2, -1, 6},
}});
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column get moved to new screen
@@ -183,7 +161,7 @@
{ 3, 1, -1, 4},
}});
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on the 3rd
@@ -215,7 +193,7 @@
{ 5, 2, -1, 6},
}});
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -244,7 +222,7 @@
{ 5, 2, 7, -1},
}}, 0);
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new Point(4, 4), new Point(3, 4)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -269,7 +247,7 @@
{ 5, 6, 7, -1},
}}, 0);
- new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
+ new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -283,54 +261,13 @@
}});
}
- private int[][][] createGrid(int[][][] typeArray) throws Exception {
- return createGrid(typeArray, 1);
- }
-
- /**
- * Initializes the DB with dummy elements to represent the provided grid structure.
- * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
- * type definitions. The first dimension represents the screens and the next
- * two represent the workspace grid.
- * @return the same grid representation where each entry is the corresponding item id.
- */
- private int[][][] createGrid(int[][][] typeArray, int startScreen) throws Exception {
- LauncherSettings.Settings.call(mContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- int[][][] ids = new int[typeArray.length][][];
-
- for (int i = 0; i < typeArray.length; i++) {
- // Add screen to DB
- int screenId = startScreen + i;
-
- // Keep the screen id counter up to date
- LauncherSettings.Settings.call(mContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
- ids[i] = new int[typeArray[i].length][];
- for (int y = 0; y < typeArray[i].length; y++) {
- ids[i][y] = new int[typeArray[i][y].length];
- for (int x = 0; x < typeArray[i][y].length; x++) {
- if (typeArray[i][y][x] < 0) {
- // Empty cell
- ids[i][y][x] = -1;
- } else {
- ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
- }
- }
- }
- }
-
- return ids;
- }
-
/**
* Verifies that the workspace items are arranged in the provided order.
* @param ids A 3d array where the first dimension represents the screen, and the rest two
* represent the workspace grid.
*/
private void verifyWorkspace(int[][][] ids) {
- IntArray allScreens = getWorkspaceScreenIds(mContext);
+ IntArray allScreens = getWorkspaceScreenIds(mDb);
assertEquals(ids.length, allScreens.size());
int total = 0;
@@ -367,42 +304,6 @@
c.close();
}
- /**
- * Adds a dummy item in the DB.
- * @param type {@link #APPLICATION} or {@link #SHORTCUT} or >= 2 for
- * folder (where the type represents the number of items in the folder).
- */
- private int addItem(int type, int screen, int container, int x, int y) throws Exception {
- int id = LauncherSettings.Settings.call(mContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
-
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites._ID, id);
- values.put(LauncherSettings.Favorites.CONTAINER, container);
- values.put(LauncherSettings.Favorites.SCREEN, screen);
- values.put(LauncherSettings.Favorites.CELLX, x);
- values.put(LauncherSettings.Favorites.CELLY, y);
- values.put(LauncherSettings.Favorites.SPANX, 1);
- values.put(LauncherSettings.Favorites.SPANY, 1);
-
- if (type == APPLICATION || type == SHORTCUT) {
- values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
- values.put(LauncherSettings.Favorites.INTENT,
- new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
- } else {
- values.put(LauncherSettings.Favorites.ITEM_TYPE,
- LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
- // Add folder items.
- for (int i = 0; i < type; i++) {
- addItem(APPLICATION, 0, id, 0, 0);
- }
- }
-
- mContext.getContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
- return id;
- }
-
@Test
public void testMultiStepMigration_small_to_large() throws Exception {
MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier();
@@ -435,7 +336,7 @@
private final LinkedList<Point> mPoints;
public MultiStepMigrationTaskVerifier(int... points) {
- super(null, null);
+ super(null, null, null);
mPoints = new LinkedList<>();
for (int i = 0; i < points.length; i += 2) {
diff --git a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
index 32808f5..31e303e 100644
--- a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -23,6 +23,11 @@
}
}
+ public SQLiteDatabase getDb() {
+ createDbIfNotExists();
+ return mOpenHelper.getWritableDatabase();
+ }
+
@Override
protected void notifyListeners() { }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 4853a90..d96855e 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -342,6 +342,14 @@
}
@Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ if (mIcon != null) {
+ mIcon.setVisible(isVisible, false);
+ }
+ }
+
+ @Override
public void onLauncherResume() {
// Reset the pressed state of icon that was locked in the press state while activity
// was launching
@@ -573,6 +581,9 @@
applyCompoundDrawables(icon);
}
mIcon = icon;
+ if (mIcon != null) {
+ mIcon.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
+ }
}
public void setIconVisible(boolean visible) {
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 4d30479..763432d 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -78,17 +78,17 @@
*/
public final float[] getVisualCenter(float[] recycle) {
final float res[] = (recycle == null) ? new float[2] : recycle;
+ Rect dragRegion = dragView.getDragRegion();
// These represent the visual top and left of drag view if a dragRect was provided.
// If a dragRect was not provided, then they correspond to the actual view left and
// top, as the dragRect is in that case taken to be the entire dragView.
- // R.dimen.dragViewOffsetY.
- int left = x - xOffset;
- int top = y - yOffset;
+ int left = x - xOffset - dragRegion.left;
+ int top = y - yOffset - dragRegion.top;
// In order to find the visual center, we shift by half the dragRect
- res[0] = left + dragView.getDragRegion().width() / 2;
- res[1] = top + dragView.getDragRegion().height() / 2;
+ res[0] = left + dragRegion.width() / 2;
+ res[1] = top + dragRegion.height() / 2;
return res;
}
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index daf587a..964e8b6 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
import android.animation.ObjectAnimator;
import android.graphics.Bitmap;
@@ -221,8 +222,15 @@
mScaleAnimation.setInterpolator(ACCEL);
mScaleAnimation.start();
} else {
- mScale = 1f;
- invalidateSelf();
+ if (isVisible()) {
+ mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f);
+ mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
+ mScaleAnimation.setInterpolator(DEACCEL);
+ mScaleAnimation.start();
+ } else {
+ mScale = 1f;
+ invalidateSelf();
+ }
}
return true;
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index dbefa45..5c842a5 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,10 +17,12 @@
package com.android.launcher3;
import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+import static com.android.launcher3.Utilities.getDevicePrefs;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Point;
@@ -46,24 +48,30 @@
public class InvariantDeviceProfile {
+ public static final String TAG = "IDP";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE =
new MainThreadInitializedObject<>(InvariantDeviceProfile::new);
- private static final String KEY_IDP_GRIP_NAME = "idp_grid_name";
+ private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
public static final int CHANGE_FLAG_GRID = 1 << 0;
- public static final int CHANGE_FLAG_ICON_SIZE = 1 << 1;
+ public static final int CHANGE_FLAG_ICON_PARAMS = 1 << 1;
+
+ public static final String KEY_ICON_PATH_REF = "pref_icon_shape_path";
// Constants that affects the interpolation curve between statically defined device profile
// buckets.
- private static float KNEARESTNEIGHBOR = 3;
- private static float WEIGHT_POWER = 5;
+ private static final float KNEARESTNEIGHBOR = 3;
+ private static final float WEIGHT_POWER = 5;
// used to offset float not being able to express extremely small weights in extreme cases.
- private static float WEIGHT_EFFICIENT = 100000f;
+ private static final float WEIGHT_EFFICIENT = 100000f;
+
+ private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
+ "config_icon_mask", "string", "android");
/**
* Number of icons per row and column in the workspace.
@@ -77,6 +85,7 @@
public int numFolderRows;
public int numFolderColumns;
public float iconSize;
+ public String iconShapePath;
public float landscapeIconSize;
public int iconBitmapSize;
public int fillResIconDpi;
@@ -107,6 +116,7 @@
numFolderRows = p.numFolderRows;
numFolderColumns = p.numFolderColumns;
iconSize = p.iconSize;
+ iconShapePath = p.iconShapePath;
landscapeIconSize = p.landscapeIconSize;
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
@@ -116,11 +126,22 @@
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
- initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRIP_NAME, null));
+ initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null));
mConfigMonitor = new ConfigMonitor(context,
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
}
+ /**
+ * Retrieve system defined or RRO overriden icon shape.
+ */
+ private static String getIconShapePath(Context context) {
+ if (CONFIG_ICON_MASK_RES_ID == 0) {
+ Log.e(TAG, "Icon mask res identifier failed to retrieve.");
+ return "";
+ }
+ return context.getResources().getString(CONFIG_ICON_MASK_RES_ID);
+ }
+
private void initGrid(Context context, String gridName) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
@@ -152,10 +173,11 @@
numFolderColumns = closestProfile.numFolderColumns;
if (!closestProfile.name.equals(gridName)) {
Utilities.getPrefs(context).edit()
- .putString(KEY_IDP_GRIP_NAME, closestProfile.name).apply();
+ .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
}
iconSize = interpolatedDisplayOption.iconSize;
+ iconShapePath = getIconShapePath(context);
landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
iconTextSize = interpolatedDisplayOption.iconTextSize;
@@ -197,12 +219,26 @@
android.os.Process.killProcess(android.os.Process.myPid());
}
+ public void verifyConfigChangedInBackground(final Context context) {
+
+ String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
+ // Good place to check if grid size changed in themepicker when launcher was dead.
+ if (savedIconMaskPath.isEmpty()) {
+ getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
+ .apply();
+ } else if (!savedIconMaskPath.equals(getIconShapePath(context))) {
+ getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
+ .apply();
+ apply(context, CHANGE_FLAG_ICON_PARAMS);
+ }
+ }
+
private void onConfigChanged(Context context) {
// Config changes, what shall we do?
InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
// Re-init grid
- initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRIP_NAME, null));
+ initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null));
int changeFlags = 0;
if (numRows != oldProfile.numRows ||
@@ -213,10 +249,14 @@
changeFlags |= CHANGE_FLAG_GRID;
}
- if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize) {
- changeFlags |= CHANGE_FLAG_ICON_SIZE;
+ if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize ||
+ !iconShapePath.equals(oldProfile.iconShapePath)) {
+ changeFlags |= CHANGE_FLAG_ICON_PARAMS;
}
+ apply(context, changeFlags);
+ }
+ private void apply(Context context, int changeFlags) {
// Create a new config monitor
mConfigMonitor.unregister();
mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged);
@@ -231,7 +271,6 @@
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
int type;
-
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) && "grid-option".equals(parser.getName())) {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 182a4ee..74fa447 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,14 +16,15 @@
package com.android.launcher3;
+import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_SIZE;
import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Handler;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -97,6 +98,7 @@
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
+ new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) {
mNotificationDotsObserver = null;
@@ -121,7 +123,7 @@
return;
}
- if ((changeFlags & CHANGE_FLAG_ICON_SIZE) != 0) {
+ if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) != 0) {
LauncherIcons.clearPool();
mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8ed3314..24dc17a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
import android.annotation.TargetApi;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
@@ -210,6 +213,7 @@
addModifiedTime(initialValues);
final int rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
if (rowId < 0) return null;
+ mOpenHelper.onAddOrDeleteOp(db);
uri = ContentUris.withAppendedId(uri, rowId);
notifyListeners();
@@ -282,6 +286,7 @@
return 0;
}
}
+ mOpenHelper.onAddOrDeleteOp(db);
t.commit();
}
@@ -290,15 +295,30 @@
return values.length;
}
+ @TargetApi(Build.VERSION_CODES.M)
@Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
createDbIfNotExists();
try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
- ContentProviderResult[] result = super.applyBatch(operations);
+ boolean isAddOrDelete = !Utilities.ATLEAST_MARSHMALLOW;
+
+ final int numOperations = operations.size();
+ final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ ContentProviderOperation op = operations.get(i);
+ results[i] = op.apply(this, results, i);
+
+ isAddOrDelete |= (op.isInsert() || op.isDelete()) &&
+ results[i].count != null && results[i].count > 0;
+ }
+ if (isAddOrDelete) {
+ mOpenHelper.onAddOrDeleteOp(t.getDb());
+ }
+
t.commit();
reloadLauncherIfExternal();
- return result;
+ return results;
}
}
@@ -315,6 +335,7 @@
}
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
+ mOpenHelper.onAddOrDeleteOp(db);
notifyListeners();
reloadLauncherIfExternal();
}
@@ -381,6 +402,17 @@
mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
return null;
}
+ case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: {
+ Bundle result = new Bundle();
+ result.putBinder(LauncherSettings.Settings.EXTRA_VALUE,
+ new SQLiteTransaction(mOpenHelper.getWritableDatabase()));
+ return result;
+ }
+ case LauncherSettings.Settings.METHOD_REFRESH_BACKUP_TABLE: {
+ mOpenHelper.mBackupTableExists =
+ tableExists(mOpenHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
+ return null;
+ }
}
return null;
}
@@ -528,17 +560,19 @@
private final Context mContext;
private int mMaxItemId = -1;
private int mMaxScreenId = -1;
+ private boolean mBackupTableExists;
DatabaseHelper(Context context, Handler widgetHostResetHandler) {
this(context, widgetHostResetHandler, LauncherFiles.LAUNCHER_DB);
// 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(Favorites.TABLE_NAME)) {
+ if (!tableExists(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.
addFavoritesTable(getWritableDatabase(), true);
}
+ mBackupTableExists = tableExists(getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
initIds();
}
@@ -564,18 +598,6 @@
}
}
- private boolean tableExists(String tableName) {
- Cursor c = getReadableDatabase().query(
- true, "sqlite_master", new String[] {"tbl_name"},
- "tbl_name = ?", new String[] {tableName},
- null, null, null, null, null);
- try {
- return c.getCount() > 0;
- } finally {
- c.close();
- }
- }
-
@Override
public void onCreate(SQLiteDatabase db) {
if (LOGD) Log.d(TAG, "creating new launcher database");
@@ -590,6 +612,13 @@
onEmptyDbCreated();
}
+ protected void onAddOrDeleteOp(SQLiteDatabase db) {
+ if (mBackupTableExists) {
+ dropTable(db, Favorites.BACKUP_TABLE_NAME);
+ mBackupTableExists = false;
+ }
+ }
+
/**
* Overriden in tests.
*/
@@ -733,7 +762,7 @@
Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP);
db.execSQL(query);
}
- db.execSQL("DROP TABLE IF EXISTS workspaceScreens");
+ dropTable(db, "workspaceScreens");
}
case 28:
// DB Upgraded successfully
@@ -762,8 +791,8 @@
*/
public void createEmptyDB(SQLiteDatabase db) {
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
- db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
- db.execSQL("DROP TABLE IF EXISTS workspaceScreens");
+ dropTable(db, Favorites.TABLE_NAME);
+ dropTable(db, "workspaceScreens");
onCreate(db);
t.commit();
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 79c8208..e248ba0 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -89,6 +89,11 @@
public static final String TABLE_NAME = "favorites";
/**
+ * Backup table created when when the favorites table is modified during grid migration
+ */
+ public static final String BACKUP_TABLE_NAME = "favorites_bakup";
+
+ /**
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
@@ -231,8 +236,13 @@
public static final String OPTIONS = "options";
public static void addTableToDb(SQLiteDatabase db, long myProfileId, boolean optional) {
+ addTableToDb(db, myProfileId, optional, TABLE_NAME);
+ }
+
+ public static void addTableToDb(SQLiteDatabase db, long myProfileId, boolean optional,
+ String tableName) {
String ifNotExists = optional ? " IF NOT EXISTS " : "";
- db.execSQL("CREATE TABLE " + ifNotExists + TABLE_NAME + " (" +
+ db.execSQL("CREATE TABLE " + ifNotExists + tableName + " (" +
"_id INTEGER PRIMARY KEY," +
"title TEXT," +
"intent TEXT," +
@@ -279,6 +289,10 @@
public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
+ public static final String METHOD_NEW_TRANSACTION = "new_db_transaction";
+
+ public static final String METHOD_REFRESH_BACKUP_TABLE = "refresh_backup_table";
+
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 23ad912..03fdc64 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -93,7 +93,10 @@
* Feature flag to handle define config changes dynamically instead of killing the process.
*/
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
- "APPLY_CONFIG_AT_RUNTIME", false, "Apply display changes dynamically");
+ "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
+
+ public static final TogglableFlag ENABLE_TASK_STABILIZER = new TogglableFlag(
+ "ENABLE_TASK_STABILIZER", false, "Stable task list across fast task switches");
public static void initialize(Context context) {
// Avoid the disk read for user builds
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index d9d0a4f..f6e220f 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -25,6 +25,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
@@ -74,8 +75,6 @@
* Binds all loaded data to actual views on the main thread.
*/
public void bindWorkspace() {
- Runnable r;
-
Callbacks callbacks = mCallbacks.get();
// Don't use these two variables in any of the callback runnables.
// Otherwise we hold a reference to them.
@@ -125,27 +124,13 @@
sortWorkspaceItemsSpatially(otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.clearPendingBinds();
- callbacks.startBinding();
- }
- }
- };
- mUiExecutor.execute(r);
+ executeCallbacksTask(c -> {
+ c.clearPendingBinds();
+ c.startBinding();
+ }, mUiExecutor);
// Bind workspace screens
- mUiExecutor.execute(new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.bindScreens(orderedScreenIds);
- }
- }
- });
+ executeCallbacksTask(c -> c.bindScreens(orderedScreenIds), mUiExecutor);
Executor mainExecutor = mUiExecutor;
// Load items on the current page.
@@ -159,46 +144,25 @@
final Executor deferredExecutor =
validFirstPage ? new ViewOnDrawExecutor() : mainExecutor;
- mainExecutor.execute(new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.finishFirstPageBind(
- validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
- }
- }
- });
+ executeCallbacksTask(c -> c.finishFirstPageBind(
+ validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null), mainExecutor);
bindWorkspaceItems(otherWorkspaceItems, deferredExecutor);
bindAppWidgets(otherAppWidgets, deferredExecutor);
// Tell the workspace that we're done binding items
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.finishBindingItems(mPageToBindFirst);
- }
- }
- };
- deferredExecutor.execute(r);
+ executeCallbacksTask(c -> c.finishBindingItems(mPageToBindFirst), deferredExecutor);
if (validFirstPage) {
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- // We are loading synchronously, which means, some of the pages will be
- // bound after first draw. Inform the callbacks that page binding is
- // not complete, and schedule the remaining pages.
- if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
- callbacks.onPageBoundSynchronously(currentScreen);
- }
- callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
- }
+ executeCallbacksTask(c -> {
+ // We are loading synchronously, which means, some of the pages will be
+ // bound after first draw. Inform the callbacks that page binding is
+ // not complete, and schedule the remaining pages.
+ if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
+ c.onPageBoundSynchronously(currentScreen);
}
- };
- mUiExecutor.execute(r);
+ c.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
+
+ }, mUiExecutor);
}
}
@@ -258,7 +222,7 @@
public int compare(ItemInfo lhs, ItemInfo rhs) {
if (lhs.container == rhs.container) {
// Within containers, order by their spatial position in that container
- switch ((int) lhs.container) {
+ switch (lhs.container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
int lr = (lhs.screenId * screenCellCount +
lhs.cellY * screenCols + lhs.cellX);
@@ -297,17 +261,9 @@
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
- final Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.bindItems(workspaceItems.subList(start, start + chunkSize),
- false);
- }
- }
- };
- executor.execute(r);
+ executeCallbacksTask(
+ c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
+ executor);
}
}
@@ -316,15 +272,8 @@
N = appWidgets.size();
for (int i = 0; i < N; i++) {
final ItemInfo widget = appWidgets.get(i);
- final Runnable r = new Runnable() {
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.bindItems(Collections.singletonList(widget), false);
- }
- }
- };
- executor.execute(r);
+ executeCallbacksTask(
+ c -> c.bindItems(Collections.singletonList(widget), false), executor);
}
}
@@ -333,21 +282,21 @@
public void bindAllApps() {
// shallow copy
@SuppressWarnings("unchecked")
- final ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
-
- Runnable r = new Runnable() {
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.bindAllApplications(list);
- }
- }
- };
- mUiExecutor.execute(r);
+ ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
+ executeCallbacksTask(c -> c.bindAllApplications(list), mUiExecutor);
}
public abstract void bindWidgets();
+ protected void executeCallbacksTask(CallbackTask task, Executor executor) {
+ executor.execute(() -> {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ task.execute(callbacks);
+ }
+ });
+ }
+
public LooperIdleLock newIdleLock(Object lock) {
LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
// If we are not binding, there is no reason to wait for idle.
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
new file mode 100644
index 0000000..804a040
--- /dev/null
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 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.model;
+
+import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Point;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+import com.android.launcher3.compat.UserManagerCompat;
+
+/**
+ * Helper class to backup and restore Favorites table into a separate table
+ * within the same data base.
+ */
+public class GridBackupTable {
+ private static final String TAG = "GridBackupTable";
+
+ private static final int ID_PROPERTY = -1;
+
+ private static final String KEY_HOTSEAT_SIZE = Favorites.SCREEN;
+ private static final String KEY_GRID_X_SIZE = Favorites.SPANX;
+ private static final String KEY_GRID_Y_SIZE = Favorites.SPANY;
+ private static final String KEY_DB_VERSION = Favorites.RANK;
+
+ private final Context mContext;
+ private final SQLiteDatabase mDb;
+
+ private final int mOldHotseatSize;
+ private final int mOldGridX;
+ private final int mOldGridY;
+
+ private int mRestoredHotseatSize;
+ private int mRestoredGridX;
+ private int mRestoredGridY;
+
+ public GridBackupTable(Context context, SQLiteDatabase db,
+ int hotseatSize, int gridX, int gridY) {
+ mContext = context;
+ mDb = db;
+
+ mOldHotseatSize = hotseatSize;
+ mOldGridX = gridX;
+ mOldGridY = gridY;
+ }
+
+ public boolean backupOrRestoreAsNeeded() {
+ // Check if backup table exists
+ if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
+ if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
+ .getBoolean(Settings.EXTRA_VALUE, false)) {
+ // No need to copy if empty DB was created.
+ return false;
+ }
+
+ copyTable(Favorites.TABLE_NAME, BACKUP_TABLE_NAME);
+ encodeDBProperties();
+ return false;
+ }
+
+ if (!loadDbProperties()) {
+ return false;
+ }
+ copyTable(BACKUP_TABLE_NAME, Favorites.TABLE_NAME);
+ Log.d(TAG, "Backup table found");
+ return true;
+ }
+
+ public int getRestoreHotseatAndGridSize(Point outGridSize) {
+ outGridSize.set(mRestoredGridX, mRestoredGridY);
+ return mRestoredHotseatSize;
+ }
+
+ private void copyTable(String from, String to) {
+ long userSerial = UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
+ Process.myUserHandle());
+ dropTable(mDb, to);
+ Favorites.addTableToDb(mDb, userSerial, false, to);
+ mDb.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
+ }
+
+ private void encodeDBProperties() {
+ ContentValues values = new ContentValues();
+ values.put(Favorites._ID, ID_PROPERTY);
+ values.put(KEY_DB_VERSION, mDb.getVersion());
+ values.put(KEY_GRID_X_SIZE, mOldGridX);
+ values.put(KEY_GRID_Y_SIZE, mOldGridY);
+ values.put(KEY_HOTSEAT_SIZE, mOldHotseatSize);
+ mDb.insert(BACKUP_TABLE_NAME, null, values);
+ }
+
+ private boolean loadDbProperties() {
+ try (Cursor c = mDb.query(BACKUP_TABLE_NAME, new String[] {
+ KEY_DB_VERSION, // 0
+ KEY_GRID_X_SIZE, // 1
+ KEY_GRID_Y_SIZE, // 2
+ KEY_HOTSEAT_SIZE}, // 3
+ "_id=" + ID_PROPERTY, null, null, null, null)) {
+ if (!c.moveToNext()) {
+ Log.e(TAG, "Meta data not found in backup table");
+ return false;
+ }
+ if (mDb.getVersion() != c.getInt(0)) {
+ return false;
+ }
+
+ mRestoredGridX = c.getInt(1);
+ mRestoredGridY = c.getInt(2);
+ mRestoredHotseatSize = c.getInt(3);
+ return true;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index a9ddccb..1c7bffa 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -1,10 +1,10 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.Utilities.parsePoint;
import android.content.ComponentName;
-import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -12,24 +12,27 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.util.Log;
+import android.util.SparseArray;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
import java.util.ArrayList;
@@ -60,13 +63,13 @@
private static final float WT_WIDGET_FACTOR = 0.6f;
private static final float WT_FOLDER_FACTOR = 0.5f;
- private final Context mContext;
- private final InvariantDeviceProfile mIdp;
+ protected final SQLiteDatabase mDb;
+ protected final Context mContext;
- private final ContentValues mTempValues = new ContentValues();
protected final IntArray mEntryToRemove = new IntArray();
- private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
+
+ private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
private final HashSet<String> mValidPackages;
private final int mSrcX, mSrcY;
@@ -76,11 +79,11 @@
private final int mSrcHotseatSize;
private final int mDestHotseatSize;
- protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp,
+ protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
HashSet<String> validPackages, Point sourceSize, Point targetSize) {
mContext = context;
+ mDb = db;
mValidPackages = validPackages;
- mIdp = idp;
mSrcX = sourceSize.x;
mSrcY = sourceSize.y;
@@ -95,11 +98,10 @@
mSrcHotseatSize = mDestHotseatSize = -1;
}
- protected GridSizeMigrationTask(Context context,
- InvariantDeviceProfile idp, HashSet<String> validPackages,
- int srcHotseatSize, int destHotseatSize) {
+ protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
+ HashSet<String> validPackages, int srcHotseatSize, int destHotseatSize) {
mContext = context;
- mIdp = idp;
+ mDb = db;
mValidPackages = validPackages;
mSrcHotseatSize = srcHotseatSize;
@@ -118,20 +120,21 @@
*/
private boolean applyOperations() throws Exception {
// Update items
- if (!mUpdateOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
+ int updateCount = mUpdateOperations.size();
+ for (int i = 0; i < updateCount; i++) {
+ mDb.update(Favorites.TABLE_NAME, mUpdateOperations.valueAt(i),
+ "_id=" + mUpdateOperations.keyAt(i), null);
}
if (!mEntryToRemove.isEmpty()) {
if (DEBUG) {
Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
}
- mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, mEntryToRemove), null);
+ mDb.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
+ Favorites._ID, mEntryToRemove), null);
}
- return !mUpdateOperations.isEmpty() || !mEntryToRemove.isEmpty();
+ return updateCount > 0 || !mEntryToRemove.isEmpty();
}
/**
@@ -181,24 +184,17 @@
}
@VisibleForTesting
- static IntArray getWorkspaceScreenIds(Context context) {
- IntSet set = new IntSet();
- try (Cursor c = context.getContentResolver().query(Favorites.CONTENT_URI,
- new String[] {Favorites.SCREEN},
+ static IntArray getWorkspaceScreenIds(SQLiteDatabase db) {
+ return LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME, Favorites.SCREEN,
Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
- null, Favorites.SCREEN)) {
- while (c.moveToNext()) {
- set.add(c.getInt(0));
- }
- }
- return set.getArray();
+ Favorites.SCREEN, Favorites.SCREEN);
}
/**
* @return true if any DB change was made
*/
protected boolean migrateWorkspace() throws Exception {
- IntArray allScreens = getWorkspaceScreenIds(mContext);
+ IntArray allScreens = getWorkspaceScreenIds(mDb);
if (allScreens.isEmpty()) {
throw new Exception("Unable to get workspace screens");
}
@@ -230,8 +226,7 @@
int newScreenId = LauncherSettings.Settings.call(
mContext.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
-
+ .getInt(EXTRA_VALUE);
for (DbEntry item : placement.finalPlacedItems) {
if (!mCarryOver.remove(itemMap.get(item.id))) {
throw new Exception("Unable to find matching items");
@@ -362,11 +357,9 @@
* Updates an item in the DB.
*/
protected void update(DbEntry item) {
- mTempValues.clear();
- item.addToContentValues(mTempValues);
- mUpdateOperations.add(ContentProviderOperation
- .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
- .withValues(mTempValues).build());
+ ContentValues values = new ContentValues();
+ item.addToContentValues(values);
+ mUpdateOperations.put(item.id, values);
}
/**
@@ -612,13 +605,13 @@
}
private ArrayList<DbEntry> loadHotseatEntries() {
- Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ Cursor c = queryWorkspace(
new String[]{
Favorites._ID, // 0
Favorites.ITEM_TYPE, // 1
Favorites.INTENT, // 2
Favorites.SCREEN}, // 3
- Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT, null, null, null);
+ Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT);
final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
@@ -796,8 +789,7 @@
}
protected Cursor queryWorkspace(String[] columns, String where) {
- return mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
- columns, where, null, null, null);
+ return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
}
/**
@@ -864,11 +856,11 @@
}
public void addToContentValues(ContentValues values) {
- values.put(LauncherSettings.Favorites.SCREEN, screenId);
- values.put(LauncherSettings.Favorites.CELLX, cellX);
- values.put(LauncherSettings.Favorites.CELLY, cellY);
- values.put(LauncherSettings.Favorites.SPANX, spanX);
- values.put(LauncherSettings.Favorites.SPANY, spanY);
+ values.put(Favorites.SCREEN, screenId);
+ values.put(Favorites.CELLX, cellX);
+ values.put(Favorites.CELLY, cellY);
+ values.put(Favorites.SPANX, spanX);
+ values.put(Favorites.SPANY, spanY);
}
}
@@ -907,34 +899,43 @@
}
long migrationStartTime = System.currentTimeMillis();
- try {
+ try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
+ context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
+ .getBinder(Settings.EXTRA_VALUE)) {
+
+ int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+ idp.numHotseatIcons);
+ Point sourceSize = parsePoint(prefs.getString(
+ KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
+
boolean dbChanged = false;
+ GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
+ srcHotseatCount, sourceSize.x, sourceSize.y);
+ if (backupTable.backupOrRestoreAsNeeded()) {
+ dbChanged = true;
+ srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
+ }
+
HashSet<String> validPackages = getValidPackages(context);
// Hotseat
- int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
- idp.numHotseatIcons);
if (srcHotseatCount != idp.numHotseatIcons) {
// Migrate hotseat.
-
- dbChanged = new GridSizeMigrationTask(context, LauncherAppState.getIDP(context),
+ dbChanged = new GridSizeMigrationTask(context, transaction.getDb(),
validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
}
// Grid size
Point targetSize = new Point(idp.numColumns, idp.numRows);
- Point sourceSize = parsePoint(prefs.getString(
- KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
-
- if (new MultiStepMigrationTask(validPackages, context).migrate(sourceSize,
- targetSize)) {
+ if (new MultiStepMigrationTask(validPackages, context, transaction.getDb())
+ .migrate(sourceSize, targetSize)) {
dbChanged = true;
}
if (dbChanged) {
// Make sure we haven't removed everything.
final Cursor c = context.getContentResolver().query(
- LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
+ Favorites.CONTENT_URI, null, null, null, null);
boolean hasData = c.moveToNext();
c.close();
if (!hasData) {
@@ -942,6 +943,8 @@
}
}
+ transaction.commit();
+ Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
return true;
} catch (Exception e) {
Log.e(TAG, "Error during grid migration", e);
@@ -982,19 +985,24 @@
*/
public static IntSparseArrayMap<Object> removeBrokenHotseatItems(Context context)
throws Exception {
- GridSizeMigrationTask task = new GridSizeMigrationTask(
- context, LauncherAppState.getIDP(context), getValidPackages(context),
- Integer.MAX_VALUE, Integer.MAX_VALUE);
+ try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
+ context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
+ .getBinder(Settings.EXTRA_VALUE)) {
+ GridSizeMigrationTask task = new GridSizeMigrationTask(
+ context, transaction.getDb(), getValidPackages(context),
+ Integer.MAX_VALUE, Integer.MAX_VALUE);
- // Load all the valid entries
- ArrayList<DbEntry> items = task.loadHotseatEntries();
- // Delete any entry marked for deletion by above load.
- task.applyOperations();
- IntSparseArrayMap<Object> positions = new IntSparseArrayMap<>();
- for (DbEntry item : items) {
- positions.put(item.screenId, item);
+ // Load all the valid entries
+ ArrayList<DbEntry> items = task.loadHotseatEntries();
+ // Delete any entry marked for deletion by above load.
+ task.applyOperations();
+ IntSparseArrayMap<Object> positions = new IntSparseArrayMap<>();
+ for (DbEntry item : items) {
+ positions.put(item.screenId, item);
+ }
+ transaction.commit();
+ return positions;
}
- return positions;
}
/**
@@ -1003,10 +1011,13 @@
protected static class MultiStepMigrationTask {
private final HashSet<String> mValidPackages;
private final Context mContext;
+ private final SQLiteDatabase mDb;
- public MultiStepMigrationTask(HashSet<String> validPackages, Context context) {
+ public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
+ SQLiteDatabase db) {
mValidPackages = validPackages;
mContext = context;
+ mDb = db;
}
public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
@@ -1042,7 +1053,7 @@
}
protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
- return new GridSizeMigrationTask(mContext, LauncherAppState.getIDP(mContext),
+ return new GridSizeMigrationTask(mContext, mDb,
mValidPackages, sourceSize, nextSize).migrateWorkspace();
}
}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index b79478a..2c843f9 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -21,6 +21,7 @@
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
+import android.os.Binder;
import android.util.Log;
import com.android.launcher3.LauncherAppState;
@@ -103,10 +104,22 @@
return out;
}
+ public static boolean tableExists(SQLiteDatabase db, String tableName) {
+ try (Cursor c = db.query(true, "sqlite_master", new String[] {"tbl_name"},
+ "tbl_name = ?", new String[] {tableName},
+ null, null, null, null, null)) {
+ return c.getCount() > 0;
+ }
+ }
+
+ public static void dropTable(SQLiteDatabase db, String tableName) {
+ db.execSQL("DROP TABLE IF EXISTS " + tableName);
+ }
+
/**
* Utility class to simplify managing sqlite transactions
*/
- public static class SQLiteTransaction implements AutoCloseable {
+ public static class SQLiteTransaction extends Binder implements AutoCloseable {
private final SQLiteDatabase mDb;
public SQLiteTransaction(SQLiteDatabase db) {
@@ -122,5 +135,9 @@
public void close() {
mDb.endTransaction();
}
+
+ public SQLiteDatabase getDb() {
+ return mDb;
+ }
}
}
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
index 9166b83..6d839f3 100644
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -37,29 +37,21 @@
*/
public class LossyScreenMigrationTask extends GridSizeMigrationTask {
- private final SQLiteDatabase mDb;
-
private final IntSparseArrayMap<DbEntry> mOriginalItems;
private final IntSparseArrayMap<DbEntry> mUpdates;
protected LossyScreenMigrationTask(
Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
// Decrease the rows count by 1
- super(context, idp, getValidPackages(context),
+ super(context, db, getValidPackages(context),
new Point(idp.numColumns, idp.numRows + 1),
new Point(idp.numColumns, idp.numRows));
- mDb = db;
mOriginalItems = new IntSparseArrayMap<>();
mUpdates = new IntSparseArrayMap<>();
}
@Override
- protected Cursor queryWorkspace(String[] columns, String where) {
- return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
- }
-
- @Override
protected void update(DbEntry item) {
mUpdates.put(item.id, item.copy());
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 17c66b4..bcca4d8 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 com.android.launcher3.provider.LauncherDbUtils.dropTable;
+
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -111,7 +113,7 @@
db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
Favorites.addTableToDb(db, newProfileId, false);
db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
- db.execSQL("DROP TABLE favorites_old;");
+ dropTable(db, "favorites_old");
}
/**
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 717acdc..607afab 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -29,6 +29,7 @@
import android.view.Display;
import android.view.WindowManager;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities.Consumer;
@@ -40,6 +41,8 @@
private static final String TAG = "ConfigMonitor";
+ private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+
private final Point mTmpPoint1 = new Point();
private final Point mTmpPoint2 = new Point();
@@ -72,7 +75,15 @@
mCallback = callback;
+ // Listen for configuration change
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+
+ // Listen for {@link OverlayManager} change
+ IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(this, filter);
+
+ // Listen for display manager change
mContext.getSystemService(DisplayManager.class)
.registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
}
@@ -80,8 +91,14 @@
@Override
public void onReceive(Context context, Intent intent) {
Configuration config = context.getResources().getConfiguration();
+ // TODO: when overlay manager service encodes more information to the Uri such as category
+ // of the overlay, only listen to the ones that are of interest to launcher.
+ if (intent != null && ACTION_OVERLAY_CHANGED.equals(intent.getAction())) {
+ Log.d(TAG, "Overlay changed.");
+ notifyChange();
+ }
if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
- Log.d(TAG, "Configuration changed");
+ Log.d(TAG, "Configuration changed.");
notifyChange();
}
}
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index b2fb32a..d2a551f 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -100,6 +100,14 @@
}
/**
+ * Sets the array to be same as {@param other}
+ */
+ public void copyFrom(IntArray other) {
+ clear();
+ addAll(other);
+ }
+
+ /**
* Ensures capacity to append at least <code>count</code> values.
*/
private void ensureCapacity(int count) {
@@ -127,6 +135,25 @@
return wrap(toArray());
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof IntArray) {
+ IntArray arr = (IntArray) obj;
+ if (mSize == arr.mSize) {
+ for (int i = 0; i < mSize; i++) {
+ if (arr.mValues[i] != mValues[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Returns the value at the specified position in this array.
*/
diff --git a/src/com/android/launcher3/util/IntSet.java b/src/com/android/launcher3/util/IntSet.java
index 2459fad..851f129 100644
--- a/src/com/android/launcher3/util/IntSet.java
+++ b/src/com/android/launcher3/util/IntSet.java
@@ -49,10 +49,29 @@
return mArray.size();
}
+ public void clear() {
+ mArray.clear();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ return (obj instanceof IntSet) && ((IntSet) obj).mArray.equals(mArray);
+ }
+
public IntArray getArray() {
return mArray;
}
+ /**
+ * Sets this set to be same as {@param other}
+ */
+ public void copyFrom(IntSet other) {
+ mArray.copyFrom(other.mArray);
+ }
+
public static IntSet wrap(IntArray array) {
IntSet set = new IntSet();
set.mArray.addAll(array);
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index a321bcc..4fea2e9 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -39,35 +39,39 @@
private static final boolean SYSTEM_TRACE = false;
private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
- public static synchronized void beginSection(String sectionName) {
+ public static void beginSection(String sectionName) {
if (ENABLED) {
- MutableLong time = sUpTimes.get(sectionName);
- if (time == null) {
- time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
- sUpTimes.put(sectionName, time);
- }
- if (time.value >= 0) {
- if (SYSTEM_TRACE) {
- Trace.beginSection(sectionName);
+ synchronized (sUpTimes) {
+ MutableLong time = sUpTimes.get(sectionName);
+ if (time == null) {
+ time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
+ sUpTimes.put(sectionName, time);
}
- time.value = SystemClock.uptimeMillis();
+ if (time.value >= 0) {
+ if (SYSTEM_TRACE) {
+ Trace.beginSection(sectionName);
+ }
+ time.value = SystemClock.uptimeMillis();
+ }
}
}
}
- public static synchronized void partitionSection(String sectionName, String partition) {
+ public static void partitionSection(String sectionName, String partition) {
if (ENABLED) {
- MutableLong time = sUpTimes.get(sectionName);
- if (time != null && time.value >= 0) {
+ synchronized (sUpTimes) {
+ MutableLong time = sUpTimes.get(sectionName);
+ if (time != null && time.value >= 0) {
- if (SYSTEM_TRACE) {
- Trace.endSection();
- Trace.beginSection(sectionName);
+ if (SYSTEM_TRACE) {
+ Trace.endSection();
+ Trace.beginSection(sectionName);
+ }
+
+ long now = SystemClock.uptimeMillis();
+ Log.d(sectionName, partition + " : " + (now - time.value));
+ time.value = now;
}
-
- long now = SystemClock.uptimeMillis();
- Log.d(sectionName, partition + " : " + (now - time.value));
- time.value = now;
}
}
}
@@ -78,14 +82,16 @@
}
}
- public static synchronized void endSection(String sectionName, String msg) {
+ public static void endSection(String sectionName, String msg) {
if (ENABLED) {
- MutableLong time = sUpTimes.get(sectionName);
- if (time != null && time.value >= 0) {
- if (SYSTEM_TRACE) {
- Trace.endSection();
+ synchronized (sUpTimes) {
+ MutableLong time = sUpTimes.get(sectionName);
+ if (time != null && time.value >= 0) {
+ if (SYSTEM_TRACE) {
+ Trace.endSection();
+ }
+ Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
}
- Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
}
}
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index 9785887..1710aef 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -42,26 +42,13 @@
synchronized (mBgDataModel) {
shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap);
}
- mUiExecutor.execute(() -> {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.bindDeepShortcutMap(shortcutMapCopy);
- }
- });
+ executeCallbacksTask(c -> c.bindDeepShortcutMap(shortcutMapCopy), mUiExecutor);
}
@Override
public void bindWidgets() {
final ArrayList<WidgetListRowEntry> widgets =
mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
- Runnable r = new Runnable() {
- public void run() {
- Callbacks callbacks = mCallbacks.get();
- if (callbacks != null) {
- callbacks.bindAllWidgets(widgets);
- }
- }
- };
- mUiExecutor.execute(r);
+ executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
}
}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 0be5f11..ebab122 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3.tests">
- <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="21"
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"
tools:overrideLibrary="android.support.test.uiautomator.v18"/>
<application android:debuggable="true">
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
index 0546015..9d0a74a 100644
--- a/tests/dummy_app/AndroidManifest.xml
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -21,7 +21,7 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.aardwolf">
- <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
<application android:label="Aardwolf">
<activity
android:name="Activity1"