Merge "Improve TaskMenuView layouts for split pairs" into tm-qpr-dev
diff --git a/Android.bp b/Android.bp
index 330c32e..6267e9f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -19,6 +19,54 @@
min_launcher3_sdk_version = "26"
+// Common source files used to build launcher (java and kotlin)
+// All sources are split so they can be reused in many other libraries/apps in other folders
+filegroup {
+ name: "launcher-src",
+ srcs: [ "src/**/*.java", "src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-quickstep-src",
+ srcs: [ "quickstep/src/**/*.java", "quickstep/src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-go-src",
+ srcs: [ "go/src/**/*.java", "go/src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-go-quickstep-src",
+ srcs: [ "go/quickstep/src/**/*.java", "go/quickstep/src/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-src_shortcuts_overrides",
+ srcs: [ "src_shortcuts_overrides/**/*.java", "src_shortcuts_overrides/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-src_ui_overrides",
+ srcs: [ "src_ui_overrides/**/*.java", "src_ui_overrides/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-ext_tests",
+ srcs: [ "ext_tests/**/*.java", "ext_tests/**/*.kt" ],
+}
+
+filegroup {
+ name: "launcher-quickstep-ext_tests",
+ srcs: [ "quickstep/ext_tests/**/*.java", "quickstep/ext_tests/**/*.kt" ],
+}
+
+// Proguard files for Launcher3
+filegroup {
+ name: "launcher-proguard-rules",
+ srcs: ["proguard.flags"],
+}
+
android_library {
name: "launcher-aosp-tapl",
libs: [
@@ -139,14 +187,10 @@
"Launcher3CommonDepsLib",
],
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "src_shortcuts_overrides/**/*.java",
- "src_shortcuts_overrides/**/*.kt",
- "src_ui_overrides/**/*.java",
- "src_ui_overrides/**/*.kt",
- "ext_tests/src/**/*.java",
- "ext_tests/src/**/*.kt",
+ ":launcher-src",
+ ":launcher-src_shortcuts_overrides",
+ ":launcher-src_ui_overrides",
+ ":launcher-ext_tests",
],
resource_dirs: [
"ext_tests/res",
@@ -202,61 +246,14 @@
}
-// Source code used for test helpers
-filegroup {
- name: "launcher-src-ext-tests",
- srcs: [
- "ext_tests/src/**/*.java",
- "ext_tests/src/**/*.kt",
- "quickstep/ext_tests/src/**/*.java",
- "quickstep/ext_tests/src/**/*.kt",
- ],
-}
-
-// Common source files used to build launcher
-filegroup {
- name: "launcher-src-no-build-config",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "src_shortcuts_overrides/**/*.java",
- "src_shortcuts_overrides/**/*.kt",
- "quickstep/src/**/*.java",
- "quickstep/src/**/*.kt",
- ],
-}
-
-// Common source files used to build go launcher except go/src files
-filegroup {
- name: "launcher-go-src-no-build-config",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "quickstep/src/**/*.java",
- "quickstep/src/**/*.kt",
- "go/quickstep/src/**/*.java",
- "go/quickstep/src/**/*.kt",
- ],
-}
-
-// Proguard files for Launcher3
-filegroup {
- name: "launcher-proguard-rules",
- srcs: ["proguard.flags"],
-}
-
// Library with all the dependencies for building Launcher Go
android_library {
name: "LauncherGoResLib",
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- "quickstep/src/**/*.java",
- "quickstep/src/**/*.kt",
- "go/src/**/*.java",
- "go/src/**/*.kt",
- "go/quickstep/src/**/*.java",
- "go/quickstep/src/**/*.kt",
+ ":launcher-src",
+ ":launcher-quickstep-src",
+ ":launcher-go-src",
+ ":launcher-go-quickstep-src",
],
resource_dirs: [
"go/res",
@@ -287,7 +284,9 @@
android_library {
name: "Launcher3QuickStepLib",
srcs: [
- ":launcher-src-no-build-config",
+ ":launcher-src",
+ ":launcher-quickstep-src",
+ ":launcher-src_shortcuts_overrides",
],
resource_dirs: [],
libs: [
@@ -319,9 +318,9 @@
static_libs: ["Launcher3CommonDepsLib"],
srcs: [
- "src/**/*.java",
- "src_ui_overrides/**/*.java",
- "go/src/**/*.java",
+ ":launcher-src",
+ ":launcher-go-src",
+ ":launcher-src_ui_overrides",
],
resource_dirs: ["go/res"],
@@ -405,12 +404,7 @@
min_sdk_version: "current",
target_sdk_version: "current",
- srcs: [
- "src/**/*.java",
- "quickstep/src/**/*.java",
- "go/src/**/*.java",
- "go/quickstep/src/**/*.java",
- ],
+ srcs: [ ],
resource_dirs: [
"go/quickstep/res",
diff --git a/go/quickstep/res/values-my/strings.xml b/go/quickstep/res/values-my/strings.xml
index 0ca0e9c..cbb485a 100644
--- a/go/quickstep/res/values-my/strings.xml
+++ b/go/quickstep/res/values-my/strings.xml
@@ -5,7 +5,7 @@
<string name="action_listen" msgid="2370304050784689486">"နားထောင်ရန်"</string>
<string name="action_translate" msgid="8028378961867277746">"ဘာသာပြန်ရန်"</string>
<string name="action_search" msgid="6269564710943755464">"Lens"</string>
- <string name="dialog_acknowledge" msgid="2804025517675853172">"ရပြီ"</string>
+ <string name="dialog_acknowledge" msgid="2804025517675853172">"နားလည်ပြီ"</string>
<string name="dialog_cancel" msgid="6464336969134856366">"မလုပ်တော့"</string>
<string name="dialog_settings" msgid="6564397136021186148">"ဆက်တင်များ"</string>
<string name="niu_actions_confirmation_title" msgid="3863451714863526143">"ဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ပါ (သို့) နားထောင်ပါ"</string>
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 10eedc8..151ec5a 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -45,6 +45,9 @@
// Stores the origin of the Item
repeated Attribute item_attributes = 12;
+
+ // Stores whether the navigation bar is in kids mode.
+ optional bool is_kids_mode = 13;
}
message LauncherAttributes{
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index f739f81..7292c44 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -18,6 +18,11 @@
}
filegroup {
+ name: "launcher3-quickstep-manifest",
+ srcs: ["AndroidManifest.xml"],
+}
+
+filegroup {
name: "launcher3-quickstep-robolectric-src",
path: "robolectric_tests",
srcs: ["robolectric_tests/src/**/*.java"],
diff --git a/quickstep/res/layout/task_desktop.xml b/quickstep/res/layout/task_desktop.xml
new file mode 100644
index 0000000..0c8543f
--- /dev/null
+++ b/quickstep/res/layout/task_desktop.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+-->
+
+<com.android.quickstep.views.DesktopTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ android:clipToOutline="true"
+ android:defaultFocusHighlightEnabled="false"
+ android:focusable="true">
+
+ <!--
+ TODO(b249371338): DesktopTaskView extends from TaskView. TaskView expects TaskThumbnailView
+ and IconView with these ids to be present. Need to refactor RecentsView to accept child
+ views that do not inherint from TaskView only or create a generic TaskView that have
+ N number of tasks.
+ -->
+ <com.android.quickstep.views.TaskThumbnailView
+ android:id="@+id/snapshot"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <com.android.quickstep.views.IconView
+ android:id="@+id/icon"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:visibility="gone" />
+
+</com.android.quickstep.views.DesktopTaskView>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 6ea7d8a..1789c2b 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"ነጻ ቅጽ"</string>
+ <string name="recent_task_option_freeform" msgid="48863056265284071">"ነፃ ቅጽ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index 905fbda..bc5d02a 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -80,4 +80,8 @@
<dimen name="taskbar_button_margin_6_5">219.6dp</dimen>
<dimen name="taskbar_button_margin_4_5">84dp</dimen>
<dimen name="taskbar_button_margin_4_4">79dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">48dp</dimen>
+ <dimen name="taskbar_suw_frame">96dp</dimen>
+ <dimen name="taskbar_suw_insets">24dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 765d36c..3add4dc 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -258,7 +258,10 @@
<dimen name="taskbar_contextual_button_padding">16dp</dimen>
<dimen name="taskbar_contextual_padding_top">8dp</dimen>
<dimen name="taskbar_nav_buttons_size">44dp</dimen>
- <dimen name="taskbar_contextual_button_margin">48dp</dimen>
+ <dimen name="taskbar_split_instructions_margin">48dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">120dp</dimen>
+ <dimen name="taskbar_suw_insets">48dp</dimen>
+ <dimen name="taskbar_suw_frame">48dp</dimen>
<dimen name="taskbar_hotseat_nav_spacing">24dp</dimen>
<dimen name="taskbar_contextual_buttons_size">35dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
@@ -284,4 +287,9 @@
<dimen name="taskbar_button_margin_4_5">47dp</dimen>
<dimen name="taskbar_button_margin_4_4">47dp</dimen>
<dimen name="taskbar_button_margin_default">47dp</dimen>
+
+ <!-- Launcher splash screen -->
+ <!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
+ <!-- starting_surface_exit_animation_window_shift_length -->
+ <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 9a1ed4d..8239d5e 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -98,6 +98,7 @@
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -122,6 +123,8 @@
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.WorkspaceRevealAnim;
import com.android.quickstep.views.FloatingWidgetView;
@@ -134,7 +137,6 @@
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.util.ArrayList;
@@ -551,7 +553,8 @@
final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
if (scrimEnabled) {
- boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps;
+ boolean useTaskbarColor = mDeviceProfile.isTaskbarPresentInApps
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get();
int scrimColor = useTaskbarColor
? mLauncher.getResources().getColor(R.color.taskbar_background)
: Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
@@ -813,10 +816,11 @@
return;
}
- ArrayList<SurfaceParams> params = new ArrayList<>();
+ SurfaceTransaction transaction = new SurfaceTransaction();
+
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
@@ -837,11 +841,11 @@
floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f - mIconAlpha.value)
- .withCornerRadius(mWindowRadius.value)
- .withShadowRadius(mShadowRadius.value);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f - mIconAlpha.value)
+ .setCornerRadius(mWindowRadius.value)
+ .setShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_CLOSING) {
if (target.localBounds != null) {
final Rect localBounds = target.localBounds;
@@ -861,29 +865,26 @@
tmpPos.y = tmp;
}
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f);
}
- params.add(builder.build());
}
if (navBarTarget != null) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
matrix.setScale(scale, scale);
matrix.postTranslate(windowTransX0, windowTransY0);
- navBuilder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- params.add(navBuilder.build());
}
-
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ surfaceApplier.scheduleApply(transaction);
}
};
appAnimator.addUpdateListener(listener);
@@ -999,37 +1000,33 @@
matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
widgetBackgroundBounds.top);
- ArrayList<SurfaceParams> params = new ArrayList<>();
+ SurfaceTransaction transaction = new SurfaceTransaction();
float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.mode == MODE_OPENING) {
floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
mCornerRadiusProgress.value);
- builder.withMatrix(matrix)
- .withWindowCrop(appWindowCrop)
- .withAlpha(mPreviewAlpha.value)
- .withCornerRadius(mWindowRadius.value / mAppWindowScale);
+ builder.setMatrix(matrix)
+ .setWindowCrop(appWindowCrop)
+ .setAlpha(mPreviewAlpha.value)
+ .setCornerRadius(mWindowRadius.value / mAppWindowScale);
}
- params.add(builder.build());
}
if (navBarTarget != null) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+ SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
- navBuilder.withMatrix(matrix)
- .withWindowCrop(appWindowCrop)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(matrix)
+ .setWindowCrop(appWindowCrop)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- params.add(navBuilder.build());
}
-
- surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+ surfaceApplier.scheduleApply(transaction);
}
});
@@ -1224,16 +1221,15 @@
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- params[i] = new SurfaceParams.Builder(target.leash)
- .withAlpha(1f)
- .withWindowCrop(target.screenSpaceBounds)
- .withCornerRadius(cornerRadius)
- .build();
+ transaction.forSurface(target.leash)
+ .setAlpha(1f)
+ .setWindowCrop(target.screenSpaceBounds)
+ .setCornerRadius(cornerRadius);
}
- surfaceApplier.scheduleApply(params);
+ surfaceApplier.scheduleApply(transaction);
}
});
return unlockAnimator;
@@ -1451,10 +1447,10 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- SurfaceParams[] params = new SurfaceParams[appTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1476,20 +1472,19 @@
tmpRect.centerY());
matrix.postTranslate(0, mDy.value);
matrix.postTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(mAlpha.value)
- .withCornerRadius(windowCornerRadius)
- .withShadowRadius(mShadowRadius.value);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(mAlpha.value)
+ .setCornerRadius(windowCornerRadius)
+ .setShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_OPENING) {
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.withMatrix(matrix)
- .withWindowCrop(crop)
- .withAlpha(1f);
+ builder.setMatrix(matrix)
+ .setWindowCrop(crop)
+ .setAlpha(1f);
}
- params[i] = builder.build();
}
- surfaceApplier.scheduleApply(params);
+ surfaceApplier.scheduleApply(transaction);
}
});
@@ -1859,10 +1854,10 @@
@Override
public void onUpdate(RectF currentRectF, float progress) {
- SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
+ SurfaceTransaction transaction = new SurfaceTransaction();
for (int i = mAppTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = mAppTargets[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+ SurfaceProperties builder = transaction.forSurface(target.leash);
if (target.localBounds != null) {
mTmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1897,18 +1892,17 @@
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpRect)
- .withAlpha(getWindowAlpha(progress))
- .withCornerRadius(getCornerRadius(progress) / scale);
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpRect)
+ .setAlpha(getWindowAlpha(progress))
+ .setCornerRadius(getCornerRadius(progress) / scale);
} else if (target.mode == MODE_OPENING) {
mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
- builder.withMatrix(mMatrix)
- .withAlpha(1f);
+ builder.setMatrix(mMatrix)
+ .setAlpha(1f);
}
- params[i] = builder.build();
}
- mSurfaceApplier.scheduleApply(params);
+ mSurfaceApplier.scheduleApply(transaction);
}
protected float getWindowAlpha(float progress) {
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 2100834..80bdb6f 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -38,6 +38,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.views.AbstractSlideInView;
@@ -192,7 +193,7 @@
icon.setEnabled(false);
icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
icon.verifyHighRes();
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1);
mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
}
}
diff --git a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
index 5bf727a..8720bd8 100644
--- a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
+++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
@@ -18,7 +18,9 @@
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
import android.content.Context;
+import android.view.View;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.appprediction.AppsDividerView;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.model.BgDataModel;
@@ -39,10 +41,13 @@
@Override
void updateAppDivider() {
OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs();
- mActivityContext.getAppsView().getFloatingHeaderView()
- .findFixedRowByType(AppsDividerView.class)
- .setShowAllAppsLabel(!onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
- onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+ if (onboardingPrefs != null) {
+ mActivityContext.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(AppsDividerView.class)
+ .setShowAllAppsLabel(
+ !onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
+ onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
+ }
}
@Override
@@ -51,4 +56,12 @@
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(item.items);
}
+
+ @Override
+ public void setLongClickListener(ActivityAllAppsContainerView<?> appsView,
+ View.OnLongClickListener onIconLongClickListener) {
+ appsView.getFloatingHeaderView()
+ .findFixedRowByType(PredictionRowView.class)
+ .setOnIconLongClickListener(onIconLongClickListener);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index e3fd3f9..2a78bdf 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -23,7 +23,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
import android.view.CrossWindowBlurListeners;
import android.view.View;
import android.view.ViewRootImpl;
@@ -32,7 +31,6 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
@@ -47,43 +45,12 @@
public class DepthController extends BaseDepthController implements StateHandler<LauncherState>,
BaseActivity.MultiWindowModeChangedListener {
- /**
- * A property that updates the background blur within a given range of values (ie. even if the
- * animator goes beyond 0..1, the interpolated value will still be bounded).
- */
- public static class ClampedDepthProperty extends FloatProperty<DepthController> {
- private final float mMinValue;
- private final float mMaxValue;
-
- public ClampedDepthProperty(float minValue, float maxValue) {
- super("depthClamped");
- mMinValue = minValue;
- mMaxValue = maxValue;
- }
-
- @Override
- public void setValue(DepthController depthController, float depth) {
- depthController.setDepth(Utilities.boundToRange(depth, mMinValue, mMaxValue));
- }
-
- @Override
- public Float get(DepthController depthController) {
- return depthController.mDepth;
- }
- }
-
private final ViewTreeObserver.OnDrawListener mOnDrawListener = this::onLauncherDraw;
private final Consumer<Boolean> mCrossWindowBlurListener = this::setCrossWindowBlursEnabled;
private final Runnable mOpaquenessListener = this::applyDepthAndBlur;
- /**
- * If we're launching and app and should not be blurring the screen for performance reasons.
- */
- private boolean mBlurDisabledForAppLaunch;
-
-
// Workaround for animating the depth when multiwindow mode changes.
private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
@@ -145,12 +112,8 @@
return;
}
- float toDepth = toState.getDepth(mLauncher);
- if (Float.compare(mDepth, toDepth) != 0) {
- setDepth(toDepth);
- } else if (toState == LauncherState.OVERVIEW) {
- applyDepthAndBlur();
- } else if (toState == LauncherState.BACKGROUND_APP) {
+ STATE_DEPTH.set(this, toState.getDepth(mLauncher));
+ if (toState == LauncherState.BACKGROUND_APP) {
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
}
@@ -164,10 +127,7 @@
}
float toDepth = toState.getDepth(mLauncher);
- if (Float.compare(mDepth, toDepth) != 0) {
- animation.setFloat(this, STATE_DEPTH, toDepth,
- config.getInterpolator(ANIM_DEPTH, LINEAR));
- }
+ animation.setFloat(this, STATE_DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR));
}
@Override
@@ -198,9 +158,9 @@
writer.println(prefix + "\tmMaxBlurRadius=" + mMaxBlurRadius);
writer.println(prefix + "\tmCrossWindowBlursEnabled=" + mCrossWindowBlursEnabled);
writer.println(prefix + "\tmSurface=" + mSurface);
- writer.println(prefix + "\tmDepth=" + mDepth);
+ writer.println(prefix + "\tmStateDepth=" + STATE_DEPTH.get(this));
+ writer.println(prefix + "\tmWidgetDepth=" + WIDGET_DEPTH.get(this));
writer.println(prefix + "\tmCurrentBlur=" + mCurrentBlur);
- writer.println(prefix + "\tmBlurDisabledForAppLaunch=" + mBlurDisabledForAppLaunch);
writer.println(prefix + "\tmInEarlyWakeUp=" + mInEarlyWakeUp);
writer.println(prefix + "\tmIgnoreStateChangesDuringMultiWindowAnimation="
+ mIgnoreStateChangesDuringMultiWindowAnimation);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 5178968..555cd65 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -173,7 +173,7 @@
}
}
- if (ENABLE_SHELL_TRANSITIONS
+ if (ENABLE_SHELL_TRANSITIONS && isResumed
&& !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
// Launcher is resumed, but in a state where taskbar is still independent, so
// ignore the state change.
@@ -276,13 +276,6 @@
&& !mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN);
}
- /**
- * Manually ends the taskbar education flow.
- */
- public void hideEdu() {
- mControllers.taskbarEduController.hideEdu();
- }
-
@Override
public void onTaskbarIconLaunched(ItemInfo item) {
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
@@ -293,8 +286,6 @@
@Override
public void setSystemGestureInProgress(boolean inProgress) {
super.setSystemGestureInProgress(inProgress);
- // TODO(b/250645563): Don't show round corners when leaving in-app state, and remove
- // forceHideBackground call entirely.
if (!FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// Launcher's ScrimView will draw the background throughout the gesture. But once the
// gesture ends, start drawing taskbar's background again since launcher might stop
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 725ce11..af422cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
-import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
@@ -54,6 +53,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
@@ -81,6 +81,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AlphaUpdateListener;
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory;
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter;
+import com.android.launcher3.util.DimensionUtils;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -181,6 +184,7 @@
private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer =
this::onComputeInsetsForSeparateWindow;
private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
+ private View mRecentsButton;
public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
mContext = context;
@@ -203,9 +207,11 @@
boolean isThreeButtonNav = mContext.isThreeButtonNav();
DeviceProfile deviceProfile = mContext.getDeviceProfile();
Resources resources = mContext.getResources();
- mNavButtonsView.getLayoutParams().height = !isPhoneMode(deviceProfile) ?
- deviceProfile.taskbarSize :
- resources.getDimensionPixelSize(R.dimen.taskbar_size);
+ Point p = !mContext.isUserSetupComplete()
+ ? new Point(0, resources.getDimensionPixelSize(R.dimen.taskbar_suw_frame))
+ : DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+ TaskbarManager.isPhoneMode(deviceProfile));
+ mNavButtonsView.getLayoutParams().height = p.y;
mIsImeRenderingNavButtons =
InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
@@ -266,89 +272,6 @@
mControllers.navButtonController);
updateButtonLayoutSpacing();
updateStateForFlag(FLAG_SMALL_SCREEN, isPhoneButtonNavMode(mContext));
- if (isInSetup) {
- // Since setup wizard only has back button enabled, it looks strange to be
- // end-aligned, so start-align instead.
- FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
- mNavButtonContainer.getLayoutParams();
- navButtonsLayoutParams.setMarginStart(
- resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin));
- navButtonsLayoutParams.setMarginEnd(0);
- navButtonsLayoutParams.gravity = Gravity.START;
- mNavButtonContainer.requestLayout();
-
- // Hide back button in SUW if keyboard is showing (IME draws its own back).
- mPropertyHolders.add(new StatePropertyHolder(
- mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW),
- flags -> (flags & FLAG_IME_VISIBLE) == 0));
-
- // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
- // it based on dark theme for now.
- int mode = resources.getConfiguration().uiMode
- & Configuration.UI_MODE_NIGHT_MASK;
- boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
- mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
- } else if (isInKidsMode) {
- int iconSize = resources.getDimensionPixelSize(
- R.dimen.taskbar_icon_size_kids);
- int buttonWidth = resources.getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_width_kids);
- int buttonHeight = resources.getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_height_kids);
- int buttonRadius = resources.getDimensionPixelSize(
- R.dimen.taskbar_nav_buttons_corner_radius_kids);
- int paddingleft = (buttonWidth - iconSize) / 2;
- int paddingRight = paddingleft;
- int paddingTop = (buttonHeight - iconSize) / 2;
- int paddingBottom = paddingTop;
-
- // Update icons
- ((ImageView) mBackButton).setImageDrawable(
- mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
- ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
- mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
- ((ImageView) mHomeButton).setImageDrawable(
- mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
- ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
- mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
-
- // Home button layout
- LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
- buttonWidth,
- buttonHeight
- );
- int homeButtonLeftMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_home_button_left_margin_kids);
- homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
- mHomeButton.setLayoutParams(homeLayoutparams);
-
- // Back button layout
- LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
- buttonWidth,
- buttonHeight
- );
- int backButtonLeftMargin = resources.getDimensionPixelSize(
- R.dimen.taskbar_back_button_left_margin_kids);
- backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
- mBackButton.setLayoutParams(backLayoutParams);
-
- // Button backgrounds
- int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
- PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
- buttonBackground.setCornerRadius(buttonRadius);
- mHomeButton.setBackground(buttonBackground);
- mBackButton.setBackground(buttonBackground);
-
- // Update alignment within taskbar
- FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
- mNavButtonContainer.getLayoutParams();
- navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd() / 2);
- navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
- navButtonsLayoutParams.gravity = Gravity.CENTER;
- mNavButtonContainer.requestLayout();
-
- mHomeButton.setOnLongClickListener(null);
- }
// Animate taskbar background when either..
// notification shade expanded AND not on keyguard
@@ -451,20 +374,20 @@
(flags & FLAG_DISABLE_HOME) == 0));
// Recents button
- View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
+ mRecentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
navContainer, navButtonController, R.id.recent_apps);
- mHitboxExtender.init(recentsButton, mNavButtonsView, mContext.getDeviceProfile(),
+ mHitboxExtender.init(mRecentsButton, mNavButtonsView, mContext.getDeviceProfile(),
() -> {
float[] recentsCoords = new float[2];
- getDescendantCoordRelativeToAncestor(recentsButton, mNavButtonsView,
+ getDescendantCoordRelativeToAncestor(mRecentsButton, mNavButtonsView,
recentsCoords, false);
return recentsCoords;
}, new Handler());
- recentsButton.setOnClickListener(v -> {
+ mRecentsButton.setOnClickListener(v -> {
navButtonController.onButtonClick(BUTTON_RECENTS, v);
mHitboxExtender.onRecentsButtonClicked();
});
- mPropertyHolders.add(new StatePropertyHolder(recentsButton,
+ mPropertyHolders.add(new StatePropertyHolder(mRecentsButton,
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0
&& !mContext.isNavBarKidsModeActive()));
@@ -745,24 +668,56 @@
if (mFloatingRotationButton != null) {
mFloatingRotationButton.onConfigurationChanged(configChanges);
}
+ if (!mContext.isUserSetupComplete()) {
+ handleSetupUi();
+ }
updateButtonLayoutSpacing();
}
+ private void handleSetupUi() {
+ // Since setup wizard only has back button enabled, it looks strange to be
+ // end-aligned, so start-align instead.
+ FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+ mNavButtonContainer.getLayoutParams();
+ Resources resources = mContext.getResources();
+ DeviceProfile deviceProfile = mContext.getDeviceProfile();
+ int setupMargin = resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin);
+ navButtonsLayoutParams.setMarginStart(setupMargin);
+ navButtonsLayoutParams.bottomMargin = !deviceProfile.isLandscape
+ ? 0
+ : setupMargin -
+ (resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2);
+ navButtonsLayoutParams.setMarginEnd(0);
+ navButtonsLayoutParams.gravity = Gravity.START;
+ mNavButtonContainer.setLayoutParams(navButtonsLayoutParams);
+ mNavButtonContainer.requestLayout();
+ }
+
/**
- * Adds the correct spacing to 3 button nav container. No-op if using gesture nav or kids mode.
+ * Adds the correct spacing to 3 button nav container. No-op if using gesture nav, setup
+ * is incomplete, or in kids mode.
*/
private void updateButtonLayoutSpacing() {
- if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()) {
+ if (!mContext.isThreeButtonNav() || mContext.isNavBarKidsModeActive()
+ || !mContext.isUserSetupComplete()) {
return;
}
-
- if (isPhoneButtonNavMode(mContext)) {
- updatePhoneButtonSpacing();
- return;
- }
-
DeviceProfile dp = mContext.getDeviceProfile();
Resources res = mContext.getResources();
+ boolean isInSetup = !mContext.isUserSetupComplete();
+ // TODO(b/244231596) we're getting the incorrect kidsMode value in small-screen
+ boolean isInKidsMode = mContext.isNavBarKidsModeActive();
+
+ if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
+ boolean isThreeButtonNav = mContext.isThreeButtonNav();
+
+ NavButtonLayoutter navButtonLayoutter =
+ NavButtonLayoutFactory.Companion.getUiLayoutter(
+ dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav,
+ TaskbarManager.isPhoneMode(dp));
+ navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing());
+ return;
+ }
// Add spacing after the end of the last nav button
FrameLayout.LayoutParams navButtonParams =
@@ -798,38 +753,84 @@
buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
}
}
- }
- /** Uniformly spaces out the 3 button nav for smaller phone screens */
- private void updatePhoneButtonSpacing() {
- DeviceProfile dp = mContext.getDeviceProfile();
- Resources res = mContext.getResources();
+ if (isInSetup) {
+ handleSetupUi();
- // TODO: Polish pending, this is just to make it usable
- FrameLayout.LayoutParams navContainerParams =
- (FrameLayout.LayoutParams) mNavButtonContainer.getLayoutParams();
- int endStartMargins = res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size);
- navContainerParams.gravity = Gravity.CENTER;
- navContainerParams.setMarginEnd(endStartMargins);
- navContainerParams.setMarginStart(endStartMargins);
- mNavButtonContainer.setLayoutParams(navContainerParams);
+ // Hide back button in SUW if keyboard is showing (IME draws its own back).
+ mPropertyHolders.add(new StatePropertyHolder(
+ mBackButtonAlpha.getProperty(ALPHA_INDEX_SUW),
+ flags -> (flags & FLAG_IME_VISIBLE) == 0));
- // Add the spaces in between the nav buttons
- int spaceInBetween = res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone);
- for (int i = 0; i < mNavButtonContainer.getChildCount(); i++) {
- View navButton = mNavButtonContainer.getChildAt(i);
- LinearLayout.LayoutParams buttonLayoutParams =
- (LinearLayout.LayoutParams) navButton.getLayoutParams();
- buttonLayoutParams.weight = 1;
- if (i == 0) {
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- } else if (i == mNavButtonContainer.getChildCount() - 1) {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- } else {
- buttonLayoutParams.setMarginStart(spaceInBetween / 2);
- buttonLayoutParams.setMarginEnd(spaceInBetween / 2);
- }
+ // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
+ // it based on dark theme for now.
+ int mode = res.getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
+ mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
+ } else if (isInKidsMode) {
+ int iconSize = res.getDimensionPixelSize(
+ R.dimen.taskbar_icon_size_kids);
+ int buttonWidth = res.getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_width_kids);
+ int buttonHeight = res.getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_height_kids);
+ int buttonRadius = res.getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_corner_radius_kids);
+ int paddingleft = (buttonWidth - iconSize) / 2;
+ int paddingRight = paddingleft;
+ int paddingTop = (buttonHeight - iconSize) / 2;
+ int paddingBottom = paddingTop;
+
+ // Update icons
+ ((ImageView) mBackButton).setImageDrawable(
+ mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
+ ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+ ((ImageView) mHomeButton).setImageDrawable(
+ mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
+ ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+
+ // Home button layout
+ LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ );
+ int homeButtonLeftMargin = res.getDimensionPixelSize(
+ R.dimen.taskbar_home_button_left_margin_kids);
+ homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
+ mHomeButton.setLayoutParams(homeLayoutparams);
+
+ // Back button layout
+ LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ );
+ int backButtonLeftMargin = res.getDimensionPixelSize(
+ R.dimen.taskbar_back_button_left_margin_kids);
+ backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
+ mBackButton.setLayoutParams(backLayoutParams);
+
+ // Button backgrounds
+ int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
+ PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
+ buttonBackground.setCornerRadius(buttonRadius);
+ mHomeButton.setBackground(buttonBackground);
+ mBackButton.setBackground(buttonBackground);
+
+ // Update alignment within taskbar
+ FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+ mNavButtonContainer.getLayoutParams();
+ navButtonsLayoutParams.setMarginStart(
+ navButtonsLayoutParams.getMarginEnd() / 2);
+ navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
+ navButtonsLayoutParams.gravity = Gravity.CENTER;
+ mNavButtonContainer.requestLayout();
+
+ mHomeButton.setOnLongClickListener(null);
}
+
}
public void onDestroy() {
@@ -841,6 +842,8 @@
moveNavButtonsBackToTaskbarWindow();
mNavButtonContainer.removeAllViews();
+ mEndContextualContainer.removeAllViews();
+ mStartContextualContainer.removeAllViews();
mAllButtons.clear();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 6b67b50..e23e27e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -101,8 +101,8 @@
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
} else {
mStashedHandleView.getLayoutParams().height = deviceProfile.taskbarSize;
- mStashedHandleWidth =
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
+ mStashedHandleWidth = resources
+ .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
}
mTaskbarStashedHandleAlpha.getProperty(ALPHA_INDEX_STASHED).setValue(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9d15ea8..4c5e0be 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -76,6 +76,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
@@ -89,6 +90,7 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
import java.io.PrintWriter;
@@ -148,11 +150,14 @@
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(this);
+ mIsUserSetupComplete = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
- mIsNavBarForceVisible = SettingsCache.INSTANCE.get(this).getValue(
+ mIsNavBarForceVisible = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
- mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
+ // TODO(b/244231596) For shared Taskbar window, update this value in init() instead so
+ // to get correct value when recreating the taskbar
+ mIsNavBarKidsMode = settingsCache.getValue(
Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
updateIconSize(resources);
@@ -197,7 +202,9 @@
new TaskbarViewController(this, taskbarView),
new TaskbarScrimViewController(this, taskbarScrimView),
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
- mWindowManager, WindowManagerGlobal.getWindowManagerService()),
+ mWindowManager,
+ new RotationChangeProvider(WindowManagerGlobal.getWindowManagerService(), this,
+ getMainExecutor())),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
@@ -205,7 +212,8 @@
new TaskbarAutohideSuspendController(this),
new TaskbarPopupController(this),
new TaskbarForceVisibleImmersiveController(this),
- new TaskbarAllAppsController(this, dp),
+ new TaskbarOverlayController(this, dp),
+ new TaskbarAllAppsController(),
new TaskbarInsetsController(this),
new VoiceInteractionWindowController(this),
isDesktopMode
@@ -237,7 +245,7 @@
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
public void updateDeviceProfile(DeviceProfile dp, NavigationMode navMode) {
mNavMode = navMode;
- mControllers.taskbarAllAppsController.updateDeviceProfile(dp);
+ mControllers.taskbarOverlayController.updateDeviceProfile(dp);
mDeviceProfile = dp.copy(this);
updateIconSize(getResources());
@@ -272,9 +280,13 @@
* @param type The window type to pass to the created WindowManager.LayoutParams.
*/
public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type) {
+ DeviceProfile deviceProfile = getDeviceProfile();
+ // Taskbar is on the logical bottom of the screen
+ boolean isVerticalBarLayout = TaskbarManager.isPhoneMode(deviceProfile) &&
+ deviceProfile.isLandscape;
WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams(
- MATCH_PARENT,
- mLastRequestedNonFullscreenHeight,
+ isVerticalBarLayout ? mLastRequestedNonFullscreenHeight : MATCH_PARENT,
+ isVerticalBarLayout ? MATCH_PARENT : mLastRequestedNonFullscreenHeight,
type,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SLIPPERY
@@ -282,7 +294,10 @@
PixelFormat.TRANSLUCENT);
windowLayoutParams.setTitle(WINDOW_TITLE);
windowLayoutParams.packageName = getPackageName();
- windowLayoutParams.gravity = Gravity.BOTTOM;
+ windowLayoutParams.gravity = !isVerticalBarLayout ?
+ Gravity.BOTTOM :
+ Gravity.END; // TODO(b/230394142): seascape
+
windowLayoutParams.setFitInsetsTypes(0);
windowLayoutParams.receiveInsetsIgnoringZOrder = true;
windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -614,6 +629,10 @@
resources.getDimensionPixelSize(R.dimen.taskbar_size) :
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
+
+ if (!isUserSetupComplete()) {
+ return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
+ }
return mDeviceProfile.taskbarSize + Math.max(getLeftCornerRadius(), getRightCornerRadius());
}
@@ -795,7 +814,7 @@
return mIsUserSetupComplete;
}
- protected boolean isNavBarKidsModeActive() {
+ public boolean isNavBarKidsModeActive() {
return mIsNavBarKidsMode && isThreeButtonNav();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 707023b..9c2d21e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -22,6 +22,7 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.systemui.shared.rotation.RotationButtonController;
import java.io.PrintWriter;
@@ -54,6 +55,7 @@
public final TaskbarInsetsController taskbarInsetsController;
public final VoiceInteractionWindowController voiceInteractionWindowController;
public final TaskbarRecentAppsController taskbarRecentAppsController;
+ public final TaskbarOverlayController taskbarOverlayController;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -81,6 +83,7 @@
TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
TaskbarPopupController taskbarPopupController,
TaskbarForceVisibleImmersiveController taskbarForceVisibleImmersiveController,
+ TaskbarOverlayController taskbarOverlayController,
TaskbarAllAppsController taskbarAllAppsController,
TaskbarInsetsController taskbarInsetsController,
VoiceInteractionWindowController voiceInteractionWindowController,
@@ -101,6 +104,7 @@
this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
this.taskbarPopupController = taskbarPopupController;
this.taskbarForceVisibleImmersiveController = taskbarForceVisibleImmersiveController;
+ this.taskbarOverlayController = taskbarOverlayController;
this.taskbarAllAppsController = taskbarAllAppsController;
this.taskbarInsetsController = taskbarInsetsController;
this.voiceInteractionWindowController = voiceInteractionWindowController;
@@ -129,6 +133,7 @@
taskbarEduController.init(this);
taskbarPopupController.init(this);
taskbarForceVisibleImmersiveController.init(this);
+ taskbarOverlayController.init(this);
taskbarAllAppsController.init(this, sharedState.allAppsVisible);
navButtonController.init(this);
taskbarInsetsController.init(this);
@@ -179,7 +184,7 @@
taskbarAutohideSuspendController.onDestroy();
taskbarPopupController.onDestroy();
taskbarForceVisibleImmersiveController.onDestroy();
- taskbarAllAppsController.onDestroy();
+ taskbarOverlayController.onDestroy();
navButtonController.onDestroy();
taskbarInsetsController.onDestroy();
voiceInteractionWindowController.onDestroy();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 025fe7a..353f1e0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -16,11 +16,13 @@
package com.android.launcher3.taskbar;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.view.ViewTreeObserver;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.util.DimensionUtils;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
@@ -177,9 +179,12 @@
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
if (TaskbarManager.isPhoneMode(deviceProfile)) {
Resources resources = mActivity.getResources();
- return mActivity.isThreeButtonNav() ?
- resources.getDimensionPixelSize(R.dimen.taskbar_size) :
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
+ Point taskbarDimensions =
+ DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
+ TaskbarManager.isPhoneMode(deviceProfile));
+ return taskbarDimensions.y == -1 ?
+ deviceProfile.getDisplayInfo().currentSize.y :
+ taskbarDimensions.y;
} else {
return deviceProfile.taskbarSize;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 95b93fe..16cc0ac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import java.io.PrintWriter;
@@ -87,8 +88,10 @@
void showEdu() {
mActivity.setTaskbarWindowFullscreen(true);
mActivity.getDragLayer().post(() -> {
- mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
- R.layout.taskbar_edu, mActivity.getDragLayer(), false);
+ TaskbarOverlayContext overlayContext =
+ mControllers.taskbarOverlayController.requestWindow();
+ mTaskbarEduView = (TaskbarEduView) overlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_edu, overlayContext.getDragLayer(), false);
mTaskbarEduView.init(new TaskbarEduCallbacks());
mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
mTaskbarEduView.setOnCloseBeginListener(
@@ -99,12 +102,6 @@
});
}
- void hideEdu() {
- if (mTaskbarEduView != null) {
- mTaskbarEduView.close(true /* animate */);
- }
- }
-
/**
* Starts the given animation, ending the previous animation first if it's still playing.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
index c0cbbd6..bb87f48 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduView.java
@@ -28,10 +28,11 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
/** Education view about the Taskbar. */
-public class TaskbarEduView extends AbstractSlideInView<TaskbarActivityContext>
+public class TaskbarEduView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable {
private static final int DEFAULT_OPEN_DURATION = 500;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 079e8a1..32c1972 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -29,11 +29,11 @@
import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
import com.android.launcher3.AbstractFloatingView
-import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
+import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
import com.android.launcher3.anim.AlphaUpdateListener
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
-import com.android.quickstep.KtR
import java.io.PrintWriter
/**
@@ -42,8 +42,7 @@
class TaskbarInsetsController(val context: TaskbarActivityContext): LoggableTaskbarController {
/** The bottom insets taskbar provides to the IME when IME is visible. */
- val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(
- KtR.dimen.taskbar_ime_size)
+ val taskbarHeightForIme: Int = context.resources.getDimensionPixelSize(R.dimen.taskbar_ime_size)
private val touchableRegion: Region = Region()
private val deviceProfileChangeListener = { _: DeviceProfile ->
onTaskbarWindowHeightOrInsetsChanged()
@@ -84,16 +83,16 @@
val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
for (provider in windowLayoutParams.providedInsets) {
if (provider.type == ITYPE_EXTRA_NAVIGATION_BAR) {
- provider.insetsSize = Insets.of(0, 0, 0, contentHeight)
+ provider.insetsSize = getInsetsByNavMode(contentHeight)
} else if (provider.type == ITYPE_BOTTOM_TAPPABLE_ELEMENT
|| provider.type == ITYPE_BOTTOM_MANDATORY_GESTURES) {
- provider.insetsSize = Insets.of(0, 0, 0, tappableHeight)
+ provider.insetsSize = getInsetsByNavMode(tappableHeight)
}
}
- val imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme)
+ val imeInsetsSize = getInsetsByNavMode(taskbarHeightForIme)
// Use 0 insets for the VoiceInteractionWindow (assistant) when gesture nav is enabled.
- val visInsetsSize = Insets.of(0, 0, 0, if (context.isGestureNav) 0 else tappableHeight)
+ val visInsetsSize = getInsetsByNavMode(if (context.isGestureNav) 0 else tappableHeight)
val insetsSizeOverride = arrayOf(
InsetsFrameProvider.InsetsSizeOverride(
TYPE_INPUT_METHOD,
@@ -110,6 +109,21 @@
}
/**
+ * @return [Insets] where the [bottomInset] is either used as a bottom inset or
+ * right/left inset if using 3 button nav
+ */
+ private fun getInsetsByNavMode(bottomInset: Int) : Insets {
+ val devicePortrait = !context.deviceProfile.isLandscape
+ if (!TaskbarManager.isPhoneButtonNavMode(context) || devicePortrait) {
+ // Taskbar or portrait phone mode
+ return Insets.of(0, 0, 0, bottomInset)
+ }
+
+ // TODO(b/230394142): seascape
+ return Insets.of(0, 0, bottomInset, 0)
+ }
+
+ /**
* Sets {@param providesInsetsTypes} as the inset types provided by {@param params}.
* @param params The window layout params.
* @param providesInsetsTypes The inset types we would like this layout params to provide.
@@ -143,8 +157,11 @@
} else if (controllers.taskbarDragController.isSystemDragInProgress) {
// Let touches pass through us.
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
- } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_ALL_APPS)) {
- // Let touches pass through us.
+ } else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_OVERLAY_PROXY)) {
+ // Let touches pass through us if icons are hidden.
+ if (controllers.taskbarViewController.areIconsVisible()) {
+ insetsInfo.touchableRegion.set(touchableRegion)
+ }
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.taskbarViewController.areIconsVisible()
|| AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index b9bcf1b..c5e1b8f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -242,6 +242,7 @@
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
+ && isInApp()
&& (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests);
}
@@ -354,6 +355,11 @@
}
return mStashedHeight;
}
+
+ if (!mActivity.isUserSetupComplete()) {
+ // Special insets for SUW.
+ return mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_suw_insets);
+ }
return mUnstashedHeight;
}
@@ -709,8 +715,16 @@
applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
}
+ /**
+ * We stash when IME or IME switcher is showing AND NOT
+ * * in small screen AND
+ * * 3 button nav AND
+ * * landscape (or seascape)
+ */
private boolean shouldStashForIme() {
- return mIsImeShowing || mIsImeSwitcherShowing;
+ return (mIsImeShowing || mIsImeSwitcherShowing) &&
+ !(isPhoneMode() && mActivity.isThreeButtonNav()
+ && mActivity.getDeviceProfile().isLandscape);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 114bfec..49dba95 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -80,10 +80,10 @@
}
/**
- * Manually closes the all apps window.
+ * Manually closes the overlay window.
*/
- public void hideAllApps() {
- mControllers.taskbarAllAppsController.hide();
+ public void hideOverlayWindow() {
+ mControllers.taskbarOverlayController.hideWindow();
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
index 64a4fa7..4c937a7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java
@@ -15,13 +15,13 @@
*/
package com.android.launcher3.taskbar;
-import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -41,16 +41,20 @@
public TaskbarUnfoldAnimationController(BaseTaskbarContext context,
ScopedUnfoldTransitionProgressProvider source,
- WindowManager windowManager, IWindowManager iWindowManager) {
+ WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mScopedUnfoldTransitionProgressProvider = source;
mNaturalUnfoldTransitionProgressProvider =
- new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source);
+ new NaturalRotationUnfoldProgressProvider(context,
+ rotationChangeProvider,
+ source);
mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
}
/**
* Initializes the controller
+ *
* @param taskbarControllers references to all other taskbar controllers
*/
public void init(TaskbarControllers taskbarControllers) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
index 51fa4d9..e41c75f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java
@@ -20,14 +20,11 @@
import android.view.WindowInsets;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.AlphabeticalAppsList;
-import com.android.launcher3.allapps.BaseAdapterProvider;
-import com.android.launcher3.allapps.BaseAllAppsAdapter;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/** All apps container accessible from taskbar. */
public class TaskbarAllAppsContainerView extends
- ActivityAllAppsContainerView<TaskbarAllAppsContext> {
+ ActivityAllAppsContainerView<TaskbarOverlayContext> {
public TaskbarAllAppsContainerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -42,12 +39,4 @@
setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
return super.onApplyWindowInsets(insets);
}
-
- @Override
- protected BaseAllAppsAdapter<TaskbarAllAppsContext> createAdapter(
- AlphabeticalAppsList<TaskbarAllAppsContext> appsList,
- BaseAdapterProvider[] adapterProviders) {
- return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
- adapterProviders);
- }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
deleted file mode 100644
index 0372f67..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2022 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.taskbar.allapps;
-
-import static android.view.KeyEvent.ACTION_UP;
-import static android.view.KeyEvent.KEYCODE_BACK;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.content.Context;
-import android.graphics.Insets;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.WindowInsets;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.ActivityAllAppsContainerView;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.dot.DotInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.taskbar.BaseTaskbarContext;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
-import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarDragController;
-import com.android.launcher3.taskbar.TaskbarStashController;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-
-/**
- * Window context for the taskbar all apps overlay.
- * <p>
- * All apps has its own window and needs a window context. Some properties are delegated to the
- * {@link TaskbarActivityContext} such as {@link DeviceProfile} and {@link PopupDataProvider}.
- */
-class TaskbarAllAppsContext extends BaseTaskbarContext {
- private final TaskbarActivityContext mTaskbarContext;
- private final OnboardingPrefs<TaskbarAllAppsContext> mOnboardingPrefs;
-
- private final TaskbarAllAppsController mWindowController;
- private final TaskbarAllAppsViewController mAllAppsViewController;
- private final TaskbarDragController mDragController;
- private final TaskbarAllAppsDragLayer mDragLayer;
- private final TaskbarAllAppsContainerView mAppsView;
-
- // We automatically stash taskbar when all apps is opened in gesture navigation mode.
- private final boolean mWillTaskbarBeVisuallyStashed;
- private final int mStashedTaskbarHeight;
-
- TaskbarAllAppsContext(
- TaskbarActivityContext taskbarContext,
- TaskbarAllAppsController windowController,
- TaskbarControllers taskbarControllers) {
- super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
- mTaskbarContext = taskbarContext;
- mWindowController = windowController;
- mDragController = new TaskbarDragController(this);
- mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
-
- mDragLayer = new TaskbarAllAppsDragLayer(this);
- TaskbarAllAppsSlideInView slideInView = (TaskbarAllAppsSlideInView) mLayoutInflater.inflate(
- R.layout.taskbar_all_apps, mDragLayer, false);
- mAllAppsViewController = new TaskbarAllAppsViewController(
- this,
- slideInView,
- windowController,
- taskbarControllers);
- mAppsView = slideInView.getAppsView();
-
- TaskbarStashController taskbarStashController = taskbarControllers.taskbarStashController;
- mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
- mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
- }
-
- TaskbarAllAppsViewController getAllAppsViewController() {
- return mAllAppsViewController;
- }
-
- @Override
- public DeviceProfile getDeviceProfile() {
- return mWindowController.getDeviceProfile();
- }
-
- @Override
- public TaskbarDragController getDragController() {
- return mDragController;
- }
-
- @Override
- public TaskbarAllAppsDragLayer getDragLayer() {
- return mDragLayer;
- }
-
- @Override
- public TaskbarAllAppsContainerView getAppsView() {
- return mAppsView;
- }
-
- @Override
- public OnboardingPrefs<TaskbarAllAppsContext> getOnboardingPrefs() {
- return mOnboardingPrefs;
- }
-
- @Override
- public boolean isBindingItems() {
- return mTaskbarContext.isBindingItems();
- }
-
- @Override
- public View.OnClickListener getItemOnClickListener() {
- return mTaskbarContext.getItemOnClickListener();
- }
-
- @Override
- public PopupDataProvider getPopupDataProvider() {
- return mTaskbarContext.getPopupDataProvider();
- }
-
- @Override
- public DotInfo getDotInfoForItem(ItemInfo info) {
- return mTaskbarContext.getDotInfoForItem(info);
- }
-
- @Override
- public void onDragStart() {}
-
- @Override
- public void onDragEnd() {
- mWindowController.maybeCloseWindow();
- }
-
- @Override
- public void onPopupVisibilityChanged(boolean isVisible) {}
-
- @Override
- public SearchAdapterProvider<?> createSearchAdapterProvider(
- ActivityAllAppsContainerView<?> appsView) {
- return new DefaultSearchAdapterProvider(this);
- }
-
- /** Root drag layer for this context. */
- private static class TaskbarAllAppsDragLayer extends
- BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInternalInsetsListener {
-
- private TaskbarAllAppsDragLayer(Context context) {
- super(context, null, 1);
- setClipChildren(false);
- recreateControllers();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
-
- @Override
- public void recreateControllers() {
- mControllers = new TouchController[]{mActivity.mDragController};
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
- if (topView != null && topView.onBackPressed()) {
- return true;
- }
- }
- return super.dispatchKeyEvent(event);
- }
-
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mActivity.mDragController.isSystemDragInProgress()) {
- inoutInfo.touchableRegion.setEmpty();
- inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
- }
- }
-
- @Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- return updateInsetsDueToStashing(insets);
- }
-
- /**
- * Taskbar automatically stashes when opening all apps, but we don't report the insets as
- * changing to avoid moving the underlying app. But internally, the apps view should still
- * layout according to the stashed insets rather than the unstashed insets. So this method
- * does two things:
- * 1) Sets navigationBars bottom inset to stashedHeight.
- * 2) Sets tappableInsets bottom inset to 0.
- */
- private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!mActivity.mWillTaskbarBeVisuallyStashed) {
- return oldInsets;
- }
- WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
-
- Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
- Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
- mActivity.mStashedTaskbarHeight);
- updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
-
- Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
- Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
- oldTappableInsets.right, 0);
- updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
-
- return updatedInsetsBuilder.build();
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 1671a0f..ea37944 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -15,34 +15,17 @@
*/
package com.android.launcher3.taskbar.allapps;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
-import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
import androidx.annotation.Nullable;
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import java.util.List;
-import java.util.Optional;
/**
* Handles the all apps overlay window initialization, updates, and its data.
@@ -57,36 +40,14 @@
*/
public final class TaskbarAllAppsController {
- private static final String WINDOW_TITLE = "Taskbar All Apps";
-
- private final TaskbarActivityContext mTaskbarContext;
- private final TaskbarAllAppsProxyView mProxyView;
- private final LayoutParams mLayoutParams;
-
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onTaskStackChanged() {
- mProxyView.close(false);
- }
- };
-
- private DeviceProfile mDeviceProfile;
private TaskbarControllers mControllers;
- /** Window context for all apps if it is open. */
- private @Nullable TaskbarAllAppsContext mAllAppsContext;
+ private @Nullable TaskbarAllAppsContainerView mAppsView;
// Application data models.
private AppInfo[] mApps;
private int mAppsModelFlags;
private List<ItemInfo> mPredictedApps;
- public TaskbarAllAppsController(TaskbarActivityContext context, DeviceProfile dp) {
- mDeviceProfile = dp;
- mTaskbarContext = context;
- mProxyView = new TaskbarAllAppsProxyView(mTaskbarContext);
- mLayoutParams = createLayoutParams();
- }
-
/** Initialize the controller. */
public void init(TaskbarControllers controllers, boolean allAppsVisible) {
if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) {
@@ -111,8 +72,8 @@
mApps = apps;
mAppsModelFlags = flags;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
+ if (mAppsView != null) {
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
}
}
@@ -123,8 +84,8 @@
}
mPredictedApps = predictedApps;
- if (mAllAppsContext != null) {
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ if (mAppsView != null) {
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
}
@@ -136,120 +97,30 @@
}
private void show(boolean animate) {
- if (mProxyView.isOpen()) {
+ if (mAppsView != null) {
return;
}
- mProxyView.show();
// mControllers and getSharedState should never be null here. Do not handle null-pointer
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = true;
- mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext, this, mControllers);
- mAllAppsContext.getDragController().init(mControllers);
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
- .ifPresent(m -> m.addView(mAllAppsContext.getDragLayer(), mLayoutParams));
+ TaskbarOverlayContext overlayContext =
+ mControllers.taskbarOverlayController.requestWindow();
+ TaskbarAllAppsSlideInView slideInView =
+ (TaskbarAllAppsSlideInView) overlayContext.getLayoutInflater().inflate(
+ R.layout.taskbar_all_apps, overlayContext.getDragLayer(), false);
+ slideInView.addOnCloseListener(() -> {
+ mControllers.getSharedState().allAppsVisible = false;
+ mAppsView = null;
+ });
+ TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
+ overlayContext, slideInView, mControllers);
- mAllAppsContext.getAppsView().getAppsStore().setApps(mApps, mAppsModelFlags);
- mAllAppsContext.getAppsView().getFloatingHeaderView()
+ viewController.show(animate);
+ mAppsView = overlayContext.getAppsView();
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags);
+ mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
- mAllAppsContext.getAllAppsViewController().show(animate);
- }
-
- /** Closes the {@link TaskbarAllAppsContainerView}. */
- public void hide() {
- mProxyView.close(true);
- }
-
- /**
- * Removes the all apps window from the hierarchy, if all floating views are closed and there is
- * no system drag operation in progress.
- * <p>
- * This method should be called after an exit animation finishes, if applicable.
- */
- void maybeCloseWindow() {
- if (mAllAppsContext != null && (AbstractFloatingView.hasOpenView(mAllAppsContext, TYPE_ALL)
- || mAllAppsContext.getDragController().isSystemDragInProgress())) {
- return;
- }
- mProxyView.close(false);
- // mControllers and getSharedState should never be null here. Do not handle null-pointer
- // to catch invalid states.
- mControllers.getSharedState().allAppsVisible = false;
- onDestroy();
- }
-
- /** Destroys the controller and any All Apps window if present. */
- public void onDestroy() {
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mAllAppsContext)
- .map(c -> c.getSystemService(WindowManager.class))
- .ifPresent(m -> m.removeViewImmediate(mAllAppsContext.getDragLayer()));
- mAllAppsContext = null;
- }
-
- /** Updates {@link DeviceProfile} instance for Taskbar's All Apps window. */
- public void updateDeviceProfile(DeviceProfile dp) {
- mDeviceProfile = dp;
- Optional.ofNullable(mAllAppsContext).ifPresent(c -> {
- AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
- c.dispatchDeviceProfileChanged();
- });
- }
-
- DeviceProfile getDeviceProfile() {
- return mDeviceProfile;
- }
-
- private LayoutParams createLayoutParams() {
- LayoutParams layoutParams = new LayoutParams(
- TYPE_APPLICATION_OVERLAY,
- WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- layoutParams.setTitle(WINDOW_TITLE);
- layoutParams.gravity = Gravity.BOTTOM;
- layoutParams.packageName = mTaskbarContext.getPackageName();
- layoutParams.setFitInsetsTypes(0); // Handled by container view.
- layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- layoutParams.setSystemApplicationOverlay(true);
- return layoutParams;
- }
-
- /**
- * Proxy view connecting taskbar drag layer to the all apps window.
- * <p>
- * The all apps view is in a separate window and has its own drag layer, but this proxy lets it
- * behave as though its in the taskbar drag layer. For instance, when the taskbar closes all
- * {@link AbstractFloatingView} instances, the all apps window will also close.
- */
- private class TaskbarAllAppsProxyView extends AbstractFloatingView {
-
- private TaskbarAllAppsProxyView(Context context) {
- super(context, null);
- }
-
- private void show() {
- mIsOpen = true;
- mTaskbarContext.getDragLayer().addView(this);
- }
-
- @Override
- protected void handleClose(boolean animate) {
- mTaskbarContext.getDragLayer().removeView(this);
- Optional.ofNullable(mAllAppsContext)
- .map(TaskbarAllAppsContext::getAllAppsViewController)
- .ifPresent(v -> v.close(animate));
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_TASKBAR_ALL_APPS) != 0;
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 9d48c8d..c8bfc2a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -28,10 +28,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.views.AbstractSlideInView;
/** Wrapper for taskbar all apps with slide-in behavior. */
-public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllAppsContext>
+public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverlayContext>
implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
private TaskbarAllAppsContainerView mAppsView;
private float mShiftRange;
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 128fa5e..54392b2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -25,6 +25,7 @@
import com.android.launcher3.taskbar.NavbarButtonsViewController;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
/**
* Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
@@ -32,16 +33,15 @@
*/
final class TaskbarAllAppsViewController {
- private final TaskbarAllAppsContext mContext;
+ private final TaskbarOverlayContext mContext;
private final TaskbarAllAppsSlideInView mSlideInView;
private final TaskbarAllAppsContainerView mAppsView;
private final TaskbarStashController mTaskbarStashController;
private final NavbarButtonsViewController mNavbarButtonsViewController;
TaskbarAllAppsViewController(
- TaskbarAllAppsContext context,
+ TaskbarOverlayContext context,
TaskbarAllAppsSlideInView slideInView,
- TaskbarAllAppsController windowController,
TaskbarControllers taskbarControllers) {
mContext = context;
@@ -53,7 +53,6 @@
setUpIconLongClick();
setUpAppDivider();
setUpTaskbarStashing();
- mSlideInView.addOnCloseListener(windowController::maybeCloseWindow);
}
/** Starts the {@link TaskbarAllAppsSlideInView} enter transition. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
new file mode 100644
index 0000000..68ea27a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Meant to be a simple container for data subclasses will need
+ *
+ * Assumes that the 3 navigation buttons (back/home/recents) have already been added to
+ * [navButtonContainer]
+ *
+ * @property navButtonContainer ViewGroup that holds the 3 navigation buttons.
+ * @property endContextualContainer ViewGroup that holds the end contextual button (ex, IME dismiss).
+ * @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
+ */
+abstract class AbstractNavButtonLayoutter(
+ val resources: Resources,
+ val navButtonContainer: LinearLayout,
+ protected val endContextualContainer: ViewGroup,
+ protected val startContextualContainer: ViewGroup
+) : NavButtonLayoutter {
+ protected val homeButton: ImageView = navButtonContainer
+ .findViewById(R.id.home)
+ protected val recentsButton: ImageView = navButtonContainer
+ .findViewById(R.id.recent_apps)
+ protected val backButton: ImageView = navButtonContainer
+ .findViewById(R.id.back)
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
new file mode 100644
index 0000000..c67ab79
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.graphics.Color
+import android.graphics.drawable.PaintDrawable
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_ICON_SIZE_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_BACK_KIDS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.DRAWABLE_SYSBAR_HOME_KIDS
+
+class KidsNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ val iconSize: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_ICON_SIZE_KIDS)
+ val buttonWidth: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
+ val buttonHeight: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS)
+ val buttonRadius: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS)
+ val paddingLeft = (buttonWidth - iconSize) / 2
+ val paddingTop = (buttonHeight - iconSize) / 2
+
+ // Update icons
+ backButton.setImageDrawable(
+ backButton.context.getDrawable(DRAWABLE_SYSBAR_BACK_KIDS))
+ backButton.scaleType = ImageView.ScaleType.FIT_CENTER
+ backButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+ homeButton.setImageDrawable(
+ homeButton.getContext().getDrawable(DRAWABLE_SYSBAR_HOME_KIDS))
+ homeButton.scaleType = ImageView.ScaleType.FIT_CENTER
+ homeButton.setPadding(paddingLeft, paddingTop, paddingLeft, paddingTop)
+
+ // Home button layout
+ val homeLayoutparams = LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ )
+ val homeButtonLeftMargin: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS)
+ homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0)
+ homeButton.layoutParams = homeLayoutparams
+
+ // Back button layout
+ val backLayoutParams = LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ )
+ val backButtonLeftMargin: Int = resources.getDimensionPixelSize(
+ DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS)
+ backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0)
+ backButton.layoutParams = backLayoutParams
+
+ // Button backgrounds
+ val whiteWith10PctAlpha = Color.argb(0.1f, 1f, 1f, 1f)
+ val buttonBackground = PaintDrawable(whiteWith10PctAlpha)
+ buttonBackground.setCornerRadius(buttonRadius.toFloat())
+ homeButton.background = buttonBackground
+ backButton.background = buttonBackground
+
+ // Update alignment within taskbar
+ val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ navButtonsLayoutParams.apply {
+ marginStart = navButtonsLayoutParams.marginEnd / 2
+ marginEnd = navButtonsLayoutParams.marginStart
+ gravity = Gravity.CENTER
+ }
+ navButtonContainer.requestLayout()
+
+ homeButton.onLongClickListener = null
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
new file mode 100644
index 0000000..0d9b855
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/LayoutResourceHelper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton;
+
+import android.annotation.DimenRes;
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+
+import com.android.launcher3.R;
+
+/**
+ * A class for retrieving resources in Kotlin.
+ *
+ * This class should be removed once the build system supports resources loading in Kotlin.
+ */
+public final class LayoutResourceHelper {
+
+ // --------------------------
+ // Kids Nav Layout
+ @DimenRes
+ public static final int DIMEN_TASKBAR_ICON_SIZE_KIDS = R.dimen.taskbar_icon_size_kids;
+ @DrawableRes
+ public static final int DRAWABLE_SYSBAR_BACK_KIDS = R.drawable.ic_sysbar_back_kids;
+ @DrawableRes
+ public static final int DRAWABLE_SYSBAR_HOME_KIDS = R.drawable.ic_sysbar_home_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_HOME_BUTTON_LEFT_MARGIN_KIDS =
+ R.dimen.taskbar_home_button_left_margin_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_BACK_BUTTON_LEFT_MARGIN_KIDS =
+ R.dimen.taskbar_back_button_left_margin_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS =
+ R.dimen.taskbar_nav_buttons_width_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_HEIGHT_KIDS =
+ R.dimen.taskbar_nav_buttons_height_kids;
+ @DimenRes
+ public static final int DIMEN_TASKBAR_NAV_BUTTONS_CORNER_RADIUS_KIDS =
+ R.dimen.taskbar_nav_buttons_corner_radius_kids;
+
+ // --------------------------
+ // Nav Layout Factory
+ @IdRes
+ public static final int ID_START_CONTEXTUAL_BUTTONS = R.id.start_contextual_buttons;
+ @IdRes
+ public static final int ID_END_CONTEXTUAL_BUTTONS = R.id.end_contextual_buttons;
+ @IdRes
+ public static final int ID_END_NAV_BUTTONS = R.id.end_nav_buttons;
+
+ private LayoutResourceHelper() {
+
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
new file mode 100644
index 0000000..db0a2d8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_END_NAV_BUTTONS
+import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.ID_START_CONTEXTUAL_BUTTONS
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion
+import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+
+/**
+ * Select the correct layout for nav buttons
+ *
+ * Since layouts are done dynamically for the nav buttons on Taskbar, this
+ * class returns a corresponding [NavButtonLayoutter] via
+ * [Companion.getUiLayoutter]
+ * that can help position the buttons based on the current [DeviceProfile]
+ */
+class NavButtonLayoutFactory {
+ companion object {
+ /**
+ * Get the correct instance of [NavButtonLayoutter]
+ *
+ * No layouts supported for configurations where:
+ * * taskbar isn't showing AND
+ * * the device is not in [phoneMode]
+ * OR
+ * * phone is showing
+ * * device is using gesture navigation
+ *
+ * @param navButtonsView ViewGroup that contains start, end, nav button ViewGroups
+ * @param isKidsMode no-op when taskbar is hidden/not showing
+ * @param isInSetup no-op when taskbar is hidden/not showing
+ * @param phoneMode refers to the device using the taskbar window on phones
+ * @param isThreeButtonNav are no-ops when taskbar is present/showing
+ */
+ fun getUiLayoutter(deviceProfile: DeviceProfile,
+ navButtonsView: FrameLayout,
+ resources: Resources,
+ isKidsMode: Boolean,
+ isInSetup: Boolean,
+ isThreeButtonNav: Boolean,
+ phoneMode: Boolean):
+ NavButtonLayoutter {
+ val navButtonContainer =
+ navButtonsView.findViewById<LinearLayout>(ID_END_NAV_BUTTONS)
+ val endContextualContainer =
+ navButtonsView.findViewById<ViewGroup>(ID_END_CONTEXTUAL_BUTTONS)
+ val startContextualContainer =
+ navButtonsView.findViewById<ViewGroup>(ID_START_CONTEXTUAL_BUTTONS)
+ val isPhoneNavMode = phoneMode && isThreeButtonNav
+ return when {
+ isPhoneNavMode -> {
+ if (!deviceProfile.isLandscape) {
+ PhonePortraitNavLayoutter(resources, navButtonContainer,
+ endContextualContainer, startContextualContainer)
+ } else {
+ PhoneLandscapeNavLayoutter(resources, navButtonContainer,
+ endContextualContainer, startContextualContainer)
+ }
+ }
+ deviceProfile.isTaskbarPresent -> {
+ return when {
+ isInSetup -> {
+ SetupNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ isKidsMode -> {
+ KidsNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ else ->
+ TaskbarNavLayoutter(resources, navButtonContainer, endContextualContainer,
+ startContextualContainer)
+ }
+ }
+ else -> error("No layoutter found")
+ }
+ }
+ }
+
+ /** Lays out and provides access to the home, recents, and back buttons for various mischief */
+ interface NavButtonLayoutter {
+ fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean)
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
new file mode 100644
index 0000000..a89476e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.core.view.children
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhoneLandscapeNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // TODO(b/230395757): Polish pending, this is just to make it usable
+ val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ val endStartMargins =
+ resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+ TaskbarManager.isPhoneMode(dp))
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.VERTICAL
+
+ navContainerParams.apply {
+ width = taskbarDimensions.x
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ gravity = Gravity.CENTER
+ topMargin = endStartMargins
+ bottomMargin = endStartMargins
+ marginEnd = 0
+ marginStart = 0
+ }
+
+ // Swap recents and back button
+ navButtonContainer.addView(recentsButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(backButton)
+
+ navButtonContainer.layoutParams = navContainerParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween: Int =
+ resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ navButtonContainer.children.forEachIndexed { i, navButton ->
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 1f
+ when (i) {
+ 0 -> {
+ buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ buttonLayoutParams.topMargin = spaceInBetween / 2
+ }
+ else -> {
+ buttonLayoutParams.bottomMargin = spaceInBetween / 2
+ buttonLayoutParams.topMargin = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
new file mode 100644
index 0000000..275f59f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.taskbar.TaskbarManager
+import com.android.launcher3.util.DimensionUtils
+
+class PhonePortraitNavLayoutter(resources: Resources, navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup) :
+ AbstractNavButtonLayoutter(resources, navBarContainer, endContextualContainer,
+ startContextualContainer) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // TODO(b/230395757): Polish pending, this is just to make it usable
+ val navContainerParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
+ TaskbarManager.isPhoneMode(dp))
+ val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ navContainerParams.width = taskbarDimensions.x
+ navContainerParams.height = ViewGroup.LayoutParams.MATCH_PARENT
+ navContainerParams.gravity = Gravity.CENTER_VERTICAL
+
+ // Ensure order of buttons is correct
+ navButtonContainer.removeAllViews()
+ navButtonContainer.orientation = LinearLayout.HORIZONTAL
+ navContainerParams.topMargin = 0
+ navContainerParams.bottomMargin = 0
+ navContainerParams.marginEnd = endStartMargins
+ navContainerParams.marginStart = endStartMargins
+ // Swap recents and back button in case we were landscape prior to this
+ navButtonContainer.addView(backButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(recentsButton)
+
+ navButtonContainer.layoutParams = navContainerParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween =
+ resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 1f
+ when (i) {
+ 0 -> {
+ // First button
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ // Last button
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ }
+ else -> {
+ // other buttons
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
new file mode 100644
index 0000000..afe70d6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+
+class SetupNavLayoutter(
+ resources: Resources,
+ navButtonContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // Since setup wizard only has back button enabled, it looks strange to be
+ // end-aligned, so start-align instead.
+ val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ navButtonsLayoutParams.apply {
+ marginStart = navButtonsLayoutParams.marginEnd
+ marginEnd = 0
+ gravity = Gravity.START
+ }
+ navButtonContainer.requestLayout()
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
new file mode 100644
index 0000000..b2ca2af
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+/**
+ * Layoutter for showing 3 button navigation on large screen
+ */
+class TaskbarNavLayoutter(
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup
+) : AbstractNavButtonLayoutter(
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer
+) {
+
+ override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ // Add spacing after the end of the last nav button
+ val navButtonParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
+ var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt()
+ val contextualWidth = endContextualContainer.width
+ // If contextual buttons are showing, we check if the end margin is enough for the
+ // contextual button to be showing - if not, move the nav buttons over a smidge
+ if (isContextualButtonShowing && navMarginEnd < contextualWidth) {
+ // Additional spacing, eat up half of space between last icon and nav button
+ navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2
+ }
+
+ navButtonParams.apply {
+ gravity = Gravity.END
+ width = FrameLayout.LayoutParams.WRAP_CONTENT
+ height = ViewGroup.LayoutParams.MATCH_PARENT
+ marginEnd = navMarginEnd
+ }
+ navButtonContainer.orientation = LinearLayout.HORIZONTAL
+ navButtonContainer.layoutParams = navButtonParams
+
+ // Add the spaces in between the nav buttons
+ val spaceInBetween = resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
+ for (i in 0 until navButtonContainer.childCount) {
+ val navButton = navButtonContainer.getChildAt(i)
+ val buttonLayoutParams = navButton.layoutParams as LinearLayout.LayoutParams
+ buttonLayoutParams.weight = 0f
+ when (i) {
+ 0 -> {
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ navButtonContainer.childCount - 1 -> {
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ }
+ else -> {
+ buttonLayoutParams.marginStart = spaceInBetween / 2
+ buttonLayoutParams.marginEnd = spaceInBetween / 2
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
new file mode 100644
index 0000000..5701de0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 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.taskbar.overlay;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.taskbar.BaseTaskbarContext;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarDragController;
+import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
+import com.android.launcher3.util.OnboardingPrefs;
+
+/**
+ * Window context for the taskbar overlays such as All Apps and EDU.
+ * <p>
+ * Overlays have their own window and need a window context. Some properties are delegated to the
+ * {@link TaskbarActivityContext} such as {@link PopupDataProvider}.
+ */
+public class TaskbarOverlayContext extends BaseTaskbarContext {
+ private final TaskbarActivityContext mTaskbarContext;
+ private final OnboardingPrefs<TaskbarOverlayContext> mOnboardingPrefs;
+
+ private final TaskbarOverlayController mOverlayController;
+ private final TaskbarDragController mDragController;
+ private final TaskbarOverlayDragLayer mDragLayer;
+
+ // We automatically stash taskbar when All Apps is opened in gesture navigation mode.
+ private final boolean mWillTaskbarBeVisuallyStashed;
+ private final int mStashedTaskbarHeight;
+
+ public TaskbarOverlayContext(
+ Context windowContext,
+ TaskbarActivityContext taskbarContext,
+ TaskbarControllers controllers) {
+ super(windowContext);
+ mTaskbarContext = taskbarContext;
+ mOverlayController = controllers.taskbarOverlayController;
+ mDragController = new TaskbarDragController(this);
+ mDragController.init(controllers);
+ mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
+ mDragLayer = new TaskbarOverlayDragLayer(this);
+
+ TaskbarStashController taskbarStashController = controllers.taskbarStashController;
+ mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
+ mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
+ }
+
+ boolean willTaskbarBeVisuallyStashed() {
+ return mWillTaskbarBeVisuallyStashed;
+ }
+
+ int getStashedTaskbarHeight() {
+ return mStashedTaskbarHeight;
+ }
+
+ public TaskbarOverlayController getOverlayController() {
+ return mOverlayController;
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mOverlayController.getDeviceProfile();
+ }
+
+ @Override
+ public TaskbarDragController getDragController() {
+ return mDragController;
+ }
+
+ @Override
+ public TaskbarOverlayDragLayer getDragLayer() {
+ return mDragLayer;
+ }
+
+ @Override
+ public TaskbarAllAppsContainerView getAppsView() {
+ return mDragLayer.findViewById(R.id.apps_view);
+ }
+
+ @Override
+ public OnboardingPrefs<TaskbarOverlayContext> getOnboardingPrefs() {
+ return mOnboardingPrefs;
+ }
+
+ @Override
+ public boolean isBindingItems() {
+ return mTaskbarContext.isBindingItems();
+ }
+
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return mTaskbarContext.getItemOnClickListener();
+ }
+
+ @Override
+ public PopupDataProvider getPopupDataProvider() {
+ return mTaskbarContext.getPopupDataProvider();
+ }
+
+ @Override
+ public DotInfo getDotInfoForItem(ItemInfo info) {
+ return mTaskbarContext.getDotInfoForItem(info);
+ }
+
+ @Override
+ public void onDragStart() {}
+
+ @Override
+ public void onDragEnd() {
+ mOverlayController.maybeCloseWindow();
+ }
+
+ @Override
+ public void onPopupVisibilityChanged(boolean isVisible) {}
+
+ @Override
+ public SearchAdapterProvider<?> createSearchAdapterProvider(
+ ActivityAllAppsContainerView<?> appsView) {
+ return new DefaultSearchAdapterProvider(this);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
new file mode 100644
index 0000000..0574058
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 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.taskbar.overlay;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.Optional;
+
+/**
+ * Handles the Taskbar overlay window lifecycle.
+ * <p>
+ * Overlays need to be inflated in a separate window so that have the correct hierarchy. For
+ * instance, they need to be below the notification tray. If there are multiple overlays open, the
+ * same window is used.
+ */
+public final class TaskbarOverlayController {
+
+ private static final String WINDOW_TITLE = "Taskbar Overlay";
+
+ private final TaskbarActivityContext mTaskbarContext;
+ private final Context mWindowContext;
+ private final TaskbarOverlayProxyView mProxyView;
+ private final LayoutParams mLayoutParams;
+
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ mProxyView.close(false);
+ }
+ };
+
+ private DeviceProfile mDeviceProfile;
+ private @Nullable TaskbarOverlayContext mOverlayContext;
+ private TaskbarControllers mControllers; // Initialized in init.
+
+ public TaskbarOverlayController(
+ TaskbarActivityContext taskbarContext, DeviceProfile deviceProfile) {
+ mTaskbarContext = taskbarContext;
+ mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+ mProxyView = new TaskbarOverlayProxyView();
+ mLayoutParams = createLayoutParams();
+ mDeviceProfile = deviceProfile;
+ }
+
+ /** Initialize the controller. */
+ public void init(TaskbarControllers controllers) {
+ mControllers = controllers;
+ }
+
+ /**
+ * Creates a window for Taskbar overlays, if it does not already exist. Returns the window
+ * context for the current overlay window.
+ */
+ public TaskbarOverlayContext requestWindow() {
+ if (mOverlayContext == null) {
+ mOverlayContext = new TaskbarOverlayContext(
+ mWindowContext, mTaskbarContext, mControllers);
+ }
+
+ if (!mProxyView.isOpen()) {
+ mProxyView.show();
+ Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
+ .ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+ }
+
+ return mOverlayContext;
+ }
+
+ /** Hides the current overlay window with animation. */
+ public void hideWindow() {
+ mProxyView.close(true);
+ }
+
+ /**
+ * Removes the overlay window from the hierarchy, if all floating views are closed and there is
+ * no system drag operation in progress.
+ * <p>
+ * This method should be called after an exit animation finishes, if applicable.
+ */
+ @SuppressLint("WrongConstant")
+ void maybeCloseWindow() {
+ if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
+ || mOverlayContext.getDragController().isSystemDragInProgress())) {
+ return;
+ }
+ mProxyView.close(false);
+ onDestroy();
+ }
+
+ /** Destroys the controller and any overlay window if present. */
+ public void onDestroy() {
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ Optional.ofNullable(mOverlayContext)
+ .map(c -> c.getSystemService(WindowManager.class))
+ .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
+ mOverlayContext = null;
+ }
+
+ /** The current device profile for the overlay window. */
+ public DeviceProfile getDeviceProfile() {
+ return mDeviceProfile;
+ }
+
+ /** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
+ public void updateDeviceProfile(DeviceProfile dp) {
+ mDeviceProfile = dp;
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> {
+ AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
+ c.dispatchDeviceProfileChanged();
+ });
+ }
+
+ @SuppressLint("WrongConstant")
+ private LayoutParams createLayoutParams() {
+ LayoutParams layoutParams = new LayoutParams(
+ TYPE_APPLICATION_OVERLAY,
+ LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.setTitle(WINDOW_TITLE);
+ layoutParams.gravity = Gravity.BOTTOM;
+ layoutParams.packageName = mTaskbarContext.getPackageName();
+ layoutParams.setFitInsetsTypes(0); // Handled by container view.
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.setSystemApplicationOverlay(true);
+ return layoutParams;
+ }
+
+ /**
+ * Proxy view connecting taskbar drag layer to the overlay window.
+ *
+ * Overlays are in a separate window and has its own drag layer, but this proxy lets its views
+ * behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
+ * all {@link AbstractFloatingView} instances, the overlay window will also close.
+ */
+ private class TaskbarOverlayProxyView extends AbstractFloatingView {
+
+ private TaskbarOverlayProxyView() {
+ super(mTaskbarContext, null);
+ }
+
+ private void show() {
+ mIsOpen = true;
+ mTaskbarContext.getDragLayer().addView(this);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ mTaskbarContext.getDragLayer().removeView(this);
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
new file mode 100644
index 0000000..044afd6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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.taskbar.overlay;
+
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+
+/** Root drag layer for the Taskbar overlay window. */
+public class TaskbarOverlayDragLayer extends
+ BaseDragLayer<TaskbarOverlayContext> implements
+ ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ TaskbarOverlayDragLayer(Context context) {
+ super(context, null, 1);
+ setClipChildren(false);
+ recreateControllers();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+
+ @Override
+ public void recreateControllers() {
+ mControllers = new TouchController[]{mActivity.getDragController()};
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null && topView.onBackPressed()) {
+ return true;
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mActivity.getDragController().isSystemDragInProgress()) {
+ inoutInfo.touchableRegion.setEmpty();
+ inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+ }
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ return updateInsetsDueToStashing(insets);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ mActivity.getOverlayController().maybeCloseWindow();
+ }
+
+ /**
+ * Taskbar automatically stashes when opening all apps, but we don't report the insets as
+ * changing to avoid moving the underlying app. But internally, the apps view should still
+ * layout according to the stashed insets rather than the unstashed insets. So this method
+ * does two things:
+ * 1) Sets navigationBars bottom inset to stashedHeight.
+ * 2) Sets tappableInsets bottom inset to 0.
+ */
+ private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
+ if (!mActivity.willTaskbarBeVisuallyStashed()) {
+ return oldInsets;
+ }
+ WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
+
+ Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
+ Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
+ mActivity.getStashedTaskbarHeight());
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
+
+ Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
+ Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
+ oldTappableInsets.right, 0);
+ updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
+
+ return updatedInsetsBuilder.build();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 5f3a990..bf0f8f7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -49,6 +49,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -271,7 +272,7 @@
mIsPinned = true;
applyFromWorkspaceItem(info);
setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = true;
invalidate();
}
@@ -280,7 +281,7 @@
*/
public void finishBinding(OnLongClickListener longClickListener) {
setOnLongClickListener(longClickListener);
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = false;
+ ((CellLayoutLayoutParams) getLayoutParams()).canReorder = false;
setTextVisibility(false);
verifyHighRes();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4ca27b1..3203f44 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -66,6 +66,7 @@
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.View;
+import android.view.WindowManagerGlobal;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -139,12 +140,14 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.unfold.UnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -173,6 +176,7 @@
// Will be updated when dragging from taskbar.
private @Nullable DragOptions mNextWorkspaceDragOptions = null;
private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
+ private @Nullable RotationChangeProvider mRotationChangeProvider;
private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
/**
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
@@ -182,6 +186,8 @@
private SafeCloseable mViewCapture;
+ private boolean mEnableWidgetDepth;
+
@Override
protected void setupViews() {
super.setupViews();
@@ -202,6 +208,9 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mHotseatPredictionController = new HotseatPredictionController(this);
+
+ mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
+ && SystemProperties.getBoolean("ro.launcher.depth.widget", true);
}
@Override
@@ -583,9 +592,7 @@
public void onWidgetsTransition(float progress) {
super.onWidgetsTransition(progress);
onTaskbarInAppDisplayProgressUpdate(progress, WIDGETS_PAGE_PROGRESS_INDEX);
- // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness
- // on very slow cuttlefish devices.
- if (ENABLE_WIDGET_PICKER_DEPTH.get() && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ if (mEnableWidgetDepth) {
WIDGET_DEPTH.set(getDepthController(),
Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
EMPHASIZED));
@@ -664,8 +671,8 @@
private void initUnfoldTransitionProgressProvider() {
final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig();
if (config.isEnabled()) {
- mUnfoldTransitionProgressProvider =
- UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
+ UnfoldSharedComponent unfoldComponent =
+ UnfoldTransitionFactory.createUnfoldSharedComponent(
/* context= */ this,
config,
ProxyScreenStatusProvider.INSTANCE,
@@ -677,13 +684,21 @@
getMainThreadHandler(),
getMainExecutor(),
/* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
- /* tracingTagPrefix= */ "launcher"
+ /* tracingTagPrefix= */ "launcher",
+ WindowManagerGlobal.getWindowManagerService()
);
+ mUnfoldTransitionProgressProvider = unfoldComponent.getUnfoldTransitionProvider()
+ .orElseThrow(() -> new IllegalStateException(
+ "Trying to create UnfoldTransitionProgressProvider when the "
+ + "transition is disabled"));
+
+ mRotationChangeProvider = unfoldComponent.getRotationChangeProvider();
mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
- this,
+ /* launcher= */ this,
getWindowManager(),
- mUnfoldTransitionProgressProvider
+ mUnfoldTransitionProgressProvider,
+ mRotationChangeProvider
);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 3fe2e59..0e1120b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -90,9 +90,9 @@
// While animating into recents, update the visible task data as needed
builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
mRecentsView.updateEmptyMessage();
- // TODO(b/238461210): Remove logging once root cause of flake detected.
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d("b/238461210", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ Log.d("b/246283207", "RecentsView#setStateWithAnimationInternal getCurrentPage(): "
+ mRecentsView.getCurrentPage()
+ ", getScrollForPage(getCurrentPage())): "
+ mRecentsView.getScrollForPage(mRecentsView.getCurrentPage()));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 4150d40..733c6a8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -26,6 +26,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -96,7 +97,7 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- if (dp.isTaskbarPresentInApps) {
+ if (dp.isTaskbarPresentInApps && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
return launcher.getColor(R.color.taskbar_background);
}
return Color.TRANSPARENT;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 9106a8f..d728b75 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -75,6 +75,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowInsets;
@@ -115,6 +116,7 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
@@ -129,10 +131,13 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -248,6 +253,13 @@
private static final float MAX_QUICK_SWITCH_RECENTS_SCALE_PROGRESS = 0.07f;
+ // Controls task thumbnail splash's reveal animation after landing on a task from quickswitch.
+ // These values match WindowManager/Shell starting_window_app_reveal_* config values.
+ private static final int SPLASH_FADE_OUT_DURATION = 133;
+ private static final int SPLASH_APP_REVEAL_DELAY = 83;
+ private static final int SPLASH_APP_REVEAL_DURATION = 266;
+ private static final int SPLASH_ANIMATION_DURATION = 349;
+
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
*/
@@ -272,7 +284,7 @@
private AnimatorControllerWithResistance mLauncherTransitionController;
private boolean mHasEndedLauncherTransition;
- private AnimationFactory mAnimationFactory = (t) -> { };
+ private AnimationFactory mAnimationFactory = (t, s) -> { };
private boolean mWasLauncherAlreadyVisible;
@@ -285,6 +297,8 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
+ private final int mSplashMainWindowShiftLength;
+
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
@@ -320,6 +334,9 @@
mQuickSwitchScaleScrollThreshold = context.getResources().getDimension(
R.dimen.quick_switch_scaling_scroll_threshold);
+ mSplashMainWindowShiftLength = -context.getResources().getDimensionPixelSize(
+ R.dimen.starting_surface_exit_animation_window_shift_length);
+
initAfterSubclassConstructor();
initStateCallbacks();
}
@@ -501,7 +518,9 @@
Runnable initAnimFactory = () -> {
mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState,
mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
- maybeUpdateRecentsAttachedState(false /* animate */);
+ maybeUpdateRecentsAttachedState(
+ false /* animate */,
+ new ActiveGestureLog.CompoundString("on Launcher start (animate=false)"));
if (mGestureState.getEndTarget() != null) {
// Update the end target in case the gesture ended before we init.
mAnimationFactory.setEndTarget(mGestureState.getEndTarget());
@@ -606,7 +625,8 @@
}
private void initializeLauncherAnimationController() {
- buildAnimationController();
+ buildAnimationController(new ActiveGestureLog.CompoundString(
+ "initializing launcher animation controller"));
Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
TraceHelper.FLAG_IGNORE_BINDERS);
@@ -625,7 +645,11 @@
@Override
public void onMotionPauseDetected() {
mHasMotionEverBeenPaused = true;
- maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
+ maybeUpdateRecentsAttachedState(
+ true/* animate */,
+ true/* moveFocusedTask */,
+ new ActiveGestureLog.CompoundString(
+ "motion pause detected (animate=true)"));
performHapticFeedback();
}
@@ -636,12 +660,13 @@
};
}
- private void maybeUpdateRecentsAttachedState() {
- maybeUpdateRecentsAttachedState(true /* animate */);
+ private void maybeUpdateRecentsAttachedState(ActiveGestureLog.CompoundString reason) {
+ maybeUpdateRecentsAttachedState(true /* animate */, reason.append(" (animate=true)"));
}
- private void maybeUpdateRecentsAttachedState(boolean animate) {
- maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */);
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, ActiveGestureLog.CompoundString reason) {
+ maybeUpdateRecentsAttachedState(animate, false /* moveFocusedTask */, reason);
}
/**
@@ -653,7 +678,8 @@
* @param animate whether to animate when attaching RecentsView
* @param moveFocusedTask whether to move focused task to front when attaching
*/
- private void maybeUpdateRecentsAttachedState(boolean animate, boolean moveFocusedTask) {
+ private void maybeUpdateRecentsAttachedState(
+ boolean animate, boolean moveFocusedTask, ActiveGestureLog.CompoundString reason) {
if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
return;
}
@@ -663,14 +689,25 @@
final boolean recentsAttachedToAppWindow;
if (mGestureState.getEndTarget() != null) {
recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
+ reason.append("; gesture state end target != null (attached=")
+ .append(Boolean.toString(recentsAttachedToAppWindow))
+ .append(")");
} else if (mContinuingLastGesture
&& mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
recentsAttachedToAppWindow = true;
+ reason.append("; continuing last gesture (attached=true)");
} else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
// The window is going away so make sure recents is always visible in this case.
recentsAttachedToAppWindow = true;
+ reason.append("; make sure recents is always visible (attached=true)");
} else {
recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
+ reason.append(mHasMotionEverBeenPaused
+ ? "; motion has been paused"
+ : "; gesture is likely to start a new task")
+ .append(" (attached=")
+ .append(Boolean.toString(recentsAttachedToAppWindow))
+ .append(")");
}
if (moveFocusedTask && !mAnimationFactory.hasRecentsEverAttachedToAppWindow()
&& recentsAttachedToAppWindow) {
@@ -678,7 +715,8 @@
// TaskView jumping to new position as we move the tasks.
mRecentsView.moveFocusedTaskToFront();
}
- mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
+ mAnimationFactory.setRecentsAttachedToAppWindow(
+ recentsAttachedToAppWindow, animate, reason);
// Reapply window transform throughout the attach animation, as the animation affects how
// much the window is bound by overscroll (vs moving freely).
@@ -698,22 +736,29 @@
}
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
- setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
+ setIsLikelyToStartNewTask(
+ isLikelyToStartNewTask,
+ true /* animate */,
+ new ActiveGestureLog.CompoundString(
+ "setting gesture likely to start (animate=true)"));
}
- private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
+ private void setIsLikelyToStartNewTask(
+ boolean isLikelyToStartNewTask,
+ boolean animate,
+ ActiveGestureLog.CompoundString reason) {
if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
mIsLikelyToStartNewTask = isLikelyToStartNewTask;
- maybeUpdateRecentsAttachedState(animate);
+ maybeUpdateRecentsAttachedState(animate, reason);
}
}
- private void buildAnimationController() {
+ private void buildAnimationController(ActiveGestureLog.CompoundString reason) {
if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
- mAnimationFactory.createActivityInterface(mTransitionDragLength);
+ mAnimationFactory.createActivityInterface(mTransitionDragLength, reason);
}
/**
@@ -728,7 +773,7 @@
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
WindowInsets result = view.onApplyWindowInsets(windowInsets);
- buildAnimationController();
+ buildAnimationController(new ActiveGestureLog.CompoundString("applying window insets"));
// Reapply the current shift to ensure it takes new insets into account, e.g. when long
// pressing to stash taskbar without moving the finger.
updateFinalShift();
@@ -900,7 +945,10 @@
});
}
notifyGestureStartedAsync();
- setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
+ setIsLikelyToStartNewTask(
+ isLikelyToStartNewTask,
+ false /* animate */,
+ new ActiveGestureLog.CompoundString("on gesture started (animate=false)"));
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
@@ -982,7 +1030,8 @@
private void onSettledOnEndTarget() {
// Fast-finish the attaching animation if it's still running.
- maybeUpdateRecentsAttachedState(false);
+ maybeUpdateRecentsAttachedState(false, new ActiveGestureLog.CompoundString(
+ "on settled on end target (animate=false)"));
final GestureEndTarget endTarget = mGestureState.getEndTarget();
// Wait until the given View (if supplied) draws before resuming the last task.
View postResumeLastTask = mActivityInterface.onSettledOnEndTarget(endTarget);
@@ -1288,7 +1337,8 @@
@UiThread
private void animateToProgressInternal(float start, float end, long duration,
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
- maybeUpdateRecentsAttachedState();
+ maybeUpdateRecentsAttachedState(new ActiveGestureLog.CompoundString(
+ "animate to progress internal"));
// If we are transitioning to launcher, then listen for the activity to be restarted while
// the transition is in progress
@@ -1607,7 +1657,9 @@
mRecentsView.post(mRecentsView::resetTaskVisuals);
}
// Make sure recents is in its final state
- maybeUpdateRecentsAttachedState(false);
+ maybeUpdateRecentsAttachedState(
+ false, new ActiveGestureLog.CompoundString(
+ "setting up window animation (animate=false)"));
mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
}
});
@@ -1829,6 +1881,7 @@
}
private void finishCurrentTransitionToRecents() {
+ // TODO(b/245569277#comment2): enable once isFreeformActive is implemented
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
if (mRecentsAnimationController != null) {
mRecentsAnimationController.detachNavigationBarFromApp(true);
@@ -2034,16 +2087,56 @@
public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
if (handleTaskAppeared(appearedTaskTargets)) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "finishRecentsAnimation",
- /* extras= */ false,
- /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
+ Optional<RemoteAnimationTargetCompat> taskTargetOptional =
+ Arrays.stream(appearedTaskTargets)
+ .filter(targetCompat ->
+ targetCompat.taskId == mGestureState.getLastStartedTaskId())
+ .findFirst();
+ if (!taskTargetOptional.isPresent()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+ RemoteAnimationTargetCompat taskTarget = taskTargetOptional.get();
+ TaskView taskView = mRecentsView.getTaskViewByTaskId(taskTarget.taskId);
+ if (taskView == null || !taskView.getThumbnail().shouldShowSplashView()) {
+ finishRecentsAnimationOnTasksAppeared();
+ return;
+ }
+
+ ViewGroup splashView = mActivity.getDragLayer();
+
+ // When revealing the app with launcher splash screen, make the app visible
+ // and behind the splash view before the splash is animated away.
+ SurfaceTransactionApplier surfaceApplier =
+ new SurfaceTransactionApplier(splashView);
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ for (RemoteAnimationTargetCompat target : appearedTaskTargets) {
+ transaction.forSurface(target.leash).setAlpha(1).setLayer(-1);
+ }
+ surfaceApplier.scheduleApply(transaction);
+
+ SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash,
+ mSplashMainWindowShiftLength, new TransactionPool(), new Rect(),
+ SPLASH_ANIMATION_DURATION, SPLASH_FADE_OUT_DURATION,
+ /* iconStartAlpha= */ 0, /* brandingStartAlpha= */ 0,
+ SPLASH_APP_REVEAL_DELAY, SPLASH_APP_REVEAL_DURATION,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishRecentsAnimationOnTasksAppeared();
+ }
+ });
}
}
}
+ private void finishRecentsAnimationOnTasksAppeared() {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false /* toRecents */, null /* onFinishComplete */);
+ }
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+
/**
* @return The index of the TaskView in RecentsView whose taskId matches the task that will
* resume if we finish the controller.
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 226b173..d432004 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -57,6 +57,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
@@ -187,7 +188,8 @@
* Closes any overlays.
*/
public void closeOverlay() {
- Optional.ofNullable(getTaskbarController()).ifPresent(TaskbarUIController::hideAllApps);
+ Optional.ofNullable(getTaskbarController()).ifPresent(
+ TaskbarUIController::hideOverlayWindow);
}
public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
@@ -393,14 +395,16 @@
public interface AnimationFactory {
- void createActivityInterface(long transitionLength);
+ void createActivityInterface(long transitionLength, ActiveGestureLog.CompoundString reason);
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
+ * @param reason Explanation for why this method is being called with the given param values
*/
- default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
+ default void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, ActiveGestureLog.CompoundString reason) { }
default boolean isRecentsAttachedToAppWindow() {
return false;
@@ -442,7 +446,8 @@
}
@Override
- public void createActivityInterface(long transitionLength) {
+ public void createActivityInterface(
+ long transitionLength, ActiveGestureLog.CompoundString reason) {
PendingAnimation pa = new PendingAnimation(transitionLength * 2);
createBackgroundToOverviewAnim(mActivity, pa);
AnimatorPlaybackController controller = pa.createPlaybackController();
@@ -465,13 +470,29 @@
// (because we set the animation as the current state animation), so we reapply the
// attached state here as well to ensure recents is shown/hidden appropriately.
if (DisplayController.getNavigationMode(mActivity) == NavigationMode.NO_BUTTON) {
- setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
+ setRecentsAttachedToAppWindow(
+ mIsAttachedToWindow,
+ false,
+ reason.append("; reapplying the attached state (attached=")
+ .append(Boolean.toString(mIsAttachedToWindow))
+ .append(", animate=false)"));
}
}
@Override
- public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
+ public void setRecentsAttachedToAppWindow(
+ boolean attached, boolean animate, ActiveGestureLog.CompoundString reason) {
+ // TODO(b/244593270): remove these logs; too verbose
+ ActiveGestureLog.INSTANCE.addLog(
+ new ActiveGestureLog.CompoundString("setRecentsAttachedToAppWindow: attached=")
+ .append(Boolean.toString(attached))
+ .append(", animate=")
+ .append(Boolean.toString(animate))
+ .append(", reason=")
+ .append(reason));
if (mIsAttachedToWindow == attached && animate) {
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: exiting early"));
return;
}
mIsAttachedToWindow = attached;
@@ -488,9 +509,21 @@
.cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
if (!recentsView.isShown() && animate) {
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, fromTranslation);
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: recents view not shown, setting ")
+ .append("ADJACENT_PAGE_HORIZONTAL_OFFSET to ")
+ .append(Float.toString(fromTranslation)));
} else {
fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(recentsView);
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: updating fromTranslation to ")
+ .append(Float.toString(fromTranslation)));
}
+ ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(
+ "setRecentsAttachedToAppWindow: fromTranslation=")
+ .append(Float.toString(fromTranslation))
+ .append(", toTranslation=")
+ .append(Float.toString(toTranslation)));
if (!animate) {
ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, toTranslation);
} else {
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 99f7bdd..e55e966 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -65,12 +65,12 @@
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -125,18 +125,18 @@
}
}
- private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
+ private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder,
RemoteAnimationTargetCompat app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
}
- private void setHomeScaleAndAlpha(SurfaceParams.Builder builder,
+ private void setHomeScaleAndAlpha(SurfaceProperties builder,
RemoteAnimationTargetCompat app, float verticalShift, float alpha) {
float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale);
mTmpMatrix.setScale(scale, scale,
app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
- builder.withMatrix(mTmpMatrix).withAlpha(alpha);
+ builder.setMatrix(mTmpMatrix).setAlpha(alpha);
}
@Override
@@ -279,12 +279,12 @@
return mTargetRect;
}
- private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withAlpha(mRecentsAlpha.value);
+ builder.setAlpha(mRecentsAlpha.value);
}
- private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
RemoteAnimationTargetCompat app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
}
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
deleted file mode 100644
index 758c6e0..0000000
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import com.android.launcher3.R;
-
-/**
- * Bridge class to allow using resources in Kotlin.
- * <br/>
- * TODO(b/204069723) Can't use resources directly in Kotlin
- */
-public class KtR {
- public static final class id {
- public static int menu_option_layout = R.id.menu_option_layout;
- }
-
- public static final class dimen {
- public static int task_menu_spacing = R.dimen.task_menu_spacing;
- public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
- public static int taskbar_ime_size = R.dimen.taskbar_ime_size;
- }
-
- public static final class layout {
- public static int task_menu_with_arrow = R.layout.task_menu_with_arrow;
- public static int task_view_menu_option = R.layout.task_view_menu_option;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 1cb17cb..8522a87 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -34,11 +34,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -129,7 +129,8 @@
float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
float toDepthRatio = OVERVIEW.getDepth(activity);
pa.addFloat(getDepthController(),
- new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+ new LauncherAnimUtils.ClampedProperty<>(
+ DepthController.STATE_DEPTH, fromDepthRatio, toDepthRatio),
fromDepthRatio, toDepthRatio, LINEAR);
}
};
@@ -289,10 +290,6 @@
} else {
om.hideOverlay(150);
}
- LauncherTaskbarUIController taskbarController = getTaskbarController();
- if (taskbarController != null) {
- taskbarController.hideEdu();
- }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 7a281dd..ee3b075 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -46,7 +46,6 @@
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
/**
* Controls the animation of swiping back and returning to launcher.
@@ -242,20 +241,17 @@
/** Transform the target window to match the target rect. */
private void applyTransform(RectF targetRect, float cornerRadius) {
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
- new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
final float scale = targetRect.width() / mStartRect.width();
mTransformMatrix.reset();
mTransformMatrix.setScale(scale, scale);
mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
- builder.withMatrix(mTransformMatrix)
- .withWindowCrop(mStartRect)
- .withCornerRadius(cornerRadius);
- SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(mTransaction);
+ if (mBackTarget.leash.isValid()) {
+ mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
+ mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
+ mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
}
+
mTransaction.apply();
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 6b616b1..7bcc661 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -17,6 +17,8 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
+import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -30,6 +32,7 @@
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.wm.shell.recents.IRecentTasksListener;
@@ -253,8 +256,9 @@
};
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
+
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (rawTask.getType() == GroupedRecentTaskInfo.TYPE_FREEFORM) {
+ if (DESKTOP_MODE_SUPPORTED && rawTask.getType() == TYPE_FREEFORM) {
GroupTask desktopTask = createDesktopTask(rawTask);
allTasks.add(desktopTask);
continue;
@@ -284,14 +288,18 @@
return allTasks;
}
- private GroupTask createDesktopTask(GroupedRecentTaskInfo taskInfo) {
- // TODO(b/244348395): create a subclass of GroupTask for desktop tile
- // We need a single task information as the primary task. Use the first task
- Task.TaskKey key = new Task.TaskKey(taskInfo.getTaskInfo1());
- Task task = new Task(key);
- task.desktopTile = true;
- task.topActivity = key.sourceComponent;
- return new GroupTask(task, null, null);
+ private DesktopTask createDesktopTask(GroupedRecentTaskInfo recentTaskInfo) {
+ ArrayList<Task> tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
+ for (ActivityManager.RecentTaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
+ Task.TaskKey key = new Task.TaskKey(taskInfo);
+ Task task = Task.from(key, taskInfo, false);
+ task.setLastSnapshotData(taskInfo);
+ task.positionInParent = taskInfo.positionInParent;
+ task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
+ // TODO(b/244348395): tasks should be sorted from oldest to most recently used
+ tasks.add(task);
+ }
+ return new DesktopTask(tasks);
}
private SplitConfigurationOptions.SplitBounds convertSplitBounds(
@@ -306,7 +314,7 @@
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
ArrayList<GroupTask> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
- newTasks.add(new GroupTask(tasks.get(i)));
+ newTasks.add(tasks.get(i).copy());
}
return newTasks;
}
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index baeb514..71e8a77 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -37,11 +37,11 @@
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.Arrays;
import java.util.function.Consumer;
@@ -335,11 +335,11 @@
}
@Override
- public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mCropRect)
- .withCornerRadius(params.getCornerRadius());
+ public void onBuildTargetParams(SurfaceProperties builder, RemoteAnimationTargetCompat app,
+ TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mCropRect)
+ .setCornerRadius(params.getCornerRadius());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 7d3d8a2..bfebbbc 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -70,6 +70,8 @@
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -80,7 +82,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.ArrayList;
import java.util.List;
@@ -250,21 +251,24 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- final SurfaceParams.Builder navBuilder =
- new SurfaceParams.Builder(navBarTarget.leash);
+
// TODO Do we need to operate over multiple TVSs for the navbar leash?
for (RemoteTargetHandle handle : remoteTargetHandles) {
+ SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceProperties navBuilder =
+ transaction.forSurface(navBarTarget.leash);
+
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
TaskViewSimulator taskViewSimulator = handle.getTaskViewSimulator();
taskViewSimulator.getCurrentCropRect().round(cropRect);
- navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
- .withWindowCrop(cropRect)
- .withAlpha(mNavFadeIn.value);
+ navBuilder.setMatrix(taskViewSimulator.getCurrentMatrix())
+ .setWindowCrop(cropRect)
+ .setAlpha(mNavFadeIn.value);
} else {
- navBuilder.withAlpha(mNavFadeOut.value);
+ navBuilder.setAlpha(mNavFadeOut.value);
}
- handle.getTransformParams().applySurfaceParams(navBuilder.build());
+ handle.getTransformParams().applySurfaceParams(transaction);
}
}
});
@@ -302,9 +306,15 @@
// to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
// Mt K(0)` K(t) Mt`
TaskThumbnailView[] thumbnails = v.getThumbnails();
- Matrix[] mt = new Matrix[simulatorCopies.length];
- Matrix[] mti = new Matrix[simulatorCopies.length];
- for (int i = 0; i < thumbnails.length; i++) {
+
+ // In case simulator copies and thumbnail size do no match, ensure we get the lesser.
+ // This ensures we do not create arrays with empty elements or attempt to references
+ // indexes out of array bounds.
+ final int matrixSize = Math.min(simulatorCopies.length, thumbnails.length);
+
+ Matrix[] mt = new Matrix[matrixSize];
+ Matrix[] mti = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
TaskThumbnailView ttv = thumbnails[i];
RectF localBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
@@ -321,14 +331,14 @@
mti[i] = localMti;
}
- Matrix[] k0i = new Matrix[simulatorCopies.length];
- for (int i = 0; i < simulatorCopies.length; i++) {
+ Matrix[] k0i = new Matrix[matrixSize];
+ for (int i = 0; i < matrixSize; i++) {
k0i[i] = new Matrix();
simulatorCopies[i].getTaskViewSimulator().getCurrentMatrix().invert(k0i[i]);
}
Matrix animationMatrix = new Matrix();
out.addOnFrameCallback(() -> {
- for (int i = 0; i < simulatorCopies.length; i++) {
+ for (int i = 0; i < matrixSize; i++) {
animationMatrix.set(mt[i]);
animationMatrix.postConcat(k0i[i]);
animationMatrix.postConcat(simulatorCopies[i]
@@ -474,7 +484,7 @@
* If {@param launchingTaskView} is not null, then this will play the tasks launch animation
* from the position of the GroupedTaskView (when user taps on the TaskView to start it).
* Technically this case should be taken care of by
- * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+ * {@link #composeRecentsSplitLaunchAnimatorLegacy} below, but the way we launch tasks whether
* it's a single task or multiple tasks results in different entry-points.
*
* If it is null, then it will simply fade in the starting apps and fade out launcher (for the
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index e32aaee..bcaae99 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -200,13 +200,12 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState) {
- super.setModalStateEnabled(isModalState);
+ public void setModalStateEnabled(boolean isModalState, boolean animate) {
if (isModalState) {
- mActivity.getStateManager().goToState(RecentsState.MODAL_TASK);
+ mActivity.getStateManager().goToState(RecentsState.MODAL_TASK, animate);
} else {
if (mActivity.isInState(RecentsState.MODAL_TASK)) {
- mActivity.getStateManager().goToState(DEFAULT);
+ mActivity.getStateManager().goToState(DEFAULT, animate);
resetModalVisuals();
}
}
@@ -243,6 +242,9 @@
if (finalState != MODAL_TASK) {
setOverviewSelectEnabled(false);
}
+ if (finalState != OVERVIEW_SPLIT_SELECT) {
+ resetFromSplitSelectionState();
+ }
if (isOverlayEnabled) {
runActionOnRemoteHandles(remoteTargetHandle ->
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 223eba5..8b5f091 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -55,7 +55,8 @@
public static final RecentsState HOME = new RecentsState(3, 0);
public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
- FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS);
+ FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS
+ | FLAG_DISABLE_RESTORE);
public final int ordinal;
private final int mFlags;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 6bc24f2..c131c05 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -53,13 +53,13 @@
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
import java.util.HashMap;
@@ -290,9 +290,9 @@
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
- builder.withMatrix(mMatrix);
+ builder.setMatrix(mMatrix);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index fa7bc04..d7ff0be 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,7 +33,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -53,9 +52,11 @@
import com.android.quickstep.RemoteTargetGluer;
import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim;
+import com.android.quickstep.util.RecordingSurfaceTransaction;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.SurfaceTransaction;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import com.android.quickstep.util.TransformParams;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -415,21 +416,23 @@
private class FakeTransformParams extends TransformParams {
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, null, this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(transaction.mockProperties, null, this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
- SurfaceParams p = params[0];
- mFakeTaskView.setAnimationMatrix(p.matrix);
- mFakePreviousTaskView.setAnimationMatrix(p.matrix);
- mFakeTaskViewRect.set(p.windowCrop);
- mFakeTaskViewRadius = p.cornerRadius;
- mFakeTaskView.invalidateOutline();
- mFakePreviousTaskView.invalidateOutline();
+ public void applySurfaceParams(SurfaceTransaction params) {
+ if (params instanceof RecordingSurfaceTransaction) {
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+ mFakeTaskView.setAnimationMatrix(p.matrix);
+ mFakePreviousTaskView.setAnimationMatrix(p.matrix);
+ mFakeTaskViewRect.set(p.windowCrop);
+ mFakeTaskViewRadius = p.cornerRadius;
+ mFakeTaskView.invalidateOutline();
+ mFakePreviousTaskView.invalidateOutline();
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 37a28e5..0c422a0 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -190,7 +190,8 @@
getCardinality(info), // cardinality = 16;
info.getWidget().getSpanX(), // span_x = 17 [default = 1];
info.getWidget().getSpanY(), // span_y = 18 [default = 1];
- getAttributes(info) /* attributes */
+ getAttributes(info) /* attributes = 19 [(log_mode) = MODE_BYTES] */,
+ info.getIsKidsMode() /* is_kids_mode = 20 */
);
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 29ae9a1..064e675 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -71,7 +71,7 @@
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
*/
- protected float mDepth;
+ private float mDepth;
protected SurfaceControl mSurface;
@@ -143,7 +143,7 @@
}
}
- protected void setDepth(float depth) {
+ private void setDepth(float depth) {
depth = Utilities.boundToRange(depth, 0, 1);
// Round out the depth to dedupe frequent, non-perceptable updates
int depthI = (int) (depth * 256);
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 143042f..2a513ee 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -16,12 +16,14 @@
package com.android.quickstep.util;
import android.annotation.CallSuper;
+import android.view.Surface.Rotation;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import java.util.HashMap;
import java.util.Map;
@@ -32,15 +34,20 @@
public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+ private final RotationChangeProvider mRotationChangeProvider;
private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
+ private final UnfoldMoveFromCenterRotationListener mRotationListener =
+ new UnfoldMoveFromCenterRotationListener();
private boolean mAnimationInProgress = false;
- public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
+ public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
+ mRotationChangeProvider = rotationChangeProvider;
}
@CallSuper
@@ -50,6 +57,7 @@
mMoveFromCenterAnimation.updateDisplayProperties();
onPrepareViewsForAnimation();
onTransitionProgress(0f);
+ mRotationChangeProvider.addCallback(mRotationListener);
}
@CallSuper
@@ -62,6 +70,7 @@
@Override
public void onTransitionFinished() {
mAnimationInProgress = false;
+ mRotationChangeProvider.removeCallback(mRotationListener);
mMoveFromCenterAnimation.onTransitionFinished();
clearRegisteredViews();
}
@@ -109,4 +118,14 @@
view.setClipChildren(originalClipChildren);
}
}
+
+ private class UnfoldMoveFromCenterRotationListener implements
+ RotationChangeProvider.RotationListener {
+
+ @Override
+ public void onRotationChanged(@Rotation int newRotation) {
+ mMoveFromCenterAnimation.updateDisplayProperties(newRotation);
+ updateRegisteredViewsIfNeeded();
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
new file mode 100644
index 0000000..433d23f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * A {@link Task} container that can contain N number of tasks that are part of the desktop in
+ * recent tasks list.
+ */
+public class DesktopTask extends GroupTask {
+
+ public ArrayList<Task> tasks;
+
+ public DesktopTask(ArrayList<Task> tasks) {
+ super(tasks.get(0), null, null, TaskView.Type.DESKTOP);
+ this.tasks = tasks;
+ }
+
+ @Override
+ public boolean containsTask(int taskId) {
+ for (Task task : tasks) {
+ if (task.key.id == taskId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean hasMultipleTasks() {
+ return true;
+ }
+
+ @Override
+ public DesktopTask copy() {
+ return new DesktopTask(tasks);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index f30d00c..2be4f0a 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -20,6 +20,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
/**
@@ -27,24 +28,25 @@
* are represented as an app-pair in the recents task list.
*/
public class GroupTask {
- public @NonNull Task task1;
- public @Nullable Task task2;
- public @Nullable
- SplitBounds mSplitBounds;
+ @NonNull
+ public final Task task1;
+ @Nullable
+ public final Task task2;
+ @Nullable
+ public final SplitBounds mSplitBounds;
+ @TaskView.Type
+ public final int taskViewType;
- public GroupTask(@NonNull Task t1, @Nullable Task t2,
- @Nullable SplitBounds splitBounds) {
+ public GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds) {
+ this(t1, t2, splitBounds, t2 != null ? TaskView.Type.GROUPED : TaskView.Type.SINGLE);
+ }
+
+ protected GroupTask(@NonNull Task t1, @Nullable Task t2, @Nullable SplitBounds splitBounds,
+ @TaskView.Type int taskViewType) {
task1 = t1;
task2 = t2;
mSplitBounds = splitBounds;
- }
-
- public GroupTask(@NonNull GroupTask group) {
- task1 = new Task(group.task1);
- task2 = group.task2 != null
- ? new Task(group.task2)
- : null;
- mSplitBounds = group.mSplitBounds;
+ this.taskViewType = taskViewType;
}
public boolean containsTask(int taskId) {
@@ -54,4 +56,14 @@
public boolean hasMultipleTasks() {
return task2 != null;
}
+
+ /**
+ * Create a copy of this instance
+ */
+ public GroupTask copy() {
+ return new GroupTask(
+ new Task(task1),
+ task2 != null ? new Task(task2) : null,
+ mSplitBounds);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 97be437..f12074b 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -24,7 +24,6 @@
import android.util.FloatProperty;
import android.util.MathUtils;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import androidx.core.view.OneShotPreDrawListener;
@@ -34,6 +33,7 @@
import com.android.launcher3.util.HorizontalInsettableView;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
@@ -62,16 +62,17 @@
public LauncherUnfoldAnimationController(
Launcher launcher,
WindowManager windowManager,
- UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
+ UnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
+ RotationChangeProvider rotationChangeProvider) {
mLauncher = launcher;
mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
unfoldTransitionProgressProvider);
mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
- windowManager);
+ windowManager, rotationChangeProvider);
mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
- WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
+ rotationChangeProvider, mProgressProvider);
mNaturalOrientationProgressProvider.init();
// Animated in all orientations
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
new file mode 100644
index 0000000..a2f48dd
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+/**
+ * Extension for {@link SurfaceTransaction} which records the commands for mocking
+ */
+public class RecordingSurfaceTransaction extends SurfaceTransaction {
+
+ /**
+ * A mock builder which can be used for recording values
+ */
+ public final MockProperties mockProperties = new MockProperties();
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 81c124f..b2e159e 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -19,10 +19,10 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.view.SurfaceControl.Transaction;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
/**
* Animation listener which fades out the closing targets
@@ -40,7 +40,7 @@
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- TransactionCompat t = new TransactionCompat();
+ Transaction t = new Transaction();
if (mFirstFrame) {
for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
t.show(target.leash);
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index eec8582..ad54a70 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -46,6 +46,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -122,8 +123,7 @@
if (grid.isVerticalBarLayout()) {
for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
View child = hotseatIcons.getChildAt(i);
- CellLayout.LayoutParams lp =
- ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
}
} else {
@@ -193,7 +193,7 @@
// Set up springs on workspace items.
for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
View child = itemsContainer.getChildAt(i);
- CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+ CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
new file mode 100644
index 0000000..7ab285d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Helper class for building a {@link Transaction}.
+ */
+public class SurfaceTransaction {
+
+ private final Transaction mTransaction = new Transaction();
+ private final float[] mTmpValues = new float[9];
+
+ /**
+ * Creates a new builder for the provided surface
+ */
+ public SurfaceProperties forSurface(SurfaceControl surface) {
+ return surface.isValid() ? new SurfaceProperties(surface) : new MockProperties();
+ }
+
+ /**
+ * Returns the final transaction
+ */
+ public Transaction getTransaction() {
+ return mTransaction;
+ }
+
+ /**
+ * Utility class to update surface params in a transaction
+ */
+ public class SurfaceProperties {
+
+ private final SurfaceControl mSurface;
+
+ SurfaceProperties(SurfaceControl surface) {
+ mSurface = surface;
+ }
+
+ /**
+ * @param alpha The alpha value to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setAlpha(float alpha) {
+ mTransaction.setAlpha(mSurface, alpha);
+ return this;
+ }
+
+ /**
+ * @param matrix The matrix to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ mTransaction.setMatrix(mSurface, matrix, mTmpValues);
+ return this;
+ }
+
+ /**
+ * @param windowCrop The window crop to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ mTransaction.setWindowCrop(mSurface, windowCrop);
+ return this;
+ }
+
+ /**
+ * @param relativeLayer The relative layer.
+ * @return this Builder
+ */
+ public SurfaceProperties setLayer(int relativeLayer) {
+ mTransaction.setLayer(mSurface, relativeLayer);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for rounded corners to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setCornerRadius(float radius) {
+ mTransaction.setCornerRadius(mSurface, radius);
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for the shadows to apply to the surface.
+ * @return this Builder
+ */
+ public SurfaceProperties setShadowRadius(float radius) {
+ mTransaction.setShadowRadius(mSurface, radius);
+ return this;
+ }
+ }
+
+ /**
+ * Extension of {@link SurfaceProperties} which just stores all the values locally
+ */
+ public class MockProperties extends SurfaceProperties {
+
+ public float alpha = -1;
+ public Matrix matrix = null;
+ public Rect windowCrop = null;
+ public float cornerRadius = 0;
+ public float shadowRadius = 0;
+
+ protected MockProperties() {
+ super(null);
+ }
+
+ @Override
+ public SurfaceProperties setAlpha(float alpha) {
+ this.alpha = alpha;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setMatrix(Matrix matrix) {
+ this.matrix = matrix;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setWindowCrop(Rect windowCrop) {
+ this.windowCrop = windowCrop;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setLayer(int relativeLayer) {
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setCornerRadius(float radius) {
+ this.cornerRadius = radius;
+ return this;
+ }
+
+ @Override
+ public SurfaceProperties setShadowRadius(float radius) {
+ this.shadowRadius = radius;
+ return this;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 1200208..95473dc 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,7 +25,6 @@
import android.view.ViewRootImpl;
import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.function.Consumer;
@@ -70,18 +69,12 @@
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(final SurfaceParams... params) {
+ public void scheduleApply(SurfaceTransaction params) {
View view = mTargetViewRootImpl.getView();
if (view == null) {
return;
}
- Transaction t = new Transaction();
- for (int i = params.length - 1; i >= 0; i--) {
- SurfaceParams surfaceParams = params[i];
- if (surfaceParams.surface.isValid()) {
- surfaceParams.applyTo(t);
- }
- }
+ Transaction t = params.getTransaction();
mLastSequenceNumber++;
final int toApplySeqNo = mLastSequenceNumber;
@@ -102,7 +95,7 @@
}
/**
- * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
+ * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
* attached if necessary.
*/
public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index c03aa3f..a2e88f9 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -46,11 +46,11 @@
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
/**
* A utility class which emulates the layout behavior of TaskView and RecentsView
@@ -191,6 +191,7 @@
mStagePosition = mThumbnailPosition.equals(splitInfo.leftTopBounds) ?
STAGE_POSITION_TOP_OR_LEFT :
STAGE_POSITION_BOTTOM_OR_RIGHT;
+ mPositionHelper.setSplitBounds(convertSplitBounds(mSplitBounds), mStagePosition);
}
/**
@@ -386,10 +387,10 @@
@Override
public void onBuildTargetParams(
- Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpCropRect)
- .withCornerRadius(getCurrentCornerRadius());
+ SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ builder.setMatrix(mMatrix)
+ .setWindowCrop(mTmpCropRect)
+ .setCornerRadius(getCurrentCornerRadius());
// If mDrawsBelowRecents is unset, no reordering will be enforced.
if (mDrawsBelowRecents != null) {
@@ -398,7 +399,7 @@
// conflict with layers that WM core positions (ie. the input consumers). For shell
// transitions, the animation leashes are reparented to an animation container so we
// can bump layers as needed.
- builder.withLayer(mDrawsBelowRecents
+ builder.setLayer(mDrawsBelowRecents
? Integer.MIN_VALUE + 1
: ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
}
@@ -418,4 +419,15 @@
return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
}
+ /**
+ * TODO(b/254378592): Remove this after consolidation of classes
+ */
+ public static com.android.wm.shell.util.SplitBounds convertSplitBounds(SplitBounds bounds) {
+ return new com.android.wm.shell.util.SplitBounds(
+ bounds.leftTopBounds,
+ bounds.rightBottomBounds,
+ bounds.leftTopTaskId,
+ bounds.rightBottomTaskId
+ );
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index a7f25d4..3d505c6 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -21,10 +21,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
public class TransformParams {
@@ -113,8 +111,7 @@
* Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
* are computed based on these TransformParams.
*/
- public TransformParams setSyncTransactionApplier(
- SurfaceTransactionApplier applier) {
+ public TransformParams setSyncTransactionApplier(SurfaceTransactionApplier applier) {
mSyncTransactionApplier = applier;
return this;
}
@@ -137,16 +134,14 @@
return this;
}
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
- final int appLength = targets.unfilteredApps.length;
- final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
- SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
+ SurfaceTransaction transaction = new SurfaceTransaction();
mRecentsSurface = getRecentsSurface(targets);
- for (int i = 0; i < appLength; i++) {
+ for (int i = 0; i < targets.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
- SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
+ SurfaceProperties builder = transaction.forSurface(app.leash);
if (app.mode == targets.targetMode) {
if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
@@ -156,9 +151,9 @@
if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
&& app.isNotInRecents) {
float progress = Utilities.boundToRange(getProgress(), 0, 1);
- builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+ builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
} else {
- builder.withAlpha(getTargetAlpha());
+ builder.setAlpha(getTargetAlpha());
}
proxy.onBuildTargetParams(builder, app, this);
@@ -166,15 +161,15 @@
} else {
mBaseBuilderProxy.onBuildTargetParams(builder, app, this);
}
- surfaceParams[i] = builder.build();
}
+
// always put wallpaper layer to bottom.
+ final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
for (int i = 0; i < wallpaperLength; i++) {
RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
- surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
- .withLayer(Integer.MIN_VALUE).build();
+ transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
}
- return surfaceParams;
+ return transaction;
}
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
@@ -213,15 +208,11 @@
return mTargetSet;
}
- public void applySurfaceParams(SurfaceParams... params) {
+ public void applySurfaceParams(SurfaceTransaction builder) {
if (mSyncTransactionApplier != null) {
- mSyncTransactionApplier.scheduleApply(params);
+ mSyncTransactionApplier.scheduleApply(builder);
} else {
- TransactionCompat t = new TransactionCompat();
- for (SurfaceParams param : params) {
- SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
- }
- t.apply();
+ builder.getTransaction().apply();
}
}
@@ -229,9 +220,9 @@
public interface BuilderProxy {
BuilderProxy NO_OP = (builder, app, params) -> { };
- BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
+ BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
- void onBuildTargetParams(SurfaceParams.Builder builder,
+ void onBuildTargetParams(SurfaceProperties builder,
RemoteAnimationTargetCompat app, TransformParams params);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index dc97dd6..01a997a 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -21,6 +21,7 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves hotseat icons from center to the sides (final position)
@@ -29,8 +30,9 @@
private final Launcher mLauncher;
- public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 354d157..95a4b8f 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
+import com.android.systemui.unfold.updates.RotationChangeProvider;
/**
* Animation that moves launcher icons and widgets from center to the sides (final position)
@@ -30,8 +31,9 @@
private final Launcher mLauncher;
- public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
- super(windowManager);
+ public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager,
+ RotationChangeProvider rotationChangeProvider) {
+ super(windowManager, rotationChangeProvider);
mLauncher = launcher;
}
diff --git a/quickstep/src/com/android/quickstep/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
index 6171c5d..ba7d7f5 100644
--- a/quickstep/src/com/android/quickstep/util/ViewCapture.java
+++ b/quickstep/src/com/android/quickstep/util/ViewCapture.java
@@ -23,10 +23,9 @@
import android.content.Context;
import android.content.res.Resources;
-import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.Process;
+import android.os.SystemClock;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Base64;
@@ -57,7 +56,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.Future;
+import java.util.concurrent.Executor;
+import java.util.concurrent.FutureTask;
+import java.util.function.Consumer;
import java.util.zip.GZIPOutputStream;
/**
@@ -83,7 +84,7 @@
private final List<WindowListener> mListeners = new ArrayList<>();
private final Context mContext;
- private final LooperExecutor mExecutor;
+ private final Executor mExecutor;
// Pool used for capturing view tree on the UI thread.
private ViewRef mPool = new ViewRef();
@@ -156,8 +157,13 @@
ViewIdProvider idProvider = new ViewIdProvider(mContext.getResources());
// Collect all the tasks first so that all the tasks are posted on the executor
- List<Pair<String, Future<ExportedData>>> tasks = mListeners.stream()
- .map(l -> Pair.create(l.name, mExecutor.submit(() -> l.dumpToProto(idProvider))))
+ List<Pair<String, FutureTask<ExportedData>>> tasks = mListeners.stream()
+ .map(l -> {
+ FutureTask<ExportedData> task =
+ new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider));
+ mExecutor.execute(task);
+ return Pair.create(l.name, task);
+ })
.collect(toList());
tasks.forEach(pair -> {
@@ -187,7 +193,6 @@
private final View mRoot;
public final String name;
- private final Handler mHandler;
private final ViewRef mViewRef = new ViewRef();
private int mFrameIndexBg = -1;
@@ -196,20 +201,23 @@
private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
private boolean mDestroyed = false;
+ private final Consumer<ViewRef> mCaptureCallback = this::captureViewPropertiesBg;
WindowListener(View view, String name) {
mRoot = view;
this.name = name;
- mHandler = new Handler(mExecutor.getLooper(), this::captureViewPropertiesBg);
}
@Override
public void onDraw() {
Trace.beginSection("view_capture");
captureViewTree(mRoot, mViewRef);
- Message m = Message.obtain(mHandler);
- m.obj = mViewRef.next;
- mHandler.sendMessage(m);
+ ViewRef captured = mViewRef.next;
+ if (captured != null) {
+ captured.callback = mCaptureCallback;
+ captured.creationTime = SystemClock.uptimeMillis();
+ mExecutor.execute(captured);
+ }
mIsFirstFrame = false;
Trace.endSection();
}
@@ -219,12 +227,8 @@
* back to the pool
*/
@WorkerThread
- private boolean captureViewPropertiesBg(Message msg) {
- ViewRef viewRefStart = (ViewRef) msg.obj;
- long time = msg.getWhen();
- if (viewRefStart == null) {
- return false;
- }
+ private void captureViewPropertiesBg(ViewRef viewRefStart) {
+ long time = viewRefStart.creationTime;
mFrameIndexBg++;
if (mFrameIndexBg >= MEMORY_SIZE) {
mFrameIndexBg = 0;
@@ -292,7 +296,6 @@
viewRefEnd = viewRefEnd.next;
}
mNodesBg[mFrameIndexBg] = resultStart;
- return true;
}
private ViewPropertyRef findInLastFrame(int hashCode) {
@@ -464,11 +467,14 @@
}
}
- private static class ViewRef {
+ private static class ViewRef implements Runnable {
public View view;
public int childCount = 0;
public ViewRef next;
+ public Consumer<ViewRef> callback = null;
+ public long creationTime = 0;
+
public void transferTo(ViewPropertyRef out) {
out.childCount = this.childCount;
@@ -495,6 +501,15 @@
out.visibility = view.getVisibility();
out.willNotDraw = view.willNotDraw();
}
+
+ @Override
+ public void run() {
+ Consumer<ViewRef> oldCallback = callback;
+ callback = null;
+ if (oldCallback != null) {
+ oldCallback.accept(this);
+ }
+ }
}
private static final class ViewIdProvider {
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
new file mode 100644
index 0000000..9874f96
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * TaskView that contains all tasks that are part of the desktop.
+ */
+// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
+public class DesktopTaskView extends TaskView {
+
+ /** Flag to indicate whether desktop mode is available on the device */
+ public static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode", false);
+
+ private static final String TAG = DesktopTaskView.class.getSimpleName();
+
+ private static final boolean DEBUG = true;
+
+ private List<Task> mTasks;
+
+ private final ArrayList<TaskThumbnailView> mSnapshotViews = new ArrayList<>();
+
+ /** Maps {@code taskIds} to corresponding {@link TaskThumbnailView}s */
+ private final SparseArray<TaskThumbnailView> mSnapshotViewMap = new SparseArray<>();
+
+ private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
+
+ public DesktopTaskView(Context context) {
+ this(context, null);
+ }
+
+ public DesktopTaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DesktopTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ float[] outerRadii = new float[8];
+ Arrays.fill(outerRadii, getTaskCornerRadius());
+ RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
+ ShapeDrawable background = new ShapeDrawable(shape);
+ background.setTint(getResources().getColor(android.R.color.system_neutral2_300));
+ // TODO(b/244348395): this should be wallpaper
+ setBackground(background);
+
+ mSnapshotViews.add(mSnapshotView);
+ }
+
+ @Override
+ public void bind(Task task, RecentsOrientedState orientedState) {
+ bind(Collections.singletonList(task), orientedState);
+ }
+
+ /**
+ * Updates this desktop task to the gives task list defined in {@code tasks}
+ */
+ public void bind(List<Task> tasks, RecentsOrientedState orientedState) {
+ if (DEBUG) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("bind tasks=").append(tasks.size()).append("\n");
+ for (Task task : tasks) {
+ sb.append(" key=").append(task.key).append("\n");
+ }
+ Log.d(TAG, sb.toString());
+ }
+ if (tasks.isEmpty()) {
+ return;
+ }
+ cancelPendingLoadTasks();
+
+ mTasks = tasks;
+ mSnapshotViewMap.clear();
+
+ // Ensure there are equal number of snapshot views and tasks.
+ // More tasks than views, add views. More views than tasks, remove views.
+ // TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews
+ if (mSnapshotViews.size() > mTasks.size()) {
+ int diff = mSnapshotViews.size() - mTasks.size();
+ for (int i = 0; i < diff; i++) {
+ TaskThumbnailView snapshotView = mSnapshotViews.remove(0);
+ removeView(snapshotView);
+ }
+ } else if (mSnapshotViews.size() < mTasks.size()) {
+ int diff = mTasks.size() - mSnapshotViews.size();
+ for (int i = 0; i < diff; i++) {
+ TaskThumbnailView snapshotView = new TaskThumbnailView(getContext());
+ mSnapshotViews.add(snapshotView);
+ addView(snapshotView, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ }
+ }
+
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ TaskThumbnailView snapshotView = mSnapshotViews.get(i);
+ snapshotView.bind(task);
+ mSnapshotViewMap.put(task.key.id, snapshotView);
+ }
+
+ updateTaskIdContainer();
+ updateTaskIdAttributeContainer();
+
+ setOrientationState(orientedState);
+ }
+
+ private void updateTaskIdContainer() {
+ // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
+ // At least 2 elements in the array
+ mTaskIdContainer = new int[Math.max(mTasks.size(), 2)];
+ for (int i = 0; i < mTasks.size(); i++) {
+ mTaskIdContainer[i] = mTasks.get(i).key.id;
+ }
+ }
+
+ private void updateTaskIdAttributeContainer() {
+ // TODO(b/249371338): TaskView expects the array to have at least 2 elements.
+ // At least 2 elements in the array
+ mTaskIdAttributeContainer = new TaskIdAttributeContainer[Math.max(mTasks.size(), 2)];
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ mTaskIdAttributeContainer[i] = createAttributeContainer(task, thumbnailView);
+ }
+ }
+
+ private TaskIdAttributeContainer createAttributeContainer(Task task,
+ TaskThumbnailView thumbnailView) {
+ return new TaskIdAttributeContainer(task, thumbnailView, null, STAGE_POSITION_UNDEFINED);
+ }
+
+ @Nullable
+ @Override
+ public Task getTask() {
+ // TODO(b/249371338): returning first task. This won't work well with multiple tasks.
+ return mTasks.size() > 0 ? mTasks.get(0) : null;
+ }
+
+ @Override
+ public TaskThumbnailView getThumbnail() {
+ // TODO(b/249371338): returning single thumbnail. This won't work well with multiple tasks.
+ Task task = getTask();
+ if (task != null) {
+ return mSnapshotViewMap.get(task.key.id);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsTaskId(int taskId) {
+ // Thumbnail map contains taskId -> thumbnail map. Use the keys for contains
+ return mSnapshotViewMap.contains(taskId);
+ }
+
+ @Override
+ public void onTaskListVisibilityChanged(boolean visible, int changes) {
+ cancelPendingLoadTasks();
+ if (visible) {
+ RecentsModel model = RecentsModel.INSTANCE.get(getContext());
+ TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
+
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ for (Task task : mTasks) {
+ CancellableTask<?> thumbLoadRequest =
+ thumbnailCache.updateThumbnailInBackground(task, thumbnailData -> {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(task, thumbnailData);
+ }
+ });
+ if (thumbLoadRequest != null) {
+ mPendingThumbnailRequests.add(thumbLoadRequest);
+ }
+ }
+ }
+ } else {
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+ for (Task task : mTasks) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(null, null);
+ }
+ // Reset the task thumbnail ref
+ task.thumbnail = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setOrientationState(RecentsOrientedState orientationState) {
+ // TODO(b/249371338): this copies logic from TaskView
+ PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
+ boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+
+ LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
+
+ int thumbnailTopMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+ int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
+ int taskMargin = deviceProfile.overviewTaskMarginPx;
+
+ orientationHandler.setTaskIconParams(iconParams, taskMargin, taskIconHeight,
+ thumbnailTopMargin, isRtl);
+
+ LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
+ snapshotParams.topMargin = thumbnailTopMargin;
+
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
+ thumbnailView.setLayoutParams(snapshotParams);
+ }
+ }
+
+ @Override
+ protected void cancelPendingLoadTasks() {
+ for (CancellableTask<?> cancellableTask : mPendingThumbnailRequests) {
+ cancellableTask.cancel();
+ }
+ mPendingThumbnailRequests.clear();
+ }
+
+ @Override
+ public boolean offerTouchToChildren(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ protected boolean showTaskMenuWithContainer(IconView iconView) {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public RunnableList launchTaskAnimated() {
+ RunnableList endCallback = new RunnableList();
+ SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ RecentsView<?, ?> recentsView = getRecentsView();
+ recentsView.addSideTaskLaunchCallback(endCallback);
+ return endCallback;
+ }
+
+ @Override
+ public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+ SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
+ callback.accept(true);
+ }
+
+ @Override
+ void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ // Sets new thumbnails based on the incoming data and refreshes the rest.
+ // Create a copy of the thumbnail map, so we can track thumbnails that need refreshing.
+ SparseArray<TaskThumbnailView> thumbnailsToRefresh = mSnapshotViewMap.clone();
+ if (thumbnailDatas != null) {
+ for (Task task : mTasks) {
+ int key = task.key.id;
+ TaskThumbnailView thumbnailView = thumbnailsToRefresh.get(key);
+ ThumbnailData thumbnailData = thumbnailDatas.get(key);
+ if (thumbnailView != null && thumbnailData != null) {
+ thumbnailView.setThumbnail(task, thumbnailData);
+ // Remove this thumbnail from the list that should be refreshed.
+ thumbnailsToRefresh.remove(key);
+ }
+ }
+ }
+
+ // Refresh the rest that were not updated.
+ for (int i = 0; i < thumbnailsToRefresh.size(); i++) {
+ thumbnailsToRefresh.valueAt(i).refresh();
+ }
+ }
+
+ @Override
+ public TaskThumbnailView[] getThumbnails() {
+ TaskThumbnailView[] thumbnails = new TaskThumbnailView[mSnapshotViewMap.size()];
+ for (int i = 0; i < thumbnails.length; i++) {
+ thumbnails[i] = mSnapshotViewMap.valueAt(i);
+ }
+ return thumbnails;
+ }
+
+ @Override
+ public void onRecycle() {
+ resetPersistentViewTransforms();
+ // Clear any references to the thumbnail (it will be re-read either from the cache or the
+ // system on next bind)
+ for (Task task : mTasks) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.setThumbnail(task, null);
+ }
+ }
+ setOverlayEnabled(false);
+ onTaskListVisibilityChanged(false);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int containerWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int containerHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(containerWidth, containerHeight);
+
+ int thumbnails = mSnapshotViewMap.size();
+ if (thumbnails == 0) {
+ return;
+ }
+
+ int windowWidth = mActivity.getDeviceProfile().widthPx;
+ int windowHeight = mActivity.getDeviceProfile().heightPx;
+
+ float scaleWidth = containerWidth / (float) windowWidth;
+ float scaleHeight = containerHeight / (float) windowHeight;
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "onMeasure: container=[" + containerWidth + "," + containerHeight + "] window=["
+ + windowWidth + "," + windowHeight + "] scale=[" + scaleWidth + ","
+ + scaleHeight + "]");
+ }
+
+ // Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
+ for (int i = 0; i < mTasks.size(); i++) {
+ Task task = mTasks.get(i);
+ Rect taskSize = task.appBounds;
+ if (taskSize == null) {
+ // Default to quarter of the desktop if we did not get app bounds.
+ taskSize = new Rect(0, 0, windowWidth / 4, windowHeight / 4);
+ }
+
+ int thumbWidth = (int) (taskSize.width() * scaleWidth);
+ int thumbHeight = (int) (taskSize.height() * scaleHeight);
+
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.get(task.key.id);
+ if (thumbnailView != null) {
+ thumbnailView.measure(MeasureSpec.makeMeasureSpec(thumbWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(thumbHeight, MeasureSpec.EXACTLY));
+
+ // Position the task to the same position as it would be on the desktop
+ Point positionInParent = task.positionInParent;
+ if (positionInParent == null) {
+ positionInParent = new Point(0, 0);
+ }
+ int taskX = (int) (positionInParent.x * scaleWidth);
+ int taskY = (int) (positionInParent.y * scaleHeight);
+ thumbnailView.setX(taskX);
+ thumbnailView.setY(taskY);
+
+ if (DEBUG) {
+ Log.d(TAG, "onMeasure: task=" + task.key + " thumb=[" + thumbWidth + ","
+ + thumbHeight + "]" + " pos=[" + taskX + "," + taskY + "]");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setOverlayEnabled(boolean overlayEnabled) {
+ // Intentional no-op to prevent setting smart actions overlay on thumbnails
+ }
+
+ @Override
+ public void setFullscreenProgress(float progress) {
+ // TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
+ progress = Utilities.boundToRange(progress, 0, 1);
+ mFullscreenProgress = progress;
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
+ thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
+ updateSnapshotRadius();
+ }
+ }
+
+ @Override
+ protected void updateSnapshotRadius() {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setFullscreenParams(mCurrentFullscreenParams);
+ }
+ }
+
+ @Override
+ protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+ // no-op
+ }
+
+ @Override
+ public void setColorTint(float amount, int tintColor) {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setDimAlpha(amount);
+ }
+ }
+
+ @Override
+ protected void applyThumbnailSplashAlpha() {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setSplashAlpha(mTaskThumbnailSplashAlpha);
+ }
+ }
+
+ @Override
+ void setThumbnailVisibility(int visibility) {
+ for (int i = 0; i < mSnapshotViewMap.size(); i++) {
+ mSnapshotViewMap.valueAt(i).setVisibility(visibility);
+ }
+ }
+
+ @Override
+ protected boolean confirmSecondSplitSelectApp() {
+ // Desktop tile can't be in split screen
+ return false;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 967814a..64b5e0b 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -17,6 +17,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
@@ -24,8 +25,10 @@
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.HashMap;
@@ -84,9 +87,19 @@
mTaskIdContainer[1] = secondary.key.id;
mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT);
- mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
+ mTaskIdAttributeContainer[0].setStagePosition(
+ SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
mSnapshotView2.bind(secondary);
mSplitBoundsConfig = splitBoundsConfig;
+ if (mSplitBoundsConfig == null) {
+ return;
+ }
+ mSnapshotView.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+ .convertSplitBounds(splitBoundsConfig),
+ PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT);
+ mSnapshotView2.getPreviewPositionHelper().setSplitBounds(TaskViewSimulator
+ .convertSplitBounds(splitBoundsConfig),
+ PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT);
}
@Override
@@ -190,7 +203,8 @@
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id,
- STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio());
+ SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+ getSplitRatio());
}
@Override
@@ -208,6 +222,12 @@
}
@Override
+ public boolean containsTaskId(int taskId) {
+ return (mTask != null && mTask.key.id == taskId)
+ || (mSecondaryTask != null && mSecondaryTask.key.id == taskId);
+ }
+
+ @Override
public TaskThumbnailView[] getThumbnails() {
return new TaskThumbnailView[]{mSnapshotView, mSnapshotView2};
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index bb8506d..2ae7136 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -162,13 +162,12 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState) {
- super.setModalStateEnabled(isModalState);
+ public void setModalStateEnabled(boolean isModalState, boolean animate) {
if (isModalState) {
- mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK);
+ mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK, animate);
} else {
if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
- mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+ mActivity.getStateManager().goToState(LauncherState.OVERVIEW, animate);
resetModalVisuals();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index abace7c..514d5b9 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -31,6 +31,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
@@ -258,7 +259,9 @@
// If in 3-button mode, shift action buttons to accommodate 3-button layout.
// (Special exception for landscape tablets, where there is enough room and we don't need to
// shift the action buttons.)
- if (mDp.areNavButtonsInline && !largeScreenLandscape) {
+ if (mDp.areNavButtonsInline && !largeScreenLandscape
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// Add extra horizontal spacing
int additionalPadding = mDp.hotseatBarEndOffset;
if (isLayoutRtl()) {
@@ -288,7 +291,8 @@
return 0;
}
- if (!mDp.isGestureMode && mDp.isTaskbarPresent) {
+ if (!mDp.isGestureMode && mDp.isTaskbarPresent
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
return mDp.getOverviewActionsClaimedSpaceBelow();
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0e0acf0..f60e08d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -54,6 +54,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
+import static com.android.quickstep.views.DesktopTaskView.DESKTOP_MODE_SUPPORTED;
import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
@@ -72,6 +73,7 @@
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.LocusId;
import android.content.res.Configuration;
@@ -171,12 +173,14 @@
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TaskVisualsChangeListener;
@@ -189,12 +193,12 @@
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.pip.IPipAnimationListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -477,10 +481,11 @@
private final InvariantDeviceProfile mIdp;
/**
- * Getting views should be done via {@link #getTaskViewFromPool(boolean)}
+ * Getting views should be done via {@link #getTaskViewFromPool(int)}
*/
private final ViewPool<TaskView> mTaskViewPool;
private final ViewPool<GroupedTaskView> mGroupedTaskViewPool;
+ private final ViewPool<DesktopTaskView> mDesktopTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
@@ -737,6 +742,8 @@
10 /* initial size */);
mGroupedTaskViewPool = new ViewPool<>(context, this,
R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
+ mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
+ 5 /* max size */, 1 /* initial size */);
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@@ -981,6 +988,8 @@
}
if (child instanceof GroupedTaskView) {
mGroupedTaskViewPool.recycle((GroupedTaskView) taskView);
+ } else if (child instanceof DesktopTaskView) {
+ mDesktopTaskViewPool.recycle((DesktopTaskView) taskView);
} else {
mTaskViewPool.recycle(taskView);
}
@@ -1068,14 +1077,15 @@
appAnimator.setInterpolator(ACCEL_DEACCEL);
appAnimator.addUpdateListener(valueAnimator -> {
float percent = valueAnimator.getAnimatedFraction();
- SurfaceParams.Builder builder = new SurfaceParams.Builder(
- apps[apps.length - 1].leash);
+ SurfaceTransaction transaction = new SurfaceTransaction();
Matrix matrix = new Matrix();
matrix.postScale(percent, percent);
matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
- builder.withAlpha(percent).withMatrix(matrix);
- surfaceApplier.scheduleApply(builder.build());
+ transaction.forSurface(apps[apps.length - 1].leash)
+ .setAlpha(percent)
+ .setMatrix(matrix);
+ surfaceApplier.scheduleApply(transaction);
});
anim.play(appAnimator);
anim.addListener(new AnimatorListenerAdapter() {
@@ -1199,8 +1209,7 @@
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
- int[] taskIds = taskView.getTaskIds();
- if (taskIds[0] == taskId || taskIds[1] == taskId) {
+ if (taskView.containsTaskId(taskId)) {
return taskView;
}
}
@@ -1237,6 +1246,8 @@
if (!mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
}
+ InteractionJankMonitorWrapper.begin(/* view= */ this,
+ InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
}
@Override
@@ -1250,6 +1261,7 @@
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
}
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
}
@Override
@@ -1481,40 +1493,28 @@
// Add views as children based on whether it's grouped or single task
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
- boolean hasMultipleTasks = groupTask.hasMultipleTasks();
- TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
+ TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);
addView(taskView);
- if (hasMultipleTasks) {
+ if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
groupTask.mSplitBounds);
+ } else if (taskView instanceof DesktopTaskView) {
+ ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
+ mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
}
+
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
}
- boolean settlingOnNewTask = mNextPage != INVALID_PAGE;
- if (settlingOnNewTask) {
- // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
- mCurrentPage = previousCurrentPage;
- } else {
- // TODO(b/238461210): Remove logging once root cause of flake detected.
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d("b/238461210", "RecentsView#applyLoadPlan() -> !settlingOnNewTask -> "
- + "previousCurrentPage: " + previousCurrentPage
- + ", getScrollForPage(previousCurrentPage): "
- + getScrollForPage(previousCurrentPage));
- }
- setCurrentPage(previousCurrentPage);
- }
-
// Keep same previous focused task
TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
// If the list changed, maybe the focused task doesn't exist anymore
@@ -1539,21 +1539,36 @@
}
int targetPage = -1;
- if (!settlingOnNewTask) {
+ if (mNextPage != INVALID_PAGE) {
+ // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
+ mCurrentPage = previousCurrentPage;
+ if (currentTaskId != -1) {
+ currentTaskView = getTaskViewByTaskId(currentTaskId);
+ if (currentTaskView != null) {
+ targetPage = indexOfChild(currentTaskView);
+ }
+ }
+ } else {
// Set the current page to the running task, but not if settling on new task.
if (runningTaskId != -1) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
targetPage = indexOfChild(requireTaskViewAt(0));
}
- } else if (currentTaskId != -1) {
- currentTaskView = getTaskViewByTaskId(currentTaskId);
- if (currentTaskView != null) {
- targetPage = indexOfChild(currentTaskView);
- }
}
if (targetPage != -1 && mCurrentPage != targetPage) {
- setCurrentPage(targetPage);
+ int finalTargetPage = targetPage;
+ runOnPageScrollsInitialized(() -> {
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d("b/246283207", "RecentsView#applyLoadPlan() -> "
+ + "previousCurrentPage: " + previousCurrentPage
+ + ", targetPage: " + finalTargetPage
+ + ", getScrollForPage(targetPage): "
+ + getScrollForPage(finalTargetPage));
+ }
+ setCurrentPage(finalTargetPage);
+ });
}
if (mIgnoreResetTaskId != -1 &&
@@ -1749,7 +1764,7 @@
private void onOrientationChanged() {
// If overview is in modal state when rotate, reset it to overview state without running
// animation.
- setModalStateEnabled(false);
+ setModalStateEnabled(/* isModalState= */ false, /* animate= */ false);
if (isSplitSelectionActive()) {
onRotateInSplitSelectionState();
}
@@ -2123,10 +2138,19 @@
* Handle the edge case where Recents could increment task count very high over long
* period of device usage. Probably will never happen, but meh.
*/
- private <T extends TaskView> T getTaskViewFromPool(boolean isGrouped) {
- T taskView = isGrouped ?
- (T) mGroupedTaskViewPool.getView() :
- (T) mTaskViewPool.getView();
+ private TaskView getTaskViewFromPool(@TaskView.Type int type) {
+ TaskView taskView;
+ switch (type) {
+ case TaskView.Type.GROUPED:
+ taskView = mGroupedTaskViewPool.getView();
+ break;
+ case TaskView.Type.DESKTOP:
+ taskView = mDesktopTaskViewPool.getView();
+ break;
+ case TaskView.Type.SINGLE:
+ default:
+ taskView = mTaskViewPool.getView();
+ }
taskView.setTaskViewId(mTaskViewIdCount);
if (mTaskViewIdCount == Integer.MAX_VALUE) {
mTaskViewIdCount = 0;
@@ -2318,12 +2342,19 @@
}
int runningTaskViewId = -1;
boolean needGroupTaskView = runningTasks.length > 1;
+ boolean needDesktopTask = hasDesktopTask(runningTasks);
if (shouldAddStubTaskView(runningTasks)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
- if (needGroupTaskView) {
- taskView = getTaskViewFromPool(true);
+ if (needDesktopTask) {
+ taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
+ mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
+ addView(taskView, 0);
+ ((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
+ mOrientationState);
+ } else if (needGroupTaskView) {
+ taskView = getTaskViewFromPool(TaskView.Type.GROUPED);
mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
addView(taskView, 0);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
@@ -2332,7 +2363,7 @@
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
mOrientationState, mSplitBoundsConfig);
} else {
- taskView = getTaskViewFromPool(false);
+ taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
addView(taskView, 0);
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
@@ -2367,6 +2398,18 @@
reloadIfNeeded();
}
+ private boolean hasDesktopTask(Task[] runningTasks) {
+ if (!DESKTOP_MODE_SUPPORTED) {
+ return false;
+ }
+ for (Task task : runningTasks) {
+ if (task.key.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Sets the running task id, cleaning up the old running task if necessary.
*/
@@ -5159,11 +5202,8 @@
setInsets(mInsets);
}
- /**
- * Enables or disables modal state for RecentsView
- * @param isModalState
- */
- public void setModalStateEnabled(boolean isModalState) { }
+ /** Enables or disables modal state for RecentsView */
+ public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
public TaskOverlayFactory getTaskOverlayFactory() {
return mTaskOverlayFactory;
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index 27ec01a..b0b111d 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -29,6 +29,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.util.DisplayController;
@@ -114,11 +115,13 @@
int getThreeButtonNavShift() {
DeviceProfile dp = mLauncher.getDeviceProfile();
if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS)
- && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))) {
+ && ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
int navButtonWidth = getResources().getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_size);
int extraMargin = getResources().getDimensionPixelSize(
- R.dimen.taskbar_contextual_button_margin);
+ R.dimen.taskbar_split_instructions_margin);
// Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
// up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
// center of the screen and we want to center it in the remaining space, therefore we
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index b586ac3..bdc0585 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -37,7 +37,6 @@
import com.android.launcher3.popup.RoundedArrowDrawable
import com.android.launcher3.popup.SystemShortcut
import com.android.launcher3.util.Themes
-import com.android.quickstep.KtR
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
@@ -53,9 +52,9 @@
.fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
val taskMenuViewWithArrow = activity.layoutInflater
.inflate(
- KtR.layout.task_menu_with_arrow,
- activity.dragLayer,
- false
+ R.layout.task_menu_with_arrow,
+ activity.dragLayer,
+ false
) as TaskMenuViewWithArrow<*>
return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow)
@@ -93,7 +92,7 @@
private var optionMeasuredHeight = 0
private val arrowHorizontalPadding: Int
get() = if (taskView.isFocusedTask)
- resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
+ resources.getDimensionPixelSize(R.dimen.task_menu_horizontal_padding)
else
0
@@ -119,7 +118,7 @@
override fun onFinishInflate() {
super.onFinishInflate()
- optionLayout = findViewById(KtR.id.menu_option_layout)
+ optionLayout = findViewById(R.id.menu_option_layout)
}
private fun populateAndShowForTask(
@@ -170,7 +169,7 @@
// Add the spaces between items
val divider = ShapeDrawable(RectShape())
divider.paint.color = resources.getColor(android.R.color.transparent)
- val dividerSpacing = resources.getDimension(KtR.dimen.task_menu_spacing).toInt()
+ val dividerSpacing = resources.getDimension(R.dimen.task_menu_spacing).toInt()
optionLayout.showDividers = SHOW_DIVIDER_MIDDLE
// Set the orientation, which makes the menu show
@@ -187,7 +186,7 @@
private fun addMenuOption(menuOption: SystemShortcut<*>) {
val menuOptionView = mActivityContext.layoutInflater.inflate(
- KtR.layout.task_view_menu_option, this, false
+ R.layout.task_view_menu_option, this, false
) as LinearLayout
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon),
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 04a0af1..6792dc5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -370,7 +370,7 @@
* <p>We want to show the splash if the aspect ratio or rotation of the thumbnail would be
* different from the task.
*/
- boolean shouldShowSplashView() {
+ public boolean shouldShowSplashView() {
return isThumbnailAspectRatioDifferentFromThumbnailData()
|| isThumbnailRotationDifferentFromTask();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 0125775..35f0f5d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,7 +18,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
-import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
@@ -87,7 +86,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
@@ -136,6 +134,17 @@
@IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
public @interface TaskDataChanges {}
+ /**
+ * Type of task view
+ */
+ @Retention(SOURCE)
+ @IntDef({Type.SINGLE, Type.GROUPED, Type.DESKTOP})
+ public @interface Type {
+ int SINGLE = 1;
+ int GROUPED = 2;
+ int DESKTOP = 3;
+ }
+
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
@@ -332,7 +341,7 @@
protected TaskThumbnailView mSnapshotView;
protected IconView mIconView;
protected final DigitalWellBeingToast mDigitalWellBeingToast;
- private float mFullscreenProgress;
+ protected float mFullscreenProgress;
private float mGridProgress;
protected float mTaskThumbnailSplashAlpha;
private float mNonGridScale = 1;
@@ -374,8 +383,8 @@
/**
* Index 0 will contain taskID of left/top task, index 1 will contain taskId of bottom/right
*/
- protected final int[] mTaskIdContainer = new int[]{-1, -1};
- protected final TaskIdAttributeContainer[] mTaskIdAttributeContainer =
+ protected int[] mTaskIdContainer = new int[]{-1, -1};
+ protected TaskIdAttributeContainer[] mTaskIdAttributeContainer =
new TaskIdAttributeContainer[2];
private boolean mShowScreenshot;
@@ -526,6 +535,13 @@
}
/**
+ * Check if given {@code taskId} is tracked in this view
+ */
+ public boolean containsTaskId(int taskId) {
+ return mTask != null && mTask.key.id == taskId;
+ }
+
+ /**
* @return integer array of two elements to be size consistent with max number of tasks possible
* index 0 will contain the taskId, index 1 will be -1 indicating a null taskID value
*/
@@ -565,13 +581,13 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
RecentsView recentsView = getRecentsView();
- if (recentsView == null || mTask == null) {
+ if (recentsView == null || getTask() == null) {
return false;
}
SplitSelectStateController splitSelectStateController =
recentsView.getSplitSelectController();
if (splitSelectStateController.isSplitSelectActive() &&
- splitSelectStateController.getInitialTaskId() == mTask.key.id) {
+ splitSelectStateController.getInitialTaskId() == getTask().key.id) {
// Prevent taps on the this taskview if it's being animated into split select state
return false;
}
@@ -598,11 +614,14 @@
* @return {@code true} if user is already in split select mode and this tap was to choose the
* second app. {@code false} otherwise
*/
- private boolean confirmSecondSplitSelectApp() {
+ protected boolean confirmSecondSplitSelectApp() {
int index = getLastSelectedChildTaskIndex();
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
- return getRecentsView().confirmSplitSelect(this, container.getTask(),
- container.getIconView(), container.getThumbnailView());
+ if (container != null) {
+ return getRecentsView().confirmSplitSelect(this, container.getTask(),
+ container.getIconView(), container.getThumbnailView());
+ }
+ return false;
}
/**
@@ -669,9 +688,7 @@
if (freezeTaskList) {
opts.setFreezeRecentTasksReordering();
}
- // TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow.
- opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView()
- ? SPLASH_SCREEN_STYLE_SOLID_COLOR : opts.getSplashScreenStyle());
+ opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView());
Task.TaskKey key = mTask.key;
UI_HELPER_EXECUTOR.execute(() -> {
if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
@@ -710,11 +727,6 @@
RecentsView recentsView = getRecentsView();
RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
RunnableList runnableList = new RunnableList();
- if (mTask != null && mTask.desktopTile) {
- // clicked on desktop
- SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps();
- return runnableList;
- }
if (isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
return runnableList;
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index c1b3beb..b903691 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,7 +36,6 @@
import android.content.ComponentName;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -111,7 +110,6 @@
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
doAnswer(i -> {
String pkg = i.getArgument(0);
- Log.e("Hello", "Getting v " + pkg);
return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
.filter(a -> pkg.equals(a.provider.getPackageName()))
.collect(Collectors.toList());
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
new file mode 100644
index 0000000..58f0949
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactoryTest.kt
@@ -0,0 +1,148 @@
+package com.android.launcher3.taskbar.navbutton
+
+import android.content.res.Resources
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.test.runner.AndroidJUnit4
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.taskbar.TaskbarManager
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import com.android.launcher3.R
+import org.junit.Assume.assumeTrue
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import java.lang.IllegalStateException
+
+@RunWith(AndroidJUnit4::class)
+class NavButtonLayoutFactoryTest {
+
+ @Mock
+ lateinit var mockDeviceProfile: DeviceProfile
+ @Mock
+ lateinit var mockParentButtonContainer: FrameLayout
+ @Mock
+ lateinit var mockNavLayout: LinearLayout
+ @Mock
+ lateinit var mockStartContextualLayout: ViewGroup
+ @Mock
+ lateinit var mockEndContextualLayout: ViewGroup
+ @Mock
+ lateinit var mockResources: Resources
+ @Mock
+ lateinit var mockBackButton: ImageView
+ @Mock
+ lateinit var mockRecentsButton: ImageView
+ @Mock
+ lateinit var mockHomeButton: ImageView
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ // Init end nav buttons
+ whenever(mockNavLayout.childCount).thenReturn(3)
+ whenever(mockNavLayout.findViewById<View>(R.id.back)).thenReturn(mockBackButton)
+ whenever(mockNavLayout.findViewById<View>(R.id.home)).thenReturn(mockHomeButton)
+ whenever(mockNavLayout.findViewById<View>(R.id.recent_apps)).thenReturn(mockRecentsButton)
+
+ // Init top level layout
+ whenever(mockParentButtonContainer.findViewById<LinearLayout>(R.id.end_nav_buttons))
+ .thenReturn(mockNavLayout)
+ whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.end_contextual_buttons))
+ .thenReturn(mockEndContextualLayout)
+ whenever(mockParentButtonContainer.findViewById<ViewGroup>(R.id.start_contextual_buttons))
+ .thenReturn(mockStartContextualLayout)
+ }
+
+ @Test
+ fun getKidsLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = true
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = true, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = false)
+ assert(layoutter is KidsNavLayoutter)
+ }
+
+ @Test
+ fun getSetupLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = true
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = true, isThreeButtonNav = false,
+ phoneMode = false)
+ assert(layoutter is SetupNavLayoutter)
+ }
+
+ @Test
+ fun getTaskbarNavLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = true
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = false)
+ assert(layoutter is TaskbarNavLayoutter)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun noValidLayoutForLargeScreenTaskbarNotPresent() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = false)
+ }
+
+ @Test
+ fun getTaskbarPortraitLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
+ phoneMode = true)
+ assert(layoutter is PhonePortraitNavLayoutter)
+ }
+
+ @Test
+ fun getTaskbarLandscapeLayoutter() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ setDeviceProfileLandscape()
+ val layoutter: NavButtonLayoutFactory.NavButtonLayoutter =
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = true,
+ phoneMode = true)
+ assert(layoutter is PhoneLandscapeNavLayoutter)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun noValidLayoutForPhoneGestureNav() {
+ assumeTrue(TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ mockDeviceProfile.isTaskbarPresent = false
+ getLayoutter(isKidsMode = false, isInSetup = false, isThreeButtonNav = false,
+ phoneMode = true)
+ }
+
+ private fun setDeviceProfileLandscape() {
+ // Use reflection to modify landscape field
+ val landscapeField = mockDeviceProfile.javaClass.getDeclaredField("isLandscape")
+ landscapeField.isAccessible = true
+ landscapeField.set(mockDeviceProfile, true)
+ }
+
+ private fun getLayoutter(isKidsMode: Boolean, isInSetup: Boolean,
+ isThreeButtonNav: Boolean, phoneMode: Boolean):
+ NavButtonLayoutFactory.NavButtonLayoutter {
+ return NavButtonLayoutFactory.getUiLayoutter(
+ deviceProfile = mockDeviceProfile,
+ navButtonsView = mockParentButtonContainer,
+ resources = mockResources,
+ isKidsMode = isKidsMode, isInSetup = isInSetup,
+ isThreeButtonNav = isThreeButtonNav, phoneMode = phoneMode
+ )
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 190b002..97eee1f 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -24,7 +24,6 @@
import android.graphics.RectF;
import android.util.ArrayMap;
import android.view.Surface;
-import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -42,8 +41,8 @@
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.SurfaceTransaction.MockProperties;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@@ -207,17 +206,21 @@
}
@Override
- public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
- SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
- proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
- return new SurfaceParams[] {builder.build()};
+ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
+ proxy.onBuildTargetParams(
+ transaction.mockProperties, mock(RemoteAnimationTargetCompat.class), this);
+ return transaction;
}
@Override
- public void applySurfaceParams(SurfaceParams[] params) {
+ public void applySurfaceParams(SurfaceTransaction params) {
+ Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
+ MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
+
// Verify that the task position remains the same
RectF newAppBounds = new RectF(mAppBounds);
- params[0].matrix.mapRect(newAppBounds);
+ p.matrix.mapRect(newAppBounds);
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
new file mode 100644
index 0000000..d5fe51e
--- /dev/null
+++ b/res/layout/page_indicator_dots.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/page_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/workspace_page_indicator_height"
+ android:layout_gravity="bottom | center_horizontal"
+ android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 4dee6e7..39f93d9 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -34,8 +34,7 @@
android:clipChildren="false"
android:orientation="horizontal"
android:paddingLeft="12dp"
- android:paddingRight="12dp"
- android:alpha="0">
+ android:paddingRight="12dp">
<com.android.launcher3.folder.FolderNameEditText
android:id="@+id/folder_name"
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 0a4943e..67998d5 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -21,9 +21,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="work_folder_name" msgid="3753320833950115786">"কৰ্মস্থান"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"এপটো ইনষ্টল কৰা নহ\'ল।"</string>
- <string name="activity_not_available" msgid="7456344436509528827">"এপটো নাই"</string>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপটোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"এপ্টো ইনষ্টল কৰা নহ\'ল।"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"এপ্টো নাই"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপ্টোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"ৱিজেটবোৰক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"শ্বৰ্টকাট নাই"</string>
<string name="home_screen" msgid="5629429142036709174">"গৃহ স্ক্ৰীন"</string>
@@ -119,8 +119,8 @@
<string name="package_state_unknown" msgid="7592128424511031410">"অজ্ঞাত"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"আঁতৰাওক"</string>
<string name="abandoned_search" msgid="891119232568284442">"সন্ধান কৰক"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপটো ইনষ্টল কৰা হোৱা নাই"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপটো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপটো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপ্টো ইনষ্টল কৰা হোৱা নাই"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপ্টো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপ্টো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হৈছে"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 3cec97d..69157bc 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -54,7 +54,7 @@
<string name="widget_education_header" msgid="4874760613775913787">"အသုံးဝင်သော အချက်အလက်များကို အလွယ်တကူ ရယူလိုက်ပါ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"အက်ပ်မဖွင့်ဘဲ အချက်အလက်များရယူရန် ပင်မစာမျက်နှာတွင် ဝိဂျက်များ ထည့်နိုင်သည်"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ဝိဂျက် ဆက်တင်များကို ပြောင်းရန် တို့ပါ"</string>
- <string name="widget_education_close_button" msgid="8676165703104836580">"ရပြီ"</string>
+ <string name="widget_education_close_button" msgid="8676165703104836580">"နားလည်ပြီ"</string>
<string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"ဝိဂျက် ဆက်တင်များကို ပြောင်းပါ"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ရှာဖွေမှု အက်ပ်များ"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"အက်ပ်များကို ဖွင့်နေသည်…"</string>
@@ -166,7 +166,7 @@
<string name="work_apps_paused_body" msgid="261634750995824906">"သင်၏ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
<string name="work_apps_paused_content_description" msgid="5149623040804051095">"အလုပ်သုံးအက်ပ်များ ပိတ်ထားသည်။ သင်၏ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
<string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"အလုပ်သုံးအက်ပ်များကို တံဆိပ်တပ်ထားပြီး သင်၏ IT စီမံခန့်ခွဲသူက မြင်နိုင်ပါသည်"</string>
- <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ရပြီ"</string>
+ <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"နားလည်ပြီ"</string>
<string name="work_apps_pause_btn_text" msgid="4669288269140620646">"အလုပ်သုံးအက်ပ်များကို ခဏရပ်ရန်"</string>
<string name="work_apps_enable_btn_text" msgid="1156432622148413741">"အလုပ်သုံးအက်ပ်များ ဖွင့်ရန်"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 47584e2..a9ba07d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -368,6 +368,7 @@
<dimen name="taskbar_hotseat_nav_spacing">0dp</dimen>
<dimen name="taskbar_button_margin_default">0dp</dimen>
<dimen name="taskbar_button_space_inbetween">0dp</dimen>
+ <dimen name="taskbar_button_space_inbetween_phone">0dp</dimen>
<dimen name="taskbar_button_margin_5_5">0dp</dimen>
<dimen name="taskbar_button_margin_6_5">0dp</dimen>
<dimen name="taskbar_button_margin_4_5">0dp</dimen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 5ee6fce..73acd87 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -94,6 +94,7 @@
public static final int TYPE_TASKBAR_EDUCATION_DIALOG = 1 << 16;
public static final int TYPE_TASKBAR_ALL_APPS = 1 << 17;
public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 18;
+ public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 19;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index fbb0a57..555fbb4 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -32,6 +32,7 @@
import androidx.annotation.Px;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.InstanceId;
@@ -265,7 +266,7 @@
}
}
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams();
ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
lp.cellX = lp.tmpCellX = widgetInfo.cellX;
lp.cellY = lp.tmpCellY = widgetInfo.cellY;
@@ -405,7 +406,7 @@
*/
private void resizeWidgetIfNeeded(boolean onDismiss) {
ViewGroup.LayoutParams wlp = mWidgetView.getLayoutParams();
- if (!(wlp instanceof CellLayout.LayoutParams)) {
+ if (!(wlp instanceof CellLayoutLayoutParams)) {
return;
}
DeviceProfile dp = mLauncher.getDeviceProfile();
@@ -420,7 +421,7 @@
mDirectionVector[0] = 0;
mDirectionVector[1] = 0;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) wlp;
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) wlp;
int spanX = lp.cellHSpan;
int spanY = lp.cellVSpan;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5fb8925..c0a00c2 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -942,6 +942,11 @@
return mIconSize;
}
+ public boolean isDisplaySearchResult() {
+ return mDisplay == DISPLAY_SEARCH_RESULT ||
+ mDisplay == DISPLAY_SEARCH_RESULT_SMALL;
+ }
+
private void updateTranslation() {
super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ mTranslationForMoveFromCenterAnimation.x
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9f3e1fa..cdd8f5a 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -19,6 +19,7 @@
import static android.animation.ValueAnimator.areAnimatorsEnabled;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
@@ -61,6 +62,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -130,7 +132,7 @@
// These arrays are used to implement the drag visualization on x-large screens.
// They are used as circular arrays, indexed by mDragOutlineCurrent.
- @Thunk final CellLayout.LayoutParams[] mDragOutlines = new CellLayout.LayoutParams[4];
+ @Thunk final CellLayoutLayoutParams[] mDragOutlines = new CellLayoutLayoutParams[4];
@Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
private final InterruptibleInOutAnimator[] mDragOutlineAnims =
new InterruptibleInOutAnimator[mDragOutlines.length];
@@ -139,7 +141,7 @@
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
- @Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
+ @Thunk final ArrayMap<CellLayoutLayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
@Thunk final ArrayMap<Reorderable, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
private boolean mItemPlacementDirty = false;
@@ -269,7 +271,7 @@
mDragCell[0] = mDragCell[1] = -1;
mDragCellSpan[0] = mDragCellSpan[1] = -1;
for (int i = 0; i < mDragOutlines.length; i++) {
- mDragOutlines[i] = new CellLayout.LayoutParams(0, 0, 0, 0);
+ mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0);
}
mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
@@ -551,7 +553,9 @@
public void setSpringLoadedProgress(float progress) {
if (Float.compare(progress, mSpringLoadedProgress) != 0) {
mSpringLoadedProgress = progress;
- updateBgAlpha();
+ if (!SHOW_HOME_GARDENING.get()) {
+ updateBgAlpha();
+ }
setGridAlpha(progress);
}
}
@@ -576,7 +580,9 @@
public void setScrollProgress(float progress) {
if (Float.compare(Math.abs(progress), mScrollProgress) != 0) {
mScrollProgress = Math.abs(progress);
- updateBgAlpha();
+ if (!SHOW_HOME_GARDENING.get()) {
+ updateBgAlpha();
+ }
}
}
@@ -615,7 +621,7 @@
}
}
- if (mVisualizeDropLocation) {
+ if (mVisualizeDropLocation && !SHOW_HOME_GARDENING.get()) {
for (int i = 0; i < mDragOutlines.length; i++) {
final float alpha = mDragOutlineAlphas[i];
if (alpha <= 0) continue;
@@ -737,9 +743,19 @@
return mContainerType == WORKSPACE;
}
- public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
- boolean markCells) {
- final LayoutParams lp = params;
+ /**
+ * Adds the given view to the CellLayout
+ *
+ * @param child view to add.
+ * @param index index of the CellLayout children where to add the view.
+ * @param childId id of the view.
+ * @param params represent the logic of the view on the CellLayout.
+ * @param markCells if the occupied cells should be marked or not
+ * @return if adding the view was successful
+ */
+ public boolean addViewToCellLayout(View child, int index, int childId,
+ CellLayoutLayoutParams params, boolean markCells) {
+ final CellLayoutLayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
@@ -1046,7 +1062,7 @@
ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
if (clc.indexOfChild(child) != -1 && (child instanceof Reorderable)) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
final ItemInfo info = (ItemInfo) child.getTag();
final Reorderable item = (Reorderable) child;
@@ -1153,7 +1169,7 @@
mDragOutlineAnims[oldIndex].animateOut();
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
- LayoutParams cell = mDragOutlines[mDragOutlineCurrent];
+ CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent];
cell.cellX = cellX;
cell.cellY = cellY;
cell.cellHSpan = spanX;
@@ -1708,7 +1724,7 @@
// cluster.
if (!cluster.views.contains(v) && v != dragView) {
if (cluster.isViewTouchingEdge(v, whichEdge)) {
- LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
if (!lp.canReorder) {
// The push solution includes the all apps button, this is not viable.
fail = true;
@@ -1919,7 +1935,7 @@
for (View child: solution.map.keySet()) {
if (child == ignoreView) continue;
CellAndSpan c = solution.map.get(child);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
if (Rect.intersects(r0, r1)) {
if (!lp.canReorder) {
@@ -2016,7 +2032,7 @@
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
CellAndSpan c;
if (temp) {
c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
@@ -2034,7 +2050,7 @@
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
CellAndSpan c = solution.map.get(child);
if (c != null) {
lp.tmpCellX = c.cellX;
@@ -2082,7 +2098,7 @@
!= null && !solution.intersectingViews.contains(child);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (c != null && !skip && (child instanceof Reorderable)) {
ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child,
mode, lp.cellX, lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY);
@@ -2273,7 +2289,7 @@
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
ItemInfo info = (ItemInfo) child.getTag();
// We do a null check here because the item info can be null in the case of the
// AllApps button in the hotseat.
@@ -2298,7 +2314,8 @@
private void setUseTempCoords(boolean useTempCoords) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
- LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mShortcutsAndWidgets.getChildAt(
+ i).getLayoutParams();
lp.useTmpCoords = useTempCoords;
}
}
@@ -2383,7 +2400,8 @@
for (int i = 0; i < count; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
if (child == dragView) continue;
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
if (Rect.intersects(r0, r1)) {
mIntersectingViews.add(child);
@@ -2408,7 +2426,8 @@
final int count = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < count; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
lp.tmpCellX = lp.cellX;
lp.tmpCellY = lp.cellY;
@@ -2685,7 +2704,8 @@
*/
void onDropChild(View child) {
if (child != null) {
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) child.getLayoutParams();
lp.dropped = true;
child.requestLayout();
markCellsAsOccupiedForView(child);
@@ -2727,7 +2747,8 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
}
@@ -2739,7 +2760,8 @@
return;
}
if (view == null || view.getParent() != mShortcutsAndWidgets) return;
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams
+ lp = (CellLayoutLayoutParams) view.getLayoutParams();
mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
}
@@ -2763,165 +2785,17 @@
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new CellLayout.LayoutParams(getContext(), attrs);
+ return new CellLayoutLayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof CellLayout.LayoutParams;
+ return p instanceof CellLayoutLayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new CellLayout.LayoutParams(p);
- }
-
- public static class LayoutParams extends ViewGroup.MarginLayoutParams {
- /**
- * Horizontal location of the item in the grid.
- */
- @ViewDebug.ExportedProperty
- public int cellX;
-
- /**
- * Vertical location of the item in the grid.
- */
- @ViewDebug.ExportedProperty
- public int cellY;
-
- /**
- * Temporary horizontal location of the item in the grid during reorder
- */
- public int tmpCellX;
-
- /**
- * Temporary vertical location of the item in the grid during reorder
- */
- public int tmpCellY;
-
- /**
- * Indicates that the temporary coordinates should be used to layout the items
- */
- public boolean useTmpCoords;
-
- /**
- * Number of cells spanned horizontally by the item.
- */
- @ViewDebug.ExportedProperty
- public int cellHSpan;
-
- /**
- * Number of cells spanned vertically by the item.
- */
- @ViewDebug.ExportedProperty
- public int cellVSpan;
-
- /**
- * Indicates whether the item will set its x, y, width and height parameters freely,
- * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
- */
- public boolean isLockedToGrid = true;
-
- /**
- * Indicates whether this item can be reordered. Always true except in the case of the
- * the AllApps button and QSB place holder.
- */
- public boolean canReorder = true;
-
- // X coordinate of the view in the layout.
- @ViewDebug.ExportedProperty
- public int x;
- // Y coordinate of the view in the layout.
- @ViewDebug.ExportedProperty
- public int y;
-
- boolean dropped;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- cellHSpan = 1;
- cellVSpan = 1;
- }
-
- public LayoutParams(LayoutParams source) {
- super(source);
- this.cellX = source.cellX;
- this.cellY = source.cellY;
- this.cellHSpan = source.cellHSpan;
- this.cellVSpan = source.cellVSpan;
- }
-
- public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
- super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- this.cellX = cellX;
- this.cellY = cellY;
- this.cellHSpan = cellHSpan;
- this.cellVSpan = cellVSpan;
- }
-
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
- int rowCount, Point borderSpace, @Nullable Rect inset) {
- setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
- borderSpace, inset);
- }
-
- /**
- * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
- * if the view needs to be scaled.
- *
- * ie. In multi-window mode, we setup widgets so that they are measured and laid out
- * using their full/invariant device profile sizes.
- */
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
- int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
- @Nullable Rect inset) {
- if (isLockedToGrid) {
- final int myCellHSpan = cellHSpan;
- final int myCellVSpan = cellVSpan;
- int myCellX = useTmpCoords ? tmpCellX : cellX;
- int myCellY = useTmpCoords ? tmpCellY : cellY;
-
- if (invertHorizontally) {
- myCellX = colCount - myCellX - cellHSpan;
- }
-
- int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
- int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
-
- float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
- float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
-
- width = Math.round(myCellWidth) - leftMargin - rightMargin;
- height = Math.round(myCellHeight) - topMargin - bottomMargin;
- x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
- y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
-
- if (inset != null) {
- x -= inset.left;
- y -= inset.top;
- width += inset.left + inset.right;
- height += inset.top + inset.bottom;
- }
- }
- }
-
- /**
- * Sets the position to the provided point
- */
- public void setCellXY(Point point) {
- cellX = point.x;
- cellY = point.y;
- }
-
- public String toString() {
- return "(" + this.cellX + ", " + this.cellY + ")";
- }
+ return new CellLayoutLayoutParams(p);
}
// This class stores info for two purposes:
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 9a1bba9..1c26f04 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -218,6 +218,9 @@
public int overviewRowSpacing;
public int overviewGridSideMargin;
+ // Split staging
+ public int splitPlaceholderInset;
+
// Widgets
private final ViewScaleProvider mViewScaleProvider;
@@ -459,6 +462,8 @@
overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
overviewGridSideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
+ splitPlaceholderInset = res.getDimensionPixelSize(R.dimen.split_placeholder_inset);
+
// Calculate all of the remaining variables.
extraSpace = updateAvailableDimensions(res);
@@ -1272,7 +1277,9 @@
* Returns the number of pixels required below OverviewActions excluding insets.
*/
public int getOverviewActionsClaimedSpaceBelow() {
- if (isTaskbarPresent && !isGestureMode) {
+ if (isTaskbarPresent && !isGestureMode
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// Align vertically to where nav buttons are.
return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
}
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 2f927d3..747b755 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,6 +24,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -91,7 +92,8 @@
protected int getAvailableScrollHeight() {
// AvailableScrollHeight = Total height of the all items - first page height
int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
- int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight;
+ int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
+ int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
return Math.max(0, availableScrollHeight);
}
@@ -144,7 +146,10 @@
// IF scroller is at the very top OR there is no scroll bar because there is probably not
// enough items to scroll, THEN it's okay for the container to be pulled down.
- return computeVerticalScrollOffset() == 0;
+ if (getCurrentScrollY() == 0) {
+ return true;
+ }
+ return getAdapter() == null || getAdapter().getItemCount() == 0;
}
/**
@@ -155,6 +160,53 @@
}
/**
+ * @return the scroll top of this recycler view.
+ */
+ public int getCurrentScrollY() {
+ Adapter adapter = getAdapter();
+ if (adapter == null) {
+ return -1;
+ }
+ if (adapter.getItemCount() == 0 || getChildCount() == 0) {
+ return -1;
+ }
+
+ int itemPosition = NO_POSITION;
+ View child = null;
+
+ LayoutManager layoutManager = getLayoutManager();
+ if (layoutManager instanceof LinearLayoutManager) {
+ // Use the LayoutManager as the source of truth for visible positions. During
+ // animations, the view group child may not correspond to the visible views that appear
+ // at the top.
+ itemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
+ child = layoutManager.findViewByPosition(itemPosition);
+ }
+
+ if (child == null) {
+ // If the layout manager returns null for any reason, which can happen before layout
+ // has occurred for the position, then look at the child of this view as a ViewGroup.
+ child = getChildAt(0);
+ itemPosition = getChildAdapterPosition(child);
+ }
+ if (itemPosition == NO_POSITION) {
+ return -1;
+ }
+ return getPaddingTop() + getItemsHeight(itemPosition)
+ - layoutManager.getDecoratedTop(child);
+ }
+
+ /**
+ * Returns the sum of the height, in pixels, of this list adapter's items from index
+ * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
+ * it returns the full height of all the items.
+ *
+ * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+ * sum of all items' height.
+ */
+ protected abstract int getItemsHeight(int untilIndex);
+
+ /**
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 447745e..3abefe0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -43,6 +43,7 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
import static com.android.launcher3.logging.StatsLogManager.EventEnum;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -97,6 +98,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.SparseArray;
@@ -163,6 +165,7 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.ArrowPopup;
@@ -1291,6 +1294,16 @@
mAllAppsController.setupViews(mScrimView, mAppsView);
}
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()
+ && WorkspacePageIndicator.class.getName().equals(name)) {
+ return LayoutInflater.from(context).inflate(R.layout.page_indicator_dots,
+ (ViewGroup) parent, false);
+ }
+ return super.onCreateView(parent, name, context, attrs);
+ }
+
/**
* Creates a view representing a shortcut.
*
@@ -2790,7 +2803,7 @@
View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
preferredItem, packageAndUserAndApp);
- if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
+ if (v != null && activeRecyclerView.getCurrentScrollY() > 0) {
RectF locationBounds = new RectF();
FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
new Rect());
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index b858d1a..4e80d41 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -218,4 +218,32 @@
}
};
}
+
+ /**
+ * A property that updates the specified property within a given range of values (ie. even if
+ * the animator goes beyond 0..1, the interpolated value will still be bounded).
+ * @param <T> the specified property
+ */
+ public static class ClampedProperty<T> extends FloatProperty<T> {
+ private final FloatProperty<T> mProperty;
+ private final float mMinValue;
+ private final float mMaxValue;
+
+ public ClampedProperty(FloatProperty<T> property, float minValue, float maxValue) {
+ super(property.getName() + "Clamped");
+ mProperty = property;
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ @Override
+ public void setValue(T t, float v) {
+ mProperty.set(t, Utilities.boundToRange(v, mMinValue, mMaxValue));
+ }
+
+ @Override
+ public Float get(T t) {
+ return mProperty.get(t);
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 66195f3..4c8f2d9 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -94,15 +94,11 @@
*/
public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
- /**
- * The favroite is a search action
- */
- public static final int ITEM_TYPE_SEARCH_ACTION = 7;
+ // *** Below enum values are used for metrics purpose but not used in Favorites DB ***
/**
* Type of the item is recents task.
- * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
*/
public static final int ITEM_TYPE_TASK = 7;
@@ -112,6 +108,11 @@
public static final int ITEM_TYPE_QSB = 8;
/**
+ * The favorite is a search action
+ */
+ public static final int ITEM_TYPE_SEARCH_ACTION = 9;
+
+ /**
* The icon package name in Intent.ShortcutIconResource
* <P>Type: TEXT</P>
*/
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 377843c..eb68adb 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -772,9 +772,9 @@
}
if (mScroller.isFinished() && pageScrollChanged) {
- // TODO(b/238461210): Remove logging once root cause of flake detected.
+ // TODO(b/246283207): Remove logging once root cause of flake detected.
if (Utilities.IS_RUNNING_IN_TEST_HARNESS && !(this instanceof Workspace)) {
- Log.d("b/238461210", this.getClass().getSimpleName() + "#onLayout() -> "
+ Log.d("b/246283207", this.getClass().getSimpleName() + "#onLayout() -> "
+ "if(mScroller.isFinished() && pageScrollChanged) -> getNextPage(): "
+ getNextPage() + ", getScrollForPage(getNextPage()): "
+ getScrollForPage(getNextPage()));
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 486a68f..8b342ea 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -32,6 +32,7 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
@@ -80,7 +81,7 @@
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
&& (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
@@ -107,7 +108,7 @@
}
public void setupLp(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (child instanceof NavigableAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
@@ -131,7 +132,7 @@
}
public void measureChild(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
final DeviceProfile dp = mActivity.getDeviceProfile();
if (child instanceof NavigableAppWidgetHostView) {
@@ -175,7 +176,6 @@
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
layoutChild(child);
}
}
@@ -185,7 +185,7 @@
* Core logic to layout a child for this ViewGroup.
*/
public void layoutChild(View child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
if (child instanceof NavigableAppWidgetHostView) {
NavigableAppWidgetHostView nahv = (NavigableAppWidgetHostView) child;
@@ -255,7 +255,7 @@
@Override
public void drawFolderLeaveBehindForIcon(FolderIcon child) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
// While the folder is open, the position of the icon cannot change.
lp.canReorder = false;
if (mContainerType == HOTSEAT) {
@@ -266,7 +266,7 @@
@Override
public void clearFolderLeaveBehind(FolderIcon child) {
- ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
+ ((CellLayoutLayoutParams) child.getLayoutParams()).canReorder = true;
if (mContainerType == HOTSEAT) {
CellLayout cl = (CellLayout) getParent();
cl.clearFolderLeaveBehind();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b49d646..dd70ad0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -53,12 +53,14 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -67,6 +69,7 @@
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
@@ -152,6 +155,8 @@
public static final int DEFAULT_PAGE = 0;
+ private final int mAllAppsIconSize;
+
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -286,7 +291,7 @@
mLauncher = Launcher.getLauncher(context);
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
mWallpaperManager = WallpaperManager.getInstance(context);
-
+ mAllAppsIconSize = mLauncher.getDeviceProfile().allAppsIconSizePx;
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
setHapticFeedbackEnabled(false);
@@ -322,6 +327,26 @@
updateCellLayoutPadding();
updateWorkspaceWidgetsSizes();
+ setPageIndicatorInset();
+ }
+
+ private void setPageIndicatorInset() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mPageIndicator.getLayoutParams();
+
+ // Set insets for page indicator
+ Rect padding = grid.workspacePadding;
+ if (grid.isVerticalBarLayout()) {
+ lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+ lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+ lp.bottomMargin = padding.bottom;
+ } else {
+ lp.leftMargin = lp.rightMargin = 0;
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.bottomMargin = grid.hotseatBarSizePx;
+ }
+ mPageIndicator.setLayoutParams(lp);
}
private void updateCellLayoutPadding() {
@@ -559,7 +584,7 @@
}
int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, cellHSpan, 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1);
lp.canReorder = false;
if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
@@ -1671,8 +1696,14 @@
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
- if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
- dragOptions.preDragCondition = ((BubbleTextView) child).startLongPressAction();
+ if (child instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) child;
+ if (!dragOptions.isAccessibleDrag) {
+ dragOptions.preDragCondition = btv.startLongPressAction();
+ }
+ if (btv.isDisplaySearchResult()) {
+ dragOptions.preDragEndScale = (float) mAllAppsIconSize / btv.getIconSize();
+ }
}
final DragView dv;
@@ -1796,7 +1827,7 @@
boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
@@ -1831,7 +1862,7 @@
}
boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
if (dropOverView != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
@@ -2048,7 +2079,7 @@
}
// update the item's position after drop
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
lp.cellX = lp.tmpCellX = mTargetCell[0];
lp.cellY = lp.tmpCellY = mTargetCell[1];
lp.cellHSpan = item.spanX;
@@ -2074,7 +2105,7 @@
}
// If we can't find a drop location, we return the item to its original position
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
mTargetCell[0] = lp.cellX;
mTargetCell[1] = lp.cellY;
CellLayout layout = (CellLayout) cell.getParent().getParent();
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 7e6e1b6..0b3a62f 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -19,6 +19,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
@@ -111,11 +112,11 @@
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
- CellLayout.LayoutParams lp;
- if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
- lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
+ CellLayoutLayoutParams lp;
+ if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) {
+ lp = new CellLayoutLayoutParams(x, y, spanX, spanY);
} else {
- lp = (CellLayout.LayoutParams) genericLp;
+ lp = (CellLayoutLayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 79214e8..dd47592 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -23,6 +23,7 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
import com.android.launcher3.dragndrop.DragView;
@@ -244,7 +245,7 @@
}
private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) host.getLayoutParams();
CellLayout layout = (CellLayout) host.getParent().getParent();
layout.markCellsAsUnoccupiedForView(host);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 368a373..fe0230a 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -26,9 +26,7 @@
import androidx.core.view.accessibility.AccessibilityRecordCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-import com.android.launcher3.util.ScrollableLayoutManager;
import com.android.launcher3.views.ActivityContext;
import java.util.List;
@@ -68,10 +66,10 @@
/**
* A subclass of GridLayoutManager that overrides accessibility values during app search.
*/
- public class AppsGridLayoutManager extends ScrollableLayoutManager {
+ public class AppsGridLayoutManager extends GridLayoutManager {
public AppsGridLayoutManager(Context context) {
- super(context);
+ super(context, 1, GridLayoutManager.VERTICAL, false);
}
@Override
@@ -131,15 +129,6 @@
}
return extraRows;
}
-
- @Override
- protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
- AllAppsGridAdapter.AdapterItem item = mApps.getAdapterItems().get(position);
- // only account for the first icon in the row since they are the same size within a row
- return (isIconViewType(item.viewType) && item.rowAppIndex != 0)
- ? heightUntilLastPos
- : (heightUntilLastPos + mCachedSizes.get(item.viewType));
- }
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 3d06fb5..0efa7c4 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,7 +15,13 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
@@ -24,10 +30,12 @@
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseIntArray;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -46,10 +54,41 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
- protected final int mNumAppsPerRow;
- private final AllAppsFastScrollHelper mFastScrollHelper;
-
protected AlphabeticalAppsList<?> mApps;
+ protected final int mNumAppsPerRow;
+
+ // The specific view heights that we use to calculate scroll
+ private final SparseIntArray mViewHeights = new SparseIntArray();
+ private final SparseIntArray mCachedScrollPositions = new SparseIntArray();
+ private final AllAppsFastScrollHelper mFastScrollHelper;
+ private int mCumulativeVerticalScroll;
+
+
+ private final AdapterDataObserver mObserver = new RecyclerView.AdapterDataObserver() {
+ public void onChanged() {
+ mCachedScrollPositions.clear();
+ }
+
+ @Override
+ public void onItemRangeChanged(int positionStart, int itemCount) {
+ onChanged();
+ }
+
+ @Override
+ public void onItemRangeInserted(int positionStart, int itemCount) {
+ onChanged();
+ }
+
+ @Override
+ public void onItemRangeRemoved(int positionStart, int itemCount) {
+ onChanged();
+ }
+
+ @Override
+ public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+ onChanged();
+ }
+ };
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -89,8 +128,12 @@
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows
* (mNumAppsPerRow + 1));
+
+ mViewHeights.clear();
+ mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
+
@Override
public void onDraw(Canvas c) {
if (DEBUG) {
@@ -120,7 +163,7 @@
StatsLogManager mgr = ActivityContext.lookupContext(getContext()).getStatsLogManager();
switch (state) {
case SCROLL_STATE_DRAGGING:
- mgr.logger().log(LAUNCHER_ALLAPPS_SCROLLED);
+ mCumulativeVerticalScroll = 0;
requestFocus();
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
@@ -129,10 +172,17 @@
case SCROLL_STATE_IDLE:
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+ logCumulativeVerticalScroll();
break;
}
}
+ @Override
+ public void onScrolled(int dx, int dy) {
+ super.onScrolled(dx, dy);
+ mCumulativeVerticalScroll += dy;
+ }
+
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
@@ -163,6 +213,17 @@
}
@Override
+ public void setAdapter(Adapter adapter) {
+ if (getAdapter() != null) {
+ getAdapter().unregisterAdapterDataObserver(mObserver);
+ }
+ super.setAdapter(adapter);
+ if (adapter != null) {
+ adapter.registerAdapterDataObserver(mObserver);
+ }
+ }
+
+ @Override
protected boolean isPaddingOffsetRequired() {
return true;
}
@@ -183,13 +244,13 @@
List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
- if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
+ if (items.isEmpty() || mNumAppsPerRow == 0) {
mScrollbar.setThumbOffsetY(-1);
return;
}
// Skip early if, there no child laid out in the container.
- int scrollY = computeVerticalScrollOffset();
+ int scrollY = getCurrentScrollY();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -244,6 +305,51 @@
}
}
+ @Override
+ protected int getItemsHeight(int position) {
+ List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
+ AllAppsGridAdapter.AdapterItem posItem = position < items.size()
+ ? items.get(position) : null;
+ int y = mCachedScrollPositions.get(position, -1);
+ if (y < 0) {
+ y = 0;
+ for (int i = 0; i < position; i++) {
+ AllAppsGridAdapter.AdapterItem item = items.get(i);
+ if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
+ // Break once we reach the desired row
+ if (posItem != null && posItem.viewType == item.viewType &&
+ posItem.rowIndex == item.rowIndex) {
+ break;
+ }
+ // Otherwise, only account for the first icon in the row since they are the same
+ // size within a row
+ if (item.rowAppIndex == 0) {
+ y += mViewHeights.get(item.viewType, 0);
+ }
+ } else {
+ // Rest of the views span the full width
+ int elHeight = mViewHeights.get(item.viewType);
+ if (elHeight == 0) {
+ ViewHolder holder = findViewHolderForAdapterPosition(i);
+ if (holder == null) {
+ holder = getAdapter().createViewHolder(this, item.viewType);
+ getAdapter().onBindViewHolder(holder, i);
+ holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
+ elHeight = holder.itemView.getMeasuredHeight();
+
+ getRecycledViewPool().putRecycledView(holder);
+ } else {
+ elHeight = holder.itemView.getMeasuredHeight();
+ }
+ }
+ y += elHeight;
+ }
+ }
+ mCachedScrollPositions.put(position, y);
+ }
+ return y;
+ }
+
public int getScrollBarTop() {
return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
}
@@ -256,4 +362,21 @@
public boolean hasOverlappingRendering() {
return false;
}
+
+ private void logCumulativeVerticalScroll() {
+ ActivityContext context = ActivityContext.lookupContext(getContext());
+ StatsLogManager mgr = context.getStatsLogManager();
+ ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText();
+ ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer(
+ SearchResultContainer
+ .newBuilder()
+ .setQueryLength((editText == null) ? -1 : editText.length())).build();
+
+ // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't
+ // know the direction of scrolling.
+ mgr.logger().withContainerInfo(containerInfo).log(
+ mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION
+ : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN
+ : LAUNCHER_ALLAPPS_SCROLLED_UP);
+ }
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 7f6247e..70c1e18 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -106,8 +106,7 @@
new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- updateHeaderScroll(
- ((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
+ updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
}
};
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 1cbb0f9..f31379e 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -68,7 +68,7 @@
mAnimator.cancel();
}
- int current = -mCurrentRV.computeVerticalScrollOffset();
+ int current = -mCurrentRV.getCurrentScrollY();
boolean headerCollapsed = mHeaderCollapsed;
moved(current);
applyVerticalMove();
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index bde4fa6..9c3dab4 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -170,12 +170,13 @@
/**
* Updates the children views of SearchRecyclerView based on the current animation progress.
*
- * @return the total height of animating views (excluding any app icons).
+ * @return the total height of animating views (excluding at most one row of app icons).
*/
private int updateSearchRecyclerViewProgress() {
int numSearchResultsAnimated = 0;
int totalHeight = 0;
int appRowHeight = 0;
+ boolean appRowComplete = false;
Integer top = null;
SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
@@ -189,67 +190,72 @@
top = searchResultView.getTop();
}
- if (searchResultView instanceof BubbleTextView
- && searchResultView.getTag() instanceof ItemInfo
- && ((ItemInfo) searchResultView.getTag()).itemType == ITEM_TYPE_APPLICATION) {
- // The first app icon will set appRowHeight, which will also contribute to
- // totalHeight. Additional app icons should remove the appRowHeight to remain in
- // the same row as the first app.
- searchResultView.setY(top + totalHeight - appRowHeight);
- if (appRowHeight == 0) {
- appRowHeight = searchResultView.getHeight();
- totalHeight += appRowHeight;
- }
- // Don't scale/fade app row.
- searchResultView.setScaleY(1);
- searchResultView.setAlpha(1);
- continue;
- }
-
- // Adjust content alpha based on start progress and stagger.
- float startContentFadeProgress = Math.max(0,
- TOP_CONTENT_FADE_PROGRESS_START - CONTENT_STAGGER * numSearchResultsAnimated);
- float endContentFadeProgress = Math.min(1,
- startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
- searchResultView.setAlpha(1 - clampToProgress(mSearchToAzProgress,
- startContentFadeProgress, endContentFadeProgress));
-
- // Adjust background (or decorator) alpha based on start progress and stagger.
- float startBackgroundFadeProgress = Math.max(0,
- TOP_BACKGROUND_FADE_PROGRESS_START
- - CONTENT_STAGGER * numSearchResultsAnimated);
- float endBackgroundFadeProgress = Math.min(1,
- startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
- float backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
- startBackgroundFadeProgress, endBackgroundFadeProgress);
int adapterPosition = searchRecyclerView.getChildAdapterPosition(searchResultView);
- boolean decoratorFilled =
- adapterPosition != NO_POSITION
- && searchRecyclerView.getApps().getAdapterItems().get(adapterPosition)
- .setDecorationFillAlpha((int) (255 * backgroundAlpha));
- if (!decoratorFilled) {
- // Try to adjust background alpha instead (e.g. for Search Edu card).
- Drawable background = searchResultView.getBackground();
- if (background != null) {
- background.setAlpha((int) (255 * backgroundAlpha));
+ int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
+ appRowComplete |= appRowHeight > 0 && spanIndex == 0;
+ // We don't animate the first (currently only) app row we see, as that is assumed to be
+ // predicted/prefix-matched apps.
+ boolean shouldAnimate = !isAppIcon(searchResultView) || appRowComplete;
+
+ float contentAlpha = 1f;
+ float backgroundAlpha = 1f;
+ if (shouldAnimate) {
+ if (spanIndex > 0) {
+ // Animate this item with the previous item on the same row.
+ numSearchResultsAnimated--;
}
+
+ // Adjust content alpha based on start progress and stagger.
+ float startContentFadeProgress = Math.max(0,
+ TOP_CONTENT_FADE_PROGRESS_START
+ - CONTENT_STAGGER * numSearchResultsAnimated);
+ float endContentFadeProgress = Math.min(1,
+ startContentFadeProgress + CONTENT_FADE_PROGRESS_DURATION);
+ contentAlpha = 1 - clampToProgress(mSearchToAzProgress,
+ startContentFadeProgress, endContentFadeProgress);
+
+ // Adjust background (or decorator) alpha based on start progress and stagger.
+ float startBackgroundFadeProgress = Math.max(0,
+ TOP_BACKGROUND_FADE_PROGRESS_START
+ - CONTENT_STAGGER * numSearchResultsAnimated);
+ float endBackgroundFadeProgress = Math.min(1,
+ startBackgroundFadeProgress + BACKGROUND_FADE_PROGRESS_DURATION);
+ backgroundAlpha = 1 - clampToProgress(mSearchToAzProgress,
+ startBackgroundFadeProgress, endBackgroundFadeProgress);
+
+ numSearchResultsAnimated++;
+ }
+ searchResultView.setAlpha(contentAlpha);
+ // Apply background alpha to decorator if possible.
+ if (adapterPosition != NO_POSITION) {
+ searchRecyclerView.getApps().getAdapterItems()
+ .get(adapterPosition).setDecorationFillAlpha((int) (255 * backgroundAlpha));
+ }
+ // Apply background alpha to view's background (e.g. for Search Edu card).
+ Drawable background = searchResultView.getBackground();
+ if (background != null) {
+ background.setAlpha((int) (255 * backgroundAlpha));
}
- float scaleY = 1 - mSearchToAzProgress;
+ float scaleY = 1;
+ if (shouldAnimate) {
+ scaleY = 1 - mSearchToAzProgress;
+ }
int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
searchResultView.setScaleY(scaleY);
// For rows with multiple elements, only count the height once and translate elements to
// the same y position.
int y = top + totalHeight;
- int spanIndex = getSpanIndex(searchRecyclerView, adapterPosition);
if (spanIndex > 0) {
// Continuation of an existing row; move this item into the row.
y -= scaledHeight;
} else {
- // Start of a new row contributes to total height and animation stagger.
- numSearchResultsAnimated++;
+ // Start of a new row contributes to total height.
totalHeight += scaledHeight;
+ if (!shouldAnimate) {
+ appRowHeight = scaledHeight;
+ }
}
searchResultView.setY(y);
}
@@ -275,6 +281,11 @@
return adapter.getSpanIndex(adapterPosition);
}
+ private boolean isAppIcon(View item) {
+ return item instanceof BubbleTextView && item.getTag() instanceof ItemInfo
+ && ((ItemInfo) item.getTag()).itemType == ITEM_TYPE_APPLICATION;
+ }
+
/** Called just before a child is attached to the SearchRecyclerView. */
private void onSearchChildAttached(View child) {
// Avoid allocating hardware layers for alpha changes.
diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
new file mode 100644
index 0000000..b14ae8d
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.celllayout;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Represents the logic of where a view is in a CellLayout and its size
+ */
+public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams {
+ /**
+ * Horizontal location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellX;
+
+ /**
+ * Vertical location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellY;
+
+ /**
+ * Temporary horizontal location of the item in the grid during reorder
+ */
+ public int tmpCellX;
+
+ /**
+ * Temporary vertical location of the item in the grid during reorder
+ */
+ public int tmpCellY;
+
+ /**
+ * Indicates that the temporary coordinates should be used to layout the items
+ */
+ public boolean useTmpCoords;
+
+ /**
+ * Number of cells spanned horizontally by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellHSpan;
+
+ /**
+ * Number of cells spanned vertically by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellVSpan;
+
+ /**
+ * Indicates whether the item will set its x, y, width and height parameters freely,
+ * or whether these will be computed based on cellX, cellY, cellHSpan and cellVSpan.
+ */
+ public boolean isLockedToGrid = true;
+
+ /**
+ * Indicates whether this item can be reordered. Always true except in the case of the
+ * the AllApps button and QSB place holder.
+ */
+ public boolean canReorder = true;
+
+ // X coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ public int x;
+ // Y coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ public int y;
+
+ public boolean dropped;
+
+ public CellLayoutLayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public CellLayoutLayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public CellLayoutLayoutParams(CellLayoutLayoutParams source) {
+ super(source);
+ this.cellX = source.cellX;
+ this.cellY = source.cellY;
+ this.cellHSpan = source.cellHSpan;
+ this.cellVSpan = source.cellVSpan;
+ }
+
+ public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
+ super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT);
+ this.cellX = cellX;
+ this.cellY = cellY;
+ this.cellHSpan = cellHSpan;
+ this.cellVSpan = cellVSpan;
+ }
+
+ /**
+ * Updates the {@link CellLayoutLayoutParams} with the right measures using their
+ * full/invariant device profile sizes.
+ */
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, Point borderSpace, @Nullable Rect inset) {
+ setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
+ borderSpace, inset);
+ }
+
+ /**
+ * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, Point, Rect)},
+ * if the view needs to be scaled.
+ *
+ * ie. In multi-window mode, we setup widgets so that they are measured and laid out
+ * using their full/invariant device profile sizes.
+ */
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, float cellScaleX, float cellScaleY, Point borderSpace,
+ @Nullable Rect inset) {
+ if (isLockedToGrid) {
+ final int myCellHSpan = cellHSpan;
+ final int myCellVSpan = cellVSpan;
+ int myCellX = useTmpCoords ? tmpCellX : cellX;
+ int myCellY = useTmpCoords ? tmpCellY : cellY;
+
+ if (invertHorizontally) {
+ myCellX = colCount - myCellX - cellHSpan;
+ }
+
+ int hBorderSpacing = (myCellHSpan - 1) * borderSpace.x;
+ int vBorderSpacing = (myCellVSpan - 1) * borderSpace.y;
+
+ float myCellWidth = ((myCellHSpan * cellWidth) + hBorderSpacing) / cellScaleX;
+ float myCellHeight = ((myCellVSpan * cellHeight) + vBorderSpacing) / cellScaleY;
+
+ width = Math.round(myCellWidth) - leftMargin - rightMargin;
+ height = Math.round(myCellHeight) - topMargin - bottomMargin;
+ x = leftMargin + (myCellX * cellWidth) + (myCellX * borderSpace.x);
+ y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpace.y);
+
+ if (inset != null) {
+ x -= inset.left;
+ y -= inset.top;
+ width += inset.left + inset.right;
+ height += inset.top + inset.bottom;
+ }
+ }
+ }
+
+ /**
+ * Sets the position to the provided point
+ */
+ public void setCellXY(Point point) {
+ cellX = point.x;
+ cellY = point.y;
+ }
+
+ /**
+ * @return the string representation of the position of the {@link CellLayoutLayoutParams}
+ */
+ public String toString() {
+ return "(" + this.cellX + ", " + this.cellY + ")";
+ }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 5ee5558..75c28f9 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -283,13 +283,12 @@
"In foldables, when reordering the icons and widgets, is now going to use both sides");
public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
- "ENABLE_WIDGET_PICKER_DEPTH", false, "Enable changing depth in widget picker.");
+ "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
- public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION_FOLDER = new DeviceFlag(
- "SHOW_DELIGHTFUL_PAGINATION_FOLDER", false,
- "Enable showing the new 'delightful pagination'"
- + " which is a brand new animation for folder pagination");
-
+ public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = new DeviceFlag(
+ "SHOW_DELIGHTFUL_PAGINATION", false,
+ "Enable showing the new 'delightful pagination' which is a brand"
+ + " new animation for folder pagination and workspace pagination");
public static final BooleanFlag POPUP_MATERIAL_U = new DeviceFlag(
"POPUP_MATERIAL_U", false, "Switch popup UX to use material U");
@@ -297,6 +296,13 @@
"SHOW_HOME_GARDENING", false,
"Show the new home gardening mode");
+ public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
+ "ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar.");
+
+ public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
+ "SECONDARY_DRAG_N_DROP_TO_PIN", false,
+ "Enable dragging and dropping to pin apps within secondary display");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 8616f35..5368397 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -31,6 +31,7 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -90,6 +91,8 @@
protected boolean mIsInPreDrag;
+ private final int DRAG_VIEW_SCALE_DURATION_MS = 500;
+
/**
* Interface to receive notifications when a drag starts or stops
*/
@@ -214,6 +217,15 @@
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
}
mIsInPreDrag = false;
+ if (mOptions.preDragEndScale != 0) {
+ mDragObject.dragView
+ .animate()
+ .scaleX(mOptions.preDragEndScale)
+ .scaleY(mOptions.preDragEndScale)
+ .setInterpolator(Interpolators.EMPHASIZED)
+ .setDuration(DRAG_VIEW_SCALE_DURATION_MS)
+ .start();
+ }
mDragObject.dragView.onDragStart();
for (DragListener listener : new ArrayList<>(mListeners)) {
listener.onDragStart(mDragObject, mOptions);
@@ -295,9 +307,9 @@
} else if (mIsInPreDrag) {
animateDragViewToOriginalPosition(null, null, -1);
}
+ mDragObject.dragView.clearAnimation();
mDragObject.dragView = null;
}
-
// Only end the drag if we are not deferred
if (!isDeferred) {
callOnDragEnd();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 8eeca7d..4bea0ad 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -43,7 +43,6 @@
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -51,6 +50,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringProperty;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.Scrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -237,7 +237,7 @@
View anchorView) {
ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
parentChildren.measureChild(child);
parentChildren.layoutChild(child);
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index e8ff8da..1ff4335 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -40,6 +40,12 @@
/** Determines when a pre-drag should transition to a drag. By default, this is immediate. */
public PreDragCondition preDragCondition = null;
+ /**
+ * A drag scale that scales the original drag view size when the preDragCondition is met (or
+ * is ignored if preDragEndScale is 0).
+ */
+ public float preDragEndScale;
+
/** Scale of the icons over the workspace icon size. */
public float intrinsicIconScaleFactor = 1f;
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 61ffd9d..d5ef9df 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -43,6 +43,7 @@
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -215,6 +216,7 @@
final int footerStartDelay;
if (isLargeFolder()) {
if (mIsOpening) {
+ mFolder.mFooter.setAlpha(0);
footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION;
footerStartDelay = mDuration - footerAlphaDuration;
} else {
@@ -341,7 +343,7 @@
ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
for (int i = 0; i < numItemsInPreview; ++i) {
final BubbleTextView btv = itemsInPreview.get(i);
- CellLayout.LayoutParams btvLp = (CellLayout.LayoutParams) btv.getLayoutParams();
+ CellLayoutLayoutParams btvLp = (CellLayoutLayoutParams) btv.getLayoutParams();
// Calculate the final values in the LayoutParams.
btvLp.isLockedToGrid = true;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 33e0902..e8759da 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -57,6 +57,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -277,7 +278,7 @@
public void onDragEnter(ItemInfo dragInfo) {
if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams();
CellLayout cl = (CellLayout) getParent().getParent();
mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 3d5aef5..0bcb97a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -41,6 +41,7 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -202,7 +203,7 @@
public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
int pageNo = rank / mOrganizer.getMaxItemsPerPage();
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
lp.setCellXY(mOrganizer.getPosForRank(rank));
getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
}
@@ -218,9 +219,9 @@
textView.setOnClickListener(ItemClickHandler.INSTANCE);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) textView.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams();
if (lp == null) {
- textView.setLayoutParams(new CellLayout.LayoutParams(
+ textView.setLayoutParams(new CellLayoutLayoutParams(
item.cellX, item.cellY, item.spanX, item.spanY));
} else {
lp.cellX = item.cellX;
@@ -314,7 +315,7 @@
}
if (v != null) {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
+ CellLayoutLayoutParams lp = (CellLayoutLayoutParams) v.getLayoutParams();
ItemInfo info = (ItemInfo) v.getTag();
lp.setCellXY(mOrganizer.getPosForRank(rank));
currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index c1bab54..482e923 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -72,6 +72,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceLayoutManager;
+import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
@@ -532,8 +533,8 @@
CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen,
false);
- CellLayout.LayoutParams lp =
- new CellLayout.LayoutParams(0, 0, firstScreen.getCountX(), 1);
+ CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, firstScreen.getCountX(),
+ 1);
lp.canReorder = false;
firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 22627b4..837e47a 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -555,6 +555,20 @@
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985),
+ @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc.")
+ LAUNCHER_ALLAPPS_SCROLLED_UP(1229),
+
+ @UiEvent(doc =
+ "User scrolled down on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc.")
+ LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230),
+
+ @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+ + "result page etc and we don't know the direction since user came back to "
+ + "original position from which they scrolled.")
+ LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION(1231),
+
@UiEvent(doc = "User tapped taskbar home button")
LAUNCHER_TASKBAR_HOME_BUTTON_TAP(1003),
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 466f63f..159af60 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -39,8 +39,10 @@
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Intent;
+import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
+import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -61,6 +63,7 @@
import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.SettingsCache;
import java.util.Optional;
@@ -74,6 +77,9 @@
// An id that doesn't match any item, including predicted apps with have an id=NO_ID
public static final int NO_MATCHING_ID = Integer.MIN_VALUE;
+ /** Hidden field Settings.Secure.NAV_BAR_KIDS_MODE */
+ private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor("nav_bar_kids_mode");
+
/**
* The id in the settings database for this item
*/
@@ -350,9 +356,11 @@
break;
case ITEM_TYPE_TASK:
itemBuilder
- .setTask(LauncherAtom.Task.newBuilder()
- .setComponentName(getTargetComponent().flattenToShortString())
- .setIndex(screenId));
+ .setTask(nullableComponent
+ .map(component -> LauncherAtom.Task.newBuilder()
+ .setComponentName(component.flattenToShortString())
+ .setIndex(screenId))
+ .orElse(LauncherAtom.Task.newBuilder()));
break;
default:
break;
@@ -388,6 +396,9 @@
protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
itemBuilder.setIsWork(!Process.myUserHandle().equals(user));
+ SettingsCache settingsCache = SettingsCache.INSTANCE.getNoCreate();
+ boolean isKidsMode = settingsCache != null && settingsCache.getValue(NAV_BAR_KIDS_MODE, 0);
+ itemBuilder.setIsKidsMode(isKidsMode);
itemBuilder.setRank(rank);
return itemBuilder;
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index b4cb0ee..98ce951 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,7 +16,7 @@
package com.android.launcher3.pageindicators;
-import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION_FOLDER;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -29,6 +29,7 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Paint.Style;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
@@ -38,6 +39,7 @@
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
+import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -47,7 +49,7 @@
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
*/
-public class PageIndicatorDots extends View implements PageIndicator {
+public class PageIndicatorDots extends View implements Insettable, PageIndicator {
private static final float SHIFT_PER_ANIMATION = 0.5f;
private static final float SHIFT_THRESHOLD = 0.1f;
@@ -128,8 +130,7 @@
mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
-
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
mPageIndicatorSize = getResources().getDimension(
R.dimen.page_indicator_size);
mPageIndicatorRadius = mPageIndicatorSize / 2;
@@ -144,7 +145,7 @@
mPageIndicatorDrawable = null;
mCircleGap = DOT_GAP_FACTOR * mDotRadius;
}
- if (!SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (!SHOW_DELIGHTFUL_PAGINATION.get()) {
setOutlineProvider(new MyOutlineProver());
}
mIsRtl = Utilities.isRtl(getResources());
@@ -161,7 +162,7 @@
currentScroll = totalScroll - currentScroll;
}
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
mCurrentScroll = currentScroll;
mTotalScroll = totalScroll;
invalidate();
@@ -296,7 +297,7 @@
}
for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
if (i != mActivePage) {
canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
mPaginationPaint);
@@ -313,7 +314,7 @@
// Here we draw the dots
mPaginationPaint.setAlpha(DOT_ALPHA);
for (int i = 0; i < mNumPages; i++) {
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
canvas.drawCircle(x, y, getRadius(x), mPaginationPaint);
} else {
canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
@@ -323,7 +324,7 @@
// Here we draw the current page indicator
mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA);
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
drawPageIndicator(canvas, 1);
} else {
canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
@@ -389,7 +390,7 @@
float diameter = 2 * mDotRadius;
float startX;
- if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (SHOW_DELIGHTFUL_PAGINATION.get()) {
startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
sTempRect.top = (getHeight() - mPageIndicatorSize) * 0.5f;
sTempRect.bottom = (getHeight() + mPageIndicatorSize) * 0.5f;
@@ -483,4 +484,12 @@
}
}
}
+
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the pagination.
+ */
+ @Override
+ public void setInsets(Rect insets) {
+ }
}
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 87ae890..bde4e52 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -14,12 +14,9 @@
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Property;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -258,21 +255,11 @@
}
}
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the page indicator.
+ */
@Override
public void setInsets(Rect insets) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-
- if (grid.isVerticalBarLayout()) {
- Rect padding = grid.workspacePadding;
- lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
- lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
- lp.bottomMargin = padding.bottom;
- } else {
- lp.leftMargin = lp.rightMargin = 0;
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.bottomMargin = grid.hotseatBarSizePx;
- }
- setLayoutParams(lp);
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
index a0ed77e..f03c62a 100644
--- a/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
+++ b/src/com/android/launcher3/secondarydisplay/PinnedAppsAdapter.java
@@ -168,15 +168,18 @@
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
- private void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
+ /**
+ * Pins or unpins apps from home screen
+ */
+ public void update(ItemInfo info, Function<ComponentKey, Boolean> op) {
ComponentKey key = new ComponentKey(info.getTargetComponent(), info.user);
if (op.apply(key)) {
createFilteredAppsList();
Set<ComponentKey> copy = new HashSet<>(mPinnedApps);
Executors.MODEL_EXECUTOR.submit(() ->
mPrefs.edit().putStringSet(PINNED_APPS_KEY,
- copy.stream().map(this::encode).collect(Collectors.toSet()))
- .apply());
+ copy.stream().map(this::encode).collect(Collectors.toSet()))
+ .apply());
}
}
@@ -210,6 +213,13 @@
mPinnedApps.contains(new ComponentKey(info.getTargetComponent(), info.user)));
}
+ /**
+ * Pins app to home screen
+ */
+ public void addPinnedApp(ItemInfo info) {
+ update(info, mPinnedApps::add);
+ }
+
private class PinUnPinShortcut extends SystemShortcut<SecondaryDisplayLauncher> {
private final boolean mIsPinned;
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index a55f7e3..7b32d8b 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -18,6 +18,9 @@
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;
import android.view.View;
import android.view.View.OnClickListener;
@@ -26,6 +29,9 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
@@ -33,6 +39,11 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.model.data.AppInfo;
@@ -52,11 +63,11 @@
* Launcher activity for secondary displays
*/
public class SecondaryDisplayLauncher extends BaseDraggingActivity
- implements BgDataModel.Callbacks {
+ implements BgDataModel.Callbacks, DragController.DragListener {
private LauncherModel mModel;
-
private BaseDragLayer mDragLayer;
+ private SecondaryDragController mDragController;
private ActivityAllAppsContainerView<SecondaryDisplayLauncher> mAppsView;
private View mAppsButton;
@@ -69,10 +80,13 @@
private boolean mBindingItems = false;
private SecondaryDisplayPredictions mSecondaryDisplayPredictions;
+ private final int[] mTempXY = new int[2];
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mModel = LauncherAppState.getInstance(this).getModel();
+ mDragController = new SecondaryDragController(this);
mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this));
mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this);
if (getWindow().getDecorView().isAttachedToWindow()) {
@@ -86,6 +100,12 @@
initUi();
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ this.getDragController().removeDragListener(this);
+ }
+
private void initUi() {
if (mDragLayer != null) {
return;
@@ -106,6 +126,7 @@
mAppsView = findViewById(R.id.apps_view);
mAppsButton = findViewById(R.id.all_apps_button);
+ mDragController.addDragListener(this);
mPopupDataProvider = new PopupDataProvider(
mAppsView.getAppsStore()::updateNotificationDots);
@@ -113,6 +134,12 @@
}
@Override
+ protected void onPause() {
+ super.onPause();
+ mDragController.cancelDrag();
+ }
+
+ @Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -129,12 +156,21 @@
showAppDrawer(false);
}
+ public DragController getDragController() {
+ return mDragController;
+ }
+
@Override
public void onBackPressed() {
if (finishAutoCancelActionMode()) {
return;
}
+ if (mDragController.isDragging()) {
+ mDragController.cancelDrag();
+ return;
+ }
+
// Note: There should be at most one log per method call. This is enforced implicitly
// by using if-else statements.
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -202,7 +238,7 @@
float closeR = Themes.getDialogCornerRadius(this);
float startR = mAppsButton.getWidth() / 2f;
- float[] buttonPos = new float[] { startR, startR};
+ float[] buttonPos = new float[]{startR, startR};
mDragLayer.getDescendantCoordRelativeToSelf(mAppsButton, buttonPos);
mDragLayer.mapCoordInSelfToDescendant(mAppsView, buttonPos);
final Animator animator = ViewAnimationUtils.createCircularReveal(mAppsView,
@@ -236,6 +272,7 @@
@Override
public void startBinding() {
mBindingItems = true;
+ mDragController.cancelDrag();
}
@Override
@@ -266,6 +303,10 @@
}
}
+ public SecondaryDisplayPredictions getSecondaryDisplayPredictions() {
+ return mSecondaryDisplayPredictions;
+ }
+
@Override
public StringCache getStringCache() {
return mStringCache;
@@ -308,4 +349,101 @@
startActivitySafely(v, intent, item);
}
}
+
+ /**
+ * Core functionality for beginning a drag operation for an item that will be dropped within
+ * the secondary display grid home screen
+ */
+ public void beginDragShared(View child, DragSource source, DragOptions options) {
+ Object dragObject = child.getTag();
+ if (!(dragObject instanceof ItemInfo)) {
+ String msg = "Drag started with a view that has no tag set. This "
+ + "will cause a crash (issue 11627249) down the line. "
+ + "View: " + child + " tag: " + child.getTag();
+ throw new IllegalStateException(msg);
+ }
+ beginDragShared(child, source, (ItemInfo) dragObject,
+ new DragPreviewProvider(child), options);
+ }
+
+ private void beginDragShared(View child, DragSource source,
+ ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions options) {
+
+ float iconScale = 1f;
+ if (child instanceof BubbleTextView) {
+ FastBitmapDrawable icon = ((BubbleTextView) child).getIcon();
+ if (icon != null) {
+ iconScale = icon.getAnimatedScale();
+ }
+ }
+
+ // clear pressed state if necessary
+ child.clearFocus();
+ child.setPressed(false);
+ if (child instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) child;
+ icon.clearPressedBackground();
+ }
+
+ DraggableView draggableView = null;
+ if (child instanceof DraggableView) {
+ draggableView = (DraggableView) child;
+ }
+
+ final View contentView = previewProvider.getContentView();
+ final float scale;
+ // The draggable drawable follows the touch point around on the screen
+ final Drawable drawable;
+ if (contentView == null) {
+ drawable = previewProvider.createDrawable();
+ scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
+ } else {
+ 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 (contentView != null) {
+ mDragController.startDrag(
+ contentView,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ options);
+ } else {
+ mDragController.startDrag(
+ drawable,
+ draggableView,
+ dragLayerX,
+ dragLayerY,
+ source,
+ dragObject,
+ dragVisualizeOffset,
+ dragRect,
+ scale * iconScale,
+ scale,
+ options);
+ }
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
+
+ @Override
+ public void onDragEnd() { }
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
index a58916a..21c50d3 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictions.java
@@ -16,8 +16,10 @@
package com.android.launcher3.secondarydisplay;
import android.content.Context;
+import android.view.View;
import com.android.launcher3.R;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -45,4 +47,12 @@
*/
public void setPredictedApps(BgDataModel.FixedContainerItems item) {
}
+
+ /**
+ * Set long click listener for predicted apps in top of app drawer.
+ */
+ public void setLongClickListener(
+ ActivityAllAppsContainerView<?> appsView,
+ View.OnLongClickListener onIconLongClickListener) {
+ }
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
new file mode 100644
index 0000000..9bf2764
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 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.secondarydisplay;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragDriver;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.testing.shared.TestProtocol;
+
+/**
+ * Drag controller for Secondary Launcher activity
+ */
+public class SecondaryDragController extends DragController<SecondaryDisplayLauncher> {
+
+ private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+ public SecondaryDragController(SecondaryDisplayLauncher secondaryLauncher) {
+ super(secondaryLauncher);
+ }
+
+ @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,
+ float dragViewScaleOnDrop, DragOptions options) {
+
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_DROP_TARGET, "5");
+ }
+
+ if (PROFILE_DRAWING_DURING_DRAG) {
+ android.os.Debug.startMethodTracing("Launcher");
+ }
+ mActivity.hideKeyboard();
+
+ mOptions = options;
+ if (mOptions.simulatedDndStartPoint != null) {
+ mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+ mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
+ }
+
+ final int registrationX = mMotionDown.x - dragLayerX;
+ final int registrationY = mMotionDown.y - dragLayerY;
+
+ final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+ final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+ mLastDropTarget = null;
+
+ mDragObject = new DropTarget.DragObject(mActivity.getApplicationContext());
+ mDragObject.originalView = originalView;
+
+ mIsInPreDrag = mOptions.preDragCondition != null
+ && !mOptions.preDragCondition.shouldStartDrag(0);
+
+ final Resources res = mActivity.getResources();
+ final float scaleDps = mIsInPreDrag
+ ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+
+ final DragView dragView = mDragObject.dragView = drawable != null
+ ? new SecondaryDragView(
+ mActivity,
+ drawable,
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps)
+ : new SecondaryDragView(
+ mActivity,
+ view,
+ view.getMeasuredWidth(),
+ view.getMeasuredHeight(),
+ registrationX,
+ registrationY,
+ initialDragViewScale,
+ dragViewScaleOnDrop,
+ scaleDps);
+ dragView.setItemInfo(dragInfo);
+ mDragObject.dragComplete = false;
+
+ mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+ mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+ mDragDriver = DragDriver.create(this, mOptions, ev -> {
+ });
+ if (!mOptions.isAccessibleDrag) {
+ mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+ }
+
+ mDragObject.dragSource = source;
+ mDragObject.dragInfo = dragInfo;
+ mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+ if (dragOffset != null) {
+ dragView.setDragVisualizeOffset(new Point(dragOffset));
+ }
+ if (dragRegion != null) {
+ dragView.setDragRegion(new Rect(dragRegion));
+ }
+
+ mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ dragView.show(mLastTouch.x, mLastTouch.y);
+ mDistanceSinceScroll = 0;
+
+ if (!mIsInPreDrag) {
+ callOnDragStart();
+ } else if (mOptions.preDragCondition != null) {
+ mOptions.preDragCondition.onPreDragStart(mDragObject);
+ }
+
+ handleMoveEvent(mLastTouch.x, mLastTouch.y);
+ return dragView;
+ }
+
+ @Override
+ protected void exitDrag() { }
+
+ @Override
+ protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+ DropTarget target = new DropTarget() {
+ @Override
+ public boolean isDropEnabled() {
+ return true;
+ }
+
+ @Override
+ public void onDrop(DragObject dragObject, DragOptions options) {
+ ((SecondaryDragLayer) mActivity.getDragLayer()).getPinnedAppsAdapter().addPinnedApp(
+ dragObject.dragInfo);
+ dragObject.dragView.remove();
+ }
+
+ @Override
+ public void onDragEnter(DragObject dragObject) {
+ if (getDistanceDragged() > mActivity.getResources().getDimensionPixelSize(
+ R.dimen.drag_distanceThreshold)) {
+ mActivity.showAppDrawer(false);
+ AbstractFloatingView.closeAllOpenViews(mActivity);
+ }
+ }
+
+ @Override
+ public void onDragOver(DragObject dragObject) { }
+
+ @Override
+ public void onDragExit(DragObject dragObject) { }
+
+ @Override
+ public boolean acceptDrop(DragObject dragObject) {
+ return true;
+ }
+
+ @Override
+ public void prepareAccessibilityDrop() { }
+
+ @Override
+ public void getHitRectRelativeToDragLayer(Rect outRect) { }
+ };
+ return target;
+ }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index c79d70d..5eac01e 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,8 +29,12 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
@@ -59,7 +63,8 @@
@Override
public void recreateControllers() {
- mControllers = new TouchController[] {new CloseAllAppsTouchController()};
+ mControllers = new TouchController[]{new CloseAllAppsTouchController(),
+ mActivity.getDragController()};
}
/**
@@ -72,7 +77,8 @@
mAppsView = findViewById(R.id.apps_view);
mAppsView.setOnIconLongClickListener(this::onIconLongClicked);
-
+ mActivity.getSecondaryDisplayPredictions()
+ .setLongClickListener(mAppsView, this::onIconLongClicked);
// Setup workspace
mWorkspace = findViewById(R.id.workspace_grid);
mPinnedAppsAdapter = new PinnedAppsAdapter(mActivity, mAppsView.getAppsStore(),
@@ -166,6 +172,10 @@
}
}
+ public PinnedAppsAdapter getPinnedAppsAdapter() {
+ return mPinnedAppsAdapter;
+ }
+
private boolean onIconLongClicked(View v) {
if (!(v instanceof BubbleTextView)) {
return false;
@@ -183,6 +193,7 @@
if (popupDataProvider == null) {
return false;
}
+
final PopupContainerWithArrow container =
(PopupContainerWithArrow) mActivity.getLayoutInflater().inflate(
R.layout.popup_container, mActivity.getDragLayer(), false);
@@ -192,7 +203,42 @@
Collections.emptyList(),
Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v),
APP_INFO.getShortcut(mActivity, item, v)));
- v.getParent().requestDisallowInterceptTouchEvent(true);
+ container.requestFocus();
+
+ if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) {
+ return true;
+ }
+
+ DragOptions options = new DragOptions();
+ DeviceProfile grid = mActivity.getDeviceProfile();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ options.preDragCondition = container.createPreDragCondition(false);
+ if (options.preDragCondition == null) {
+ options.preDragCondition = new DragOptions.PreDragCondition() {
+ private DragView<SecondaryDisplayLauncher> mDragView;
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return mDragView != null && mDragView.isAnimationFinished();
+ }
+
+ @Override
+ public void onPreDragStart(DropTarget.DragObject dragObject) {
+ mDragView = dragObject.dragView;
+ if (!shouldStartDrag(0)) {
+ mDragView.setOnAnimationEndCallback(() -> {
+ mActivity.beginDragShared(v, mActivity.getAppsView(), options);
+ });
+ }
+ }
+
+ @Override
+ public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ mDragView = null;
+ }
+ };
+ }
+ mActivity.beginDragShared(v, mActivity.getAppsView(), options);
return true;
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
new file mode 100644
index 0000000..0168b8f
--- /dev/null
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragView.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.secondarydisplay;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragView;
+
+/**
+ * A DragView drawn/used by the Secondary Launcher activity.
+ */
+public class SecondaryDragView extends DragView<SecondaryDisplayLauncher> {
+
+ public SecondaryDragView(SecondaryDisplayLauncher launcher,
+ Drawable drawable,
+ int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+ float finalScaleDps) {
+ super(launcher, drawable, registrationX, registrationY, initialScale, scaleOnDrop,
+ finalScaleDps);
+ }
+
+ public SecondaryDragView(SecondaryDisplayLauncher launcher, View content, int width, int height,
+ int registrationX, int registrationY, float initialScale, float scaleOnDrop,
+ float finalScaleDps) {
+ super(launcher, content, width, height, registrationX, registrationY, initialScale,
+ scaleOnDrop, finalScaleDps);
+ }
+
+ @Override
+ public void animateTo(int toTouchX, int toTouchY, Runnable onCompleteRunnable, int duration) {
+ Runnable onAnimationEnd = () -> {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ mActivity.getDragLayer().removeView(this);
+ };
+
+ duration = Math.max(duration,
+ getResources().getInteger(R.integer.config_dropAnimMinDuration));
+
+ animate()
+ .translationX(toTouchX - mRegistrationX)
+ .translationY(toTouchY - mRegistrationY)
+ .scaleX(mScaleOnDrop)
+ .scaleY(mScaleOnDrop)
+ .withEndAction(onAnimationEnd)
+ .setDuration(duration)
+ .start();
+ }
+}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 3286afb..f5d511c 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.states;
+import static com.android.launcher3.config.FeatureFlags.SHOW_HOME_GARDENING;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import android.content.Context;
@@ -44,6 +45,11 @@
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+
+ if (SHOW_HOME_GARDENING.get()) {
+ return super.getWorkspaceScaleAndTranslation(launcher);
+ }
+
DeviceProfile grid = launcher.getDeviceProfile();
Workspace<?> ws = launcher.getWorkspace();
if (ws.getChildCount() == 0) {
@@ -62,6 +68,9 @@
@Override
protected float getDepthUnchecked(Context context) {
+ if (SHOW_HOME_GARDENING.get()) {
+ return 0;
+ }
return 0.5f;
}
@@ -72,6 +81,10 @@
@Override
public float getWorkspaceBackgroundAlpha(Launcher launcher) {
+ if (SHOW_HOME_GARDENING.get()) {
+ return 0;
+ }
+
return 0.2f;
}
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 269baf0..d3c9bc9 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -112,12 +112,12 @@
case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> l.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset());
+ l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
}
case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> WidgetsFullSheet.getWidgetsView(l).computeVerticalScrollOffset());
+ l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
}
case TestProtocol.REQUEST_TARGET_INSETS: {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 155ecf3..820162c 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -413,8 +413,8 @@
// In fake land/seascape, the placeholder always needs to go to the "top" of the device,
// which is the same bounds as 0 rotation.
int width = dp.widthPx;
- int insetThickness = dp.getInsets().top;
- out.set(0, 0, width, placeholderHeight + insetThickness);
+ int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp);
+ out.set(0, 0, width, placeholderHeight + insetSizeAdjustment);
out.inset(placeholderInset, 0);
// Adjust the top to account for content off screen. This will help to animate the view in
@@ -431,13 +431,21 @@
float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
- float inset = dp.getInsets().top;
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f;
out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+ out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- 1.0f * drawableHeight / 2));
}
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private int getPlaceholderSizeAdjustment(DeviceProfile dp) {
+ return Math.max(dp.getInsets().top - dp.splitPlaceholderInset, 0);
+ }
+
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index f4ba76e..af689dc 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -50,6 +50,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
@@ -413,13 +414,9 @@
int screenWidth = dp.widthPx;
int screenHeight = dp.heightPx;
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
- int insetThickness;
- if (!dp.isLandscape) {
- insetThickness = dp.getInsets().top;
- } else {
- insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
- }
- out.set(0, 0, screenWidth, placeholderHeight + insetThickness);
+ int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight);
+
+ out.set(0, 0, screenWidth, placeholderHeight + insetSizeAdjustment);
if (!dp.isLandscape) {
// portrait, phone or tablet - spans width of screen, nothing else to do
out.inset(placeholderInset, 0);
@@ -464,20 +461,18 @@
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition) {
boolean pinToRight = stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+ float insetAdjustment = getPlaceholderSizeAdjustment(dp, pinToRight) / 2f;
if (!dp.isLandscape) {
- float inset = dp.getInsets().top;
out.setX(Math.round(onScreenRectCenterX / fullscreenScaleX
- 1.0f * drawableWidth / 2));
- out.setY(Math.round((onScreenRectCenterY + (inset / 2f)) / fullscreenScaleY
+ out.setY(Math.round((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- 1.0f * drawableHeight / 2));
} else {
if (pinToRight) {
- float inset = dp.getInsets().right;
- out.setX(Math.round((onScreenRectCenterX - (inset / 2f)) / fullscreenScaleX
+ out.setX(Math.round((onScreenRectCenterX - insetAdjustment) / fullscreenScaleX
- 1.0f * drawableWidth / 2));
} else {
- float inset = dp.getInsets().left;
- out.setX(Math.round((onScreenRectCenterX + (inset / 2f)) / fullscreenScaleX
+ out.setX(Math.round((onScreenRectCenterX + insetAdjustment) / fullscreenScaleX
- 1.0f * drawableWidth / 2));
}
out.setY(Math.round(onScreenRectCenterY / fullscreenScaleY
@@ -485,6 +480,20 @@
}
}
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private int getPlaceholderSizeAdjustment(DeviceProfile dp, boolean pinToRight) {
+ int insetThickness;
+ if (!dp.isLandscape) {
+ insetThickness = dp.getInsets().top;
+ } else {
+ insetThickness = pinToRight ? dp.getInsets().right : dp.getInsets().left;
+ }
+ return Math.max(insetThickness - dp.splitPlaceholderInset, 0);
+ }
+
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
@@ -493,7 +502,9 @@
out.setRotation(getDegreesRotated());
int distanceToEdge;
if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
- && (dp.isTwoPanels || dp.isTablet)) {
+ && (dp.isTwoPanels || dp.isTablet)
+ // If taskbar is in overview, overview action has dedicated space above nav buttons
+ && !FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()) {
// If 3-button nav is active, align the splitInstructionsView with it.
distanceToEdge = dp.getTaskbarOffsetY()
+ ((dp.taskbarSize - splitInstructionsHeight) / 2);
@@ -530,8 +541,12 @@
int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
// Adjust for any insets on the bottom edge
int insetCorrectionY = dp.getInsets().bottom;
+ // Adjust for taskbar in overview
+ int taskbarCorrectionY =
+ dp.isTaskbarPresent && FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get()
+ ? dp.taskbarSize : 0;
out.setTranslationX(insetCorrectionX + threeButtonNavShift);
- out.setTranslationY(-distanceToEdge + insetCorrectionY);
+ out.setTranslationY(-distanceToEdge + insetCorrectionY - taskbarCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
lp.gravity = CENTER_HORIZONTAL | BOTTOM;
out.setLayoutParams(lp);
@@ -580,15 +595,17 @@
if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
if (isLandscape) {
- outRect.right = outRect.left + (int) (outRect.width() * topLeftTaskPercent);
+ outRect.right = outRect.left + Math.round(outRect.width() * topLeftTaskPercent);
} else {
- outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
+ outRect.bottom = outRect.top + Math.round(outRect.height() * topLeftTaskPercent);
}
} else {
if (isLandscape) {
- outRect.left += (int) (outRect.width() * (topLeftTaskPercent + dividerBarPercent));
+ outRect.left += Math.round(outRect.width()
+ * (topLeftTaskPercent + dividerBarPercent));
} else {
- outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
+ outRect.top += Math.round(outRect.height()
+ * (topLeftTaskPercent + dividerBarPercent));
}
}
}
@@ -601,7 +618,7 @@
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
int dividerBar = Math.round(splitBoundsConfig.appsStackedVertically
? splitBoundsConfig.dividerHeightPercent * totalThumbnailHeight
- : splitBoundsConfig.dividerWidthPercent * totalThumbnailHeight);
+ : splitBoundsConfig.dividerWidthPercent * parentWidth);
int primarySnapshotHeight;
int primarySnapshotWidth;
int secondarySnapshotHeight;
@@ -610,7 +627,7 @@
splitBoundsConfig.topTaskPercent : splitBoundsConfig.leftTaskPercent;
if (dp.isLandscape) {
primarySnapshotHeight = totalThumbnailHeight;
- primarySnapshotWidth = (int) (parentWidth * taskPercent);
+ primarySnapshotWidth = Math.round(parentWidth * taskPercent);
secondarySnapshotHeight = totalThumbnailHeight;
secondarySnapshotWidth = parentWidth - primarySnapshotWidth - dividerBar;
@@ -625,7 +642,7 @@
secondarySnapshot.setTranslationY(spaceAboveSnapshot);
} else {
primarySnapshotWidth = parentWidth;
- primarySnapshotHeight = (int) (totalThumbnailHeight * taskPercent);
+ primarySnapshotHeight = Math.round(totalThumbnailHeight * taskPercent);
secondarySnapshotWidth = parentWidth;
secondarySnapshotHeight = totalThumbnailHeight - primarySnapshotHeight - dividerBar;
diff --git a/src/com/android/launcher3/util/DimensionUtils.kt b/src/com/android/launcher3/util/DimensionUtils.kt
new file mode 100644
index 0000000..758b3a9
--- /dev/null
+++ b/src/com/android/launcher3/util/DimensionUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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
+
+import android.content.res.Resources
+import android.graphics.Point
+import android.view.ViewGroup
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+
+object DimensionUtils {
+ /**
+ * Point where x is width, and y is height of taskbar based on provided [deviceProfile]
+ * x or y could also be -1 to indicate there is no dimension specified
+ */
+ @JvmStatic
+ fun getTaskbarPhoneDimensions(deviceProfile: DeviceProfile, res: Resources,
+ isPhoneMode: Boolean): Point {
+ val p = Point()
+ // Taskbar for large screen
+ if (!isPhoneMode) {
+ p.x = ViewGroup.LayoutParams.MATCH_PARENT
+ p.y = deviceProfile.taskbarSize
+ return p
+ }
+
+ // Taskbar on phone using gesture nav, it will always be stashed
+ if (deviceProfile.isGestureMode) {
+ p.x = ViewGroup.LayoutParams.MATCH_PARENT
+ p.y = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size)
+ return p
+ }
+
+ // Taskbar on phone, portrait
+ if (!deviceProfile.isLandscape) {
+ p.x = ViewGroup.LayoutParams.MATCH_PARENT
+ p.y = res.getDimensionPixelSize(R.dimen.taskbar_size)
+ return p
+ }
+
+ // Taskbar on phone, landscape
+ p.x = res.getDimensionPixelSize(R.dimen.taskbar_size)
+ p.y = ViewGroup.LayoutParams.MATCH_PARENT
+ return p
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/MultiPropertyFactory.java b/src/com/android/launcher3/util/MultiPropertyFactory.java
index e7a7785..43daf08 100644
--- a/src/com/android/launcher3/util/MultiPropertyFactory.java
+++ b/src/com/android/launcher3/util/MultiPropertyFactory.java
@@ -107,12 +107,9 @@
@Override
public Float get(T object) {
- // The scale of the view should match mLastAggregatedValue. Still, if it has been
- // changed without using this property, it can differ. As this get method is usually
- // used to set the starting point on an animation, this would result in some jumps
- // when the view scale is different than the last aggregated value. To stay on the
- // safe side, let's return the real view scale.
- return mProperty.get(object);
+ // Callers of MultiProperty should only care about the sub-property that it sets. If
+ // the overall value is needed, mProperty.get should be called directly.
+ return mValue;
}
@Override
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
deleted file mode 100644
index 17eaefd..0000000
--- a/src/com/android/launcher3/util/ScrollableLayoutManager.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2022 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;
-
-import android.content.Context;
-import android.util.SparseIntArray;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-import androidx.recyclerview.widget.RecyclerView.State;
-import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
-/**
- * Extension of {@link GridLayoutManager} with support for smooth scrolling
- */
-public class ScrollableLayoutManager extends GridLayoutManager {
-
- // keyed on item type
- protected final SparseIntArray mCachedSizes = new SparseIntArray();
-
- private RecyclerView mRv;
-
- /**
- * Precalculated total height keyed on the item position. This is always incremental.
- * Subclass can override {@link #incrementTotalHeight} to incorporate the layout logic.
- * For example all-apps should have same values for items in same row,
- * sample values: 0, 10, 10, 10, 10, 20, 20, 20, 20
- * whereas widgets will have strictly increasing values
- * sample values: 0, 10, 50, 60, 110
- */
-
- //
- private int[] mTotalHeightCache = new int[1];
- private int mLastValidHeightIndex = 0;
-
- public ScrollableLayoutManager(Context context) {
- super(context, 1, GridLayoutManager.VERTICAL, false);
- }
-
- @Override
- public void onAttachedToWindow(RecyclerView view) {
- super.onAttachedToWindow(view);
- mRv = view;
- }
-
- @Override
- public void layoutDecorated(@NonNull View child, int left, int top, int right, int bottom) {
- super.layoutDecorated(child, left, top, right, bottom);
- mCachedSizes.put(
- mRv.getChildViewHolder(child).getItemViewType(), child.getMeasuredHeight());
- }
-
- @Override
- public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,
- int bottom) {
- super.layoutDecoratedWithMargins(child, left, top, right, bottom);
- mCachedSizes.put(
- mRv.getChildViewHolder(child).getItemViewType(), child.getMeasuredHeight());
- }
-
- @Override
- public int computeVerticalScrollExtent(State state) {
- return mRv == null ? 0 : mRv.getHeight();
- }
-
- @Override
- public int computeVerticalScrollOffset(State state) {
- Adapter adapter = mRv == null ? null : mRv.getAdapter();
- if (adapter == null) {
- return 0;
- }
- if (adapter.getItemCount() == 0 || getChildCount() == 0) {
- return 0;
- }
- View child = getChildAt(0);
- ViewHolder holder = mRv.findContainingViewHolder(child);
- if (holder == null) {
- return 0;
- }
- int itemPosition = holder.getLayoutPosition();
- if (itemPosition < 0) {
- return 0;
- }
- return getPaddingTop() + getItemsHeight(adapter, itemPosition) - getDecoratedTop(child);
- }
-
- @Override
- public int computeVerticalScrollRange(State state) {
- Adapter adapter = mRv == null ? null : mRv.getAdapter();
- return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
- }
-
- /**
- * Returns the sum of the height, in pixels, of this list adapter's items from index
- * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
- * it returns the full height of all the items.
- *
- * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
- * sum of all items' height.
- */
- private int getItemsHeight(Adapter adapter, int untilIndex) {
- final int totalItems = adapter.getItemCount();
- if (mTotalHeightCache.length < (totalItems + 1)) {
- mTotalHeightCache = new int[totalItems + 1];
- mLastValidHeightIndex = 0;
- }
- if (untilIndex > totalItems) {
- untilIndex = totalItems;
- } else if (untilIndex < 0) {
- untilIndex = 0;
- }
- if (untilIndex <= mLastValidHeightIndex) {
- return mTotalHeightCache[untilIndex];
- }
-
- int totalItemsHeight = mTotalHeightCache[mLastValidHeightIndex];
- for (int i = mLastValidHeightIndex; i < untilIndex; i++) {
- totalItemsHeight = incrementTotalHeight(adapter, i, totalItemsHeight);
- mTotalHeightCache[i + 1] = totalItemsHeight;
- }
- mLastValidHeightIndex = untilIndex;
- return totalItemsHeight;
- }
-
- /**
- * The current implementation assumes a linear list with every item taking up the whole row.
- * Subclasses should override this method to account for any spanning logic
- */
- protected int incrementTotalHeight(Adapter adapter, int position, int heightUntilLastPos) {
- return heightUntilLastPos + mCachedSizes.get(adapter.getItemViewType(position));
- }
-
- private void invalidateScrollCache() {
- mLastValidHeightIndex = 0;
- }
-
- @Override
- public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
- super.onItemsAdded(recyclerView, positionStart, itemCount);
- invalidateScrollCache();
- }
-
- @Override
- public void onItemsChanged(RecyclerView recyclerView) {
- super.onItemsChanged(recyclerView);
- invalidateScrollCache();
- }
-
- @Override
- public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
- super.onItemsRemoved(recyclerView, positionStart, itemCount);
- invalidateScrollCache();
- }
-
- @Override
- public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
- super.onItemsMoved(recyclerView, from, to, itemCount);
- invalidateScrollCache();
- }
-
- @Override
- public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount,
- Object payload) {
- super.onItemsUpdated(recyclerView, positionStart, itemCount, payload);
- invalidateScrollCache();
- }
-}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 88e1b22..3eff783 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -100,6 +100,7 @@
* with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
*
* If you make changes here, consider making the same changes there
+ * TODO(b/254378592): We really need to consolidate this
*/
public static class SplitBounds {
public final Rect leftTopBounds;
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 800b1f6..1e154a2 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -273,6 +273,12 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getActionIndex() > 0) {
+ // This means there is multiple touch inputs, ignore it, we could also cancel the
+ // previous touch but the user might cancel the drag by accident.
+ return true;
+ }
+
switch (ev.getAction()) {
case ACTION_DOWN: {
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 3af2e3c..40e4ce1 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -119,6 +119,7 @@
// prevent jumping, this offset is applied as the user scrolls.
protected int mTouchOffsetY;
protected int mThumbOffsetY;
+ protected int mRvOffsetY;
// Fast scroller popup
private TextView mPopupView;
@@ -206,11 +207,16 @@
public void setThumbOffsetY(int y) {
if (mThumbOffsetY == y) {
+ int rvCurrentOffsetY = mRv.getCurrentScrollY();
+ if (mRvOffsetY != rvCurrentOffsetY) {
+ mRvOffsetY = mRv.getCurrentScrollY();
+ }
return;
}
updatePopupY(y);
mThumbOffsetY = y;
invalidate();
+ mRvOffsetY = mRv.getCurrentScrollY();
}
public int getThumbOffsetY() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 5969e3e..35fa7a4 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
+import android.util.SparseIntArray;
import android.view.MotionEvent;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -27,7 +28,6 @@
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.R;
-import com.android.launcher3.util.ScrollableLayoutManager;
/**
* The widgets recycler view.
@@ -42,6 +42,14 @@
private boolean mTouchDownOnScroller;
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
+ /**
+ * There is always 1 or 0 item of VIEW_TYPE_WIDGETS_LIST. Other types have fixes sizes, so the
+ * the size can be used for all other items of same type. Caching the last know size for
+ * VIEW_TYPE_WIDGETS_LIST allows us to use it to estimate full size even when
+ * VIEW_TYPE_WIDGETS_LIST is not visible on the screen.
+ */
+ private final SparseIntArray mCachedSizes = new SparseIntArray();
+
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -60,7 +68,9 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- setLayoutManager(new ScrollableLayoutManager(getContext()));
+ // create a layout manager with Launcher's context so that scroll position
+ // can be preserved during screen rotation.
+ setLayoutManager(new LinearLayoutManager(getContext()));
}
@Override
@@ -104,7 +114,7 @@
}
// Skip early if, there no child laid out in the container.
- int scrollY = computeVerticalScrollOffset();
+ int scrollY = getCurrentScrollY();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -154,6 +164,39 @@
}
/**
+ * Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
+ * {@code untilIndex}.
+ *
+ * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+ * sum of all items' height.
+ */
+ @Override
+ protected int getItemsHeight(int untilIndex) {
+ // Initialize cache
+ int childCount = getChildCount();
+ int startPosition;
+ if (childCount > 0
+ && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
+ int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
+ for (int i = 0; i < loopCount; i++) {
+ mCachedSizes.put(
+ mAdapter.getItemViewType(startPosition + i),
+ getChildAt(i).getMeasuredHeight());
+ }
+ }
+
+ if (untilIndex > mAdapter.getItems().size()) {
+ untilIndex = mAdapter.getItems().size();
+ }
+ int totalItemsHeight = 0;
+ for (int i = 0; i < untilIndex; i++) {
+ int type = mAdapter.getItemViewType(i);
+ totalItemsHeight += mCachedSizes.get(type);
+ }
+ return totalItemsHeight;
+ }
+
+ /**
* Provides dimensions of the header view that is shown at the top of a
* {@link WidgetsRecyclerView}.
*/
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 70d122b..32b62fb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -537,7 +537,7 @@
}
protected int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
+ return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
}
private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 07bfe4c..a3eee00 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -18,8 +18,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import android.platform.test.annotations.IwTest;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -31,6 +29,7 @@
import android.content.Intent;
import android.graphics.Point;
+import android.platform.test.annotations.IwTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -303,7 +302,7 @@
}
private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).computeVerticalScrollOffset();
+ return getWidgetsView(launcher).getCurrentScrollY();
}
private boolean isOptionsPopupVisible(Launcher launcher) {
@@ -437,6 +436,23 @@
@Test
@PortraitLandscape
+ public void testDragAndCancelAppIcon() {
+ final HomeAppIcon homeAppIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME);
+ Point positionBeforeDrag =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+ assertNotNull("App not found in Workspace before dragging.", positionBeforeDrag);
+
+ mLauncher.getWorkspace().dragAndCancelAppIcon(homeAppIcon);
+
+ Point positionAfterDrag =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions().get(GMAIL_APP_NAME);
+ assertNotNull("App not found in Workspace after dragging.", positionAfterDrag);
+ assertEquals("App not returned to same position in Workspace after drag & cancel",
+ positionBeforeDrag, positionAfterDrag);
+ }
+
+ @Test
+ @PortraitLandscape
public void testDeleteFromWorkspace() throws Exception {
// test delete both built-in apps and user-installed app from workspace
for (String appName : new String[]{"Gmail", "Play Store", APP_NAME}) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3545c27..41bb7f3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -48,7 +48,6 @@
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
-import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -138,6 +137,15 @@
OUTSIDE_WITH_KEYCODE,
}
+ /**
+ * Represents a point in the code at which a callback can run.
+ */
+ public enum CALLBACK_RUN_POINT {
+ CALLBACK_HOLD_BEFORE_DROP
+ }
+
+ private Consumer<CALLBACK_RUN_POINT> mCallbackAtRunPoint = null;
+
// Base class for launcher containers.
abstract static class VisibleContainer {
protected final LauncherInstrumentation mLauncher;
@@ -180,7 +188,7 @@
private final UiDevice mDevice;
private final Instrumentation mInstrumentation;
- private int mExpectedRotation = Surface.ROTATION_0;
+ private Integer mExpectedRotation = null;
private final Uri mTestProviderUri;
private final Deque<String> mDiagnosticContext = new LinkedList<>();
private Function<Long, String> mSystemHealthSupplier;
@@ -193,6 +201,7 @@
private boolean mCheckEventsForSuccessfulGestures = false;
private Runnable mOnLauncherCrashed;
+
private static Pattern getTouchEventPattern(String prefix, String action) {
// The pattern includes checks that we don't get a multi-touch events or other surprises.
return Pattern.compile(
@@ -297,8 +306,8 @@
final String testSuffix = ".test";
return testPackage.endsWith(testSuffix) && testPackage.length() > testSuffix.length()
- && testPackage.substring(0, testPackage.length() - testSuffix.length())
- .equals(targetPackage);
+ && testPackage.substring(0, testPackage.length() - testSuffix.length())
+ .equals(targetPackage);
}
public void enableCheckEventsForSuccessfulGestures() {
@@ -685,15 +694,20 @@
* Whether to ignore verifying the task bar visibility during instrumenting.
*
* @param ignoreTaskbarVisibility {@code true} will ignore the instrumentation implicitly
- * verifying the task bar visibility with
- * {@link VisibleContainer#verifyActiveContainer}.
- * {@code false} otherwise.
+ * verifying the task bar visibility with
+ * {@link VisibleContainer#verifyActiveContainer}.
+ * {@code false} otherwise.
*/
public void setIgnoreTaskbarVisibility(boolean ignoreTaskbarVisibility) {
mIgnoreTaskbarVisibility = ignoreTaskbarVisibility;
}
- public void setExpectedRotation(int expectedRotation) {
+ /**
+ * Sets expected rotation.
+ * TAPL periodically checks that Launcher didn't suddenly change the rotation to unexpected one.
+ * Null parameter disables checks. The initial state is "no checks".
+ */
+ public void setExpectedRotation(Integer expectedRotation) {
mExpectedRotation = expectedRotation;
}
@@ -730,8 +744,10 @@
private UiObject2 verifyContainerType(ContainerType containerType) {
waitForLauncherInitialized();
- assertEquals("Unexpected display rotation",
- mExpectedRotation, mDevice.getDisplayRotation());
+ if (mExpectedRotation != null) {
+ assertEquals("Unexpected display rotation",
+ mExpectedRotation, mDevice.getDisplayRotation());
+ }
final String error = getNavigationModeMismatchError(true);
assertTrue(error, error == null);
@@ -1951,4 +1967,20 @@
LauncherInstrumentation.GestureScope.INSIDE);
}
}
+
+ /**
+ * Sets the consumer to run callbacks at all run-points.
+ */
+ public void setRunPointCallback(Consumer<CALLBACK_RUN_POINT> callback) {
+ mCallbackAtRunPoint = callback;
+ }
+
+ /**
+ * Runs the callback at the specified point if it exists.
+ */
+ void runCallbackIfActive(CALLBACK_RUN_POINT runPoint) {
+ if (mCallbackAtRunPoint != null) {
+ mCallbackAtRunPoint.accept(runPoint);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 2c9fdb3..425a90a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -18,6 +18,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
+import static com.android.launcher3.tapl.LauncherInstrumentation.CALLBACK_RUN_POINT.CALLBACK_HOLD_BEFORE_DROP;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -302,6 +303,31 @@
}
/**
+ * Drag the appIcon from the workspace and cancel by dragging icon to corner of screen where no
+ * drop point exists.
+ *
+ * @param homeAppIcon to be dragged.
+ */
+ @NonNull
+ public Workspace dragAndCancelAppIcon(HomeAppIcon homeAppIcon) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "dragging app icon across workspace")) {
+ dragIconToWorkspace(
+ mLauncher,
+ homeAppIcon,
+ () -> new Point(0, 0),
+ () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+ null);
+
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "dragged the app across workspace")) {
+ return new Workspace(mLauncher);
+ }
+ }
+ }
+
+ /**
* Delete the appIcon from the workspace.
*
* @param homeAppIcon to be deleted.
@@ -493,6 +519,7 @@
launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
downTime, SystemClock.uptimeMillis(), false,
LauncherInstrumentation.GestureScope.INSIDE);
+ launcher.runCallbackIfActive(CALLBACK_HOLD_BEFORE_DROP);
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
}
}