Merge "Adding a new Edit Mode LauncherState" into udc-dev
diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
index 04e87be..4e67629 100644
--- a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
@@ -59,6 +59,29 @@
app:layout_constraintStart_toEndOf="@id/thumbnail1"
app:layout_constraintEnd_toEndOf="parent"/>
+ <ImageView
+ android:id="@+id/icon1"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:importantForAccessibility="no"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail1"
+ app:layout_constraintStart_toStartOf="@id/thumbnail1"/>
+
+ <ImageView
+ android:id="@+id/icon2"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:importantForAccessibility="no"
+ android:visibility="gone"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail2"
+ app:layout_constraintStart_toStartOf="@id/thumbnail2"/>
+
</androidx.constraintlayout.widget.ConstraintLayout>
</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
diff --git a/quickstep/res/layout/keyboard_quick_switch_overview.xml b/quickstep/res/layout/keyboard_quick_switch_overview.xml
index 062a9c9..e7b1f23 100644
--- a/quickstep/res/layout/keyboard_quick_switch_overview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_overview.xml
@@ -41,8 +41,8 @@
android:layout_height="@dimen/keyboard_quick_switch_recents_icon_size"
android:layout_marginBottom="8dp"
android:src="@drawable/ic_empty_recents"
+ android:tint="?androidprv:attr/materialColorOnSurface"
- app:tint="?androidprv:attr/materialColorOnSurface"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/text"
@@ -50,7 +50,7 @@
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
- style="@style/KeyboardQuickSwitchOverview"
+ style="@style/KeyboardQuickSwitchText"
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
index 691df6e..4d213fa 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
@@ -59,6 +59,29 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
+ <ImageView
+ android:id="@+id/icon1"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:importantForAccessibility="no"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail1"
+ app:layout_constraintStart_toStartOf="@id/thumbnail1"/>
+
+ <ImageView
+ android:id="@+id/icon2"
+ android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
+ android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
+ android:importantForAccessibility="no"
+ android:visibility="gone"
+
+ app:layout_constraintTop_toTopOf="@id/thumbnail2"
+ app:layout_constraintStart_toStartOf="@id/thumbnail2"/>
+
</androidx.constraintlayout.widget.ConstraintLayout>
</com.android.launcher3.taskbar.KeyboardQuickSwitchTaskView>
diff --git a/quickstep/res/layout/keyboard_quick_switch_thumbnail.xml b/quickstep/res/layout/keyboard_quick_switch_thumbnail.xml
index cd6587c..dde9cac 100644
--- a/quickstep/res/layout/keyboard_quick_switch_thumbnail.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_thumbnail.xml
@@ -19,4 +19,5 @@
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:background="@drawable/keyboard_quick_switch_task_view_background"
- android:clipToOutline="true"/>
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"/>
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 58c0c40..16abdee 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -15,6 +15,7 @@
-->
<com.android.launcher3.taskbar.KeyboardQuickSwitchView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -27,6 +28,43 @@
android:focusableInTouchMode="true"
app:layout_ignoreInsets="true">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/no_recent_items_pane"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/keyboard_quick_switch_taskview_height"
+ android:paddingVertical="@dimen/keyboard_quick_switch_view_spacing"
+ android:alpha="0"
+ android:visibility="gone">
+
+ <ImageView
+ android:id="@+id/no_recent_items_icon"
+ android:layout_width="@dimen/keyboard_quick_switch_no_recent_items_icon_size"
+ android:layout_height="@dimen/keyboard_quick_switch_no_recent_items_icon_size"
+ android:layout_marginBottom="@dimen/keyboard_quick_switch_no_recent_items_icon_margin"
+ android:src="@drawable/ic_empty_recents"
+ android:tint="?androidprv:attr/materialColorOnSurfaceInverse"
+ android:importantForAccessibility="no"
+
+ app:layout_constraintVertical_chainStyle="packed"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/no_recent_items_text"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <TextView
+ style="@style/KeyboardQuickSwitchText.OnBackground"
+ android:id="@+id/no_recent_items_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/recents_empty_message"
+
+ app:layout_constraintTop_toBottomOf="@id/no_recent_items_icon"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
<HorizontalScrollView
android:id="@+id/scroll_view"
android:layout_width="wrap_content"
@@ -34,7 +72,7 @@
android:fillViewport="true"
android:scrollbars="none"
android:alpha="0"
- android:visibility="invisible"
+ android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d69b155..bb4f74d 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -372,6 +372,8 @@
<dimen name="keyboard_quick_switch_border_width">4dp</dimen>
<dimen name="keyboard_quick_switch_taskview_width">104dp</dimen>
<dimen name="keyboard_quick_switch_taskview_height">134dp</dimen>
+ <dimen name="keyboard_quick_switch_taskview_icon_size">28dp</dimen>
+ <dimen name="keyboard_quick_switch_taskview_icon_margin">4dp</dimen>
<dimen name="keyboard_quick_switch_recents_icon_size">20dp</dimen>
<dimen name="keyboard_quick_switch_margin_top">56dp</dimen>
<dimen name="keyboard_quick_switch_margin_ends">16dp</dimen>
@@ -379,4 +381,6 @@
<dimen name="keyboard_quick_switch_split_view_spacing">2dp</dimen>
<dimen name="keyboard_quick_switch_view_radius">28dp</dimen>
<dimen name="keyboard_quick_switch_task_view_radius">16dp</dimen>
+ <dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
+ <dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 2b6f749..2d8c45a 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -295,6 +295,7 @@
=1{Show # more app.}
other{Show # more apps.}
}</string>
+
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
</resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index e1afb26..ead5343 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -215,13 +215,17 @@
<item name="android:textSize">14sp</item>
</style>
- <style name="KeyboardQuickSwitchOverview">
+ <style name="KeyboardQuickSwitchText">
<item name="fontFamily">google-sans-text</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="lineHeight">20sp</item>
</style>
+ <style name="KeyboardQuickSwitchText.OnBackground" parent="KeyboardQuickSwitchText">
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceInverse</item>
+ </style>
+
<style name="GestureTutorialActivity"
parent="@style/AppTheme">
<item name="background">@android:color/transparent</item>
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index ed4a212..f981610 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -21,12 +21,15 @@
import android.animation.Animator;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StateManager;
import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.views.RecentsView;
+import java.util.stream.Stream;
+
/**
* A data source which integrates with the fallback RecentsActivity instance (for 3P launchers).
*/
@@ -81,18 +84,15 @@
* Currently this animation just force stashes the taskbar in Overview.
*/
public Animator createAnimToRecentsState(RecentsState toState, long duration) {
- // Force stash the taskbar in overview modal state or when going home. We do not force
- // stash on home when running in a test as 3p launchers rely on taskbar instead of hotseat.
- boolean isGoingHome = toState == RecentsState.HOME && !isRunningInTestHarness();
- boolean useStashedLauncherState = toState.hasOverviewActions() || isGoingHome;
- boolean stashedLauncherState = useStashedLauncherState && (
- (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get() && toState == RecentsState.MODAL_TASK)
- || isGoingHome);
+ // Force stash taskbar (disallow unstashing) when:
+ // - in a 3P launcher or overview task.
+ // - not running in a test harness (unstash is needed for tests)
+ boolean forceStash = isIn3pHomeOrRecents() && !isRunningInTestHarness();
TaskbarStashController stashController = mControllers.taskbarStashController;
// Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected.
// For all other states, just use the current stashed-in-app setting (e.g. if long clicked).
- stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState);
- stashController.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState);
+ stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, forceStash);
+ stashController.updateStateForFlag(FLAG_IN_APP, !forceStash);
return stashController.createApplyStateAnimator(duration);
}
@@ -108,4 +108,20 @@
public RecentsView getRecentsView() {
return mRecentsActivity.getOverviewPanel();
}
+
+ @Override
+ Stream<SystemShortcut.Factory<BaseTaskbarContext>> getSplitMenuOptions() {
+ if (isIn3pHomeOrRecents()) {
+ // Split from Taskbar is not supported in fallback launcher, so return empty stream
+ return Stream.empty();
+ } else {
+ return super.getSplitMenuOptions();
+ }
+ }
+
+ private boolean isIn3pHomeOrRecents() {
+ TopTaskTracker.CachedTaskInfo topTask = TopTaskTracker.INSTANCE
+ .get(mControllers.taskbarActivityContext).getCachedTopTask(true);
+ return topTask.isHomeTask() || topTask.isRecentsTask();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index c4962cd..7f655cf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -129,8 +129,8 @@
*/
int launchFocusedTask() {
// Return -1 so that the RecentsView is not incorrectly opened when the user closes the
- // quick switch view by tapping the screen.
- return mQuickSwitchViewController == null
+ // quick switch view by tapping the screen or when there are no recent tasks.
+ return mQuickSwitchViewController == null || mTasks.isEmpty()
? -1 : mQuickSwitchViewController.launchFocusedTask();
}
@@ -181,7 +181,7 @@
mModel.getThumbnailCache().updateThumbnailInBackground(task, callback);
}
- void updateTitleInBackground(Task task, Consumer<Task> callback) {
+ void updateIconInBackground(Task task, Consumer<Task> callback) {
mModel.getIconCache().updateIconInBackground(task, callback);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 926ede1..49dfe46 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -20,6 +20,7 @@
import android.animation.Animator;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
@@ -46,6 +47,8 @@
@Nullable private ImageView mThumbnailView1;
@Nullable private ImageView mThumbnailView2;
+ @Nullable private ImageView mIcon1;
+ @Nullable private ImageView mIcon2;
@Nullable private View mContent;
public KeyboardQuickSwitchTaskView(@NonNull Context context) {
@@ -67,6 +70,9 @@
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
+
setWillNotDraw(false);
Resources resources = context.getResources();
mBorderAnimator = new BorderAnimator(
@@ -75,17 +81,8 @@
R.dimen.keyboard_quick_switch_border_width),
/* borderRadiusPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_task_view_radius),
- /* borderColor= */ attrs == null
- ? DEFAULT_BORDER_COLOR
- : context.getTheme()
- .obtainStyledAttributes(
- attrs,
- R.styleable.TaskView,
- defStyleAttr,
- defStyleRes)
- .getColor(
- R.styleable.TaskView_borderColor,
- DEFAULT_BORDER_COLOR),
+ /* borderColor= */ ta.getColor(
+ R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR),
/* invalidateViewCallback= */ KeyboardQuickSwitchTaskView.this::invalidate,
/* viewScaleTargetProvider= */ new BorderAnimator.ViewScaleTargetProvider() {
@NonNull
@@ -100,14 +97,16 @@
return mContent;
}
});
+ ta.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
mThumbnailView1 = findViewById(R.id.thumbnail1);
mThumbnailView2 = findViewById(R.id.thumbnail2);
+ mIcon1 = findViewById(R.id.icon1);
+ mIcon2 = findViewById(R.id.icon2);
mContent = findViewById(R.id.content);
}
@@ -126,11 +125,11 @@
@NonNull Task task1,
@Nullable Task task2,
@Nullable ThumbnailUpdateFunction thumbnailUpdateFunction,
- @Nullable TitleUpdateFunction titleUpdateFunction) {
+ @Nullable IconUpdateFunction iconUpdateFunction) {
applyThumbnail(mThumbnailView1, task1, thumbnailUpdateFunction);
applyThumbnail(mThumbnailView2, task2, thumbnailUpdateFunction);
- if (titleUpdateFunction == null) {
+ if (iconUpdateFunction == null) {
setContentDescription(task2 == null
? task1.titleDescription
: getContext().getString(
@@ -139,16 +138,23 @@
task2.titleDescription));
return;
}
- titleUpdateFunction.updateTitleInBackground(task1, t ->
- setContentDescription(task1.titleDescription));
+ iconUpdateFunction.updateIconInBackground(task1, t -> {
+ applyIcon(mIcon1, task1);
+ if (task2 != null) {
+ return;
+ }
+ setContentDescription(task1.titleDescription);
+ });
if (task2 == null) {
return;
}
- titleUpdateFunction.updateTitleInBackground(task2, t ->
- setContentDescription(getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription)));
+ iconUpdateFunction.updateIconInBackground(task2, t -> {
+ applyIcon(mIcon2, task2);
+ setContentDescription(getContext().getString(
+ R.string.quick_switch_split_task,
+ task1.titleDescription,
+ task2.titleDescription));
+ });
}
private void applyThumbnail(
@@ -177,13 +183,21 @@
thumbnailView.setImageBitmap(bm);
}
+ private void applyIcon(@Nullable ImageView iconView, @NonNull Task task) {
+ if (iconView == null) {
+ return;
+ }
+ iconView.setVisibility(VISIBLE);
+ iconView.setImageDrawable(task.icon);
+ }
+
protected interface ThumbnailUpdateFunction {
void updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback);
}
- protected interface TitleUpdateFunction {
+ protected interface IconUpdateFunction {
- void updateTitleInBackground(Task task, Consumer<Task> callback);
+ void updateIconInBackground(Task task, Consumer<Task> callback);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 745defc..2cdfb18 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -85,6 +85,8 @@
private final AnimatedFloat mOutlineAnimationProgress = new AnimatedFloat(
this::invalidateOutline);
+ private boolean mDisplayingRecentTasks;
+ private View mNoRecentItemsPane;
private HorizontalScrollView mScrollView;
private ConstraintLayout mContent;
@@ -119,6 +121,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mNoRecentItemsPane = findViewById(R.id.no_recent_items_pane);
mScrollView = findViewById(R.id.scroll_view);
mContent = findViewById(R.id.content);
@@ -145,20 +148,20 @@
taskView.setOnClickListener(v -> mViewCallbacks.launchTappedTask(index));
LayoutParams lp = new LayoutParams(width, mTaskViewHeight);
- // Create a right-to-left ordering of views (or left-to-right in RTL locales)
+ // Create a left-to-right ordering of views (or right-to-left in RTL locales)
if (previousView != null) {
- lp.endToStart = previousView.getId();
+ lp.startToEnd = previousView.getId();
} else {
- lp.endToEnd = PARENT_ID;
+ lp.startToStart = PARENT_ID;
}
lp.topToTop = PARENT_ID;
lp.bottomToBottom = PARENT_ID;
// Add spacing between views
- lp.setMarginEnd(mSpacing);
+ lp.setMarginStart(mSpacing);
if (isFinalView) {
- // Add spacing to the start of the final view so that scrolling ends with some padding.
- lp.startToStart = PARENT_ID;
- lp.setMarginStart(mSpacing);
+ // Add spacing to the end of the final view so that scrolling ends with some padding.
+ lp.endToEnd = PARENT_ID;
+ lp.setMarginEnd(mSpacing);
lp.horizontalBias = 1f;
}
@@ -167,7 +170,7 @@
groupTask.task1,
groupTask.task2,
updateTasks ? mViewCallbacks::updateThumbnailInBackground : null,
- updateTasks ? mViewCallbacks::updateTitleInBackground : null);
+ updateTasks ? mViewCallbacks::updateIconInBackground : null);
mContent.addView(taskView, lp);
return taskView;
@@ -187,8 +190,8 @@
ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
width, mTaskViewHeight);
- lp.startToStart = PARENT_ID;
- lp.endToStart = previousView.getId();
+ lp.endToEnd = PARENT_ID;
+ lp.startToEnd = previousView.getId();
lp.topToTop = PARENT_ID;
lp.bottomToBottom = PARENT_ID;
lp.setMarginEnd(mSpacing);
@@ -204,10 +207,6 @@
boolean updateTasks,
int currentFocusIndexOverride,
@NonNull KeyboardQuickSwitchViewController.ViewCallbacks viewCallbacks) {
- if (groupTasks.isEmpty()) {
- // Do not show the quick switch view.
- return;
- }
mViewCallbacks = viewCallbacks;
Resources resources = context.getResources();
int width = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_taskview_width);
@@ -237,6 +236,7 @@
resources.getString(R.string.quick_switch_overflow),
Locale.getDefault()).format(args));
}
+ mDisplayingRecentTasks = !groupTasks.isEmpty();
getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -262,13 +262,16 @@
alphaAnimation.setDuration(ALPHA_ANIMATION_DURATION_MS);
closeAnimation.play(alphaAnimation);
+ View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
Animator translationYAnimation = ObjectAnimator.ofFloat(
- mScrollView, TRANSLATION_Y, 0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
+ displayedContent,
+ TRANSLATION_Y,
+ 0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
closeAnimation.play(translationYAnimation);
- Animator contentAlphaAnimation = ObjectAnimator.ofFloat(mScrollView, ALPHA, 1f, 0f);
+ Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 1f, 0f);
contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
closeAnimation.play(contentAlphaAnimation);
@@ -300,19 +303,24 @@
alphaAnimation.setDuration(ALPHA_ANIMATION_DURATION_MS);
mOpenAnimation.play(alphaAnimation);
+ View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
Animator translationXAnimation = ObjectAnimator.ofFloat(
- mScrollView, TRANSLATION_X, -Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
+ displayedContent,
+ TRANSLATION_X,
+ -Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
mOpenAnimation.play(translationXAnimation);
Animator translationYAnimation = ObjectAnimator.ofFloat(
- mScrollView, TRANSLATION_Y, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
+ displayedContent,
+ TRANSLATION_Y,
+ -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
mOpenAnimation.play(translationYAnimation);
- Animator contentAlphaAnimation = ObjectAnimator.ofFloat(mScrollView, ALPHA, 0f, 1f);
+ Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 0f, 1f);
contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
mOpenAnimation.play(contentAlphaAnimation);
@@ -353,7 +361,7 @@
} else {
animateFocusMove(-1, currentFocusIndexOverride);
}
- mScrollView.setVisibility(VISIBLE);
+ displayedContent.setVisibility(VISIBLE);
setVisibility(VISIBLE);
requestFocus();
}
@@ -372,6 +380,9 @@
}
protected void animateFocusMove(int fromIndex, int toIndex) {
+ if (!mDisplayingRecentTasks) {
+ return;
+ }
KeyboardQuickSwitchTaskView focusedTask = getTaskAt(toIndex);
if (focusedTask == null) {
return;
@@ -402,16 +413,16 @@
} else if (toIndex > fromIndex || toIndex == 0) {
// Scrolling to next task view
if (mIsRtl) {
- scrollRightTo(focusedTask);
- } else {
scrollLeftTo(focusedTask);
+ } else {
+ scrollRightTo(focusedTask);
}
} else {
// Scrolling to previous task view
if (mIsRtl) {
- scrollLeftTo(focusedTask);
- } else {
scrollRightTo(focusedTask);
+ } else {
+ scrollLeftTo(focusedTask);
}
}
if (mViewCallbacks != null) {
@@ -425,11 +436,15 @@
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- return (mViewCallbacks != null && mViewCallbacks.onKeyUp(keyCode, event, mIsRtl))
+ return (mViewCallbacks != null
+ && mViewCallbacks.onKeyUp(keyCode, event, mIsRtl, mDisplayingRecentTasks))
|| super.onKeyUp(keyCode, event);
}
private void initializeScroll(int index, boolean shouldTruncateTarget) {
+ if (!mDisplayingRecentTasks) {
+ return;
+ }
View task = getTaskAt(index);
if (task == null) {
return;
@@ -449,6 +464,9 @@
private void scrollRightTo(
@NonNull View targetTask, boolean shouldTruncateTarget, boolean smoothScroll) {
+ if (!mDisplayingRecentTasks) {
+ return;
+ }
if (smoothScroll && !shouldScroll(targetTask, shouldTruncateTarget)) {
return;
}
@@ -468,6 +486,9 @@
private void scrollLeftTo(
@NonNull View targetTask, boolean shouldTruncateTarget, boolean smoothScroll) {
+ if (!mDisplayingRecentTasks) {
+ return;
+ }
if (smoothScroll && !shouldScroll(targetTask, shouldTruncateTarget)) {
return;
}
@@ -491,7 +512,7 @@
@Nullable
protected KeyboardQuickSwitchTaskView getTaskAt(int index) {
- return index < 0 || index >= mContent.getChildCount()
+ return !mDisplayingRecentTasks || index < 0 || index >= mContent.getChildCount()
? null : (KeyboardQuickSwitchTaskView) mContent.getChildAt(index);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index c1f764f..7bd8898 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -169,7 +169,7 @@
class ViewCallbacks {
- boolean onKeyUp(int keyCode, KeyEvent event, boolean isRTL) {
+ boolean onKeyUp(int keyCode, KeyEvent event, boolean isRTL, boolean allowTraversal) {
if (keyCode != KeyEvent.KEYCODE_TAB
&& keyCode != KeyEvent.KEYCODE_DPAD_RIGHT
&& keyCode != KeyEvent.KEYCODE_DPAD_LEFT
@@ -181,6 +181,9 @@
closeQuickSwitchView(true);
return true;
}
+ if (!allowTraversal) {
+ return false;
+ }
boolean traverseBackwards = (keyCode == KeyEvent.KEYCODE_TAB && event.isShiftPressed())
|| (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && !isRTL)
|| (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && isRTL);
@@ -195,6 +198,9 @@
// focus a less recent app or loop back to the opposite end
: ((mCurrentFocusIndex + 1) % taskCount));
+ if (mCurrentFocusIndex == toIndex) {
+ return true;
+ }
mKeyboardQuickSwitchView.animateFocusMove(mCurrentFocusIndex, toIndex);
return true;
@@ -213,8 +219,8 @@
mControllerCallbacks.updateThumbnailInBackground(task, callback);
}
- void updateTitleInBackground(Task task, Consumer<Task> callback) {
- mControllerCallbacks.updateTitleInBackground(task, callback);
+ void updateIconInBackground(Task task, Consumer<Task> callback) {
+ mControllerCallbacks.updateIconInBackground(task, callback);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index fdef39f..ba6f165 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -197,6 +197,10 @@
return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
}
+ public void refreshResumedState() {
+ onLauncherResumedOrPaused(mLauncher.hasBeenResumed());
+ }
+
/**
* Create Taskbar animation when going from an app to Launcher as part of recents transition.
* @param toState If known, the state we will end up in when reaching Launcher.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8bc1fca..a1c9f05 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
@@ -43,6 +44,7 @@
import android.content.pm.ActivityInfo.Config;
import android.content.pm.LauncherApps;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -95,10 +97,13 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
+import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TraceHelper;
@@ -444,11 +449,6 @@
return mControllers.taskbarDragController;
}
- @Nullable
- public BubbleControllers getBubbleControllers() {
- return mControllers.bubbleControllers.orElse(null);
- }
-
@Override
public ViewCache getViewCache() {
return mViewCache;
@@ -572,6 +572,22 @@
}
}
+ @Override
+ public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+ RunnableList callbacks = new RunnableList();
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(
+ this, 0, 0, Color.TRANSPARENT,
+ Executors.MAIN_EXECUTOR.getHandler(), null,
+ elapsedRealTime -> callbacks.executeAllAndDestroy());
+ options.setSplashScreenStyle(splashScreenStyle);
+ return new ActivityOptionsWrapper(options, callbacks);
+ }
+
+ @Override
+ public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+ return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
+ }
+
/**
* Sets a new data-source for this taskbar instance
*/
@@ -625,12 +641,8 @@
mControllers.taskbarForceVisibleImmersiveController.updateSysuiFlags(systemUiStateFlags);
mControllers.voiceInteractionWindowController.setIsVoiceInteractionWindowVisible(
(systemUiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0, fromInit);
+
mControllers.uiController.updateStateForSysuiFlags(systemUiStateFlags);
- mControllers.bubbleControllers.ifPresent(controllers -> {
- controllers.bubbleBarController.updateStateForSysuiFlags(systemUiStateFlags);
- controllers.bubbleStashedHandleViewController.setIsHomeButtonDisabled(
- mControllers.navbarButtonsViewController.isHomeDisabled());
- });
}
/**
@@ -726,7 +738,7 @@
}
}
mWindowLayoutParams.height = height;
- mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+ mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged();
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
}
@@ -984,24 +996,20 @@
* Called when we want to unstash taskbar when user performs swipes up gesture.
*/
public void onSwipeToUnstashTaskbar() {
- mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
mControllers.taskbarEduTooltipController.hide();
}
- /**
- * Called when we want to open bubblebar when user performs swipes up gesture.
- */
- public void onSwipeToOpenBubblebar() {
- mControllers.bubbleControllers.ifPresent(controllers -> {
- controllers.bubbleStashController.showBubbleBar(/* expandBubbles= */ true);
- });
- }
-
- /** Returns {@code true} if taskbar All Apps is open. */
+ /** Returns {@code true} if Taskbar All Apps is open. */
public boolean isTaskbarAllAppsOpen() {
return mControllers.taskbarAllAppsController.isOpen();
}
+ /** Toggles the Taskbar's stash state. */
+ public void toggleTaskbarStash() {
+ mControllers.taskbarStashController.toggleTaskbarStash();
+ }
+
/**
* Called to start the taskbar translation spring to its settled translation (0).
*/
@@ -1163,6 +1171,10 @@
return mControllers.taskbarStashController.isInApp();
}
+ public boolean isInStashedLauncherState() {
+ return mControllers.taskbarStashController.isInStashedLauncherState();
+ }
+
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarActivityContext:");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 72add4f..88fea31 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -198,7 +198,7 @@
@Override
public boolean shouldStartDrag(double distanceDragged) {
- return mDragView != null && mDragView.isAnimationFinished();
+ return mDragView != null && mDragView.isScaleAnimationFinished();
}
@Override
@@ -231,7 +231,6 @@
dragLayerY,
(View target, DropTarget.DragObject d, boolean success) -> {} /* DragSource */,
(ItemInfo) btv.getTag(),
- /* dragVisualizeOffset = */ null,
dragRect,
scale * iconScale,
scale,
@@ -241,7 +240,7 @@
@Override
protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
- ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+ ItemInfo dragInfo, Rect dragRegion, float initialDragViewScale,
float dragViewScaleOnDrop, DragOptions options) {
mOptions = options;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 77d5a26..d6e559a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -55,13 +55,13 @@
private val touchableRegion: Region = Region()
private val insetsOwner: IBinder = Binder()
private val deviceProfileChangeListener = { _: DeviceProfile ->
- onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ onTaskbarWindowHeightOrInsetsChanged()
}
private val gestureNavSettingsObserver =
GestureNavigationSettingsObserver(
context.mainThreadHandler,
context,
- this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged
+ this::onTaskbarWindowHeightOrInsetsChanged
)
// Initialized in init.
@@ -71,7 +71,7 @@
fun init(controllers: TaskbarControllers) {
this.controllers = controllers
windowLayoutParams = context.windowLayoutParams
- onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
+ onTaskbarWindowHeightOrInsetsChanged()
context.addOnDeviceProfileChangeListener(deviceProfileChangeListener)
gestureNavSettingsObserver.registerForCallingUser()
@@ -82,7 +82,7 @@
gestureNavSettingsObserver.unregister()
}
- fun onTaskbarOrBubblebarWindowHeightOrInsetsChanged() {
+ fun onTaskbarWindowHeightOrInsetsChanged() {
if (context.isGestureNav) {
windowLayoutParams.providedInsets =
arrayOf(
@@ -104,33 +104,13 @@
)
}
- val taskbarTouchableHeight = controllers.taskbarStashController.touchableHeight
- val bubblesTouchableHeight =
- if (controllers.bubbleControllers.isPresent)
- controllers.bubbleControllers.get().bubbleStashController.touchableHeight
- else 0
- val touchableHeight = Math.max(taskbarTouchableHeight, bubblesTouchableHeight)
-
- if (
- controllers.bubbleControllers.isPresent &&
- controllers.bubbleControllers.get().bubbleStashController.isBubblesShowingOnHome
- ) {
- val iconBounds =
- controllers.bubbleControllers.get().bubbleBarViewController.bubbleBarBounds
- touchableRegion.set(
- iconBounds.left,
- iconBounds.top,
- iconBounds.right,
- iconBounds.bottom
- )
- } else {
- touchableRegion.set(
- 0,
- windowLayoutParams.height - touchableHeight,
- context.deviceProfile.widthPx,
- windowLayoutParams.height
- )
- }
+ val touchableHeight = controllers.taskbarStashController.touchableHeight
+ touchableRegion.set(
+ 0,
+ windowLayoutParams.height - touchableHeight,
+ context.deviceProfile.widthPx,
+ windowLayoutParams.height
+ )
val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
val res = context.resources
@@ -219,9 +199,6 @@
context.dragLayer,
insetsInfo.touchableRegion
)
- val bubbleBarVisible =
- controllers.bubbleControllers.isPresent &&
- controllers.bubbleControllers.get().bubbleBarViewController.isBubbleBarVisible()
var insetsIsTouchableRegion = true
if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
@@ -242,9 +219,7 @@
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME)
insetsIsTouchableRegion = false
} else if (
- controllers.taskbarViewController.areIconsVisible() ||
- context.isNavBarKidsModeActive ||
- bubbleBarVisible
+ controllers.taskbarViewController.areIconsVisible() || context.isNavBarKidsModeActive
) {
// Taskbar has some touchable elements, take over the full taskbar area
if (
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 6ece903..008f5f6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -207,10 +207,6 @@
com.android.launcher3.taskbar.Utilities.setOverviewDragState(
mControllers, finalState.disallowTaskbarGlobalDrag(),
disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
- // LauncherTaskbarUIController depends on the state when checking whether
- // to handle resume, so it should also be poked if current state changes
- mLauncher.getTaskbarUIController().onLauncherResumedOrPaused(
- mLauncher.hasBeenResumed());
}
};
@@ -408,14 +404,6 @@
+ ", mLauncherState: " + mLauncherState
+ ", toAlignment: " + toAlignment);
}
- mControllers.bubbleControllers.ifPresent(controllers -> {
- // Show the bubble bar when on launcher home or in overview.
- boolean onHome = isInLauncher && mLauncherState == LauncherState.NORMAL;
- boolean onOverview = mLauncherState == LauncherState.OVERVIEW;
- controllers.bubbleStashController.setBubblesShowingOnHome(onHome);
- controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview);
- });
-
AnimatorSet animatorSet = new AnimatorSet();
if (hasAnyFlag(changedFlags, FLAG_LAUNCHER_IN_STATE_TRANSITION)) {
@@ -486,8 +474,7 @@
public void onAnimationEnd(Animator animation) {
TaskbarStashController stashController =
mControllers.taskbarStashController;
- stashController.updateAndAnimateTransientTaskbar(
- /* stash */ true, /* duration */ 0, true /* bubblesShouldFollow */);
+ stashController.updateAndAnimateTransientTaskbar(/* stash */ true);
}
});
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index a442849..5eec726 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -32,7 +32,6 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -205,9 +204,7 @@
// append split options to APP_INFO shortcut, the order here will reflect in the popup
return Stream.concat(
Stream.of(APP_INFO),
- Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
- .stream()
- .map(this::createSplitShortcutFactory)
+ mControllers.uiController.getSplitMenuOptions()
);
}
@@ -265,7 +262,7 @@
* right.
* @return A factory function to be used in populating the long-press menu.
*/
- private SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
+ SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
SplitPositionOption position) {
return (context, itemInfo, originalView) -> new TaskbarSplitShortcut(context, itemInfo,
originalView, position, mAllowInitialSplitSelection);
@@ -328,6 +325,7 @@
mItemInfo.getIntent().getComponent(),
null,
mItemInfo.user),
+ mItemInfo.user.getIdentifier(),
new Intent(),
getPosition().stagePosition,
null,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 1c250bf..5ea00cf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.taskbar.bubbles.BubbleBarController.BUBBLE_BAR_ENABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
@@ -24,7 +23,6 @@
import android.view.animation.PathInterpolator;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.util.DisplayController;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
@@ -65,10 +63,6 @@
* Updates the scrim state based on the flags.
*/
public void updateStateForSysuiFlags(int stateFlags, boolean skipAnim) {
- if (BUBBLE_BAR_ENABLED && DisplayController.isTransientTaskbar(mActivity)) {
- // These scrims aren't used if bubble bar & transient taskbar are active.
- return;
- }
final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
final boolean manageMenuExpanded =
(stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index 054689b..e8c8fc4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -111,7 +111,8 @@
item.getIntent().getComponent(),
/* startActivityOptions= */null,
item.user),
- new Intent(), side, null, instanceIds.first);
+ item.user.getIdentifier(), new Intent(), side, null,
+ instanceIds.first);
}
return true;
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
index d65b5c0..f87c21e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.anim.AnimatedFloat.VALUE;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import androidx.annotation.Nullable;
@@ -85,6 +86,15 @@
.build(mTranslationForStash, VALUE);
}
+ /**
+ * Returns an animation to reset the stash translation back to 0 when unstashing.
+ */
+ public @Nullable ObjectAnimator createResetAnimForUnstash() {
+ if (!mIsTransientTaskbar) {
+ return null;
+ }
+ return mTranslationForStash.animateToValue(0);
+ }
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 0dd8310..6f82c7d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -255,15 +255,14 @@
private boolean mEnableBlockingTimeoutDuringTests = false;
// Evaluate whether the handle should be stashed
- private final IntPredicate mIsStashedPredicate = flags -> {
- boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
- boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
- boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
- boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED);
- return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
- };
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
- mIsStashedPredicate);
+ flags -> {
+ boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
+ boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
+ boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
+ boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED);
+ return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
+ });
private boolean mIsTaskbarSystemActionRegistered = false;
private TaskbarSharedState mTaskbarSharedState;
@@ -502,30 +501,9 @@
}
/**
- * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION.
- * If bubble bar exists, it will match taskbars stashing behavior.
+ * Stash or unstashes the transient taskbar.
*/
public void updateAndAnimateTransientTaskbar(boolean stash) {
- updateAndAnimateTransientTaskbar(stash, TASKBAR_STASH_DURATION,
- /* shouldBubblesFollow= */ true);
- }
-
- /**
- * Stash or unstashes the transient taskbar, using the default TASKBAR_STASH_DURATION.
- * If bubble bar exists, it will match taskbars stashing behavior.
- */
- public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow) {
- updateAndAnimateTransientTaskbar(stash, TASKBAR_STASH_DURATION, shouldBubblesFollow);
- }
-
- /**
- * Stash or unstashes the transient taskbar.
- * @param stash whether transient taskbar should be stashed.
- * @param duration how long the duration of the stash should take.
- * @param shouldBubblesFollow whether bubbles should match taskbars behavior.
- */
- public void updateAndAnimateTransientTaskbar(boolean stash, long duration,
- boolean shouldBubblesFollow) {
if (!DisplayController.isTransientTaskbar(mActivity)) {
return;
}
@@ -541,34 +519,6 @@
updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
applyState();
}
-
- mControllers.bubbleControllers.ifPresent(controllers -> {
- if (shouldBubblesFollow) {
- final boolean willStash = mIsStashedPredicate.test(mState);
- if (willStash != controllers.bubbleStashController.isStashed()) {
- // Typically bubbles gets stashed / unstashed along with Taskbar, however, if
- // taskbar is becoming stashed because bubbles is being expanded, we don't want
- // to stash bubbles.
- if (willStash) {
- controllers.bubbleStashController.stashBubbleBar();
- } else {
- controllers.bubbleStashController.showBubbleBar(false /* expandBubbles */);
- }
- }
- }
- });
- }
-
- /**
- * Stashes transient taskbar after it has timed out.
- */
- private void updateAndAnimateTransientTaskbarForTimeout() {
- // If bubbles are expanded we shouldn't stash them when taskbar is hidden
- // for the timeout.
- boolean bubbleBarExpanded = mControllers.bubbleControllers.isPresent()
- && mControllers.bubbleControllers.get().bubbleBarViewController.isExpanded();
- updateAndAnimateTransientTaskbar(/* stash= */ true,
- /* shouldBubblesFollow= */ !bubbleBarExpanded);
}
/**
@@ -618,6 +568,12 @@
return false;
}
+ /** Toggles the Taskbar's stash state. */
+ public void toggleTaskbarStash() {
+ if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return;
+ updateAndAnimateTransientTaskbar(!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
+ }
+
/**
* Adds the Taskbar unstash to Hotseat animator to the animator set.
*
@@ -842,6 +798,9 @@
if (isStashed) {
play(skippable, mControllers.taskbarSpringOnStashController.createSpringToStash(),
0, duration, LINEAR);
+ } else {
+ play(skippable, mControllers.taskbarSpringOnStashController.createResetAnimForUnstash(),
+ 0, duration, LINEAR);
}
mControllers.taskbarViewController.addRevealAnimToIsStashed(skippable, isStashed, duration,
@@ -923,7 +882,7 @@
private void onIsStashedChanged(boolean isStashed) {
mControllers.runAfterInit(() -> {
mControllers.stashedHandleViewController.onIsStashedChanged(isStashed);
- mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+ mControllers.taskbarInsetsController.onTaskbarWindowHeightOrInsetsChanged();
});
}
@@ -1170,7 +1129,7 @@
if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
return;
}
- updateAndAnimateTransientTaskbarForTimeout();
+ updateAndAnimateTransientTaskbar(true);
}
@Override
@@ -1261,6 +1220,15 @@
&& mLastStartedTransitionType == TRANSITION_DEFAULT
&& animationType != TRANSITION_DEFAULT;
+ // It is possible for stash=false to be requested by TRANSITION_HOME_TO_APP and
+ // TRANSITION_DEFAULT in quick succession. In this case, we should ignore
+ // transitionTypeChanged because the animations are exactly the same.
+ if (transitionTypeChanged
+ && (!mIsStashed && !isStashed)
+ && animationType == TRANSITION_HOME_TO_APP) {
+ transitionTypeChanged = false;
+ }
+
if (mIsStashed != isStashed || transitionTypeChanged) {
mIsStashed = isStashed;
mLastStartedTransitionType = animationType;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
index b194c8e..1cc6672 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -108,18 +108,7 @@
}
override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean {
- val bubbleControllers = controllers.bubbleControllers.orElse(null)
- if (!enabled || bubbleControllers == null) {
- return false
- }
- if (bubbleControllers.bubbleBarViewController.isExpanded) {
- // WMShell / bubbles will handle collapsing
- return false
- }
- if (
- controllers.taskbarStashController.isStashed &&
- bubbleControllers.bubbleStashController.isStashed
- ) {
+ if (!enabled || controllers.taskbarStashController.isStashed) {
return false
}
@@ -133,12 +122,7 @@
return true
}
} else if (ev.action == MotionEvent.ACTION_DOWN) {
- val isDownOnBubbleBar =
- (bubbleControllers != null &&
- bubbleControllers.bubbleBarViewController.isEventOverAnyItem(
- screenCoordinatesEv
- ))
- if (!isDownOnBubbleBar && screenCoordinatesEv.y < gestureHeightYThreshold) {
+ if (screenCoordinatesEv.y < gestureHeightYThreshold) {
controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 2456f4b..065d111 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -92,10 +92,6 @@
mControllers.stashedHandleViewController.setTranslationYForSwipe(transY);
mControllers.taskbarViewController.setTranslationYForSwipe(transY);
mControllers.taskbarDragLayerController.setTranslationYForSwipe(transY);
- mControllers.bubbleControllers.ifPresent(controllers -> {
- controllers.bubbleBarViewController.setTranslationYForSwipe(transY);
- controllers.bubbleStashedHandleViewController.setTranslationYForSwipe(transY);
- });
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index f3e2ee2..be5cbac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -30,8 +30,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
@@ -41,6 +43,7 @@
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import java.io.PrintWriter;
+import java.util.stream.Stream;
/**
* Base class for providing different taskbar UI
@@ -318,4 +321,19 @@
}
return null;
}
+
+ /**
+ * Refreshes the resumed state of this ui controller.
+ */
+ public void refreshResumedState() {}
+
+ /**
+ * Returns a stream of split screen menu options appropriate to the device.
+ */
+ Stream<SystemShortcut.Factory<BaseTaskbarContext>> getSplitMenuOptions() {
+ return Utilities
+ .getSplitPositionOptions(mControllers.taskbarActivityContext.getDeviceProfile())
+ .stream()
+ .map(mControllers.taskbarPopupController::createSplitShortcutFactory);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 3786189..82494c6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -28,8 +28,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarInsetsController;
-import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
@@ -53,8 +51,6 @@
// Initialized in init.
private BubbleStashController mBubbleStashController;
private BubbleBarController mBubbleBarController;
- private TaskbarStashController mTaskbarStashController;
- private TaskbarInsetsController mTaskbarInsetsController;
private View.OnClickListener mBubbleClickListener;
private View.OnClickListener mBubbleBarClickListener;
@@ -84,8 +80,6 @@
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
- mTaskbarStashController = controllers.taskbarStashController;
- mTaskbarInsetsController = controllers.taskbarInsetsController;
mActivity.addOnDeviceProfileChangeListener(dp ->
mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight
@@ -95,9 +89,7 @@
mBubbleClickListener = v -> onBubbleClicked(v);
mBubbleBarClickListener = v -> setExpanded(true);
mBarView.setOnClickListener(mBubbleBarClickListener);
- mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
- );
+ // TODO: when barView layout changes tell taskbarInsetsController the insets have changed.
}
private void onBubbleClicked(View v) {
@@ -291,8 +283,7 @@
} else {
Log.w(TAG, "trying to expand bubbles when there isn't one selected");
}
- mTaskbarStashController.updateAndAnimateTransientTaskbar(true /* stash */,
- false /* shouldBubblesFollow */);
+ // TODO: Tell taskbar stash controller to stash without bubbles following
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index b3c7d41..0ab53b0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -25,7 +25,6 @@
import com.android.launcher3.taskbar.StashedHandleViewController;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.util.MultiPropertyFactory;
@@ -51,7 +50,6 @@
// Initialized in init.
private TaskbarControllers mControllers;
- private TaskbarInsetsController mTaskbarInsetsController;
private BubbleBarViewController mBarViewController;
private BubbleStashedHandleViewController mHandleViewController;
private TaskbarStashController mTaskbarStashController;
@@ -79,7 +77,6 @@
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
mControllers = controllers;
- mTaskbarInsetsController = controllers.taskbarInsetsController;
mBarViewController = bubbleControllers.bubbleBarViewController;
mHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
mTaskbarStashController = controllers.taskbarStashController;
@@ -274,7 +271,7 @@
private void onIsStashedChanged() {
mControllers.runAfterInit(() -> {
mHandleViewController.onIsStashedChanged();
- mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
+ // TODO: when stash changes tell taskbarInsetsController the insets have changed.
});
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 0eef70e..a8b7698 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -57,7 +57,6 @@
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
@@ -183,7 +182,16 @@
: null;
super.applyFromWorkspaceItem(info, animate, staggerIndex);
int oldPlateColor = mPlateColor;
- int newPlateColor = ColorUtils.setAlphaComponent(mDotParams.appColor, 200);
+
+ int newPlateColor;
+ if (getIcon().isThemed()) {
+ newPlateColor = getResources().getColor(android.R.color.system_accent1_300);
+ } else {
+ float[] hctPlateColor = new float[3];
+ ColorUtils.colorToM3HCT(mDotParams.appColor, hctPlateColor);
+ newPlateColor = ColorUtils.M3HCTtoColor(hctPlateColor[0], 36, 85);
+ }
+
if (!animate) {
mPlateColor = newPlateColor;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 65f449c..79a301a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -68,6 +68,7 @@
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.SensorManager;
@@ -143,6 +144,7 @@
import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.ObjectWrapper;
@@ -343,14 +345,16 @@
}
@Override
- public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
// Only pause is taskbar controller is not present
mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
- boolean started = super.startActivitySafely(v, intent, item);
- if (getTaskbarUIController() == null && !started) {
+ RunnableList result = super.startActivitySafely(v, intent, item);
+ if (getTaskbarUIController() == null && result == null) {
mHotseatPredictionController.setPauseUIUpdate(false);
+ } else {
+ result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false));
}
- return started;
+ return result;
}
@Override
@@ -370,11 +374,6 @@
| ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
}
-
- if (((changeBits & ACTIVITY_STATE_STARTED) != 0
- || (changeBits & getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
- mHotseatPredictionController.setPauseUIUpdate(false);
- }
}
@Override
@@ -1102,6 +1101,17 @@
}
@Override
+ public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+ RunnableList callbacks = new RunnableList();
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(
+ this, 0, 0, Color.TRANSPARENT,
+ Executors.MAIN_EXECUTOR.getHandler(), null,
+ elapsedRealTime -> callbacks.executeAllAndDestroy());
+ options.setSplashScreenStyle(splashScreenStyle);
+ return new ActivityOptionsWrapper(options, callbacks);
+ }
+
+ @Override
@BinderThread
public void enterStageSplitFromRunningApp(boolean leftOrTop) {
mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 40dfd82..8cbd6e8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -41,11 +41,9 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.TaskUtils;
-import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.views.RecentsView;
@@ -108,11 +106,6 @@
if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
return true;
}
- if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
- && TopTaskTracker.INSTANCE.get(mLauncher).getCachedTopTask(false)
- .isExcludedAssistant()) {
- return true;
- }
return false;
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index d3e4ce5..64ec1d8 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -58,6 +58,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -87,6 +88,10 @@
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+ // TODO: Move to quickstep contract
+ private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 9;
+ private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 2;
+
private final Context mContext;
private final DisplayController mDisplayController;
private final int mDisplayId;
@@ -577,6 +582,19 @@
&& ((mSystemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0);
}
+ /**
+ * Returns the touch slop for {@link InputConsumer}s to compare against before pilfering
+ * pointers. Note that this is squared because it expects to be compared against
+ * {@link com.android.launcher3.Utilities#squaredHypot} (to avoid square root on each event).
+ */
+ public float getSquaredTouchSlop() {
+ float slopMultiplier = isFullyGesturalNavMode()
+ ? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL
+ : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
+ float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ return slopMultiplier * touchSlop * touchSlop;
+ }
+
public String getSystemUiStateString() {
return QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0549d9f..29aed25 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -683,12 +683,12 @@
}
}
- public void startIntentAndTask(PendingIntent pendingIntent, Bundle options1, int taskId,
- Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
+ public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
+ int taskId, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startIntentAndTask(pendingIntent, options1, taskId, options2,
+ mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
splitPosition, splitRatio, remoteTransition, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntentAndTask");
@@ -696,15 +696,16 @@
}
}
- public void startIntents(PendingIntent pendingIntent1, @Nullable ShortcutInfo shortcutInfo1,
- Bundle options1, PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
- Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition,
- float splitRatio, RemoteTransition remoteTransition, InstanceId instanceId) {
+ public void startIntents(PendingIntent pendingIntent1, int userId1,
+ @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
+ int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
+ @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
+ RemoteTransition remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startIntents(pendingIntent1, shortcutInfo1, options1, pendingIntent2,
- shortcutInfo2, options2, splitPosition, splitRatio, remoteTransition,
- instanceId);
+ mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
+ pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, splitRatio,
+ remoteTransition, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntents");
}
@@ -740,14 +741,14 @@
}
}
- public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+ public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
Bundle options1, int taskId, Bundle options2,
@SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio,
RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, options1, taskId,
- options2, splitPosition, splitRatio, adapter, instanceId);
+ mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1,
+ options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
}
@@ -771,16 +772,16 @@
* Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
* non-null shortcut info means to start the app as a shortcut.
*/
- public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1,
+ public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
@Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
- PendingIntent pendingIntent2, @Nullable ShortcutInfo shortcutInfo2,
+ PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
@Nullable Bundle options2, @SplitConfigurationOptions.StagePosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
- mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, shortcutInfo1,
- options1, pendingIntent2, shortcutInfo2, options2, sidePosition, splitRatio,
- adapter, instanceId);
+ mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1,
+ shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2,
+ sidePosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntentsWithLegacyTransition");
}
@@ -799,11 +800,12 @@
}
}
- public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
+ public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
Bundle options, InstanceId instanceId) {
if (mSplitScreen != null) {
try {
- mSplitScreen.startIntent(intent, fillInIntent, position, options, instanceId);
+ mSplitScreen.startIntent(intent, userId, fillInIntent, position, options,
+ instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntent");
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index c8c6292..4c4b9b4 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -236,6 +236,9 @@
homeIsOnTop = true;
}
}
+ if (activityInterface.allowAllAppsFromOverview()) {
+ homeIsOnTop = true;
+ }
if (!homeIsOnTop) {
options.setTransientLaunch();
}
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 6f502d0..d34cddf 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.content.Intent.ACTION_CHOOSER;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -127,23 +128,16 @@
@Override
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
- // If task is not visible but we are tracking it, stop tracking it
- if (!visible) {
+ // If a task is not visible anymore or has been moved to undefined, stop tracking it.
+ if (!visible || stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
if (mMainStagePosition.taskId == taskId) {
- resetTaskId(mMainStagePosition);
+ mMainStagePosition.taskId = INVALID_TASK_ID;
} else if (mSideStagePosition.taskId == taskId) {
- resetTaskId(mSideStagePosition);
+ mSideStagePosition.taskId = INVALID_TASK_ID;
} // else it's an un-tracked child
return;
}
- // If stage has moved to undefined, stop tracking the task
- if (stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
- resetTaskId(taskId == mMainStagePosition.taskId
- ? mMainStagePosition : mSideStagePosition);
- return;
- }
-
if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
mMainStagePosition.taskId = taskId;
} else {
@@ -161,10 +155,6 @@
mPinnedTaskId = INVALID_TASK_ID;
}
- private void resetTaskId(SplitStageInfo taskPosition) {
- taskPosition.taskId = -1;
- }
-
/**
* @return index 0 will be task in left/top position, index 1 in right/bottom position.
* Will return empty array if device is not in staged split
@@ -255,6 +245,11 @@
.getActivityType() == ACTIVITY_TYPE_HOME;
}
+ public boolean isRecentsTask() {
+ return mTopTask != null && mTopTask.configuration.windowConfiguration
+ .getActivityType() == ACTIVITY_TYPE_RECENTS;
+ }
+
/**
* Returns {@code true} if this task windowing mode is set to {@link
* android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 682763f..1fbfbe6 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -24,7 +24,6 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
-import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
@@ -85,6 +84,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -207,7 +207,15 @@
@BinderThread
@Override
public void onTaskbarToggled() {
- // To be implemented.
+ if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) return;
+ MAIN_EXECUTOR.execute(() -> {
+ TaskbarActivityContext activityContext =
+ mTaskbarManager.getCurrentActivityContext();
+
+ if (activityContext != null) {
+ activityContext.toggleTaskbarStash();
+ }
+ });
}
@BinderThread
@@ -862,7 +870,8 @@
if (tac != null) {
// Present always on large screen or on small screen w/ flag
DeviceProfile dp = tac.getDeviceProfile();
- boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp);
+ boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp)
+ && !tac.isInStashedLauncherState();
if (canStartSystemGesture && useTaskbarConsumer) {
reasonString.append(NEWLINE_PREFIX)
.append(reasonPrefix)
@@ -1088,22 +1097,17 @@
boolean hasWindowFocus = activity.getRootView().hasWindowFocus();
boolean isPreviousGestureAnimatingToLauncher =
previousGestureState.isRunningAnimationToLauncher();
- boolean forcingOverviewInputConsumer =
- ASSISTANT_GIVES_LAUNCHER_FOCUS.get() && forceOverviewInputConsumer;
boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode();
reasonString.append(SUBSTRING_PREFIX)
.append(hasWindowFocus
? "activity has window focus"
: (isPreviousGestureAnimatingToLauncher
? "previous gesture is still animating to launcher"
- : (forcingOverviewInputConsumer
- ? "assistant gives launcher focus and forcing focus"
- : (isInLiveTileMode
- ? "device is in live mode"
- : "all overview focus conditions failed"))));
+ : isInLiveTileMode
+ ? "device is in live mode"
+ : "all overview focus conditions failed"));
if (hasWindowFocus
|| isPreviousGestureAnimatingToLauncher
- || forcingOverviewInputConsumer
|| isInLiveTileMode) {
reasonString.append(SUBSTRING_PREFIX)
.append("overview should have focus, using OverviewInputConsumer");
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 42a74d9..59a9582 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -20,7 +20,6 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
@@ -115,7 +114,7 @@
mDeviceState = deviceState;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
- mTouchSlopSquared = squaredTouchSlop(context);
+ mTouchSlopSquared = mDeviceState.getSquaredTouchSlop();
mTransformParams = new TransformParams();
mInputMonitorCompat = inputMonitorCompat;
mMaxTranslationY = context.getResources().getDimensionPixelSize(
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index d7ed79b..5387c8a 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -21,8 +21,8 @@
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.testing.shared.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
import android.content.Context;
import android.graphics.Point;
@@ -31,7 +31,6 @@
import com.android.launcher3.R;
import com.android.launcher3.testing.shared.ResourceUtils;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
@@ -69,7 +68,7 @@
mDeviceState = deviceState;
mDragDistThreshold = context.getResources().getDimensionPixelSize(
R.dimen.gestures_onehanded_drag_threshold);
- mSquaredSlop = Utilities.squaredTouchSlop(context);
+ mSquaredSlop = mDeviceState.getSquaredTouchSlop();
mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE,
mContext.getResources());
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 2dcbbb9..f9cd4ee 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -80,10 +80,6 @@
public static final String DOWN_EVT = "OtherActivityInputConsumer.DOWN";
private static final String UP_EVT = "OtherActivityInputConsumer.UP";
- // TODO: Move to quickstep contract
- public static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 9;
- public static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 2;
-
// Minimum angle of a gesture's coordinate where a release goes to overview.
public static final int OVERVIEW_MIN_DEGREES = 15;
@@ -157,11 +153,8 @@
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
- float slopMultiplier = mDeviceState.isFullyGesturalNavMode()
- ? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL
- : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
- mSquaredTouchSlop = slopMultiplier * mTouchSlop * mTouchSlop;
+ mSquaredTouchSlop = mDeviceState.getSquaredTouchSlop();
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 64165b6..3388642 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -51,6 +51,7 @@
private final boolean mStartingInActivityBounds;
private boolean mTargetHandledTouch;
+ private boolean mHasSetTouchModeForFirstDPadEvent;
public OverviewInputConsumer(GestureState gestureState, T activity,
@Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
@@ -95,6 +96,9 @@
mInputMonitor.pilferPointers();
}
}
+ if (mHasSetTouchModeForFirstDPadEvent) {
+ mActivity.getRootView().clearFocus();
+ }
}
@Override
@@ -112,6 +116,19 @@
mgr.dispatchVolumeKeyEventAsSystemService(ev,
AudioManager.USE_DEFAULT_STREAM_TYPE);
break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (!mHasSetTouchModeForFirstDPadEvent) {
+ // When Overview is launched via meta+tab or swipe up from an app, the touch
+ // mode somehow is not changed to false by the Android framework. The subsequent
+ // key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched to focused
+ // views, while focus can only be requested in
+ // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To
+ // note, here we launch overview with live tile.
+ mHasSetTouchModeForFirstDPadEvent = true;
+ mActivity.getRootView().getViewRootImpl().touchModeChanged(false);
+ }
+ break;
default:
break;
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
index b9126eb..fbe7fde 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java
@@ -37,7 +37,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
-import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.InputConsumer;
@@ -63,7 +62,6 @@
private final int mTaskbarNavThresholdY;
private final boolean mIsTaskbarAllAppsOpen;
private boolean mHasPassedTaskbarNavThreshold;
- private boolean mIsInBubbleBarArea;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
@@ -83,6 +81,7 @@
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
super(delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
+ // TODO(b/270395798): remove this when cleaning up old Persistent Taskbar code.
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx;
@@ -137,7 +136,7 @@
mHasPassedTaskbarNavThreshold = false;
mTaskbarActivityContext.setAutohideSuspendFlag(
FLAG_AUTOHIDE_SUSPEND_TOUCHING, true);
- if (isInTaskbarArea(x)) {
+ if (isInArea(x)) {
if (!mIsTransientTaskbar) {
mLongPressDownX = x;
mLongPressDownY = y;
@@ -146,12 +145,10 @@
mCanceledUnstashHint = false;
}
}
+
if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) {
mTransitionCallback.onActionDown();
}
- if (mIsTransientTaskbar && isInBubbleBarArea(x)) {
- mIsInBubbleBarArea = true;
- }
break;
case MotionEvent.ACTION_POINTER_UP:
int ptrIdx = ev.getActionIndex();
@@ -188,11 +185,7 @@
if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) {
mHasPassedTaskbarNavThreshold = true;
- if (mIsInBubbleBarArea) {
- mTaskbarActivityContext.onSwipeToOpenBubblebar();
- } else {
- mTaskbarActivityContext.onSwipeToUnstashTaskbar();
- }
+ mTaskbarActivityContext.onSwipeToUnstashTaskbar();
}
if (dY < 0) {
@@ -215,32 +208,21 @@
mTransitionCallback.onActionEnd();
}
mHasPassedTaskbarNavThreshold = false;
- mIsInBubbleBarArea = false;
break;
}
}
}
}
- private boolean isInTaskbarArea(float x) {
+ private boolean isInArea(float x) {
float areaFromMiddle = mUnstashArea / 2.0f;
float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x);
return distFromMiddle < areaFromMiddle;
}
- private boolean isInBubbleBarArea(float x) {
- if (mTaskbarActivityContext != null && mIsTransientTaskbar) {
- BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers();
- if (controllers == null) return false;
- Rect bubbleBarBounds = controllers.bubbleBarViewController.getBubbleBarBounds();
- return x >= bubbleBarBounds.left && x <= bubbleBarBounds.right;
- }
- return false;
- }
-
private void onLongPressDetected(MotionEvent motionEvent) {
if (mTaskbarActivityContext != null
- && isInTaskbarArea(motionEvent.getRawX())
+ && isInArea(motionEvent.getRawX())
&& !mIsTransientTaskbar) {
boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
if (taskBarPressed) {
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 11c9e37..0497f0a 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -106,6 +106,7 @@
case BACK_CANCELLED_FROM_LEFT:
case BACK_CANCELLED_FROM_RIGHT:
case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ resetTaskView();
showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
break;
}
@@ -135,6 +136,7 @@
}
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ resetTaskView();
showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
break;
case OVERVIEW_GESTURE_COMPLETED:
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index dfbcf4d..c5d0ebe 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -21,7 +21,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.graphics.PointF;
import android.os.Build;
@@ -112,6 +111,7 @@
case BACK_CANCELLED_FROM_LEFT:
case BACK_CANCELLED_FROM_RIGHT:
case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ resetTaskView();
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
break;
}
@@ -142,33 +142,15 @@
}
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ resetTaskView();
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
break;
case OVERVIEW_GESTURE_COMPLETED:
+ setGestureCompleted();
mTutorialFragment.releaseFeedbackAnimation();
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- onMotionPaused(true /*arbitrary value*/);
- animateTaskViewToOverview(() -> {
- mFakeTaskView.setVisibility(View.INVISIBLE);
- if(!mTutorialFragment.isLargeScreen()){
- mFakePreviousTaskView.animateToFillScreen(() -> {
- mFakeLauncherView.setBackgroundColor(
- mContext.getColor(
- R.color.gesture_overview_tutorial_swipe_rect
- ));
- showSuccessFeedback();
- });
- } else {
- mFakeLauncherView.setBackgroundColor(
- mContext.getColor(
- R.color.gesture_overview_tutorial_swipe_rect
- ));
- showSuccessFeedback();
- }
- });
- } else {
- animateTaskViewToOverview(null);
- onMotionPaused(true /*arbitrary value*/);
+ animateTaskViewToOverview(ENABLE_NEW_GESTURE_NAV_TUTORIAL.get());
+ onMotionPaused(true /*arbitrary value*/);
+ if (!ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
showSuccessFeedback();
}
break;
@@ -189,21 +171,28 @@
/**
* runnable executed with slight delay to ease the swipe animation after landing on overview
- * @param runnable
*/
- public void animateTaskViewToOverview(@Nullable Runnable runnable) {
+ public void animateTaskViewToOverview(boolean animateDelayedSuccessFeedback) {
PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
anim.setFloat(mTaskViewSwipeUpAnimation
.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- if (runnable != null) {
- new Handler().postDelayed(runnable, 300);
+ if (animateDelayedSuccessFeedback) {
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ new Handler().postDelayed(() -> {
+ mFakeTaskView.setVisibility(View.INVISIBLE);
+ if (!mTutorialFragment.isLargeScreen()) {
+ mFakePreviousTaskView.animateToFillScreen(
+ () -> onSuccessAnimationComplete());
+ } else {
+ onSuccessAnimationComplete();
+ }
+ }, TASK_VIEW_FILL_SCREEN_ANIMATION_DELAY_MILLIS);
}
- }
- });
+ });
+ }
ArrayList<Animator> animators = new ArrayList<>();
@@ -222,4 +211,9 @@
animset.start();
mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
}
+
+ private void onSuccessAnimationComplete() {
+ setLauncherViewColor(R.color.gesture_overview_tutorial_swipe_rect);
+ showSuccessFeedback();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index c471a13..01074dd 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -73,7 +73,7 @@
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
- controller.animateTaskViewToOverview(null);
+ controller.animateTaskViewToOverview(false);
}
});
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index a8af05e..66c659a 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -61,9 +61,10 @@
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
- private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
+ private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(24);
protected static final long TASK_VIEW_END_ANIMATION_DURATION_MILLIS = 300;
+ protected static final long TASK_VIEW_FILL_SCREEN_ANIMATION_DELAY_MILLIS = 300;
private static final long HOME_SWIPE_ANIMATION_DURATION_MILLIS = 625;
private static final long OVERVIEW_SWIPE_ANIMATION_DURATION_MILLIS = 1000;
@@ -77,23 +78,7 @@
private final AnimatorListenerAdapter mResetTaskView = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mFakeHotseatView.setVisibility(View.INVISIBLE);
- mFakeIconView.setVisibility(View.INVISIBLE);
- if (mTutorialFragment.getActivity() != null) {
- int height = mTutorialFragment.getRootView().getFullscreenHeight();
- int width = mTutorialFragment.getRootView().getWidth();
- mFakeTaskViewRect.set(0, 0, width, height);
- }
- mFakeTaskViewRadius = 0;
- mFakeTaskView.invalidateOutline();
- mFakeTaskView.setVisibility(View.VISIBLE);
- mFakeTaskView.setAlpha(1);
- mFakePreviousTaskView.setVisibility(View.INVISIBLE);
- mFakePreviousTaskView.setAlpha(1);
- mFakePreviousTaskView.setToSingleRowLayout(false);
- mShowTasks = false;
- mShowPreviousTasks = false;
- mRunningWindowAnim = null;
+ resetTaskView();
}
};
@@ -137,6 +122,26 @@
mRunningWindowAnim = null;
}
+ void resetTaskView() {
+ mFakeHotseatView.setVisibility(View.INVISIBLE);
+ mFakeIconView.setVisibility(View.INVISIBLE);
+ if (mTutorialFragment.getActivity() != null) {
+ int height = mTutorialFragment.getRootView().getFullscreenHeight();
+ int width = mTutorialFragment.getRootView().getWidth();
+ mFakeTaskViewRect.set(0, 0, width, height);
+ }
+ mFakeTaskViewRadius = 0;
+ mFakeTaskView.invalidateOutline();
+ mFakeTaskView.setVisibility(View.VISIBLE);
+ mFakeTaskView.setAlpha(1);
+ mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+ mFakePreviousTaskView.setAlpha(1);
+ mFakePreviousTaskView.setToSingleRowLayout(false);
+ mShowTasks = false;
+ mShowPreviousTasks = false;
+ mRunningWindowAnim = null;
+ }
+
/** Fades the task view, optionally after animating to a fake Overview. */
void fadeOutFakeTaskView(boolean toOverviewFirst, boolean reset,
@Nullable Runnable onEndRunnable) {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index d4ff457..198305f 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -317,6 +317,14 @@
}
/**
+ * Only use this when a gesture is completed, but the feedback shouldn't be shown immediately.
+ * In that case, call this method immediately instead.
+ */
+ public void setGestureCompleted() {
+ mGestureCompleted = true;
+ }
+
+ /**
* Show feedback reflecting a successful gesture attempt.
**/
void showSuccessFeedback() {
@@ -641,13 +649,17 @@
}
}
+ void setLauncherViewColor(@ColorRes int backgroundColorRes) {
+ mFakeLauncherView.setBackgroundColor(mContext.getColor(backgroundColorRes));
+ }
+
private void updateDrawables() {
if (mContext != null) {
mTutorialFragment.getRootView().setBackground(AppCompatResources.getDrawable(
mContext, getMockWallpaperResId()));
mTutorialFragment.updateFeedbackAnimation();
- mFakeLauncherView.setBackgroundColor(
- mContext.getColor(R.color.gesture_tutorial_fake_wallpaper_color));
+ setLauncherViewColor(ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
+ ? getSwipeActionColorResId() : R.color.gesture_tutorial_fake_wallpaper_color);
updateFakeViewLayout(mFakeHotseatView, getMockHotseatResId());
mHotseatIconView = mFakeHotseatView.findViewById(R.id.hotseat_icon_1);
updateFakeViewLayout(mFakeTaskView, getMockAppTaskLayoutResId());
@@ -657,11 +669,6 @@
getMockPreviousAppTaskThumbnailColorResId()));
mFakeIconView.setBackground(AppCompatResources.getDrawable(
mContext, getMockAppIconResId()));
-
- if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
- mFakeLauncherView.setBackgroundColor(
- mContext.getColor(getSwipeActionColorResId()));
- }
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 3d5c143..d3a01f2 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -50,6 +50,7 @@
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
import org.xmlpull.v1.XmlPullParser;
@@ -62,7 +63,8 @@
* Utility class to log launcher settings changes
*/
public class SettingsChangeLogger implements
- DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener {
+ DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener,
+ SafeCloseable {
/**
* Singleton instance
@@ -188,6 +190,12 @@
prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
}
+ @Override
+ public void close() {
+ getPrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
+ getDevicePrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
+ }
+
private static class LoggablePref {
public boolean defaultValue;
public int eventIdOn;
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index ebea58c..614dfe8 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -207,6 +207,8 @@
secondTaskId,
initialPendingIntent,
secondPendingIntent,
+ initialUser?.identifier ?: -1,
+ secondUser?.identifier ?: -1,
initialShortcut,
secondShortcut,
itemInfo,
@@ -291,6 +293,8 @@
var secondTaskId: Int = INVALID_TASK_ID,
var initialPendingIntent: PendingIntent? = null,
var secondPendingIntent: PendingIntent? = null,
+ var initialUserId: Int = -1,
+ var secondUserId: Int = -1,
var initialShortcut: ShortcutInfo? = null,
var secondShortcut: ShortcutInfo? = null,
var itemInfo: ItemInfo? = null,
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index acc3ba1..da81410 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -372,12 +372,13 @@
shellInstanceId);
} else {
mSystemUiProxy.startIntents(getPendingIntent(intent1, mInitialUser),
- getShortcutInfo(intent1, mInitialUser), options1.toBundle(),
- hasSecondaryPendingIntent
+ mInitialUser.getIdentifier(), getShortcutInfo(intent1, mInitialUser),
+ options1.toBundle(), hasSecondaryPendingIntent
? mSecondPendingIntent
: getPendingIntent(intent2, mSecondUser),
- getShortcutInfo(intent2, mSecondUser), null /* options2 */,
- stagePosition, splitRatio, remoteTransition, shellInstanceId);
+ mSecondUser.getIdentifier(), getShortcutInfo(intent2, mSecondUser),
+ null /* options2 */, stagePosition, splitRatio, remoteTransition,
+ shellInstanceId);
}
} else {
final RemoteSplitLaunchAnimationRunner animationRunner =
@@ -399,13 +400,13 @@
shellInstanceId);
} else {
mSystemUiProxy.startIntentsWithLegacyTransition(
- getPendingIntent(intent1, mInitialUser),
+ getPendingIntent(intent1, mInitialUser), mInitialUser.getIdentifier(),
getShortcutInfo(intent1, mInitialUser), options1.toBundle(),
hasSecondaryPendingIntent
? mSecondPendingIntent
: getPendingIntent(intent2, mSecondUser),
- getShortcutInfo(intent2, mSecondUser), null /* options2 */, stagePosition,
- splitRatio, adapter, shellInstanceId);
+ mSecondUser.getIdentifier(), getShortcutInfo(intent2, mSecondUser),
+ null /* options2 */, stagePosition, splitRatio, adapter, shellInstanceId);
}
}
}
@@ -425,6 +426,8 @@
ShortcutInfo secondShortcut = launchData.getSecondShortcut();
PendingIntent firstPI = launchData.getInitialPendingIntent();
PendingIntent secondPI = launchData.getSecondPendingIntent();
+ int firstUserId = launchData.getInitialUserId();
+ int secondUserId = launchData.getSecondUserId();
int initialStagePosition = launchData.getInitialStagePosition();
Bundle optionsBundle = options1.toBundle();
@@ -441,8 +444,8 @@
remoteTransition, shellInstanceId);
case SPLIT_TASK_PENDINGINTENT ->
- mSystemUiProxy.startIntentAndTask(secondPI, optionsBundle, firstTaskId,
- null /*options2*/, initialStagePosition, splitRatio,
+ mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle,
+ firstTaskId, null /*options2*/, initialStagePosition, splitRatio,
remoteTransition, shellInstanceId);
case SPLIT_TASK_SHORTCUT ->
@@ -451,13 +454,14 @@
remoteTransition, shellInstanceId);
case SPLIT_PENDINGINTENT_TASK ->
- mSystemUiProxy.startIntentAndTask(firstPI, optionsBundle, secondTaskId,
- null /*options2*/, initialStagePosition, splitRatio,
+ mSystemUiProxy.startIntentAndTask(firstPI, firstUserId, optionsBundle,
+ secondTaskId, null /*options2*/, initialStagePosition, splitRatio,
remoteTransition, shellInstanceId);
case SPLIT_PENDINGINTENT_PENDINGINTENT ->
- mSystemUiProxy.startIntents(firstPI, firstShortcut, optionsBundle, secondPI,
- secondShortcut, null /*options2*/, initialStagePosition, splitRatio,
+ mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut,
+ optionsBundle, secondPI, secondUserId, secondShortcut,
+ null /*options2*/, initialStagePosition, splitRatio,
remoteTransition, shellInstanceId);
case SPLIT_SHORTCUT_TASK ->
@@ -479,8 +483,8 @@
case SPLIT_TASK_PENDINGINTENT ->
mSystemUiProxy.startIntentAndTaskWithLegacyTransition(secondPI,
- optionsBundle, firstTaskId, null /*options2*/, initialStagePosition,
- splitRatio, adapter, shellInstanceId);
+ secondUserId, optionsBundle, firstTaskId, null /*options2*/,
+ initialStagePosition, splitRatio, adapter, shellInstanceId);
case SPLIT_TASK_SHORTCUT ->
mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(secondShortcut,
@@ -488,14 +492,15 @@
splitRatio, adapter, shellInstanceId);
case SPLIT_PENDINGINTENT_TASK ->
- mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI,
+ mSystemUiProxy.startIntentAndTaskWithLegacyTransition(firstPI, firstUserId,
optionsBundle, secondTaskId, null /*options2*/,
initialStagePosition, splitRatio, adapter, shellInstanceId);
case SPLIT_PENDINGINTENT_PENDINGINTENT ->
- mSystemUiProxy.startIntentsWithLegacyTransition(firstPI, firstShortcut,
- optionsBundle, secondPI, secondShortcut, null /*options2*/,
- initialStagePosition, splitRatio, adapter, shellInstanceId);
+ mSystemUiProxy.startIntentsWithLegacyTransition(firstPI, firstUserId,
+ firstShortcut, optionsBundle, secondPI, secondUserId,
+ secondShortcut, null /*options2*/, initialStagePosition, splitRatio,
+ adapter, shellInstanceId);
case SPLIT_SHORTCUT_TASK ->
mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(firstShortcut,
@@ -514,7 +519,7 @@
options1.toBundle(), taskId, null /* options2 */, stagePosition,
splitRatio, remoteTransition, shellInstanceId);
} else {
- mSystemUiProxy.startIntentAndTask(getPendingIntent(intent, user),
+ mSystemUiProxy.startIntentAndTask(getPendingIntent(intent, user), user.getIdentifier(),
options1.toBundle(), taskId, null /* options2 */, stagePosition, splitRatio,
remoteTransition, shellInstanceId);
}
@@ -531,8 +536,9 @@
splitRatio, adapter, shellInstanceId);
} else {
mSystemUiProxy.startIntentAndTaskWithLegacyTransition(
- getPendingIntent(intent, user), options1.toBundle(), taskId,
- null /* options2 */, stagePosition, splitRatio, adapter, shellInstanceId);
+ getPendingIntent(intent, user), user.getIdentifier(), options1.toBundle(),
+ taskId, null /* options2 */, stagePosition, splitRatio, adapter,
+ shellInstanceId);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f17f074..d8fe32d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2345,7 +2345,6 @@
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
});
resetFromSplitSelectionState();
- mSplitSelectStateController.resetState();
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -4740,6 +4739,7 @@
setTaskViewsPrimarySplitTranslation(0);
setTaskViewsSecondarySplitTranslation(0);
+ mSplitSelectStateController.resetState();
if (mSplitHiddenTaskViewIndex == -1) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 134ef6c..c47c946 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -45,6 +45,7 @@
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -438,6 +439,9 @@
setWillNotDraw(!keyboardFocusHighlightEnabled);
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
+
mBorderAnimator = !keyboardFocusHighlightEnabled
? null
: new BorderAnimator(
@@ -445,18 +449,10 @@
/* borderWidthPx= */ context.getResources().getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
/* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
- /* borderColor= */ attrs == null
- ? DEFAULT_BORDER_COLOR
- : context.getTheme()
- .obtainStyledAttributes(
- attrs,
- R.styleable.TaskView,
- defStyleAttr,
- defStyleRes)
- .getColor(
- R.styleable.TaskView_borderColor,
- DEFAULT_BORDER_COLOR),
+ /* borderColor= */ ta.getColor(
+ R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR),
/* invalidateViewCallback= */ TaskView.this::invalidate);
+ ta.recycle();
}
protected void updateBorderBounds(Rect bounds) {
@@ -849,6 +845,13 @@
// QuickstepTransitionManager.createWallpaperOpenAnimations when launcher
// shows again
getRecentsView().startHome(false /* animated */);
+ RecentsView rv = getRecentsView();
+ if (rv != null && rv.mSizeStrategy.getTaskbarController() != null) {
+ // LauncherTaskbarUIController depends on the launcher state when checking
+ // whether to handle resume, but that can come in before startHome() changes
+ // the state, so force-refresh here to ensure the taskbar is updated
+ rv.mSizeStrategy.getTaskbarController().refreshResumedState();
+ }
});
}
// Indicate success once the system has indicated that the transition has started
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 62d46d3..97e34c5 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -62,6 +62,7 @@
import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.ViewCaptureRule;
import com.android.quickstep.views.RecentsView;
import org.junit.After;
@@ -115,10 +116,12 @@
Utilities.enableRunningInTestHarnessForTests();
}
+ final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
mOrderSensitiveRules = RuleChain
.outerRule(new SamplerRule())
.around(new NavigationModeSwitchRule(mLauncher))
- .around(new FailureWatcher(mDevice, mLauncher));
+ .around(viewCaptureRule)
+ .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
getHomeIntentInPackage(context),
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 985fe77..0c036ff 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -28,6 +28,9 @@
<color name="popup_notification_dot_light">@android:color/system_accent1_100</color>
<color name="popup_notification_dot_dark">@android:color/system_accent2_600</color>
+ <color name="notification_dot_color_light">@android:color/system_accent3_200</color>
+ <color name="notification_dot_color_dark">@android:color/system_accent3_100</color>
+
<color name="workspace_text_color_light">@android:color/system_neutral1_0</color>
<color name="workspace_text_color_dark">@android:color/system_neutral1_1000</color>
@@ -40,7 +43,6 @@
<color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
- <color name="folder_dot_color">@android:color/system_accent3_100</color>
<color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
<color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8136534..418f5a7 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -40,8 +40,8 @@
<attr name="eduHalfSheetBGColor" format="color" />
<attr name="overviewScrimColor" format="color" />
<attr name="popupNotificationDotColor" format="color" />
+ <attr name="notificationDotColor" format="color" />
- <attr name="folderDotColor" format="color" />
<attr name="folderPaginationColor" format="color" />
<attr name="folderPreviewColor" format="color" />
<attr name="folderBackgroundColor" format="color" />
@@ -90,7 +90,6 @@
<declare-styleable name="FolderIconPreview">
<attr name="folderPreviewColor" />
<attr name="folderIconBorderColor" />
- <attr name="folderDotColor" />
</declare-styleable>
<declare-styleable name="SearchResultSuggestion">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 9e73453..b1bff18 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -54,6 +54,9 @@
<color name="popup_notification_dot_light">#FFF</color>
<color name="popup_notification_dot_dark">#757575</color>
+ <color name="notification_dot_color_light">#6DD58C</color>
+ <color name="notification_dot_color_dark">#C4EED0</color>
+
<color name="workspace_text_color_light">#FFF</color>
<color name="workspace_text_color_dark">#FF000000</color>
@@ -66,7 +69,6 @@
<color name="folder_preview_light">#F9F9F9</color>
<color name="folder_preview_dark">#464746</color>
- <color name="folder_dot_color">?attr/colorPrimary</color>
<color name="folder_pagination_color_light">#ff006c5f</color>
<color name="folder_pagination_color_dark">#ffbfebe3</color>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 11861b9..8f75550 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -41,6 +41,7 @@
<item name="popupShadeSecond">@color/popup_shade_second_light</item>
<item name="popupShadeThird">@color/popup_shade_third_light</item>
<item name="popupNotificationDotColor">@color/popup_notification_dot_light</item>
+ <item name="notificationDotColor">@color/notification_dot_color_light</item>
<item name="isMainColorDark">false</item>
<item name="isWorkspaceDarkText">false</item>
<item name="workspaceTextColor">@color/workspace_text_color_light</item>
@@ -49,7 +50,6 @@
<item name="workspaceKeyShadowColor">#89000000</item>
<item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
- <item name="folderDotColor">@color/folder_dot_color</item>
<item name="folderPaginationColor">@color/folder_pagination_color_light</item>
<item name="folderPreviewColor">@color/folder_preview_light</item>
<item name="folderBackgroundColor">@color/folder_background_light</item>
@@ -109,8 +109,8 @@
<item name="popupShadeFirst">@color/popup_shade_first_dark</item>
<item name="popupShadeSecond">@color/popup_shade_second_dark</item>
<item name="popupShadeThird">@color/popup_shade_third_dark</item>
+ <item name="notificationDotColor">@color/notification_dot_color_dark</item>
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
- <item name="folderDotColor">@color/folder_dot_color</item>
<item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
<item name="folderPreviewColor">@color/folder_preview_dark</item>
<item name="folderBackgroundColor">@color/folder_background_dark</item>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index f041ffb..3dd8627 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -6,7 +6,6 @@
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -16,13 +15,15 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.DatabaseHelper;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.LauncherWidgetHolder;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -52,7 +53,7 @@
* Updates the app widgets whose id has changed during the restore process.
*/
@WorkerThread
- public static void restoreAppWidgetIds(Context context, DatabaseHelper helper,
+ public static void restoreAppWidgetIds(Context context, ModelDbController controller,
int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
if (WidgetsModel.GO_DISABLE_WIDGETS) {
Log.e(TAG, "Skipping widget ID remap as widgets not supported");
@@ -69,9 +70,24 @@
}
return;
}
- final ContentResolver cr = context.getContentResolver();
+
final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+ Log.d(TAG, "restoreAppWidgetIds: "
+ + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString()
+ + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString());
+
+ try {
+ IntArray result = LauncherDbUtils.queryIntArray(false, controller.getDb(),
+ Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
+ Favorites.APPWIDGET_ID + "!=" + LauncherAppWidgetInfo.NO_ID, null, null);
+ // TODO(b/234700507): Remove the logs after the bug is fixed
+ Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
+ + result.toConcatString());
+ } catch (Exception ex) {
+ Log.e(TAG, "Getting widget ids from the database failed", ex);
+ }
+
for (int i = 0; i < oldWidgetIds.length; i++) {
Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
@@ -92,22 +108,24 @@
final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
int result = new ContentWriter(context,
- new ContentWriter.CommitParams(helper, where, args))
+ new ContentWriter.CommitParams(controller, where, args))
.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
.put(LauncherSettings.Favorites.RESTORED, state)
.commit();
if (result == 0) {
- Cursor cursor = helper.getWritableDatabase().query(
+ // TODO(b/234700507): Remove the logs after the bug is fixed
+ Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in"
+ + " the database anymore");
+ try (Cursor cursor = controller.getDb().query(
Favorites.TABLE_NAME,
- new String[] {Favorites.APPWIDGET_ID},
- "appWidgetId=?", new String[] { oldWidgetId }, null, null, null);
- try {
+ new String[]{Favorites.APPWIDGET_ID},
+ "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) {
if (!cursor.moveToFirst()) {
// The widget no long exists.
+ Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
+ + oldWidgetId);
host.deleteAppWidgetId(newWidgetIds[i]);
}
- } finally {
- cursor.close();
}
}
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 45b03c2..8876a1b 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -156,6 +156,13 @@
}
@Override
+ public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+ ActivityOptionsWrapper wrapper = super.makeDefaultActivityOptions(splashScreenStyle);
+ addOnResumeCallback(wrapper.onEndCallback::executeAllAndDestroy);
+ return wrapper;
+ }
+
+ @Override
protected void onStart() {
super.onStart();
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 450a9f0..f920d75 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -77,6 +77,7 @@
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ShortcutUtil;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconLabelDotView;
@@ -387,8 +388,7 @@
}
FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
mDotParams.appColor = iconDrawable.getIconColor();
- mDotParams.dotColor = getContext().getResources()
- .getColor(android.R.color.system_accent3_200, getContext().getTheme());
+ mDotParams.dotColor = Themes.getAttrColor(getContext(), R.attr.notificationDotColor);
setIcon(iconDrawable);
applyLabel(info);
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 1a86009..108e557 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -115,6 +115,12 @@
setContentDescription(mText);
}
+ protected void updateText(CharSequence text) {
+ setText(text);
+ mText = getText();
+ setContentDescription(mText);
+ }
+
protected void setDrawable(int resId) {
// We do not set the drawable in the xml as that inflates two drawables corresponding to
// drawableLeft and drawableStart.
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a0d942a..0b75c45 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2148,30 +2148,38 @@
}
@Override
- public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
if (!hasBeenResumed()) {
+ RunnableList result = new RunnableList();
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
- addOnResumeCallback(() -> startActivitySafely(v, intent, item));
+ addOnResumeCallback(() -> {
+ RunnableList actualResult = startActivitySafely(v, intent, item);
+ if (actualResult != null) {
+ actualResult.add(result::executeAllAndDestroy);
+ } else {
+ result.executeAllAndDestroy();
+ }
+ });
if (mOnDeferredActivityLaunchCallback != null) {
mOnDeferredActivityLaunchCallback.run();
mOnDeferredActivityLaunchCallback = null;
}
- return true;
+ return result;
}
- boolean success = super.startActivitySafely(v, intent, item);
- if (success && v instanceof BubbleTextView) {
+ RunnableList result = super.startActivitySafely(v, intent, item);
+ if (result != null && v instanceof BubbleTextView) {
// This is set to the view that launched the activity that navigated the user away
// from launcher. Since there is no callback for when the activity has finished
// launching, enable the press state and keep this reference to reset the press
// state when we return to launcher.
BubbleTextView btv = (BubbleTextView) v;
btv.setStayPressed(true);
- addOnResumeCallback(() -> btv.setStayPressed(false));
+ result.add(() -> btv.setStayPressed(false));
}
- return success;
+ return result;
}
boolean isHotseatLayout(View layout) {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index abf5866..4d15ac7 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -111,6 +111,7 @@
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
+ Intent.ACTION_PROFILE_INACCESSIBLE,
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
if (FeatureFlags.IS_STUDIO_BUILD) {
modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d3e94e1..617afcb 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -92,6 +92,15 @@
static final String TAG = "Launcher.Model";
+ // Broadcast intent to track when the profile gets locked:
+ // ACTION_MANAGED_PROFILE_UNAVAILABLE can be used until Android U where profile no longer gets
+ // locked when paused.
+ // ACTION_PROFILE_INACCESSIBLE always means that the profile is getting locked but it only
+ // appeared in Android S.
+ private static final String ACTION_PROFILE_LOCKED = Utilities.ATLEAST_U
+ ? Intent.ACTION_PROFILE_INACCESSIBLE
+ : Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
+
@NonNull
private final LauncherAppState mApp;
@NonNull
@@ -289,9 +298,10 @@
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
- } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
- Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
+ } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)
+ || Intent.ACTION_PROFILE_INACCESSIBLE.equals(action)) {
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.WORK_TAB_MISSING, "onBroadcastIntent intentAction: " + action +
@@ -304,10 +314,8 @@
PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE, user));
}
- // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so
- // we need to run the state change task again.
- if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
- Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
+ if (ACTION_PROFILE_LOCKED.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
enqueueModelUpdateTask(new UserLockStateChangedTask(
user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)));
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 0df4bd4..9abec50 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -263,18 +263,6 @@
getModelDbController().refreshHotseatRestoreTable();
return null;
}
- case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
- Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().updateCurrentOpenHelper(arg /* dbFile */));
- return result;
- }
- case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
- Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- getModelDbController().prepareForPreview(arg /* dbFile */));
- return result;
- }
}
return null;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index b65e96b..7fda326 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -148,11 +148,6 @@
public static final String HYBRID_HOTSEAT_BACKUP_TABLE = "hotseat_restore_backup";
/**
- * Temporary table used specifically for grid migrations during wallpaper preview
- */
- public static final String PREVIEW_TABLE_NAME = "favorites_preview";
-
- /**
* Temporary table used specifically for multi-db grid migrations
*/
public static final String TMP_TABLE = "favorites_tmp";
@@ -164,18 +159,6 @@
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
- * The content:// style URL for "favorites_preview" table
- */
- public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
-
- /**
- * The content:// style URL for "favorites_tmp" table
- */
- public static final Uri TMP_CONTENT_URI = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + TMP_TABLE);
-
- /**
* The content:// style URL for a given row, identified by its id.
*
* @param id The row id.
@@ -376,10 +359,6 @@
public static final String METHOD_REFRESH_HOTSEAT_RESTORE_TABLE = "restore_hotseat_table";
- public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
-
- public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
-
public static final String EXTRA_VALUE = "value";
public static final String EXTRA_DB_NAME = "db_name";
@@ -393,11 +372,8 @@
}
public static Bundle call(ContentResolver cr, String method, String arg) {
- return call(cr, method, arg, null /* extras */);
+ return cr.call(CONTENT_URI, method, arg, null);
}
- public static Bundle call(ContentResolver cr, String method, String arg, Bundle extras) {
- return cr.call(CONTENT_URI, method, arg, extras);
- }
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 052d075..73bb828 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1666,17 +1666,14 @@
scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
}
- int halfPadding = previewProvider.previewPadding / 2;
int dragLayerX = mTempXY[0];
int dragLayerY = mTempXY[1];
- Point dragVisualizeOffset = null;
Rect dragRect = new Rect();
if (draggableView != null) {
draggableView.getSourceVisualDragBounds(dragRect);
dragLayerY += dragRect.top;
- dragVisualizeOffset = new Point(-halfPadding, halfPadding);
}
@@ -1694,6 +1691,15 @@
}
}
+ if (dragOptions.preDragCondition != null) {
+ int xDragOffSet = dragOptions.preDragCondition.getDragOffset().x;
+ int yDragOffSet = dragOptions.preDragCondition.getDragOffset().y;
+ if (xDragOffSet != 0 || yDragOffSet != 0) {
+ dragLayerX += xDragOffSet;
+ dragLayerY += yDragOffSet;
+ }
+ }
+
final DragView dv;
if (contentView instanceof View) {
if (contentView instanceof LauncherAppWidgetHostView) {
@@ -1706,7 +1712,6 @@
dragLayerY,
source,
dragObject,
- dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
@@ -1719,7 +1724,6 @@
dragLayerY,
source,
dragObject,
- dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index bf36e02..a671c6e 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -97,7 +97,11 @@
StatsLogManager statsLogManager) {
mUserManager = userManager;
mAllApps = allApps;
- if (FeatureFlags.ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER.get()) {
+ boolean cloningChanges = FeatureFlags.ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER.get();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(WORK_TAB_MISSING, "matcher flag: " + cloningChanges);
+ }
+ if (cloningChanges) {
mMatcher = ofWorkProfileUser(userManager);
} else {
mMatcher = mAllApps.mPersonalMatcher.negate();
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index 714304b..64fd237 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -62,7 +62,8 @@
if (mHighlightedView instanceof BubbleTextView
&& mHighlightedView.getTag() instanceof ItemInfo) {
ItemInfo itemInfo = (ItemInfo) mHighlightedView.getTag();
- return mLauncher.startActivitySafely(mHighlightedView, itemInfo.getIntent(), itemInfo);
+ return mLauncher.startActivitySafely(
+ mHighlightedView, itemInfo.getIntent(), itemInfo) != null;
}
return false;
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8dc1204..6699825 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -86,7 +86,7 @@
"ENABLE_ONE_SEARCH_MOTION", ENABLED, "Enables animations in OneSearch.");
public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag(
- 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", TEAMFOOD,
+ 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", DISABLED,
"Enable option to replace decorator-based search result backgrounds with drawables");
public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag(
@@ -142,10 +142,6 @@
"Enables haptics opening/closing All apps");
// TODO(Block 6): Clean up flags
- public static final BooleanFlag WIDGETS_IN_LAUNCHER_PREVIEW = getDebugFlag(270393268,
- "WIDGETS_IN_LAUNCHER_PREVIEW", ENABLED,
- "Enables widgets in Launcher preview for the Wallpaper app.");
-
public static final BooleanFlag ENABLE_ALL_APPS_SEARCH_IN_TASKBAR = getDebugFlag(270393900,
"ENABLE_ALL_APPS_SEARCH_IN_TASKBAR", DISABLED,
"Enables Search box in Taskbar All Apps.");
@@ -211,17 +207,9 @@
"Allows on device search in all apps logging");
// TODO(Block 14): Cleanup flags
- public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(270391641,
- "ASSISTANT_GIVES_LAUNCHER_FOCUS", DISABLED,
- "Allow Launcher to handle nav bar gestures while Assistant is running over it");
-
public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag(270393108, "NOTIFY_CRASHES",
DISABLED, "Sends a notification whenever launcher encounters an uncaught exception.");
- public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(270395077,
- "FORCE_PERSISTENT_TASKBAR", DISABLED, "Forces taskbar to be persistent, even in gesture"
- + " nav mode and when transient taskbar is enabled.");
-
public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(270395798,
"ENABLE_TRANSIENT_TASKBAR", ENABLED, "Enables transient taskbar.");
@@ -314,6 +302,10 @@
"Enables receiving unfold animation events from sysui instead of calculating "
+ "them in launcher process using hinge sensor values.");
+ public static final BooleanFlag ENABLE_WIDGET_TRANSITION_FOR_RESIZING = getDebugFlag(268553314,
+ "ENABLE_WIDGET_TRANSITION_FOR_RESIZING", DISABLED,
+ "Enable widget transition animation when resizing the widgets");
+
public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209,
"PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED,
"Enables starting the unfold animation preemptively when unfolding, without"
@@ -394,7 +386,7 @@
"Enable initiating split screen from workspace to workspace.");
public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
- "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
+ "ENABLE_TRACKPAD_GESTURE", DISABLED, "Enables trackpad gesture.");
// TODO(Block 29): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
@@ -404,6 +396,10 @@
public static final BooleanFlag ENABLE_KEYBOARD_QUICK_SWITCH = getDebugFlag(270396844,
"ENABLE_KEYBOARD_QUICK_SWITCH", ENABLED, "Enables keyboard quick switching");
+ public static final BooleanFlag ENABLE_KEYBOARD_TASKBAR_TOGGLE = getDebugFlag(281726846,
+ "ENABLE_KEYBOARD_TASKBAR_TOGGLE", ENABLED,
+ "Enables keyboard taskbar stash toggling");
+
// TODO(Block 30): Clean up flags
public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(270395010,
"USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", DISABLED,
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 328d769..9867268 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -143,14 +143,12 @@
int dragLayerY,
DragSource source,
ItemInfo dragInfo,
- Point dragOffset,
Rect dragRegion,
float initialDragViewScale,
float dragViewScaleOnDrop,
DragOptions options) {
- return startDrag(drawable, /* view= */ null, originalView, dragLayerX, dragLayerY,
- source, dragInfo, dragOffset, dragRegion, initialDragViewScale, dragViewScaleOnDrop,
- options);
+ return startDrag(drawable, /* view= */ null, originalView, dragLayerX, dragLayerY, source,
+ dragInfo, dragRegion, initialDragViewScale, dragViewScaleOnDrop, options);
}
/**
@@ -180,14 +178,12 @@
int dragLayerY,
DragSource source,
ItemInfo dragInfo,
- Point dragOffset,
Rect dragRegion,
float initialDragViewScale,
float dragViewScaleOnDrop,
DragOptions options) {
- return startDrag(/* drawable= */ null, view, originalView, dragLayerX, dragLayerY,
- source, dragInfo, dragOffset, dragRegion, initialDragViewScale, dragViewScaleOnDrop,
- options);
+ return startDrag(/* drawable= */ null, view, originalView, dragLayerX, dragLayerY, source,
+ dragInfo, dragRegion, initialDragViewScale, dragViewScaleOnDrop, options);
}
protected abstract DragView startDrag(
@@ -198,7 +194,6 @@
int dragLayerY,
DragSource source,
ItemInfo dragInfo,
- Point dragOffset,
Rect dragRegion,
float initialDragViewScale,
float dragViewScaleOnDrop,
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 1ff4335..2af81db 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -78,5 +78,12 @@
* This will be true if the condition was met, otherwise false.
*/
void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted);
+
+ /**
+ * The offset points that should be overridden to update the dragLayer.
+ */
+ default Point getDragOffset() {
+ return new Point(0,0);
+ }
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 46c8e81..0d0717e 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -37,7 +37,6 @@
import android.graphics.ColorFilter;
import android.graphics.Path;
import android.graphics.Picture;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
@@ -89,15 +88,17 @@
private final RunnableList mOnDragStartCallback = new RunnableList();
- private Point mDragVisualizeOffset = null;
+ private boolean mHasDragOffset;
private Rect mDragRegion = null;
protected final T mActivity;
private final BaseDragLayer<T> mDragLayer;
private boolean mHasDrawn = false;
- final ValueAnimator mAnim;
+ final ValueAnimator mScaleAnim;
+ final ValueAnimator mShiftAnim;
+
// Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends.
- private boolean mAnimStarted;
+ private boolean mScaleAnimStarted;
private Runnable mOnAnimEndCallback = null;
private int mLastTouchX;
@@ -166,9 +167,9 @@
setScaleY(initialScale);
// Animate the view into the correct position
- mAnim = ValueAnimator.ofFloat(0f, 1f);
- mAnim.setDuration(VIEW_ZOOM_DURATION);
- mAnim.addUpdateListener(animation -> {
+ mScaleAnim = ValueAnimator.ofFloat(0f, 1f);
+ mScaleAnim.setDuration(VIEW_ZOOM_DURATION);
+ mScaleAnim.addUpdateListener(animation -> {
final float value = (Float) animation.getAnimatedValue();
setScaleX(Utilities.mapRange(value, initialScale, mEndScale));
setScaleY(Utilities.mapRange(value, initialScale, mEndScale));
@@ -176,10 +177,10 @@
animation.cancel();
}
});
- mAnim.addListener(new AnimatorListenerAdapter() {
+ mScaleAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mAnimStarted = true;
+ mScaleAnimStarted = true;
}
@Override
@@ -190,6 +191,8 @@
}
}
});
+ // Set up the shift animator.
+ mShiftAnim = ValueAnimator.ofFloat(0f, 1f);
setDragRegion(new Rect(0, 0, width, height));
@@ -319,12 +322,12 @@
return mDragRegion.height();
}
- public void setDragVisualizeOffset(Point p) {
- mDragVisualizeOffset = p;
+ public void setHasDragOffset(boolean hasDragOffset) {
+ mHasDragOffset = hasDragOffset;
}
- public Point getDragVisualizeOffset() {
- return mDragVisualizeOffset;
+ public boolean getHasDragOffset() {
+ return mHasDragOffset;
}
public void setDragRegion(Rect r) {
@@ -392,22 +395,29 @@
if (mContent != null) {
// At the drag start, the source view visibility is set to invisible.
- mContent.setVisibility(VISIBLE);
+ if (getHasDragOffset()) {
+ // If there is any dragOffset, this means the content will show away of the original
+ // icon location, otherwise it's fine since original content would just show at the
+ // same spot.
+ mContent.setVisibility(INVISIBLE);
+ } else {
+ mContent.setVisibility(VISIBLE);
+ }
}
move(touchX, touchY);
// Post the animation to skip other expensive work happening on the first frame
- post(mAnim::start);
+ post(mScaleAnim::start);
}
public void cancelAnimation() {
- if (mAnim != null && mAnim.isRunning()) {
- mAnim.cancel();
+ if (mScaleAnim != null && mScaleAnim.isRunning()) {
+ mScaleAnim.cancel();
}
}
- public boolean isAnimationFinished() {
- return mAnimStarted && !mAnim.isRunning();
+ public boolean isScaleAnimationFinished() {
+ return mScaleAnimStarted && !mScaleAnim.isRunning();
}
/**
@@ -434,13 +444,15 @@
int duration);
public void animateShift(final int shiftX, final int shiftY) {
- if (mAnim.isStarted()) {
- return;
- }
+ if (mShiftAnim.isStarted()) return;
+
+ // Set mContent visibility to visible to show icon regardless in case it is INVISIBLE.
+ if (mContent != null) mContent.setVisibility(VISIBLE);
+
mAnimatedShiftX = shiftX;
mAnimatedShiftY = shiftY;
applyTranslation();
- mAnim.addUpdateListener(new AnimatorUpdateListener() {
+ mShiftAnim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = 1 - animation.getAnimatedFraction();
@@ -449,6 +461,7 @@
applyTranslation();
}
});
+ mShiftAnim.start();
}
private void applyTranslation() {
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index 90bf8e8..da6f446 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.res.Resources;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.HapticFeedbackConstants;
@@ -61,7 +60,6 @@
int dragLayerY,
DragSource source,
ItemInfo dragInfo,
- Point dragOffset,
Rect dragRegion,
float initialDragViewScale,
float dragViewScaleOnDrop,
@@ -130,9 +128,11 @@
mDragObject.dragInfo = dragInfo;
mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
- if (dragOffset != null) {
- dragView.setDragVisualizeOffset(new Point(dragOffset));
+ if (mOptions.preDragCondition != null) {
+ dragView.setHasDragOffset(mOptions.preDragCondition.getDragOffset().x != 0 ||
+ mOptions.preDragCondition.getDragOffset().y != 0);
}
+
if (dragRegion != null) {
dragView.setDragRegion(new Rect(dragRegion));
}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 2465745..406955c 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -43,6 +43,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
/**
@@ -150,7 +151,7 @@
mInvalidateDelegate = invalidateDelegate;
TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
- mDotColor = ta.getColor(R.styleable.FolderIconPreview_folderDotColor, 0);
+ mDotColor = Themes.getAttrColor(context, R.attr.notificationDotColor);
mStrokeColor = ta.getColor(R.styleable.FolderIconPreview_folderIconBorderColor, 0);
mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0);
ta.recycle();
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index b438e86..47677ea 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -105,7 +105,6 @@
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
import com.android.launcher3.widget.LocalColorExtractor;
-import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import com.android.launcher3.widget.util.WidgetSizes;
@@ -281,9 +280,7 @@
} else {
mWallpaperColorResources = null;
}
- mAppWidgetHost = FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()
- ? new LauncherPreviewAppWidgetHost(context)
- : null;
+ mAppWidgetHost = new LauncherPreviewAppWidgetHost(context);
}
/** Populate preview and render it. */
@@ -416,19 +413,8 @@
private void inflateAndAddWidgets(
LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
- AppWidgetHostView view;
- if (FeatureFlags.WIDGETS_IN_LAUNCHER_PREVIEW.get()) {
- view = mAppWidgetHost.createView(mContext, info.appWidgetId, providerInfo);
- } else {
- view = new NavigableAppWidgetHostView(this) {
- @Override
- protected boolean shouldAllowDirectClick() {
- return false;
- }
- };
- view.setAppWidget(-1, providerInfo);
- view.updateAppWidget(null);
- }
+ AppWidgetHostView view = mAppWidgetHost.createView(
+ mContext, info.appWidgetId, providerInfo);
if (mWallpaperColorResources != null) {
view.setColorResources(mWallpaperColorResources);
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 372e9bf..e89c0c5 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -16,6 +16,7 @@
package com.android.launcher3.graphics;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -50,8 +51,12 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.GridSizeMigrationUtil;
+import com.android.launcher3.model.LauncherBinder;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
@@ -145,7 +150,9 @@
final String query = LauncherSettings.Favorites.ITEM_TYPE + " = "
+ LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
- try (Cursor c = context.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ ModelDbController mainController =
+ LauncherAppState.getInstance(mContext).getModel().getModelDbController();
+ try (Cursor c = mainController.query(TABLE_NAME,
new String[] {
LauncherSettings.Favorites.APPWIDGET_ID,
LauncherSettings.Favorites.SPANX,
@@ -190,8 +197,6 @@
@WorkerThread
private void loadModelData() {
- final boolean migrated = doGridMigrationIfNecessary();
-
final Context inflationContext;
if (mWallpaperColors != null) {
// Create a themed context, without affecting the main application context
@@ -209,14 +214,28 @@
Themes.getActivityThemeRes(mContext));
}
- if (migrated) {
+ if (GridSizeMigrationUtil.needsToMigrate(inflationContext, mIdp)) {
+ // Start the migration
PreviewContext previewContext = new PreviewContext(inflationContext, mIdp);
+ // Copy existing data to preview DB
+ LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
+ .getModel().getModelDbController().getDb(),
+ TABLE_NAME,
+ LauncherAppState.getInstance(previewContext)
+ .getModel().getModelDbController().getDb(),
+ TABLE_NAME,
+ mContext);
+ LauncherAppState.getInstance(previewContext)
+ .getModel().getModelDbController().clearEmptyDbFlag();
+
+ BgDataModel bgModel = new BgDataModel();
new LoaderTask(
LauncherAppState.getInstance(previewContext),
/* bgAllAppsList= */ null,
- new BgDataModel(),
+ bgModel,
LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
- /* results= */ null) {
+ new LauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
+ /* bgAllAppsList= */ null, new Callbacks[0])) {
@Override
public void run() {
@@ -229,8 +248,7 @@
query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+ Workspace.SECOND_SCREEN_ID;
}
- loadWorkspaceForPreviewSurfaceRenderer(new ArrayList<>(),
- LauncherSettings.Favorites.PREVIEW_CONTENT_URI, query);
+ loadWorkspace(new ArrayList<>(), query, null);
final SparseArray<Size> spanInfo =
getLoadedLauncherWidgetInfo(previewContext.getBaseContext());
@@ -253,14 +271,6 @@
}
}
- @WorkerThread
- private boolean doGridMigrationIfNecessary() {
- if (!GridSizeMigrationUtil.needsToMigrate(mContext, mIdp)) {
- return false;
- }
- return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, mIdp);
- }
-
@UiThread
private void renderView(Context inflationContext, BgDataModel dataModel,
Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index dc5fcf7..ecf5f67 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
-import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import android.content.ContentValues;
import android.content.Context;
@@ -36,9 +36,6 @@
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherFiles;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
@@ -58,6 +55,7 @@
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Locale;
+import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
/**
@@ -76,45 +74,23 @@
private static final boolean LOGD = false;
private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
- public static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
private final Context mContext;
- private final boolean mForMigration;
+ private final ToLongFunction<UserHandle> mUserSerialProvider;
+ private final Runnable mOnEmptyDbCreateCallback;
+
private int mMaxItemId = -1;
public boolean mHotseatRestoreTableExists;
- public static DatabaseHelper createDatabaseHelper(Context context, boolean forMigration) {
- return createDatabaseHelper(context, null, forMigration);
- }
-
- public static DatabaseHelper createDatabaseHelper(Context context, String dbName,
- boolean forMigration) {
- if (dbName == null) {
- dbName = InvariantDeviceProfile.INSTANCE.get(context).dbFile;
- }
- DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName, forMigration);
- // Table creation sometimes fails silently, which leads to a crash loop.
- // This way, we will try to create a table every time after crash, so the device
- // would eventually be able to recover.
- if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
- Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
- // This operation is a no-op if the table already exists.
- databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
- }
- databaseHelper.mHotseatRestoreTableExists = tableExists(
- databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
-
- databaseHelper.initIds();
- return databaseHelper;
- }
-
/**
* Constructor used in tests and for restore.
*/
- public DatabaseHelper(Context context, String dbName, boolean forMigration) {
+ public DatabaseHelper(Context context, String dbName,
+ ToLongFunction<UserHandle> userSerialProvider, Runnable onEmptyDbCreateCallback) {
super(context, dbName, SCHEMA_VERSION);
mContext = context;
- mForMigration = forMigration;
+ mUserSerialProvider = userSerialProvider;
+ mOnEmptyDbCreateCallback = onEmptyDbCreateCallback;
}
protected void initIds() {
@@ -131,13 +107,11 @@
mMaxItemId = 1;
- addFavoritesTable(db, false);
+ addTableToDb(db, getDefaultUserSerial(), false /* optional */);
// Fresh and clean launcher DB.
mMaxItemId = initializeMaxItemId(db);
- if (!mForMigration) {
- onEmptyDbCreated();
- }
+ mOnEmptyDbCreateCallback.run();
}
public void onAddOrDeleteOp(SQLiteDatabase db) {
@@ -147,38 +121,8 @@
}
}
- /**
- * Re-composite given key in respect to database. If the current db is
- * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
- * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
- * string will be "EMPTY_DATABASE_CREATED@minimal.db".
- */
- public String getKey(final String key) {
- if (TextUtils.equals(getDatabaseName(), LauncherFiles.LAUNCHER_DB)) {
- return key;
- }
- return key + "@" + getDatabaseName();
- }
-
- /**
- * Overridden in tests.
- */
- protected void onEmptyDbCreated() {
- // Set the flag for empty DB
- LauncherPrefs.getPrefs(mContext).edit().putBoolean(getKey(EMPTY_DATABASE_CREATED), true)
- .commit();
- }
-
- public long getSerialNumberForUser(UserHandle user) {
- return UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user);
- }
-
- public long getDefaultUserSerial() {
- return getSerialNumberForUser(Process.myUserHandle());
- }
-
- private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
- Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
+ private long getDefaultUserSerial() {
+ return mUserSerialProvider.applyAsLong(Process.myUserHandle());
}
@Override
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index eded5ea..9a6cde6 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -16,6 +16,9 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
+import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.content.ComponentName;
@@ -34,16 +37,15 @@
import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -89,81 +91,38 @@
return needsToMigrate;
}
- /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
- public static boolean migrateGridIfNeeded(Context context) {
- if (context instanceof LauncherPreviewRenderer.PreviewContext) {
- return true;
- }
- return migrateGridIfNeeded(context, null);
- }
-
/**
- * When migrating the grid for preview, we copy the table
- * {@link LauncherSettings.Favorites#TABLE_NAME} into
- * {@link LauncherSettings.Favorites#PREVIEW_TABLE_NAME}, run grid size migration from the
- * former to the later, then use the later table for preview.
- *
- * Similarly when doing the actual grid migration, the former grid option's table
- * {@link LauncherSettings.Favorites#TABLE_NAME} is copied into the new grid option's
- * {@link LauncherSettings.Favorites#TMP_TABLE}, we then run the grid size migration algorithm
+ * When migrating the grid, we copy the table
+ * {@link LauncherSettings.Favorites#TABLE_NAME} from {@code source} into
+ * {@link LauncherSettings.Favorites#TMP_TABLE}, run the grid size migration algorithm
* to migrate the later to the former, and load the workspace from the default
* {@link LauncherSettings.Favorites#TABLE_NAME}.
*
* @return false if the migration failed.
*/
- public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
- boolean migrateForPreview = idp != null;
- if (!migrateForPreview) {
- idp = LauncherAppState.getIDP(context);
- }
+ public static boolean migrateGridIfNeeded(
+ @NonNull Context context,
+ @NonNull InvariantDeviceProfile idp,
+ @NonNull DatabaseHelper target,
+ @NonNull SQLiteDatabase source) {
DeviceGridState srcDeviceState = new DeviceGridState(context);
DeviceGridState destDeviceState = new DeviceGridState(idp);
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
+ copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
HashSet<String> validPackages = getValidPackages(context);
-
- if (migrateForPreview) {
- if (!LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW,
- destDeviceState.getDbFile()).getBoolean(
- LauncherSettings.Settings.EXTRA_VALUE)) {
- return false;
- }
- } else if (!LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER,
- destDeviceState.getDbFile()).getBoolean(
- LauncherSettings.Settings.EXTRA_VALUE)) {
- return false;
- }
-
long migrationStartTime = System.currentTimeMillis();
- try (SQLiteTransaction t = (SQLiteTransaction) LauncherSettings.Settings.call(
- context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
- LauncherSettings.Settings.EXTRA_VALUE)) {
-
- DbReader srcReader = new DbReader(t.getDb(),
- migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
- : LauncherSettings.Favorites.TMP_TABLE,
- context, validPackages);
- DbReader destReader = new DbReader(t.getDb(),
- migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
- : LauncherSettings.Favorites.TABLE_NAME,
- context, validPackages);
+ try (SQLiteTransaction t = new SQLiteTransaction(target.getWritableDatabase())) {
+ DbReader srcReader = new DbReader(t.getDb(), TMP_TABLE, context, validPackages);
+ DbReader destReader = new DbReader(t.getDb(), TABLE_NAME, context, validPackages);
Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
- migrate(context, t.getDb(), srcReader, destReader, destDeviceState.getNumHotseat(),
+ migrate(target, srcReader, destReader, destDeviceState.getNumHotseat(),
targetSize, srcDeviceState, destDeviceState);
-
- if (!migrateForPreview) {
- dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
- }
-
+ dropTable(t.getDb(), TMP_TABLE);
t.commit();
return true;
} catch (Exception e) {
@@ -174,7 +133,7 @@
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
- if (!migrateForPreview) {
+ if (!(context instanceof SandboxContext)) {
// Save current configuration, so that the migration does not run again.
destDeviceState.writeToPrefs(context);
}
@@ -182,7 +141,7 @@
}
public static boolean migrate(
- @NonNull final Context context, @NonNull final SQLiteDatabase db,
+ @NonNull DatabaseHelper helper,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
final int destHotseatSize, @NonNull final Point targetSize,
@NonNull final DeviceGridState srcDeviceState,
@@ -234,8 +193,8 @@
Collections.sort(workspaceToBeAdded);
// Migrate hotseat
- solveHotseatPlacement(db, srcReader,
- destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded);
+ solveHotseatPlacement(helper, destHotseatSize,
+ srcReader, destReader, dstHotseatItems, hotseatToBeAdded);
// Migrate workspace.
// First we create a collection of the screens
@@ -255,8 +214,8 @@
if (DEBUG) {
Log.d(TAG, "Migrating " + screenId);
}
- solveGridPlacement(db, srcReader,
- destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false);
+ solveGridPlacement(helper, srcReader,
+ destReader, screenId, trgX, trgY, workspaceToBeAdded, false);
if (workspaceToBeAdded.isEmpty()) {
break;
}
@@ -266,8 +225,8 @@
// any of the screens, in this case we add them to new screens until all of them are placed.
int screenId = destReader.mLastScreenId + 1;
while (!workspaceToBeAdded.isEmpty()) {
- solveGridPlacement(db, srcReader,
- destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
+ solveGridPlacement(helper, srcReader,
+ destReader, screenId, trgX, trgY, workspaceToBeAdded, preservePages);
screenId++;
}
@@ -298,33 +257,33 @@
});
}
- private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
+ private static void insertEntryInDb(DatabaseHelper helper, DbEntry entry,
String srcTableName, String destTableName) {
- int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
+ int id = copyEntryAndUpdate(helper, entry, srcTableName, destTableName);
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
for (Set<Integer> itemIds : entry.mFolderItems.values()) {
for (int itemId : itemIds) {
- copyEntryAndUpdate(db, context, itemId, id, srcTableName, destTableName);
+ copyEntryAndUpdate(helper, itemId, id, srcTableName, destTableName);
}
}
}
}
- private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ private static int copyEntryAndUpdate(DatabaseHelper helper,
DbEntry entry, String srcTableName, String destTableName) {
- return copyEntryAndUpdate(db, context, entry, -1, -1, srcTableName, destTableName);
+ return copyEntryAndUpdate(helper, entry, -1, -1, srcTableName, destTableName);
}
- private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
+ private static int copyEntryAndUpdate(DatabaseHelper helper,
int id, int folderId, String srcTableName, String destTableName) {
- return copyEntryAndUpdate(db, context, null, id, folderId, srcTableName, destTableName);
+ return copyEntryAndUpdate(helper, null, id, folderId, srcTableName, destTableName);
}
- private static int copyEntryAndUpdate(SQLiteDatabase db, Context context,
- DbEntry entry, int id, int folderId, String srcTableName, String destTableName) {
+ private static int copyEntryAndUpdate(DatabaseHelper helper, DbEntry entry,
+ int id, int folderId, String srcTableName, String destTableName) {
int newId = -1;
- Cursor c = db.query(srcTableName, null,
+ Cursor c = helper.getWritableDatabase().query(srcTableName, null,
LauncherSettings.Favorites._ID + " = '" + (entry != null ? entry.id : id) + "'",
null, null, null, null);
while (c.moveToNext()) {
@@ -335,11 +294,9 @@
} else {
values.put(LauncherSettings.Favorites.CONTAINER, folderId);
}
- newId = LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
- LauncherSettings.Settings.EXTRA_VALUE);
+ newId = helper.generateNewItemId();
values.put(LauncherSettings.Favorites._ID, newId);
- db.insert(destTableName, null, values);
+ helper.getWritableDatabase().insert(destTableName, null, values);
}
c.close();
return newId;
@@ -367,9 +324,9 @@
return validPackages;
}
- private static void solveGridPlacement(@NonNull final SQLiteDatabase db,
+ private static void solveGridPlacement(@NonNull final DatabaseHelper helper,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
- @NonNull final Context context, final int screenId, final int trgX, final int trgY,
+ final int screenId, final int trgX, final int trgY,
@NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
final Point trg = new Point(trgX, trgY);
@@ -391,7 +348,7 @@
continue;
}
if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
- insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
+ insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
iterator.remove();
}
}
@@ -428,9 +385,9 @@
return false;
}
- private static void solveHotseatPlacement(@NonNull final SQLiteDatabase db,
+ private static void solveHotseatPlacement(
+ @NonNull final DatabaseHelper helper, final int hotseatSize,
@NonNull final DbReader srcReader, @NonNull final DbReader destReader,
- @NonNull final Context context, final int hotseatSize,
@NonNull final List<DbEntry> placedHotseatItems,
@NonNull final List<DbEntry> itemsToPlace) {
@@ -447,7 +404,7 @@
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
- insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
+ insertEntryInDb(helper, entry, srcReader.mTableName, destReader.mTableName);
occupied[entry.screenId] = true;
}
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index a5dccc1..2054d93 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,16 +16,16 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
-import android.net.Uri;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -66,9 +66,7 @@
private final LongSparseArray<UserHandle> allUsers;
private final LauncherAppState mApp;
- private final Uri mContentUri;
private final Context mContext;
- private final PackageManager mPM;
private final IconCache mIconCache;
private final InvariantDeviceProfile mIDP;
@@ -108,17 +106,14 @@
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app,
- UserManagerState userManagerState) {
+ public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState) {
super(cursor);
mApp = app;
allUsers = userManagerState.allUsers;
- mContentUri = contentUri;
mContext = app.getContext();
mIconCache = app.getIconCache();
mIDP = app.getInvariantDeviceProfile();
- mPM = mContext.getPackageManager();
// Init column indices
mIconIndex = getColumnIndexOrThrow(Favorites.ICON);
@@ -390,7 +385,7 @@
*/
public ContentWriter updater() {
return new ContentWriter(mContext, new ContentWriter.CommitParams(
- mApp.getModel().getModelDbController().getDatabaseHelper(),
+ mApp.getModel().getModelDbController(),
BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
}
@@ -409,8 +404,8 @@
public boolean commitDeleted() {
if (mItemsToRemove.size() > 0) {
// Remove dead items
- mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery(
- Favorites._ID, mItemsToRemove), null);
+ mApp.getModel().getModelDbController().delete(TABLE_NAME,
+ Utilities.createDbSelectionQuery(Favorites._ID, mItemsToRemove), null);
return true;
}
return false;
@@ -435,9 +430,8 @@
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(Favorites.RESTORED, 0);
- mContext.getContentResolver().update(mContentUri, values,
- Utilities.createDbSelectionQuery(
- Favorites._ID, mRestoredRows), null);
+ mApp.getModel().getModelDbController().update(TABLE_NAME, values,
+ Utilities.createDbSelectionQuery(Favorites._ID, mRestoredRows), null);
}
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 9053d19..1a8cf24 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@@ -41,7 +42,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Point;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Trace;
import android.os.UserHandle;
@@ -50,8 +50,8 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
-import android.util.TimingLogger;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
@@ -119,6 +119,7 @@
private static final boolean DEBUG = true;
+ @NonNull
protected final LauncherAppState mApp;
private final AllAppsList mBgAllAppsList;
protected final BgDataModel mBgDataModel;
@@ -126,6 +127,7 @@
private FirstScreenBroadcast mFirstScreenBroadcast;
+ @NonNull
private final LauncherBinder mLauncherBinder;
private final LauncherApps mLauncherApps;
@@ -146,11 +148,11 @@
private boolean mItemsDeleted = false;
private String mDbName;
- public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
- ModelDelegate modelDelegate, LauncherBinder launcherBinder) {
+ public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
+ ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder) {
mApp = app;
mBgAllAppsList = bgAllAppsList;
- mBgDataModel = dataModel;
+ mBgDataModel = bgModel;
mModelDelegate = modelDelegate;
mLauncherBinder = launcherBinder;
@@ -200,25 +202,10 @@
}
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
- TimingLogger timingLogger = new TimingLogger(TAG, "run");
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
- Trace.beginSection("LoadWorkspace");
- try {
- loadWorkspace(allShortcuts, memoryLogger);
- } finally {
- Trace.endSection();
- }
- logASplit(timingLogger, "loadWorkspace");
-
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- verifyNotStopped();
- mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
- mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- mModelDelegate.markActive();
- logASplit(timingLogger, "workspaceDelegateItems");
- }
+ loadWorkspace(allShortcuts, "", memoryLogger);
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
// sanitizeData should not be invoked if the workspace is loaded from a db different
@@ -228,21 +215,21 @@
verifyNotStopped();
sanitizeFolders(mItemsDeleted);
sanitizeWidgetsShortcutsAndPackages();
- logASplit(timingLogger, "sanitizeData");
+ logASplit("sanitizeData");
}
verifyNotStopped();
mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
- logASplit(timingLogger, "bindWorkspace");
+ logASplit("bindWorkspace");
mModelDelegate.workspaceLoadComplete();
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
- logASplit(timingLogger, "sendFirstScreenActiveInstallsBroadcast");
+ logASplit("sendFirstScreenActiveInstallsBroadcast");
// Take a break
waitForIdle();
- logASplit(timingLogger, "step 1 complete");
+ logASplit("step 1 complete");
verifyNotStopped();
// second step
@@ -253,16 +240,16 @@
} finally {
Trace.endSection();
}
- logASplit(timingLogger, "loadAllApps");
+ logASplit("loadAllApps");
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- logASplit(timingLogger, "allAppsDelegateItems");
+ logASplit("allAppsDelegateItems");
}
verifyNotStopped();
mLauncherBinder.bindAllApps();
- logASplit(timingLogger, "bindAllApps");
+ logASplit("bindAllApps");
verifyNotStopped();
IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
@@ -270,75 +257,73 @@
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.newInstance(mApp.getContext()),
mApp.getModel()::onPackageIconsUpdated);
- logASplit(timingLogger, "update icon cache");
+ logASplit("update icon cache");
verifyNotStopped();
- logASplit(timingLogger, "save shortcuts in icon cache");
+ logASplit("save shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
mApp.getModel()::onPackageIconsUpdated);
// Take a break
waitForIdle();
- logASplit(timingLogger, "step 2 complete");
+ logASplit("step 2 complete");
verifyNotStopped();
// third step
List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
- logASplit(timingLogger, "loadDeepShortcuts");
+ logASplit("loadDeepShortcuts");
verifyNotStopped();
mLauncherBinder.bindDeepShortcuts();
- logASplit(timingLogger, "bindDeepShortcuts");
+ logASplit("bindDeepShortcuts");
verifyNotStopped();
- logASplit(timingLogger, "save deep shortcuts in icon cache");
+ logASplit("save deep shortcuts in icon cache");
updateHandler.updateIcons(allDeepShortcuts,
new ShortcutCachingLogic(), (pkgs, user) -> { });
// Take a break
waitForIdle();
- logASplit(timingLogger, "step 3 complete");
+ logASplit("step 3 complete");
verifyNotStopped();
// fourth step
List<ComponentWithLabelAndIcon> allWidgetsList =
mBgDataModel.widgetsModel.update(mApp, null);
- logASplit(timingLogger, "load widgets");
+ logASplit("load widgets");
verifyNotStopped();
mLauncherBinder.bindWidgets();
- logASplit(timingLogger, "bindWidgets");
+ logASplit("bindWidgets");
verifyNotStopped();
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
- logASplit(timingLogger, "otherDelegateItems");
+ logASplit("otherDelegateItems");
verifyNotStopped();
}
updateHandler.updateIcons(allWidgetsList,
new ComponentWithIconCachingLogic(mApp.getContext(), true),
mApp.getModel()::onWidgetLabelsUpdated);
- logASplit(timingLogger, "save widgets in icon cache");
+ logASplit("save widgets in icon cache");
// fifth step
loadFolderNames();
verifyNotStopped();
updateHandler.finish();
- logASplit(timingLogger, "finish icon update");
+ logASplit("finish icon update");
mModelDelegate.modelLoadComplete();
transaction.commit();
memoryLogger.clearLogs();
} catch (CancellationException e) {
// Loader stopped, ignore
- logASplit(timingLogger, "Cancelled");
+ logASplit("Cancelled");
} catch (Exception e) {
memoryLogger.printLogs();
throw e;
- } finally {
- timingLogger.dumpToLog();
}
TraceHelper.INSTANCE.endSection(traceToken);
}
@@ -348,25 +333,29 @@
this.notify();
}
- private void loadWorkspace(
- List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger memoryLogger) {
- loadWorkspace(allDeepShortcuts, Favorites.CONTENT_URI,
- null /* selection */, memoryLogger);
- }
+ protected void loadWorkspace(
+ List<ShortcutInfo> allDeepShortcuts,
+ String selection,
+ LoaderMemoryLogger memoryLogger) {
+ Trace.beginSection("LoadWorkspace");
+ try {
+ loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger);
+ } finally {
+ Trace.endSection();
+ }
+ logASplit("loadWorkspace");
- protected void loadWorkspaceForPreviewSurfaceRenderer(
- List<ShortcutInfo> allDeepShortcuts, Uri contentUri, String selection) {
- loadWorkspace(allDeepShortcuts, contentUri, selection, null);
if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
+ verifyNotStopped();
mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
mModelDelegate.markActive();
+ logASplit("workspaceDelegateItems");
}
}
- protected void loadWorkspace(
+ private void loadWorkspaceImpl(
List<ShortcutInfo> allDeepShortcuts,
- Uri contentUri,
String selection,
@Nullable LoaderMemoryLogger memoryLogger) {
final Context context = mApp.getContext();
@@ -377,7 +366,7 @@
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false;
- if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
+ if (!mApp.getModel().getModelDbController().migrateGridIfNeeded()) {
// Migration failed. Clear workspace.
clearDb = true;
}
@@ -402,8 +391,9 @@
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mShortcutKeyToPinnedShortcuts = new HashMap<>();
+ ModelDbController dbController = mApp.getModel().getModelDbController();
final LoaderCursor c = new LoaderCursor(
- contentResolver.query(contentUri, null, selection, null, null), contentUri,
+ dbController.query(TABLE_NAME, null, selection, null, null),
mApp, mUserManagerState);
final Bundle extras = c.getExtras();
mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME);
@@ -1112,12 +1102,9 @@
FileLog.d(TAG, widgetDimension.toString());
}
- private static void logASplit(@Nullable TimingLogger timingLogger, String label) {
- if (timingLogger != null) {
- timingLogger.addSplit(label);
- if (DEBUG) {
- Log.d(TAG, label);
- }
+ private static void logASplit(String label) {
+ if (DEBUG) {
+ Log.d(TAG, label);
}
}
}
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 97bce8c..f0e5ef6 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -19,11 +19,10 @@
import static android.util.Base64.NO_WRAP;
import static com.android.launcher3.DefaultLayoutParser.RES_PARTNER_DEFAULT_LAYOUT;
+import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
-import static com.android.launcher3.model.DatabaseHelper.EMPTY_DATABASE_CREATED;
-import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import android.app.blob.BlobHandle;
@@ -31,7 +30,6 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
@@ -43,6 +41,7 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
@@ -54,18 +53,22 @@
import com.android.launcher3.AutoInstallsLayout;
import com.android.launcher3.AutoInstallsLayout.SourceResources;
+import com.android.launcher3.ConstantItem;
import com.android.launcher3.DefaultLayoutParser;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.util.Partner;
import com.android.launcher3.widget.LauncherWidgetHolder;
@@ -73,7 +76,6 @@
import java.io.InputStream;
import java.io.StringReader;
-import java.util.function.Supplier;
/**
* Utility class which maintains an instance of Launcher database and provides utility methods
@@ -82,6 +84,8 @@
public class ModelDbController {
private static final String TAG = "LauncherProvider";
+ private static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
+
protected DatabaseHelper mOpenHelper;
private final Context mContext;
@@ -92,26 +96,36 @@
private synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
- mOpenHelper = DatabaseHelper.createDatabaseHelper(
- mContext, false /* forMigration */);
-
- RestoreDbTask.restoreIfNeeded(mContext, mOpenHelper);
+ mOpenHelper = createDatabaseHelper(false /* forMigration */);
+ RestoreDbTask.restoreIfNeeded(mContext, this);
}
}
- private synchronized boolean prepForMigration(String dbFile, String targetTableName,
- Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
- if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
- Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile);
- return false;
- }
+ protected DatabaseHelper createDatabaseHelper(boolean forMigration) {
+ boolean isSandbox = mContext instanceof SandboxContext;
+ String dbName = isSandbox ? null : InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
- final DatabaseHelper helper = src.get();
- mOpenHelper = dst.get();
- copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
- mOpenHelper.getWritableDatabase(), targetTableName, mContext);
- helper.close();
- return true;
+ // Set the flag for empty DB
+ Runnable onEmptyDbCreateCallback = forMigration ? () -> { }
+ : () -> LauncherPrefs.get(mContext).putSync(getEmptyDbCreatedKey(dbName).to(true));
+
+ DatabaseHelper databaseHelper = new DatabaseHelper(mContext, dbName,
+ this::getSerialNumberForUser, onEmptyDbCreateCallback);
+ // Table creation sometimes fails silently, which leads to a crash loop.
+ // This way, we will try to create a table every time after crash, so the device
+ // would eventually be able to recover.
+ if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
+ Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
+ // This operation is a no-op if the table already exists.
+ addTableToDb(databaseHelper.getWritableDatabase(),
+ getSerialNumberForUser(Process.myUserHandle()),
+ true /* optional */);
+ }
+ databaseHelper.mHotseatRestoreTableExists = tableExists(
+ databaseHelper.getReadableDatabase(), Favorites.HYBRID_HOTSEAT_BACKUP_TABLE);
+
+ databaseHelper.initIds();
+ return databaseHelper;
}
/**
@@ -267,42 +281,41 @@
}
/**
- * Updates the current DB and copies all the existing data to the temp table
- * @param dbFile name of the target db file name
+ * Migrates the DB if needed, and returns false if the migration failed
+ * and DB needs to be cleared.
+ * @return true if migration was success or ignored, false if migration failed
+ * and the DB should be reset.
*/
- @WorkerThread
- public boolean updateCurrentOpenHelper(String dbFile) {
+ public boolean migrateGridIfNeeded() {
createDbIfNotExists();
- return prepForMigration(
- dbFile,
- Favorites.TMP_TABLE,
- () -> mOpenHelper,
- () -> DatabaseHelper.createDatabaseHelper(
- mContext, true /* forMigration */));
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
+ if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) {
+ return true;
+ }
+ String targetDbName = new DeviceGridState(idp).getDbFile();
+ if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) {
+ Log.e(TAG, "migrateGridIfNeeded - target db is same as current: " + targetDbName);
+ return false;
+ }
+ DatabaseHelper oldHelper = mOpenHelper;
+ mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
+ : createDatabaseHelper(true /* forMigration */);
+ try {
+ return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, idp, mOpenHelper,
+ oldHelper.getWritableDatabase());
+ } finally {
+ if (mOpenHelper != oldHelper) {
+ oldHelper.close();
+ }
+ }
}
/**
- * Returns the current DatabaseHelper.
- * Only for tests
+ * Returns the underlying model database
*/
- @WorkerThread
- public DatabaseHelper getDatabaseHelper() {
+ public SQLiteDatabase getDb() {
createDbIfNotExists();
- return mOpenHelper;
- }
-
- /**
- * Prepares the DB for preview by copying all existing data to preview table
- */
- @WorkerThread
- public boolean prepareForPreview(String dbFile) {
- createDbIfNotExists();
- return prepForMigration(
- dbFile,
- Favorites.PREVIEW_TABLE_NAME,
- () -> DatabaseHelper.createDatabaseHelper(
- mContext, dbFile, true /* forMigration */),
- () -> mOpenHelper);
+ return mOpenHelper.getWritableDatabase();
}
private void onAddOrDeleteOp(SQLiteDatabase db) {
@@ -345,8 +358,7 @@
}
private void clearFlagEmptyDbCreated() {
- LauncherPrefs.getPrefs(mContext).edit()
- .remove(mOpenHelper.getKey(EMPTY_DATABASE_CREATED)).commit();
+ LauncherPrefs.get(mContext).removeSync(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()));
}
/**
@@ -359,9 +371,8 @@
@WorkerThread
public synchronized void loadDefaultFavoritesIfNecessary() {
createDbIfNotExists();
- SharedPreferences sp = LauncherPrefs.getPrefs(mContext);
- if (sp.getBoolean(mOpenHelper.getKey(EMPTY_DATABASE_CREATED), false)) {
+ if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()))) {
Log.d(TAG, "loading default workspace");
LauncherWidgetHolder widgetHolder = mOpenHelper.newLauncherWidgetHolder();
@@ -479,4 +490,27 @@
return new DefaultLayoutParser(mContext, widgetHolder,
mOpenHelper, mContext.getResources(), defaultLayout);
}
+
+ /**
+ * Re-composite given key in respect to database. If the current db is
+ * {@link LauncherFiles#LAUNCHER_DB}, return the key as-is. Otherwise append the db name to
+ * given key. e.g. consider key="EMPTY_DATABASE_CREATED", dbName="minimal.db", the returning
+ * string will be "EMPTY_DATABASE_CREATED@minimal.db".
+ */
+ private ConstantItem<Boolean> getEmptyDbCreatedKey(String dbName) {
+ if (mContext instanceof SandboxContext) {
+ return LauncherPrefs.nonRestorableItem(EMPTY_DATABASE_CREATED,
+ false /* default value */, false /* boot aware */);
+ }
+ String key = TextUtils.equals(dbName, LauncherFiles.LAUNCHER_DB)
+ ? EMPTY_DATABASE_CREATED : EMPTY_DATABASE_CREATED + "@" + dbName;
+ return LauncherPrefs.backedUpItem(key, false /* default value */, false /* boot aware */);
+ }
+
+ /**
+ * Returns the serial number for the provided user
+ */
+ public long getSerialNumberForUser(UserHandle user) {
+ return UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user);
+ }
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index d4a5e1b..8c938f4 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -95,7 +95,7 @@
mPackages = packages;
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.WORK_TAB_MISSING, "PackageUpdatedTask mOp: " + mOp +
- " packageCount: " + mPackages.length);
+ " packageCount: " + mPackages.length + " user: " + user);
DEBUG = true;
}
}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 48969fc..c718dcc 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -101,7 +101,7 @@
UserManagerState ums = new UserManagerState();
ums.init(UserCache.INSTANCE.get(context),
context.getSystemService(UserManager.class));
- LoaderCursor lc = new LoaderCursor(c, null, LauncherAppState.getInstance(context), ums);
+ LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums);
IntSet deletedShortcuts = new IntSet();
while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index ac72164..a6e064a 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,6 +16,8 @@
package com.android.launcher3.provider;
+import static android.os.Process.myUserHandle;
+
import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
@@ -47,8 +49,8 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.DatabaseHelper;
import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -83,12 +85,12 @@
/**
* Tries to restore the backup DB if needed
*/
- public static void restoreIfNeeded(Context context, DatabaseHelper helper) {
+ public static void restoreIfNeeded(Context context, ModelDbController dbController) {
if (!isPending(context)) {
return;
}
- if (!performRestore(context, helper)) {
- helper.createEmptyDB(helper.getWritableDatabase());
+ if (!performRestore(context, dbController)) {
+ dbController.createEmptyDB();
}
// Obtain InvariantDeviceProfile first before setting pending to false, so
@@ -102,12 +104,12 @@
idp.reinitializeAfterRestore(context);
}
- private static boolean performRestore(Context context, DatabaseHelper helper) {
- SQLiteDatabase db = helper.getWritableDatabase();
+ private static boolean performRestore(Context context, ModelDbController controller) {
+ SQLiteDatabase db = controller.getDb();
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, helper, db, new BackupManager(context));
- task.restoreAppWidgetIdsIfExists(context, helper);
+ task.sanitizeDB(context, controller, db, new BackupManager(context));
+ task.restoreAppWidgetIdsIfExists(context, controller);
t.commit();
return true;
} catch (Exception e) {
@@ -129,10 +131,10 @@
* @return number of items deleted.
*/
@VisibleForTesting
- protected int sanitizeDB(Context context, DatabaseHelper helper, SQLiteDatabase db,
+ protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db,
BackupManager backupManager) throws Exception {
// Primary user ids
- long myProfileId = helper.getDefaultUserSerial();
+ long myProfileId = controller.getSerialNumberForUser(myUserHandle());
long oldProfileId = getDefaultProfileId(db);
LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId);
LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size()
@@ -144,7 +146,7 @@
long oldManagedProfileId = oldManagedProfileIds.keyAt(i);
UserHandle user = getUserForAncestralSerialNumber(backupManager, oldManagedProfileId);
if (user != null) {
- long newManagedProfileId = helper.getSerialNumberForUser(user);
+ long newManagedProfileId = controller.getSerialNumberForUser(user);
profileMapping.put(oldManagedProfileId, newManagedProfileId);
}
}
@@ -213,7 +215,7 @@
}
// Override shortcuts
- maybeOverrideShortcuts(context, helper, db, myProfileId);
+ maybeOverrideShortcuts(context, controller, db, myProfileId);
return itemsDeleted;
}
@@ -321,11 +323,11 @@
.putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
}
- private void restoreAppWidgetIdsIfExists(Context context, DatabaseHelper helper) {
+ private void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
LauncherPrefs lp = LauncherPrefs.get(context);
if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
- AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, helper,
+ AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, controller,
IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
host);
@@ -343,7 +345,7 @@
APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
}
- protected static void maybeOverrideShortcuts(Context context, DatabaseHelper helper,
+ protected static void maybeOverrideShortcuts(Context context, ModelDbController controller,
SQLiteDatabase db, long currentUser) {
Map<String, LauncherActivityInfo> activityOverrides = ApiWrapper.getActivityOverrides(
context);
@@ -367,7 +369,7 @@
if (override != null) {
ContentValues values = new ContentValues();
values.put(Favorites.PROFILE_ID,
- helper.getSerialNumberForUser(override.getUser()));
+ controller.getSerialNumberForUser(override.getUser()));
values.put(Favorites.INTENT, AppInfo.makeLaunchIntent(override).toUri(0));
db.update(Favorites.TABLE_NAME, values, String.format("%s=?", Favorites._ID),
new String[]{String.valueOf(c.getInt(idIndex))});
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 56dffa9..fcc62a7 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -405,17 +404,25 @@
drawable = null;
scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
}
- int halfPadding = previewProvider.previewPadding / 2;
+
int dragLayerX = mTempXY[0];
int dragLayerY = mTempXY[1];
- Point dragVisualizeOffset = null;
Rect dragRect = new Rect();
if (draggableView != null) {
draggableView.getSourceVisualDragBounds(dragRect);
dragLayerY += dragRect.top;
- dragVisualizeOffset = new Point(-halfPadding, halfPadding);
}
+
+ if (options.preDragCondition != null) {
+ int xOffSet = options.preDragCondition.getDragOffset().x;
+ int yOffSet = options.preDragCondition.getDragOffset().y;
+ if (xOffSet != 0 && yOffSet != 0) {
+ dragLayerX += xOffSet;
+ dragLayerY += yOffSet;
+ }
+ }
+
if (contentView != null) {
mDragController.startDrag(
contentView,
@@ -424,7 +431,6 @@
dragLayerY,
source,
dragObject,
- dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
@@ -437,7 +443,6 @@
dragLayerY,
source,
dragObject,
- dragVisualizeOffset,
dragRect,
scale * iconScale,
scale,
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
index b1a9b86..8d1d96b 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
@@ -17,7 +17,6 @@
package com.android.launcher3.secondarydisplay;
import android.content.res.Resources;
-import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.HapticFeedbackConstants;
@@ -51,7 +50,7 @@
@Override
protected DragView startDrag(@Nullable Drawable drawable, @Nullable View view,
DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
- ItemInfo dragInfo, Point dragOffset, Rect dragRegion, float initialDragViewScale,
+ ItemInfo dragInfo, Rect dragRegion, float initialDragViewScale,
float dragViewScaleOnDrop, DragOptions options) {
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
@@ -117,9 +116,11 @@
mDragObject.dragInfo = dragInfo;
mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
- if (dragOffset != null) {
- dragView.setDragVisualizeOffset(new Point(dragOffset));
+ if (mOptions.preDragCondition != null) {
+ dragView.setHasDragOffset(mOptions.preDragCondition.getDragOffset().x != 0 ||
+ mOptions.preDragCondition.getDragOffset().y != 0);
}
+
if (dragRegion != null) {
dragView.setDragRegion(new Rect(dragRegion));
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index a917b68..87afcab 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -236,7 +236,7 @@
@Override
public boolean shouldStartDrag(double distanceDragged) {
- return mDragView != null && mDragView.isAnimationFinished();
+ return mDragView != null && mDragView.isScaleAnimationFinished();
}
@Override
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 89d89d6..198dad3 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -229,8 +229,10 @@
listener.onAnimationEnd(null);
}
return;
- } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
- // We are running the same animation as requested
+ } else if ((!mConfig.userControlled && animated && mConfig.targetState == state)
+ || mState.shouldPreserveDataStateOnReapply()) {
+ // We are running the same animation as requested, and/or target state should not be
+ // reset -- allow the current animation to complete instead of canceling it.
if (listener != null) {
mConfig.currentAnimation.addListener(listener);
}
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 050e88f..6a972eb 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -589,17 +589,17 @@
float scaledDividerHeight = dividerHeight * scale;
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- if (splitInfo.appsStackedVertically) {
- outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
- } else {
+ if (dp.isLandscape) {
outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
+ } else {
+ outRect.bottom = Math.round(outRect.top + scaledTopTaskHeight);
}
} else {
- if (splitInfo.appsStackedVertically) {
- outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
- } else {
+ if (dp.isLandscape) {
outRect.left += Math.round(outRect.width()
* (topLeftTaskPercent + dividerBarPercent));
+ } else {
+ outRect.top += Math.round(scaledTopTaskHeight + scaledDividerHeight);
}
}
}
@@ -610,9 +610,9 @@
DeviceProfile dp, boolean isRtl) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = Math.round(splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent * dp.availableHeightPx
- : splitBoundsConfig.dividerWidthPercent * parentWidth);
+ float dividerScale = splitBoundsConfig.appsStackedVertically
+ ? splitBoundsConfig.dividerHeightPercent
+ : splitBoundsConfig.dividerWidthPercent;
int primarySnapshotHeight;
int primarySnapshotWidth;
int secondarySnapshotHeight;
@@ -620,12 +620,13 @@
float taskPercent = splitBoundsConfig.appsStackedVertically ?
splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
if (dp.isLandscape) {
+ int scaledDividerBar = Math.round(parentWidth * dividerScale);
primarySnapshotHeight = totalThumbnailHeight;
primarySnapshotWidth = Math.round(parentWidth * taskPercent);
secondarySnapshotHeight = totalThumbnailHeight;
- secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar;
- int translationX = primarySnapshotWidth + dividerBar;
+ secondarySnapshotWidth = parentWidth - primarySnapshotWidth - scaledDividerBar;
+ int translationX = primarySnapshotWidth + scaledDividerBar;
if (isRtl) {
primarySnapshot.setTranslationX(-translationX);
secondarySnapshot.setTranslationX(0);
@@ -640,7 +641,7 @@
} else {
float scale = (float) totalThumbnailHeight / dp.availableHeightPx;
float topTaskHeight = dp.availableHeightPx * taskPercent;
- float finalDividerHeight = dividerBar * scale;
+ float finalDividerHeight = Math.round(totalThumbnailHeight * dividerScale);
float scaledTopTaskHeight = topTaskHeight * scale;
primarySnapshotWidth = parentWidth;
primarySnapshotHeight = Math.round(scaledTopTaskHeight);
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index e509235..7c5ef4d 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -26,7 +26,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.model.DatabaseHelper;
+import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.pm.UserCache;
/**
@@ -106,7 +106,7 @@
public int commit() {
if (mCommitParams != null) {
- mCommitParams.mDatabaseHelper.getWritableDatabase().update(
+ mCommitParams.mDbController.update(
Favorites.TABLE_NAME, getValues(mContext),
mCommitParams.mWhere, mCommitParams.mSelectionArgs);
}
@@ -115,12 +115,12 @@
public static final class CommitParams {
- final DatabaseHelper mDatabaseHelper;
+ final ModelDbController mDbController;
final String mWhere;
final String[] mSelectionArgs;
- public CommitParams(DatabaseHelper helper, String where, String[] selectionArgs) {
- mDatabaseHelper = helper;
+ public CommitParams(ModelDbController controller, String where, String[] selectionArgs) {
+ mDbController = controller;
mWhere = where;
mSelectionArgs = selectionArgs;
}
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index b6af314..8a27381 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.os.UserHandle;
+import android.util.Log;
import androidx.annotation.NonNull;
@@ -25,6 +26,7 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.testing.shared.TestProtocol;
import java.util.Collection;
import java.util.HashSet;
@@ -42,7 +44,14 @@
private static final ComponentName EMPTY_COMPONENT = new ComponentName("", "");
public static Predicate<ItemInfo> ofUser(UserHandle user) {
- return info -> info != null && info.user.equals(user);
+ return info -> {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_TAB_MISSING, "userHandle: " + user
+ + ", itemUserHandle: " + info.user
+ + " package: " + info.getTargetPackage());
+ }
+ return info != null && info.user.equals(user);
+ };
}
public static Predicate<ItemInfo> ofComponents(
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index badcd35..6a4e528 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -136,16 +136,19 @@
if (mDestroyed) {
Log.e(TAG, "Static object access with a destroyed context");
}
- if (!mAllowedObjects.contains(object)) {
- throw new IllegalStateException(
- "Leaking unknown objects " + object + " " + provider);
- }
+
T t = (T) mObjectMap.get(object);
if (t != null) {
return t;
}
if (Looper.myLooper() == Looper.getMainLooper()) {
t = createObject(provider);
+ // Check if we've explicitly allowed the object or if it's a SafeCloseable,
+ // it will get destroyed in onDestroy()
+ if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {
+ throw new IllegalStateException(
+ "Leaking unknown objects " + object + " " + provider + " " + t);
+ }
mObjectMap.put(object, t);
mOrderedObjects.add(t);
return t;
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 31b1934..515a2d8 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.views;
+import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
+
+import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
@@ -45,7 +48,6 @@
import android.view.WindowInsetsController;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
-import android.window.SplashScreen;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -289,27 +291,27 @@
}
}
-
/**
* Sends a pending intent animating from a view.
*
* @param v View to animate.
* @param intent The pending intent being launched.
* @param item Item associated with the view.
- * @return {@code true} if the intent is sent successfully.
+ * @return RunnableList for listening for animation finish if the activity was properly
+ * or started, {@code null} if the launch finished
*/
- default boolean sendPendingIntentWithAnimation(
+ default RunnableList sendPendingIntentWithAnimation(
@NonNull View v, PendingIntent intent, @Nullable ItemInfo item) {
- Bundle optsBundle = getActivityLaunchOptions(v, item).toBundle();
+ ActivityOptionsWrapper options = getActivityLaunchOptions(v, item);
try {
- intent.send(null, 0, null, null, null, null, optsBundle);
- return true;
+ intent.send(null, 0, null, null, null, null, options.toBundle());
+ return options.onEndCallback;
} catch (PendingIntent.CanceledException e) {
Toast.makeText(v.getContext(),
v.getContext().getResources().getText(R.string.shortcut_not_available),
Toast.LENGTH_SHORT).show();
}
- return false;
+ return null;
}
/**
@@ -318,28 +320,23 @@
* @param v View starting the activity.
* @param intent Base intent being launched.
* @param item Item associated with the view.
- * @return {@code true} if the activity starts successfully.
+ * @return RunnableList for listening for animation finish if the activity was properly
+ * or started, {@code null} if the launch finished
*/
- default boolean startActivitySafely(
+ default RunnableList startActivitySafely(
View v, Intent intent, @Nullable ItemInfo item) {
Preconditions.assertUIThread();
Context context = (Context) this;
if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
- return false;
+ return null;
}
- Bundle optsBundle = null;
- if (v != null) {
- optsBundle = getActivityLaunchOptions(v, item).toBundle();
- } else if (android.os.Build.VERSION.SDK_INT >= 33
- && item != null
- && item.animationType == LauncherSettings.Animation.DEFAULT_NO_ICON) {
- optsBundle = ActivityOptions.makeBasic()
- .setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR).toBundle();
- }
+ ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
+ : makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
+ ? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
UserHandle user = item == null ? null : item.user;
-
+ Bundle optsBundle = options.toBundle();
// Prepare intent
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (v != null) {
@@ -364,12 +361,12 @@
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
logAppLaunch(getStatsLogManager(), item, instanceId);
}
- return true;
+ return options.onEndCallback;
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
}
- return false;
+ return null;
}
/** Returns {@code true} if an app launch is blocked due to safe mode. */
@@ -417,6 +414,17 @@
}
/**
+ * Creates a default activity option and we do not want association with any launcher element.
+ */
+ default ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+ ActivityOptions options = ActivityOptions.makeBasic();
+ if (Utilities.ATLEAST_T) {
+ options.setSplashScreenStyle(splashScreenStyle);
+ }
+ return new ActivityOptionsWrapper(options, new RunnableList());
+ }
+
+ /**
* Safely launches an intent for a shortcut.
*
* @param intent Intent to start.
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 78a9b58..4641e31 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -284,7 +284,7 @@
if (!TextUtils.isEmpty(pickerPackage)) {
intent.setPackage(pickerPackage);
}
- return launcher.startActivitySafely(v, intent, placeholderInfo(intent));
+ return launcher.startActivitySafely(v, intent, placeholderInfo(intent)) != null;
}
static WorkspaceItemInfo placeholderInfo(Intent intent) {
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index f269434..faad307 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -103,7 +103,6 @@
final int previewWidth;
final int previewHeight;
final float scale;
- final Point dragOffset;
final Rect dragRegion;
mEstimatedCellSize = launcher.getWorkspace().estimateItemSize(mAddInfo);
@@ -173,7 +172,6 @@
scale = previewBounds.width() / (float) previewWidth;
launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView));
- dragOffset = null;
dragRegion = null;
draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_WIDGET);
} else {
@@ -188,8 +186,6 @@
li.recycle();
scale = ((float) launcher.getDeviceProfile().iconSizePx) / previewWidth;
- dragOffset = new Point(previewPadding / 2, previewPadding / 2);
-
// Create a preview same as the workspace cell size and draw the icon at the
// appropriate position.
DeviceProfile dp = launcher.getDeviceProfile();
@@ -217,11 +213,10 @@
// Start the drag
if (mAppWidgetHostViewPreview != null) {
launcher.getDragController().startDrag(mAppWidgetHostViewPreview, draggableView,
- dragLayerX, dragLayerY, source, mAddInfo, dragOffset, dragRegion, scale, scale,
- options);
+ dragLayerX, dragLayerY, source, mAddInfo, dragRegion, scale, scale, options);
} else {
launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY,
- source, mAddInfo, dragOffset, dragRegion, scale, scale, options);
+ source, mAddInfo, dragRegion, scale, scale, options);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index d565dc9..33c4f8d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -608,6 +608,12 @@
}
}
+ /** b/209579563: "Widgets" header should be focused first. */
+ @Override
+ protected View getAccessibilityInitialFocusView() {
+ return mHeaderTitle;
+ }
+
protected float getMaxTableHeight(float noWidgetsViewHeight) {
return (mContent.getMeasuredHeight()
- mTabsHeight - getHeaderViewHeight()
diff --git a/tests/Android.bp b/tests/Android.bp
index fa0cdf2..e7f4084 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -51,6 +51,7 @@
"src/com/android/launcher3/util/WidgetUtils.java",
"src/com/android/launcher3/util/rule/FailureWatcher.java",
"src/com/android/launcher3/util/rule/LauncherActivityRule.java",
+ "src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
"src/com/android/launcher3/util/rule/SamplerRule.java",
"src/com/android/launcher3/util/rule/ScreenRecordRule.java",
"src/com/android/launcher3/util/rule/ShellCommandRule.java",
@@ -132,4 +133,4 @@
manifest: "shared/AndroidManifest.xml",
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
- }
\ No newline at end of file
+ }
diff --git a/tests/src/com/android/launcher3/DeleteDropTargetTest.kt b/tests/src/com/android/launcher3/DeleteDropTargetTest.kt
index 84c617c..bcfb90b 100644
--- a/tests/src/com/android/launcher3/DeleteDropTargetTest.kt
+++ b/tests/src/com/android/launcher3/DeleteDropTargetTest.kt
@@ -8,7 +8,6 @@
import com.android.launcher3.util.ActivityContextWrapper
import com.google.common.truth.Truth.assertThat
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -26,19 +25,15 @@
enableRunningInTestHarnessForTests()
}
- // Needs mText, mTempRect, getPaddingTop, getPaddingBottom
- // availableHeight as a parameter
- @Ignore("TODO(b/279464742)")
@Test
fun isTextClippedVerticallyTest() {
- buttonDropTarget.mText = "My Test"
+ buttonDropTarget.updateText("My Test")
+ buttonDropTarget.setPadding(0, 0, 0, 0)
+ buttonDropTarget.setTextMultiLine(false)
// No space for text
assertThat(buttonDropTarget.isTextClippedVertically(30)).isTrue()
- // Some space for text, and just enough that the text should not be clipped
- assertThat(buttonDropTarget.isTextClippedVertically(50)).isFalse()
-
// A lot of space for text so the text should not be clipped
assertThat(buttonDropTarget.isTextClippedVertically(100)).isFalse()
}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index e3de500..ee05fe6 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -87,15 +87,13 @@
// modify the device profile.
dp.inv.numColumns = width;
dp.inv.numRows = height;
+ dp.cellLayoutBorderSpacePx = new Point(0, 0);
CellLayout cl = new CellLayout(getWrappedContext(c, dp));
// I put a very large number for width and height so that all the items can fit, it doesn't
// need to be exact, just bigger than the sum of cell border
cl.measure(View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.EXACTLY));
-
- cl.measure(View.MeasureSpec.makeMeasureSpec(cl.getDesiredWidth(), View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(cl.getDesiredHeight(), View.MeasureSpec.EXACTLY));
return cl;
}
diff --git a/tests/src/com/android/launcher3/icons/IconCacheTest.java b/tests/src/com/android/launcher3/icons/IconCacheTest.java
index 08d6df3..495d583 100644
--- a/tests/src/com/android/launcher3/icons/IconCacheTest.java
+++ b/tests/src/com/android/launcher3/icons/IconCacheTest.java
@@ -116,7 +116,7 @@
@Nullable ComponentName cn, @Nullable String badgeOverride) throws Exception {
Builder builder = new Builder(context, "test-shortcut")
.setIntent(new Intent(Intent.ACTION_VIEW))
- .setTitle("Test");
+ .setShortLabel("Test");
if (cn != null) {
builder.setActivity(cn);
}
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index 0a1a9ba..cea95e5 100644
--- a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -39,6 +39,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
+import com.android.launcher3.pm.UserCache;
import org.junit.Before;
import org.junit.Test;
@@ -222,7 +223,9 @@
private class MyDatabaseHelper extends DatabaseHelper {
MyDatabaseHelper() {
- super(mContext, DB_FILE, false);
+ super(mContext, DB_FILE,
+ UserCache.INSTANCE.get(mContext)::getSerialNumberForUser,
+ () -> { });
}
@Override
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index f24f0da..3b480ca 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.model
+import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.Cursor
@@ -23,6 +24,7 @@
import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
@@ -32,7 +34,6 @@
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.LauncherDbUtils
import com.android.launcher3.util.LauncherModelHelper
-import com.android.launcher3.util.LauncherModelHelper.*
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
@@ -43,11 +44,13 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class GridSizeMigrationUtilTest {
+
private lateinit var modelHelper: LauncherModelHelper
private lateinit var context: Context
- private lateinit var db: SQLiteDatabase
private lateinit var validPackages: Set<String>
private lateinit var idp: InvariantDeviceProfile
+ private lateinit var dbHelper: DatabaseHelper
+ private lateinit var db: SQLiteDatabase
private val testPackage1 = "com.android.launcher3.validpackage1"
private val testPackage2 = "com.android.launcher3.validpackage2"
private val testPackage3 = "com.android.launcher3.validpackage3"
@@ -63,11 +66,16 @@
fun setUp() {
modelHelper = LauncherModelHelper()
context = modelHelper.sandboxContext
- db = modelHelper.provider.db
+ dbHelper =
+ DatabaseHelper(
+ context,
+ null,
+ UserCache.INSTANCE.get(context)::getSerialNumberForUser
+ ) {}
+ db = dbHelper.writableDatabase
validPackages =
setOf(
- TEST_PACKAGE,
testPackage1,
testPackage2,
testPackage3,
@@ -99,26 +107,26 @@
@Throws(Exception::class)
fun testMigration() {
// Src Hotseat icons
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Src grid icons
// _ _ _ _ _
// _ _ _ _ 5
// _ _ 6 _ 7
// _ _ 8 _ 9
// _ _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 3, testPackage9, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 1, testPackage5, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage6, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 2, testPackage7, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage8, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
// Dest hotseat icons
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Dest grid icons
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage10)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -126,8 +134,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -138,12 +145,13 @@
// Check hotseat items
var c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -168,12 +176,13 @@
// Check workspace items
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -209,30 +218,30 @@
fun testMigrationBackAndForth() {
// Hotseat items in grid A
// 1 2 _ 3 4
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Workspace items in grid A
// _ _ _ _ _
// _ _ _ _ 5
// _ _ 6 _ 7
// _ _ 8 _ _
// _ _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 1, testPackage5, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage6, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 2, testPackage7, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage8, 8, TMP_TABLE)
// Hotseat items in grid B
// 2 _ _ _
- modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Workspace items in grid B
// _ _ _ _
// _ _ _ 10
// _ _ _ _
// _ _ _ _
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 3, testPackage10)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -241,8 +250,7 @@
val readerGridB = DbReader(db, TABLE_NAME, context, validPackages)
// migrate from A -> B
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
readerGridA,
readerGridB,
idp.numDatabaseHotseatIcons,
@@ -253,12 +261,13 @@
// Check hotseat items in grid B
var c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -272,12 +281,13 @@
// Check workspace items in grid B
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -294,12 +304,11 @@
assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1))
// add item in B
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 2, testPackage9)
// migrate from B -> A
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
readerGridB,
readerGridA,
5,
@@ -309,12 +318,13 @@
)
// Check hotseat items in grid A
c =
- context.contentResolver.query(
- TMP_CONTENT_URI,
+ db.query(
+ TMP_TABLE,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -328,12 +338,13 @@
// Check workspace items in grid A
c =
- context.contentResolver.query(
- TMP_CONTENT_URI,
+ db.query(
+ TMP_TABLE,
arrayOf(SCREEN, CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -354,12 +365,11 @@
assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2))
// remove item from B
- modelHelper.deleteItem(7, TMP_TABLE)
+ db.delete(TMP_TABLE, "$_ID=7", null)
// migrate from A -> B
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
readerGridA,
readerGridB,
idp.numDatabaseHotseatIcons,
@@ -370,12 +380,13 @@
// Check hotseat items in grid B
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -389,12 +400,13 @@
// Check workspace items in grid B
c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, CELLX, CELLY, INTENT),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -443,10 +455,28 @@
fun migrateToLargerHotseat() {
val srcHotseatItems =
intArrayOf(
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
- modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
- modelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
- modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
+ addItem(
+ ITEM_TYPE_APPLICATION,
+ 0,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage1,
+ 1,
+ TMP_TABLE
+ ),
+ addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE),
+ addItem(
+ ITEM_TYPE_APPLICATION,
+ 2,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage3,
+ 3,
+ TMP_TABLE
+ ),
+ addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
)
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
idp.numDatabaseHotseatIcons = 6
@@ -455,8 +485,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -467,12 +496,13 @@
// Check hotseat items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -501,11 +531,11 @@
@Test
fun migrateFromLargerHotseat() {
- modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI)
- modelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 5, HOTSEAT, 0, 0, testPackage5, 5, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -513,8 +543,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -525,12 +554,13 @@
// Check hotseat items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(SCREEN, INTENT),
"container=$CONTAINER_HOTSEAT",
null,
SCREEN,
+ null,
null
)
?: throw IllegalStateException()
@@ -568,11 +598,11 @@
enableNewMigrationLogic("4,4")
// Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 1, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 3, 2, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 3, 3, testPackage5, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage1, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 3, testPackage2, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 1, testPackage3, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 3, 2, testPackage4, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 3, 3, testPackage5, 9, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 6
@@ -581,8 +611,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -593,12 +622,13 @@
// Get workspace items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(INTENT, SCREEN),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -630,11 +660,11 @@
enableNewMigrationLogic("2,2")
// Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 1, testPackage1, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 1, testPackage2, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 0, 0, testPackage3, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 1, 0, testPackage4, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 0, 0, testPackage5, 9, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 5
@@ -642,8 +672,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -654,12 +683,13 @@
// Get workspace items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(INTENT, SCREEN),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -691,11 +721,11 @@
enableNewMigrationLogic("5,5")
// Setup src grid
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 1, testPackage1, 5, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 1, testPackage2, 6, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 0, 0, testPackage3, 7, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 1, DESKTOP, 1, 0, testPackage4, 8, TMP_CONTENT_URI)
- modelHelper.addItem(APP_ICON, 2, DESKTOP, 0, 0, testPackage5, 9, TMP_CONTENT_URI)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 0, 1, testPackage1, 5, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 1, 1, testPackage2, 6, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 0, 0, testPackage3, 7, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 1, CONTAINER_DESKTOP, 1, 0, testPackage4, 8, TMP_TABLE)
+ addItem(ITEM_TYPE_APPLICATION, 2, CONTAINER_DESKTOP, 0, 0, testPackage5, 9, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
idp.numColumns = 4
@@ -703,8 +733,7 @@
val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
val destReader = DbReader(db, TABLE_NAME, context, validPackages)
GridSizeMigrationUtil.migrate(
- context,
- db,
+ dbHelper,
srcReader,
destReader,
idp.numDatabaseHotseatIcons,
@@ -715,12 +744,13 @@
// Get workspace items
val c =
- context.contentResolver.query(
- CONTENT_URI,
+ db.query(
+ TABLE_NAME,
arrayOf(INTENT, SCREEN),
"container=$CONTAINER_DESKTOP",
null,
null,
+ null,
null
)
?: throw IllegalStateException()
@@ -747,4 +777,48 @@
private fun enableNewMigrationLogic(srcGridSize: String) {
LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize))
}
+
+ private fun addItem(
+ type: Int,
+ screen: Int,
+ container: Int,
+ x: Int,
+ y: Int,
+ packageName: String?
+ ): Int {
+ return addItem(
+ type,
+ screen,
+ container,
+ x,
+ y,
+ packageName,
+ dbHelper.generateNewItemId(),
+ TABLE_NAME
+ )
+ }
+
+ private fun addItem(
+ type: Int,
+ screen: Int,
+ container: Int,
+ x: Int,
+ y: Int,
+ packageName: String?,
+ id: Int,
+ tableName: String
+ ): Int {
+ val values = ContentValues()
+ values.put(_ID, id)
+ values.put(CONTAINER, container)
+ values.put(SCREEN, screen)
+ values.put(CELLX, x)
+ values.put(CELLY, y)
+ values.put(SPANX, 1)
+ values.put(SPANY, 1)
+ values.put(ITEM_TYPE, type)
+ values.put(INTENT, Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0))
+ db.insert(tableName, null, values)
+ return id
+ }
}
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index d192be4..78812c0 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -59,7 +59,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
@@ -102,7 +101,7 @@
});
UserManagerState ums = new UserManagerState();
- mLoaderCursor = new LoaderCursor(mCursor, Favorites.CONTENT_URI, mApp, ums);
+ mLoaderCursor = new LoaderCursor(mCursor, mApp, ums);
ums.allUsers.put(0, Process.myUserHandle());
}
diff --git a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
index 2b6f9ff..d1befd0 100644
--- a/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
+++ b/tests/src/com/android/launcher3/provider/LauncherDbUtilsTest.java
@@ -45,6 +45,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.DatabaseHelper;
import com.android.launcher3.model.DbDowngradeHelper;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.settings.SettingsActivity;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IOUtils;
@@ -128,7 +129,6 @@
assertEquals(1, getFavoriteDataCount(db));
ShortcutInfo info = mInfoArgumentCaptor.getValue();
assertNotNull(info);
- assertEquals("Hello", info.getTitle());
try (Cursor c = db.query(Favorites.TABLE_NAME, null, null, null, null, null, null)) {
c.moveToNext();
assertEquals(Favorites.ITEM_TYPE_DEEP_SHORTCUT, c.getInt(c.getColumnIndex(ITEM_TYPE)));
@@ -165,12 +165,11 @@
private class MyDatabaseHelper extends DatabaseHelper {
MyDatabaseHelper() {
- super(mContext, null, false);
+ super(mContext, null, UserCache.INSTANCE.get(mContext)::getSerialNumberForUser,
+ () -> { });
}
@Override
protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
-
- protected void onEmptyDbCreated() { }
}
}
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 67de1f5..73bb586 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -45,8 +45,11 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.DatabaseHelper;
+import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.util.LauncherModelHelper;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,15 +64,29 @@
private final UserHandle mWorkUser = UserHandle.getUserHandleForUid(PER_USER_RANGE);
+ private LauncherModelHelper mModelHelper;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mModelHelper = new LauncherModelHelper();
+ mContext = mModelHelper.sandboxContext;
+ }
+
+ @After
+ public void teardown() {
+ mModelHelper.destroy();
+ }
+
@Test
public void testGetProfileId() throws Exception {
- SQLiteDatabase db = new MyDatabaseHelper(23).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(23).getDb();
assertEquals(23, new RestoreDbTask().getDefaultProfileId(db));
}
@Test
public void testMigrateProfileId() throws Exception {
- SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
@@ -89,7 +106,7 @@
@Test
public void testChangeDefaultColumn() throws Exception {
- SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
@@ -112,28 +129,27 @@
@Test
public void testSanitizeDB_bothProfiles() throws Exception {
- Context context = getInstrumentation().getTargetContext();
UserHandle myUser = myUserHandle();
- long myProfileId = context.getSystemService(UserManager.class)
+ long myProfileId = mContext.getSystemService(UserManager.class)
.getSerialNumberForUser(myUser);
long myProfileId_old = myProfileId + 1;
long workProfileId = myProfileId + 2;
long workProfileId_old = myProfileId + 3;
- MyDatabaseHelper helper = new MyDatabaseHelper(myProfileId);
- SQLiteDatabase db = helper.getWritableDatabase();
- BackupManager bm = spy(new BackupManager(context));
+ MyModelDbController controller = new MyModelDbController(myProfileId);
+ SQLiteDatabase db = controller.getDb();
+ BackupManager bm = spy(new BackupManager(mContext));
doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
doReturn(mWorkUser).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
- helper.users.put(workProfileId, mWorkUser);
+ controller.users.put(workProfileId, mWorkUser);
- addIconsBulk(helper, 10, 1, myProfileId_old);
- addIconsBulk(helper, 6, 2, workProfileId_old);
+ addIconsBulk(controller, 10, 1, myProfileId_old);
+ addIconsBulk(controller, 6, 2, workProfileId_old);
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, helper, helper.getWritableDatabase(), bm);
+ task.sanitizeDB(mContext, controller, controller.getDb(), bm);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -144,27 +160,26 @@
@Test
public void testSanitizeDB_workItemsRemoved() throws Exception {
- Context context = getInstrumentation().getTargetContext();
UserHandle myUser = myUserHandle();
- long myProfileId = context.getSystemService(UserManager.class)
+ long myProfileId = mContext.getSystemService(UserManager.class)
.getSerialNumberForUser(myUser);
long myProfileId_old = myProfileId + 1;
long workProfileId_old = myProfileId + 3;
- MyDatabaseHelper helper = new MyDatabaseHelper(myProfileId);
- SQLiteDatabase db = helper.getWritableDatabase();
- BackupManager bm = spy(new BackupManager(context));
+ MyModelDbController controller = new MyModelDbController(myProfileId);
+ SQLiteDatabase db = controller.getDb();
+ BackupManager bm = spy(new BackupManager(mContext));
doReturn(myUserHandle()).when(bm).getUserForAncestralSerialNumber(eq(myProfileId_old));
// Work profile is not migrated
doReturn(null).when(bm).getUserForAncestralSerialNumber(eq(workProfileId_old));
- addIconsBulk(helper, 10, 1, myProfileId_old);
- addIconsBulk(helper, 6, 2, workProfileId_old);
+ addIconsBulk(controller, 10, 1, myProfileId_old);
+ addIconsBulk(controller, 6, 2, workProfileId_old);
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
RestoreDbTask task = new RestoreDbTask();
- task.sanitizeDB(context, helper, helper.getWritableDatabase(), bm);
+ task.sanitizeDB(mContext, controller, controller.getDb(), bm);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -173,12 +188,13 @@
assertEquals(10, getCount(db, "select * from favorites"));
}
- private void addIconsBulk(DatabaseHelper helper, int count, int screen, long profileId) {
- int columns = LauncherAppState.getIDP(getInstrumentation().getTargetContext()).numColumns;
+ private void addIconsBulk(MyModelDbController controller,
+ int count, int screen, long profileId) {
+ int columns = LauncherAppState.getIDP(mContext).numColumns;
String packageName = getInstrumentation().getContext().getPackageName();
for (int i = 0; i < count; i++) {
ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites._ID, helper.generateNewItemId());
+ values.put(LauncherSettings.Favorites._ID, controller.generateNewItemId());
values.put(LauncherSettings.Favorites.CONTAINER, CONTAINER_DESKTOP);
values.put(LauncherSettings.Favorites.SCREEN, screen);
values.put(LauncherSettings.Favorites.CELLX, i % columns);
@@ -189,11 +205,11 @@
values.put(LauncherSettings.Favorites.ITEM_TYPE, ITEM_TYPE_APPLICATION);
values.put(LauncherSettings.Favorites.INTENT,
new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
- helper.getWritableDatabase().insert(TABLE_NAME, null, values);
+
+ controller.insert(TABLE_NAME, values);
}
}
-
@Test
public void testRemoveScreenIdGaps_firstScreenEmpty() {
runRemoveScreenIdGapsTest(
@@ -216,7 +232,7 @@
}
private void runRemoveScreenIdGapsTest(int[] screenIds, int[] expectedScreenIds) {
- SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ SQLiteDatabase db = new MyModelDbController(42).getDb();
// Add some mock data
for (int i = 0; i < screenIds.length; i++) {
ContentValues values = new ContentValues();
@@ -254,13 +270,12 @@
}
}
- private class MyDatabaseHelper extends DatabaseHelper {
+ private class MyModelDbController extends ModelDbController {
- public final LongSparseArray<UserHandle> users;
+ public final LongSparseArray<UserHandle> users = new LongSparseArray<>();
- MyDatabaseHelper(long profileId) {
- super(getInstrumentation().getTargetContext(), null, false);
- users = new LongSparseArray<>();
+ MyModelDbController(long profileId) {
+ super(mContext);
users.put(profileId, myUserHandle());
}
@@ -269,10 +284,5 @@
int index = users.indexOfValue(user);
return index >= 0 ? users.keyAt(index) : -1;
}
-
- @Override
- protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
-
- protected void onEmptyDbCreated() { }
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 3141c7b..d7c4ae3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -72,6 +72,7 @@
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.util.rule.ViewCaptureRule;
import org.junit.After;
import org.junit.Assert;
@@ -215,14 +216,15 @@
}
protected TestRule getRulesInsideActivityMonitor() {
+ final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
final RuleChain inner = RuleChain
.outerRule(new PortraitLandscapeRunner(this))
- .around(new FailureWatcher(mDevice, mLauncher));
+ .around(viewCaptureRule)
+ .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
return TestHelpers.isInLauncherProcess()
- ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
- .around(inner) :
- inner;
+ ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
+ : inner;
}
@Rule
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index bf31e39..976afcd 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -113,7 +113,7 @@
private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
private final MockContentResolver mMockResolver = new MockContentResolver();
public final TestLauncherProvider provider;
- public final SanboxModelContext sandboxContext;
+ public final SandboxModelContext sandboxContext;
public final long defaultProfileId;
@@ -128,7 +128,7 @@
Settings.Global.getString(context.getContentResolver(), "test");
provider = new TestLauncherProvider();
- sandboxContext = new SanboxModelContext();
+ sandboxContext = new SandboxModelContext();
defaultProfileId = UserCache.INSTANCE.get(sandboxContext)
.getSerialNumberForUser(Process.myUserHandle());
setupProvider(LauncherProvider.AUTHORITY, provider);
@@ -363,12 +363,6 @@
sandboxContext.getContentResolver().insert(contentUri, values);
}
- public void deleteItem(int itemId, @NonNull final String tableName) {
- final Uri uri = Uri.parse("content://"
- + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId);
- sandboxContext.getContentResolver().delete(uri, null, null);
- }
-
/**
* Sets up a mock provider to load the provided layout by default, next time the layout loads
*/
@@ -426,7 +420,7 @@
}
public SQLiteDatabase getDb() {
- return getModelDbController().getDatabaseHelper().getWritableDatabase();
+ return getModelDbController().getDb();
}
}
@@ -446,13 +440,13 @@
return success;
}
- public class SanboxModelContext extends SandboxContext {
+ public class SandboxModelContext extends SandboxContext {
private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
private final PackageManager mPm;
private final File mDbDir;
- SanboxModelContext() {
+ SandboxModelContext() {
super(ApplicationProvider.getApplicationContext(),
UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
@@ -463,7 +457,7 @@
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
}
- public SanboxModelContext allow(MainThreadInitializedObject object) {
+ public SandboxModelContext allow(MainThreadInitializedObject object) {
mAllowedObjects.add(object);
return this;
}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 51facf4..19e7b13 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -7,8 +7,12 @@
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.uiautomator.UiDevice;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -29,10 +33,14 @@
private static boolean sSavedBugreport = false;
final private UiDevice mDevice;
private final LauncherInstrumentation mLauncher;
+ @NonNull
+ private final ViewCapture mViewCapture;
- public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
+ public FailureWatcher(UiDevice device, LauncherInstrumentation launcher,
+ @NonNull ViewCapture viewCapture) {
mDevice = device;
mLauncher = launcher;
+ mViewCapture = viewCapture;
}
@Override
@@ -82,7 +90,7 @@
@Override
protected void failed(Throwable e, Description description) {
- onError(mLauncher, description, e);
+ onError(mLauncher, description, e, mViewCapture);
}
static File diagFile(Description description, String prefix, String ext) {
@@ -93,8 +101,12 @@
public static void onError(LauncherInstrumentation launcher, Description description,
Throwable e) {
- final UiDevice device = launcher.getDevice();
- if (device == null) return;
+ onError(launcher, description, e, null);
+ }
+
+ private static void onError(LauncherInstrumentation launcher, Description description,
+ Throwable e, @Nullable ViewCapture viewCapture) {
+
final File sceenshot = diagFile(description, "TestScreenshot", "png");
final File hierarchy = diagFile(description, "Hierarchy", "zip");
@@ -109,13 +121,20 @@
out.putNextEntry(new ZipEntry("visible_windows.zip"));
dumpCommand("cmd window dump-visible-window-views", out);
out.closeEntry();
- } catch (IOException ex) {
+
+ if (viewCapture != null) {
+ out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc"));
+ viewCapture.dumpTo(out, ApplicationProvider.getApplicationContext());
+ out.closeEntry();
+ }
+ } catch (Exception ignored) {
}
Log.e(TAG, "Failed test " + description.getMethodName()
+ ",\nscreenshot will be saved to " + sceenshot
+ ",\nUI dump at: " + hierarchy
+ " (use go/web-hv to open the dump file)", e);
+ final UiDevice device = launcher.getDevice();
device.takeScreenshot(sceenshot);
// Dump accessibility hierarchy
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 2093682..e9a52f8 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -56,4 +56,4 @@
return launcher.getWorkspace().getFirstMatch(op) != null;
};
}
-}
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
index 1dbba6a..2eedec3 100644
--- a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java
@@ -102,4 +102,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
new file mode 100644
index 0000000..e4713b2
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.util.rule
+
+import android.app.Activity
+import android.app.Application
+import android.media.permission.SafeCloseable
+import android.os.Bundle
+import androidx.test.core.app.ApplicationProvider
+import com.android.app.viewcapture.SimpleViewCapture
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * This JUnit TestRule registers a listener for activity lifecycle events to attach a ViewCapture
+ * instance that other test rules use to dump the timelapse hierarchy upon an error during a test.
+ *
+ * This rule will not work in OOP tests that don't have access to the activity under test.
+ */
+class ViewCaptureRule : TestRule {
+ val viewCapture = SimpleViewCapture("test-view-capture")
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ val windowListenerCloseables = mutableListOf<SafeCloseable>()
+
+ val lifecycleCallbacks =
+ object : ActivityLifecycleCallbacksAdapter {
+ override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
+ super.onActivityCreated(activity, bundle)
+ windowListenerCloseables.add(
+ viewCapture.startCapture(
+ activity.window.decorView,
+ "${description.testClass?.simpleName}.${description.methodName}"
+ )
+ )
+ }
+
+ override fun onActivityDestroyed(activity: Activity) {
+ super.onActivityDestroyed(activity)
+ viewCapture.stopCapture(activity.window.decorView)
+ }
+ }
+
+ val application = ApplicationProvider.getApplicationContext<Application>()
+ application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
+
+ try {
+ base.evaluate()
+ } finally {
+ application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
+
+ // Clean up ViewCapture references here rather than in onActivityDestroyed so
+ // test code can access view hierarchy capture. onActivityDestroyed would delete
+ // view capture data before FailureWatcher could output it as a test artifact.
+ windowListenerCloseables.onEach(SafeCloseable::close)
+ }
+ }
+ }
+ }
+}