Merge "Refresh taskbar apps show on DeviceProfile change." into tm-qpr-dev
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
index 0c1f05f..0b17a7b 100644
--- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -15,24 +15,13 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
import android.content.Context;
-import android.content.res.Resources;
import android.os.Bundle;
import androidx.annotation.Nullable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.testing.DebugTestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.quickstep.TouchInteractionService.TISBinder;
-import com.android.quickstep.util.TISBindHelper;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
/**
* Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
@@ -49,78 +38,14 @@
@Override
public Bundle call(String method, String arg, @Nullable Bundle extras) {
Bundle response = new Bundle();
- switch (method) {
- case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
- runOnTISBinder(tisBinder -> {
- enableManualTaskbarStashing(tisBinder, true);
- });
- return response;
-
- case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
- runOnTISBinder(tisBinder -> {
- enableManualTaskbarStashing(tisBinder, false);
- });
- return response;
-
- case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
- runOnTISBinder(tisBinder -> {
- enableManualTaskbarStashing(tisBinder, true);
-
- // Allow null-pointer to catch illegal states.
- tisBinder.getTaskbarManager().getCurrentActivityContext()
- .unstashTaskbarIfStashed();
-
- enableManualTaskbarStashing(tisBinder, false);
- });
- return response;
-
- case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
- final Resources resources = mContext.getResources();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
- return response;
- }
-
- case TestProtocol.REQUEST_RECREATE_TASKBAR:
- // Allow null-pointer to catch illegal states.
- runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
- return response;
-
- default:
- response = super.call(method, arg, extras);
- if (response != null) return response;
- return mDebugTestInformationHandler.call(method, arg, extras);
+ if (TestProtocol.REQUEST_RECREATE_TASKBAR.equals(method)) {
+ // Allow null-pointer to catch illegal states.
+ runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
+ return response;
}
- }
-
- private void enableManualTaskbarStashing(TISBinder tisBinder, boolean enable) {
- // Allow null-pointer to catch illegal states.
- tisBinder.getTaskbarManager().getCurrentActivityContext().enableManualStashingForTests(
- enable);
- }
-
- /**
- * Runs the given command on the UI thread, after ensuring we are connected to
- * TouchInteractionService.
- */
- private void runOnTISBinder(Consumer<TISBinder> connectionCallback) {
- try {
- CountDownLatch countDownLatch = new CountDownLatch(1);
- TISBindHelper helper = MAIN_EXECUTOR.submit(() ->
- new TISBindHelper(mContext, tisBinder -> {
- connectionCallback.accept(tisBinder);
- countDownLatch.countDown();
- })).get();
- countDownLatch.await();
- MAIN_EXECUTOR.submit(helper::onDestroy);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- private interface UIThreadCommand {
-
- void execute(Launcher launcher);
+ response = super.call(method, arg, extras);
+ if (response != null) return response;
+ return mDebugTestInformationHandler.call(method, arg, extras);
}
}
diff --git a/quickstep/res/drawable/ic_floating_task_button.xml b/quickstep/res/drawable/ic_floating_task_button.xml
new file mode 100644
index 0000000..e50f65c
--- /dev/null
+++ b/quickstep/res/drawable/ic_floating_task_button.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z"
+ android:fillColor="#636C6F"/>
+ <path
+ android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z"
+ android:fillColor="#636C6F"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c85e71c..fb78ef7 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -258,7 +258,7 @@
<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">47dp</dimen>
+ <dimen name="taskbar_contextual_button_margin">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>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 3d251d4..9a1ed4d 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -57,6 +57,8 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -83,6 +85,7 @@
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -119,14 +122,10 @@
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;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -135,7 +134,7 @@
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.WindowManagerWrapper;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.util.ArrayList;
@@ -294,8 +293,10 @@
RemoteAnimationAdapterCompat adapterCompat =
new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay,
mLauncher.getIApplicationThread());
- return new ActivityOptionsWrapper(
- ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
+ ActivityOptions options = ActivityOptions.makeRemoteAnimation(
+ adapterCompat.getWrapped(),
+ adapterCompat.getRemoteTransition().getTransition());
+ return new ActivityOptionsWrapper(options, onEndCallback);
}
/**
@@ -812,11 +813,10 @@
return;
}
- SurfaceTransaction transaction = new SurfaceTransaction();
-
+ ArrayList<SurfaceParams> params = new ArrayList<>();
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceProperties builder = transaction.forSurface(target.leash);
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
@@ -837,11 +837,11 @@
floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
- builder.setMatrix(matrix)
- .setWindowCrop(crop)
- .setAlpha(1f - mIconAlpha.value)
- .setCornerRadius(mWindowRadius.value)
- .setShadowRadius(mShadowRadius.value);
+ builder.withMatrix(matrix)
+ .withWindowCrop(crop)
+ .withAlpha(1f - mIconAlpha.value)
+ .withCornerRadius(mWindowRadius.value)
+ .withShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_CLOSING) {
if (target.localBounds != null) {
final Rect localBounds = target.localBounds;
@@ -861,26 +861,29 @@
tmpPos.y = tmp;
}
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.setMatrix(matrix)
- .setWindowCrop(crop)
- .setAlpha(1f);
+ builder.withMatrix(matrix)
+ .withWindowCrop(crop)
+ .withAlpha(1f);
}
+ params.add(builder.build());
}
if (navBarTarget != null) {
- SurfaceProperties navBuilder =
- transaction.forSurface(navBarTarget.leash);
+ final SurfaceParams.Builder navBuilder =
+ new SurfaceParams.Builder(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
matrix.setScale(scale, scale);
matrix.postTranslate(windowTransX0, windowTransY0);
- navBuilder.setMatrix(matrix)
- .setWindowCrop(crop)
- .setAlpha(mNavFadeIn.value);
+ navBuilder.withMatrix(matrix)
+ .withWindowCrop(crop)
+ .withAlpha(mNavFadeIn.value);
} else {
- navBuilder.setAlpha(mNavFadeOut.value);
+ navBuilder.withAlpha(mNavFadeOut.value);
}
+ params.add(navBuilder.build());
}
- surfaceApplier.scheduleApply(transaction);
+
+ surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
}
};
appAnimator.addUpdateListener(listener);
@@ -996,33 +999,37 @@
matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
widgetBackgroundBounds.top);
- SurfaceTransaction transaction = new SurfaceTransaction();
+ ArrayList<SurfaceParams> params = new ArrayList<>();
float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceProperties builder = transaction.forSurface(target.leash);
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
if (target.mode == MODE_OPENING) {
floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
mCornerRadiusProgress.value);
- builder.setMatrix(matrix)
- .setWindowCrop(appWindowCrop)
- .setAlpha(mPreviewAlpha.value)
- .setCornerRadius(mWindowRadius.value / mAppWindowScale);
+ builder.withMatrix(matrix)
+ .withWindowCrop(appWindowCrop)
+ .withAlpha(mPreviewAlpha.value)
+ .withCornerRadius(mWindowRadius.value / mAppWindowScale);
}
+ params.add(builder.build());
}
if (navBarTarget != null) {
- SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
+ final SurfaceParams.Builder navBuilder =
+ new SurfaceParams.Builder(navBarTarget.leash);
if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
- navBuilder.setMatrix(matrix)
- .setWindowCrop(appWindowCrop)
- .setAlpha(mNavFadeIn.value);
+ navBuilder.withMatrix(matrix)
+ .withWindowCrop(appWindowCrop)
+ .withAlpha(mNavFadeIn.value);
} else {
- navBuilder.setAlpha(mNavFadeOut.value);
+ navBuilder.withAlpha(mNavFadeOut.value);
}
+ params.add(navBuilder.build());
}
- surfaceApplier.scheduleApply(transaction);
+
+ surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
}
});
@@ -1087,8 +1094,8 @@
mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
- definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
- WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
+ definition.addRemoteAnimation(WindowManager.TRANSIT_OLD_WALLPAPER_OPEN,
+ WindowConfiguration.ACTIVITY_TYPE_STANDARD,
new RemoteAnimationAdapterCompat(
new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
false /* startAtFrontOfQueue */),
@@ -1098,7 +1105,7 @@
if (KEYGUARD_ANIMATION.get()) {
mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
definition.addRemoteAnimation(
- WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
new RemoteAnimationAdapterCompat(
new LauncherAnimationRunner(
mHandler, mKeyguardGoingAwayRunner,
@@ -1107,7 +1114,7 @@
mLauncher.getIApplicationThread()));
}
- new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
+ mLauncher.registerRemoteAnimations(definition.getWrapped());
}
}
@@ -1143,7 +1150,7 @@
return;
}
if (hasControlRemoteAppTransitionPermission()) {
- new ActivityCompat(mLauncher).unregisterRemoteAnimations();
+ mLauncher.unregisterRemoteAnimations();
// Also clear strong references to the runners registered with the remote animation
// definition so we don't have to wait for the system gc
@@ -1217,15 +1224,16 @@
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- transaction.forSurface(target.leash)
- .setAlpha(1f)
- .setWindowCrop(target.screenSpaceBounds)
- .setCornerRadius(cornerRadius);
+ params[i] = new SurfaceParams.Builder(target.leash)
+ .withAlpha(1f)
+ .withWindowCrop(target.screenSpaceBounds)
+ .withCornerRadius(cornerRadius)
+ .build();
}
- surfaceApplier.scheduleApply(transaction);
+ surfaceApplier.scheduleApply(params);
}
});
return unlockAnimator;
@@ -1443,10 +1451,10 @@
@Override
public void onUpdate(float percent, boolean initOnly) {
- SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceParams[] params = new SurfaceParams[appTargets.length];
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
- SurfaceProperties builder = transaction.forSurface(target.leash);
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1468,19 +1476,20 @@
tmpRect.centerY());
matrix.postTranslate(0, mDy.value);
matrix.postTranslate(tmpPos.x, tmpPos.y);
- builder.setMatrix(matrix)
- .setWindowCrop(crop)
- .setAlpha(mAlpha.value)
- .setCornerRadius(windowCornerRadius)
- .setShadowRadius(mShadowRadius.value);
+ builder.withMatrix(matrix)
+ .withWindowCrop(crop)
+ .withAlpha(mAlpha.value)
+ .withCornerRadius(windowCornerRadius)
+ .withShadowRadius(mShadowRadius.value);
} else if (target.mode == MODE_OPENING) {
matrix.setTranslate(tmpPos.x, tmpPos.y);
- builder.setMatrix(matrix)
- .setWindowCrop(crop)
- .setAlpha(1f);
+ builder.withMatrix(matrix)
+ .withWindowCrop(crop)
+ .withAlpha(1f);
}
+ params[i] = builder.build();
}
- surfaceApplier.scheduleApply(transaction);
+ surfaceApplier.scheduleApply(params);
}
});
@@ -1850,10 +1859,10 @@
@Override
public void onUpdate(RectF currentRectF, float progress) {
- SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceParams[] params = new SurfaceParams[mAppTargets.length];
for (int i = mAppTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = mAppTargets[i];
- SurfaceProperties builder = transaction.forSurface(target.leash);
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
if (target.localBounds != null) {
mTmpPos.set(target.localBounds.left, target.localBounds.top);
@@ -1888,17 +1897,18 @@
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
- builder.setMatrix(mMatrix)
- .setWindowCrop(mTmpRect)
- .setAlpha(getWindowAlpha(progress))
- .setCornerRadius(getCornerRadius(progress) / scale);
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mTmpRect)
+ .withAlpha(getWindowAlpha(progress))
+ .withCornerRadius(getCornerRadius(progress) / scale);
} else if (target.mode == MODE_OPENING) {
mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
- builder.setMatrix(mMatrix)
- .setAlpha(1f);
+ builder.withMatrix(mMatrix)
+ .withAlpha(1f);
}
+ params[i] = builder.build();
}
- mSurfaceApplier.scheduleApply(transaction);
+ mSurfaceApplier.scheduleApply(params);
}
protected float getWindowAlpha(float progress) {
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 351a3bc..c54d119 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -33,6 +33,7 @@
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
@@ -117,9 +118,14 @@
@Override
public int getExpectedHeight() {
- return getVisibility() == GONE ? 0
- : mActivityContext.getDeviceProfile().allAppsCellHeightPx + getPaddingTop()
- + getPaddingBottom();
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ int iconHeight = deviceProfile.allAppsIconSizePx;
+ int iconPadding = deviceProfile.allAppsIconDrawablePaddingPx;
+ int textHeight = Utilities.calculateTextHeight(deviceProfile.allAppsIconTextSizePx);
+ int verticalPadding = getResources().getDimensionPixelSize(
+ R.dimen.all_apps_predicted_icon_vertical_padding);
+ int totalHeight = iconHeight + iconPadding + textHeight + verticalPadding * 2;
+ return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index 7e3ee7d..bc3253f 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -27,6 +27,8 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -52,7 +54,8 @@
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
Context context = app.getContext();
// TODO: remove this
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 9cd9d85..7a483a8 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -21,6 +21,8 @@
import android.content.ComponentName;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -52,7 +54,8 @@
* workspace.
*/
@Override
- public void execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState appState,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
widget -> new ComponentKey(widget.providerName, widget.user)).collect(
Collectors.toSet());
diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
index a0cf6cb..7c3281a 100644
--- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
+++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java
@@ -15,14 +15,16 @@
*/
package com.android.launcher3.popup;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
import android.util.Log;
import android.view.View;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -53,6 +55,7 @@
@Override
public void onClick(View view) {
+ // Initiate splitscreen from the Home screen or Home All Apps
Bitmap bitmap;
Intent intent;
if (mItemInfo instanceof WorkspaceItemInfo) {
@@ -70,9 +73,10 @@
}
RecentsView recentsView = mTarget.getOverviewPanel();
+ StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition);
recentsView.initiateSplitSelect(
new SplitSelectSource(mOriginalView, new BitmapDrawable(bitmap), intent,
- mPosition, mItemInfo.user));
+ mPosition, mItemInfo, splitEvent));
}
}
@@ -82,15 +86,18 @@
public final Drawable drawable;
public final Intent intent;
public final SplitPositionOption position;
- public final UserHandle user;
+ public final ItemInfo mItemInfo;
+ public final StatsLogManager.EventEnum splitEvent;
public SplitSelectSource(View view, Drawable drawable, Intent intent,
- SplitPositionOption position, UserHandle user) {
+ SplitPositionOption position, ItemInfo itemInfo,
+ StatsLogManager.EventEnum splitEvent) {
this.view = view;
this.drawable = drawable;
this.intent = intent;
this.position = position;
- this.user = user;
+ this.mItemInfo = itemInfo;
+ this.splitEvent = splitEvent;
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java b/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java
new file mode 100644
index 0000000..5f4d239
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/FloatingTaskIntentResolver.java
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.R;
+
+// TODO: This would be replaced by the thing that has the role and provides the intent.
+/**
+ * Helper to determine what intent should be used to display in a floating window, if one
+ * exists.
+ */
+public class FloatingTaskIntentResolver {
+ private static final String TAG = FloatingTaskIntentResolver.class.getSimpleName();
+
+ @Nullable
+ /** Gets an intent for a floating task, if one exists. */
+ public static Intent getIntent(Context context) {
+ PackageManager pm = context.getPackageManager();
+ String pkg = context.getString(R.string.floating_task_package);
+ String action = context.getString(R.string.floating_task_action);
+ if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(action)) {
+ Log.d(TAG, "intent could not be found, pkg= " + pkg + " action= " + action);
+ return null;
+ }
+ Intent intent = createIntent(pm, null, pkg, action);
+ if (intent != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+ Log.d(TAG, "No valid intent found!");
+ return null;
+ }
+
+ @Nullable
+ private static Intent createIntent(PackageManager pm, @Nullable String activityName,
+ String packageName, String action) {
+ if (TextUtils.isEmpty(activityName)) {
+ activityName = queryActivityForAction(pm, packageName, action);
+ }
+ if (TextUtils.isEmpty(activityName)) {
+ Log.d(TAG, "Activity name is empty even after action search: " + action);
+ return null;
+ }
+ ComponentName component = new ComponentName(packageName, activityName);
+ Intent intent = new Intent(action).setComponent(component).setPackage(packageName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Log.d(TAG, "createIntent returning: " + intent);
+ return intent;
+ }
+
+ @Nullable
+ private static String queryActivityForAction(PackageManager pm, String packageName,
+ String action) {
+ Intent intent = new Intent(action).setPackage(packageName);
+ ResolveInfo resolveInfo = pm.resolveActivity(intent, MATCH_DEFAULT_ONLY);
+ if (resolveInfo == null || resolveInfo.activityInfo == null) {
+ Log.d(TAG, "queryActivityForAction: + " + resolveInfo);
+ return null;
+ }
+ ActivityInfo info = resolveInfo.activityInfo;
+ if (!info.exported) {
+ Log.d(TAG, "queryActivityForAction: + " + info + " not exported");
+ return null;
+ }
+ if (!info.enabled) {
+ Log.d(TAG, "queryActivityForAction: + " + info + " not enabled");
+ return null;
+ }
+ return resolveInfo.activityInfo.name;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java b/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java
new file mode 100644
index 0000000..b15669b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/LaunchFloatingTaskButton.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.FastBitmapDrawable;
+
+/**
+ * Button in Taskbar that opens something in a floating task.
+ */
+public class LaunchFloatingTaskButton extends BubbleTextView {
+
+ public LaunchFloatingTaskButton(Context context) {
+ this(context, null);
+ }
+
+ public LaunchFloatingTaskButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LaunchFloatingTaskButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
+ Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
+ .createScaledBitmapWithShadow(
+ theme.getDrawable(R.drawable.ic_floating_task_button));
+ setIcon(new FastBitmapDrawable(bitmap));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 520487e..a219ac6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,9 +15,10 @@
*/
package com.android.launcher3.taskbar;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
import android.animation.Animator;
import android.animation.AnimatorSet;
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 4fda50e..c27caf1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -17,6 +17,7 @@
import static android.view.View.AccessibilityDelegate;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
@@ -42,7 +43,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
-import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
@@ -69,6 +69,7 @@
import android.view.View.OnClickListener;
import android.view.View.OnHoverListener;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -88,7 +89,6 @@
import com.android.systemui.shared.rotation.RotationButton;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -116,6 +116,11 @@
private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11;
private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
private static final int FLAG_SMALL_SCREEN = 1 << 13;
+ private static final int FLAG_SLIDE_IN_VIEW_VISIBLE = 1 << 14;
+
+ /** Flags where a UI could be over a slide in view, so the color override should be disabled. */
+ private static final int FLAGS_SLIDE_IN_VIEW_ICON_COLOR_OVERRIDE_DISABLED =
+ FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_VOICE_INTERACTION_WINDOW_SHOWING;
private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
@@ -135,6 +140,8 @@
private final ViewGroup mStartContextualContainer;
private final int mLightIconColor;
private final int mDarkIconColor;
+ /** Color to use for navigation bar buttons, if a slide in view is visible. */
+ private final int mSlideInViewIconColor;
private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
this::updateNavButtonTranslationY);
@@ -149,6 +156,9 @@
this::updateNavButtonDarkIntensity);
private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
this::updateNavButtonDarkIntensity);
+ /** Overrides the navigation button color to {@code mSlideInViewIconColor} when {@code 1}. */
+ private final AnimatedFloat mSlideInViewNavButtonColorOverride = new AnimatedFloat(
+ this::updateNavButtonDarkIntensity);
private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
private final Rect mFloatingRotationButtonBounds = new Rect();
@@ -167,7 +177,7 @@
// Variables for moving nav buttons to a separate window above IME
private boolean mAreNavButtonsInSeparateWindow = false;
private BaseDragLayer<TaskbarActivityContext> mSeparateWindowParent; // Initialized in init.
- private final ViewTreeObserverWrapper.OnComputeInsetsListener mSeparateWindowInsetsComputer =
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mSeparateWindowInsetsComputer =
this::onComputeInsetsForSeparateWindow;
private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
@@ -180,6 +190,8 @@
mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
+ // Can precompute color since dark theme change recreates taskbar.
+ mSlideInViewIconColor = Utilities.isDarkTheme(context) ? mLightIconColor : mDarkIconColor;
}
/**
@@ -243,6 +255,11 @@
flags -> (flags & FLAG_IME_VISIBLE) != 0 && !isInKidsMode, AnimatedFloat.VALUE,
transForIme, defaultButtonTransY));
+ mPropertyHolders.add(new StatePropertyHolder(
+ mSlideInViewNavButtonColorOverride,
+ flags -> ((flags & FLAG_SLIDE_IN_VIEW_VISIBLE) != 0)
+ && ((flags & FLAGS_SLIDE_IN_VIEW_ICON_COLOR_OVERRIDE_DISABLED) == 0)));
+
if (alwaysShowButtons) {
initButtons(mNavButtonContainer, mEndContextualContainer,
mControllers.navButtonController);
@@ -253,7 +270,8 @@
// end-aligned, so start-align instead.
FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
mNavButtonContainer.getLayoutParams();
- navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd());
+ navButtonsLayoutParams.setMarginStart(
+ resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin));
navButtonsLayoutParams.setMarginEnd(0);
navButtonsLayoutParams.gravity = Gravity.START;
mNavButtonContainer.requestLayout();
@@ -524,6 +542,12 @@
applyState();
}
+ /** {@code true} if a slide in view is currently visible over taskbar. */
+ public void setSlideInViewVisible(boolean isSlideInViewVisible) {
+ updateStateForFlag(FLAG_SLIDE_IN_VIEW_VISIBLE, isSlideInViewVisible);
+ applyState();
+ }
+
/**
* Returns true if IME bar is visible
*/
@@ -669,8 +693,11 @@
private void updateNavButtonDarkIntensity() {
float darkIntensity = mTaskbarNavButtonDarkIntensity.value
* mNavButtonDarkIntensityMultiplier.value;
- int iconColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightIconColor,
- mDarkIconColor);
+ ArgbEvaluator argbEvaluator = ArgbEvaluator.getInstance();
+ int iconColor = (int) argbEvaluator.evaluate(
+ darkIntensity, mLightIconColor, mDarkIconColor);
+ iconColor = (int) argbEvaluator.evaluate(
+ mSlideInViewNavButtonColorOverride.value, iconColor, mSlideInViewIconColor);
for (ImageView button : mAllButtons) {
button.setImageTintList(ColorStateList.valueOf(iconColor));
}
@@ -827,14 +854,14 @@
mSeparateWindowParent.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
- ViewTreeObserverWrapper.addOnComputeInsetsListener(
- mSeparateWindowParent.getViewTreeObserver(), mSeparateWindowInsetsComputer);
+ mSeparateWindowParent.getViewTreeObserver().addOnComputeInternalInsetsListener(
+ mSeparateWindowInsetsComputer);
}
@Override
public void onViewDetachedFromWindow(View view) {
mSeparateWindowParent.removeOnAttachStateChangeListener(this);
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(
+ mSeparateWindowParent.getViewTreeObserver().removeOnComputeInternalInsetsListener(
mSeparateWindowInsetsComputer);
}
});
@@ -862,7 +889,7 @@
mContext.getDragLayer().addView(mNavButtonsView);
}
- private void onComputeInsetsForSeparateWindow(ViewTreeObserverWrapper.InsetsInfo insetsInfo) {
+ private void onComputeInsetsForSeparateWindow(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
addVisibleButtonsRegion(mSeparateWindowParent, insetsInfo.touchableRegion);
insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 8697b69..9d15ea8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -778,8 +778,8 @@
* stash/unstash the taskbar.
*/
@VisibleForTesting
- public void enableManualStashingForTests(boolean enableManualStashing) {
- mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
+ public void enableManualStashingDuringTests(boolean enableManualStashing) {
+ mControllers.taskbarStashController.enableManualStashingDuringTests(enableManualStashing);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 97029fe..9a1e064 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -32,6 +32,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import android.util.Pair;
import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -42,7 +43,6 @@
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DragSource;
@@ -69,6 +69,7 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.quickstep.util.LogUtils;
import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
@@ -359,11 +360,11 @@
}
if (clipDescription != null && intent != null) {
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
// Need to share the same InstanceId between launcher3 and WM Shell (internal).
- InstanceId internalInstanceId = new InstanceIdSequence(
- com.android.launcher3.logging.InstanceId.INSTANCE_ID_MAX).newInstanceId();
- com.android.launcher3.logging.InstanceId launcherInstanceId =
- new com.android.launcher3.logging.InstanceId(internalInstanceId.getId());
+ InstanceId internalInstanceId = instanceIds.first;
+ com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index ee17ad0..7e75779 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -24,6 +24,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -32,9 +33,6 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
/**
* Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
@@ -42,7 +40,8 @@
public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private final TaskbarBackgroundRenderer mBackgroundRenderer;
- private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mTaskbarInsetsComputer =
+ this::onComputeTaskbarInsets;
// Initialized in init.
private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
@@ -80,7 +79,7 @@
mControllers = mControllerCallbacks.getTouchControllers();
}
- private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
+ private void onComputeTaskbarInsets(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
if (mControllerCallbacks != null) {
mControllerCallbacks.updateInsetsTouchability(insetsInfo);
}
@@ -88,7 +87,7 @@
protected void onDestroy(boolean forceDestroy) {
if (forceDestroy) {
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
}
}
@@ -99,8 +98,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
- mTaskbarInsetsComputer);
+ getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index ec9760c..025fe7a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -17,12 +17,12 @@
import android.content.res.Resources;
import android.graphics.Rect;
+import android.view.ViewTreeObserver;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.AnimatedFloat;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
import java.io.PrintWriter;
@@ -157,9 +157,9 @@
/**
* Called to update the touchable insets.
- * @see InsetsInfo#setTouchableInsets(int)
+ * @see ViewTreeObserver.InternalInsetsInfo#setTouchableInsets(int)
*/
- public void updateInsetsTouchability(InsetsInfo insetsInfo) {
+ public void updateInsetsTouchability(ViewTreeObserver.InternalInsetsInfo insetsInfo) {
mControllers.taskbarInsetsController.updateInsetsTouchability(insetsInfo);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
index 32a3c10..454a2a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduController.java
@@ -90,6 +90,9 @@
mTaskbarEduView = (TaskbarEduView) mActivity.getLayoutInflater().inflate(
R.layout.taskbar_edu, mActivity.getDragLayer(), false);
mTaskbarEduView.init(new TaskbarEduCallbacks());
+ mControllers.navbarButtonsViewController.setSlideInViewVisible(true);
+ mTaskbarEduView.setOnCloseBeginListener(
+ () -> mControllers.navbarButtonsViewController.setSlideInViewVisible(false));
mTaskbarEduView.addOnCloseListener(() -> mTaskbarEduView = null);
mTaskbarEduView.show();
startAnim(createWaveAnim());
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 48fde8f..7b2b7ec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -20,6 +20,11 @@
import android.view.InsetsFrameProvider
import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
import android.view.InsetsState
+import android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT
+import android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR
+import android.view.ViewTreeObserver
+import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME
+import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
import android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION
@@ -29,9 +34,6 @@
import com.android.launcher3.anim.AlphaUpdateListener
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.quickstep.KtR
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo
-import com.android.systemui.shared.system.WindowManagerWrapper
-import com.android.systemui.shared.system.WindowManagerWrapper.*
import java.io.PrintWriter
/**
@@ -55,8 +57,7 @@
this.controllers = controllers
windowLayoutParams = context.windowLayoutParams
- val wmWrapper: WindowManagerWrapper = getInstance()
- wmWrapper.setProvidesInsetsTypes(
+ setProvidesInsetsTypes(
windowLayoutParams,
intArrayOf(
ITYPE_EXTRA_NAVIGATION_BAR,
@@ -108,10 +109,22 @@
}
/**
- * Called to update the touchable insets.
- * @see InsetsInfo.setTouchableInsets
+ * 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.
*/
- fun updateInsetsTouchability(insetsInfo: InsetsInfo) {
+ fun setProvidesInsetsTypes(params: WindowManager.LayoutParams, providesInsetsTypes: IntArray) {
+ params.providedInsets = arrayOfNulls<InsetsFrameProvider>(providesInsetsTypes.size);
+ for (i in providesInsetsTypes.indices) {
+ params.providedInsets[i] = InsetsFrameProvider(providesInsetsTypes[i]);
+ }
+ }
+
+ /**
+ * Called to update the touchable insets.
+ * @see InternalInsetsInfo.setTouchableInsets
+ */
+ fun updateInsetsTouchability(insetsInfo: ViewTreeObserver.InternalInsetsInfo) {
insetsInfo.touchableRegion.setEmpty()
// Always have nav buttons be touchable
controllers.navbarButtonsViewController.addVisibleButtonsRegion(
@@ -120,18 +133,18 @@
var insetsIsTouchableRegion = true
if (context.dragLayer.alpha < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.navbarButtonsViewController.isImeVisible) {
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (!controllers.uiController.isTaskbarTouchable) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.taskbarDragController.isSystemDragInProgress) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (AbstractFloatingView.hasOpenView(context, TYPE_TASKBAR_ALL_APPS)) {
// Let touches pass through us.
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
} else if (controllers.taskbarViewController.areIconsVisible()
|| AbstractFloatingView.hasOpenView(context, AbstractFloatingView.TYPE_ALL)
|| context.isNavBarKidsModeActive
@@ -139,15 +152,15 @@
// Taskbar has some touchable elements, take over the full taskbar area
insetsInfo.setTouchableInsets(
if (context.isTaskbarWindowFullscreen) {
- InsetsInfo.TOUCHABLE_INSETS_FRAME
+ TOUCHABLE_INSETS_FRAME
} else {
insetsInfo.touchableRegion.set(contentRegion)
- InsetsInfo.TOUCHABLE_INSETS_REGION
+ TOUCHABLE_INSETS_REGION
}
)
insetsIsTouchableRegion = false
} else {
- insetsInfo.setTouchableInsets(InsetsInfo.TOUCHABLE_INSETS_REGION)
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION)
}
context.excludeFromMagnificationRegion(insetsIsTouchableRegion)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 58c689b..de37b70 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -50,7 +50,6 @@
import java.util.HashMap;
import java.util.StringJoiner;
import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
@@ -65,15 +64,12 @@
public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
+ private static final int FLAGS_LAUNCHER = FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING;
/** Equivalent to an int with all 1s for binary operation purposes */
private static final int FLAGS_ALL = ~0;
- private final AnimatedFloat mIconAlignmentForResumedState =
- new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
- private final AnimatedFloat mIconAlignmentForGestureState =
- new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
- private final AnimatedFloat mIconAlignmentForLauncherState =
- new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
+ private final AnimatedFloat mIconAlignment =
+ new AnimatedFloat(this::onIconAlignmentRatioChanged);
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
@@ -86,8 +82,7 @@
private @Nullable TaskBarRecentsAnimationListener mTaskBarRecentsAnimationListener;
- private boolean mIsAnimatingToLauncherViaGesture;
- private boolean mIsAnimatingToLauncherViaResume;
+ private boolean mIsAnimatingToLauncher;
private boolean mShouldDelayLauncherStateAnim;
@@ -148,8 +143,8 @@
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mIconAlphaForHome.setConsumer(mIconAlphaForHomeConsumer);
- mIconAlignmentForResumedState.finishAnimation();
- onIconAlignmentRatioChangedForAppAndHomeTransition();
+ mIconAlignment.finishAnimation();
+ onIconAlignmentRatioChanged();
mLauncher.getStateManager().addStateListener(mStateListener);
@@ -165,9 +160,7 @@
public void onDestroy() {
mCanSyncViews = false;
- mIconAlignmentForResumedState.finishAnimation();
- mIconAlignmentForGestureState.finishAnimation();
- mIconAlignmentForLauncherState.finishAnimation();
+ mIconAlignment.finishAnimation();
mIconAlphaForHome.setConsumer(null);
mLauncher.getHotseat().setIconsAlpha(1f);
@@ -187,6 +180,9 @@
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
toState.isTaskbarStashed(mLauncher));
+ if (DEBUG) {
+ Log.d(TAG, "createAnimToLauncher - FLAG_IN_APP: " + false);
+ }
stashController.updateStateForFlag(FLAG_IN_APP, false);
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
@@ -201,7 +197,7 @@
}
public boolean isAnimatingToLauncher() {
- return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+ return mIsAnimatingToLauncher;
}
public void setShouldDelayLauncherStateAnim(boolean shouldDelayLauncherStateAnim) {
@@ -261,11 +257,29 @@
}
private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
+ boolean goingToLauncher = isInLauncher();
+ final float toAlignment;
+ if (goingToLauncher) {
+ boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
+ boolean willStashVisually = isInStashedState
+ && mControllers.taskbarStashController.supportsVisualStashing();
+ boolean isTaskbarAlignedWithHotseat =
+ mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
+ toAlignment = isTaskbarAlignedWithHotseat && !willStashVisually ? 1 : 0;
+ } else {
+ toAlignment = 0;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState)
+ + ", changedFlags: " + getStateString(changedFlags)
+ + ", goingToLauncher: " + goingToLauncher
+ + ", mLauncherState: " + mLauncherState
+ + ", toAlignment: " + toAlignment);
+ }
AnimatorSet animatorSet = new AnimatorSet();
// Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can
// determine whether goingToUnstashedLauncherStateChanged.
- boolean wasGoingToUnstashedLauncherState = goingToUnstashedLauncherState();
if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) {
boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING);
playStateTransitionAnim(animatorSet, duration, committed);
@@ -276,95 +290,69 @@
applyState(0 /* duration */);
}
}
- boolean goingToUnstashedLauncherStateChanged = wasGoingToUnstashedLauncherState
- != goingToUnstashedLauncherState();
- boolean launcherStateChangedDuringAnimToResumeAlignment =
- mIconAlignmentForResumedState.isAnimating() && goingToUnstashedLauncherStateChanged;
- if (hasAnyFlag(changedFlags, FLAG_RESUMED)
- || launcherStateChangedDuringAnimToResumeAlignment) {
- boolean isResumed = isResumed();
- // If launcher is resumed, we show the icons when going to an unstashed launcher state
- // or launcher state is not changed (e.g. in overview, launcher is paused and resumed).
- float toAlignmentForResumedState = isResumed && (goingToUnstashedLauncherState()
- || !goingToUnstashedLauncherStateChanged) ? 1 : 0;
- // If we're already animating to the value, just leave it be instead of restarting it.
- if (!mIconAlignmentForResumedState.isAnimatingToValue(toAlignmentForResumedState)) {
- ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState
- .animateToValue(toAlignmentForResumedState)
- .setDuration(duration);
- if (DEBUG) {
- Log.d(TAG, "mIconAlignmentForResumedState - "
- + mIconAlignmentForResumedState.value
- + " -> " + toAlignmentForResumedState + ": " + duration);
+ if (hasAnyFlag(changedFlags, FLAGS_LAUNCHER)) {
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimatingToLauncher = false;
}
- resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingToLauncherViaResume = false;
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsAnimatingToLauncher = goingToLauncher;
+
+ TaskbarStashController stashController =
+ mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "onAnimationStart - FLAG_IN_APP: " + !goingToLauncher);
}
-
- @Override
- public void onAnimationStart(Animator animation) {
- mIsAnimatingToLauncherViaResume = isResumed;
-
- TaskbarStashController stashController =
- mControllers.taskbarStashController;
- stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
- stashController.applyState(duration);
- }
- });
- animatorSet.play(resumeAlignAnim);
- }
- }
-
-
- boolean launcherStateChangedDuringAnimToGestureAlignment =
- mIconAlignmentForGestureState.isAnimating() && goingToUnstashedLauncherStateChanged;
- if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)
- || launcherStateChangedDuringAnimToGestureAlignment) {
- boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
- float toAlignmentForGestureState = isRecentsAnimationRunning
- && goingToUnstashedLauncherState() ? 1 : 0;
- // If we're already animating to the value, just leave it be instead of restarting it.
- if (!mIconAlignmentForGestureState.isAnimatingToValue(toAlignmentForGestureState)) {
- Animator gestureAlignAnim = mIconAlignmentForGestureState
- .animateToValue(toAlignmentForGestureState);
- if (isRecentsAnimationRunning) {
- gestureAlignAnim.setDuration(duration);
+ stashController.updateStateForFlag(FLAG_IN_APP, !goingToLauncher);
+ stashController.applyState(duration);
}
- if (DEBUG) {
- Log.d(TAG, "mIconAlignmentForGestureState - "
- + mIconAlignmentForGestureState.value
- + " -> " + toAlignmentForGestureState + ": " + duration);
- }
- gestureAlignAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIsAnimatingToLauncherViaGesture = false;
- }
+ });
- @Override
- public void onAnimationStart(Animator animation) {
- mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
- }
- });
- animatorSet.play(gestureAlignAnim);
- }
- }
-
- if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) {
- boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING);
if (goingToLauncher) {
// Handle closing open popups when going home/overview
AbstractFloatingView.closeAllOpenViews(mControllers.taskbarActivityContext);
}
- animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1)
+ }
+
+ float backgroundAlpha =
+ goingToLauncher && mLauncherState.isTaskbarAlignedWithHotseat(mLauncher)
+ ? 0 : 1;
+ // Don't animate if background has reached desired value.
+ if (mTaskbarBackgroundAlpha.isAnimating()
+ || mTaskbarBackgroundAlpha.value != backgroundAlpha) {
+ mTaskbarBackgroundAlpha.cancelAnimation();
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - taskbarBackgroundAlpha - "
+ + mTaskbarBackgroundAlpha.value
+ + " -> " + backgroundAlpha + ": " + duration);
+ }
+ animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(backgroundAlpha)
.setDuration(duration));
}
+ if (mIconAlignment.isAnimatingToValue(toAlignment)
+ || mIconAlignment.isSettledOnValue(toAlignment)) {
+ // Already at desired value, but make sure we run the callback at the end.
+ animatorSet.addListener(AnimatorListeners.forEndCallback(
+ this::onIconAlignmentRatioChanged));
+ } else {
+ mIconAlignment.cancelAnimation();
+ ObjectAnimator iconAlignAnim = mIconAlignment
+ .animateToValue(toAlignment)
+ .setDuration(duration);
+ if (DEBUG) {
+ Log.d(TAG, "onStateChangeApplied - iconAlignment - "
+ + mIconAlignment.value
+ + " -> " + toAlignment + ": " + duration);
+ }
+ animatorSet.play(iconAlignAnim);
+ }
animatorSet.setInterpolator(EMPHASIZED);
+
if (start) {
animatorSet.start();
}
@@ -372,18 +360,13 @@
}
/** Returns whether we're going to a state where taskbar icons should align with launcher. */
- private boolean goingToUnstashedLauncherState() {
- return !mControllers.taskbarStashController.isInStashedLauncherState();
+ public boolean goingToAlignedLauncherState() {
+ return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher);
}
private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
boolean committed) {
boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
- boolean willStashVisually =
- isInStashedState && mControllers.taskbarStashController.supportsVisualStashing();
- float toAlignment =
- mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) && !willStashVisually ? 1 : 0;
-
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
Animator stashAnimator = stashController.applyStateWithoutStart(duration);
@@ -406,55 +389,23 @@
});
animatorSet.play(stashAnimator);
}
- if (mIconAlignmentForLauncherState.value == toAlignment) {
- // Already at expected value, but make sure we run the callback at the end.
- animatorSet.addListener(AnimatorListeners.forEndCallback(
- this::onIconAlignmentRatioChangedForStateTransition));
- }
- if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) {
- // If we're already animating to the value, just leave it be instead of restarting it.
- mIconAlignmentForLauncherState.finishAnimation();
- animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
- .setDuration(duration));
- if (DEBUG) {
- Log.d(TAG, "mIconAlignmentForLauncherState - "
- + mIconAlignmentForLauncherState.value
- + " -> " + toAlignment + ": " + duration);
- }
- animatorSet.setInterpolator(EMPHASIZED);
- }
}
- private boolean isResumed() {
- return (mState & FLAG_RESUMED) != 0;
+ private boolean isInLauncher() {
+ return (mState & FLAGS_LAUNCHER) != 0;
}
- private boolean isRecentsAnimationRunning() {
- return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0;
- }
-
- private void onIconAlignmentRatioChangedForStateTransition() {
- if (!isResumed() && mTaskBarRecentsAnimationListener == null) {
- return;
- }
- onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioForLauncherState);
- }
-
- private void onIconAlignmentRatioChangedForAppAndHomeTransition() {
- onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome);
- }
-
- private void onIconAlignmentRatioChanged(Supplier<AnimatedFloat> alignmentSupplier) {
- if (mControllers == null) {
- return;
- }
- AnimatedFloat animatedFloat = alignmentSupplier.get();
+ private void onIconAlignmentRatioChanged() {
float currentValue = mIconAlphaForHome.getValue();
- boolean taskbarWillBeVisible = animatedFloat.value < 1;
+ boolean taskbarWillBeVisible = mIconAlignment.value < 1;
boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0)
|| (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0);
- updateIconAlignment(animatedFloat.value, animatedFloat.getEndValue());
+ mControllers.taskbarViewController.setLauncherIconAlignment(
+ mIconAlignment.value, mIconAlignment.getEndValue(), mLauncher.getDeviceProfile());
+ mControllers.navbarButtonsViewController.updateTaskbarAlignment(mIconAlignment.value);
+ // Switch taskbar and hotseat in last frame
+ mIconAlphaForHome.setValue(taskbarWillBeVisible ? 1 : 0);
// Sync the first frame where we swap taskbar and hotseat.
if (firstFrameVisChanged && mCanSyncViews && !Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -464,28 +415,6 @@
}
}
- private void updateIconAlignment(float alignment, Float endAlignment) {
- mControllers.taskbarViewController.setLauncherIconAlignment(
- alignment, endAlignment, mLauncher.getDeviceProfile());
-
- // Switch taskbar and hotseat in last frame
- setTaskbarViewVisible(alignment < 1);
- mControllers.navbarButtonsViewController.updateTaskbarAlignment(alignment);
- }
-
- private AnimatedFloat getCurrentIconAlignmentRatioBetweenAppAndHome() {
- return mIconAlignmentForResumedState.value > mIconAlignmentForGestureState.value
- ? mIconAlignmentForResumedState : mIconAlignmentForGestureState;
- }
-
- private AnimatedFloat getCurrentIconAlignmentRatioForLauncherState() {
- return mIconAlignmentForLauncherState;
- }
-
- private void setTaskbarViewVisible(boolean isVisible) {
- mIconAlphaForHome.setValue(isVisible ? 1 : 0);
- }
-
private final class TaskBarRecentsAnimationListener implements
RecentsAnimationCallbacks.RecentsAnimationListener {
private final RecentsAnimationCallbacks mCallbacks;
@@ -515,11 +444,11 @@
updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
updateStateForFlag(FLAG_RESUMED, launcherResumed);
applyState();
- // Set this last because applyState() might also animate it.
- mIconAlignmentForResumedState.cancelAnimation();
- mIconAlignmentForResumedState.updateValue(launcherResumed ? 1 : 0);
TaskbarStashController controller = mControllers.taskbarStashController;
+ if (DEBUG) {
+ Log.d(TAG, "endGestureStateOverride - FLAG_IN_APP: " + finishedToApp);
+ }
controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
controller.applyState();
}
@@ -527,29 +456,24 @@
private static String getStateString(int flags) {
StringJoiner str = new StringJoiner("|");
- str.add((flags & FLAG_RESUMED) != 0 ? "FLAG_RESUMED" : "");
- str.add((flags & FLAG_RECENTS_ANIMATION_RUNNING) != 0
- ? "FLAG_RECENTS_ANIMATION_RUNNING" : "");
- str.add((flags & FLAG_TRANSITION_STATE_RUNNING) != 0
- ? "FLAG_TRANSITION_STATE_RUNNING" : "");
+ if ((flags & FLAG_RESUMED) != 0) {
+ str.add("FLAG_RESUMED");
+ }
+ if ((flags & FLAG_RECENTS_ANIMATION_RUNNING) != 0) {
+ str.add("FLAG_RECENTS_ANIMATION_RUNNING");
+ }
+ if ((flags & FLAG_TRANSITION_STATE_RUNNING) != 0) {
+ str.add("FLAG_TRANSITION_STATE_RUNNING");
+ }
return str.toString();
}
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(prefix + "TaskbarLauncherStateController:");
-
pw.println(String.format(
- "%s\tmIconAlignmentForResumedState=%.2f",
+ "%s\tmIconAlignment=%.2f",
prefix,
- mIconAlignmentForResumedState.value));
- pw.println(String.format(
- "%s\tmIconAlignmentForGestureState=%.2f",
- prefix,
- mIconAlignmentForGestureState.value));
- pw.println(String.format(
- "%s\tmIconAlignmentForLauncherState=%.2f",
- prefix,
- mIconAlignmentForLauncherState.value));
+ mIconAlignment.value));
pw.println(String.format(
"%s\tmTaskbarBackgroundAlpha=%.2f", prefix, mTaskbarBackgroundAlpha.value));
pw.println(String.format(
@@ -558,13 +482,9 @@
pw.println(String.format("%s\tmState=%s", prefix, getStateString(mState)));
pw.println(String.format("%s\tmLauncherState=%s", prefix, mLauncherState));
pw.println(String.format(
- "%s\tmIsAnimatingToLauncherViaGesture=%b",
+ "%s\tmIsAnimatingToLauncher=%b",
prefix,
- mIsAnimatingToLauncherViaGesture));
- pw.println(String.format(
- "%s\tmIsAnimatingToLauncherViaResume=%b",
- prefix,
- mIsAnimatingToLauncherViaResume));
+ mIsAnimatingToLauncher));
pw.println(String.format(
"%s\tmShouldDelayLauncherStateAnim=%b", prefix, mShouldDelayLauncherStateAnim));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 7b4501a..da6dab1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,14 +15,20 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
+
+import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Point;
+import android.os.Bundle;
+import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
+import com.android.internal.logging.InstanceId;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
@@ -47,6 +53,7 @@
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LogUtils;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -263,8 +270,15 @@
@Override
public void onClick(View view) {
- AbstractFloatingView.closeAllOpenViews(mTarget);
+ // Initiate splitscreen from the in-app Taskbar or Taskbar All Apps
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
+ mTarget.getStatsLogManager().logger()
+ .withItemInfo(mItemInfo)
+ .withInstanceId(instanceIds.second)
+ .log(getLogEventForPosition(mPosition.stagePosition));
+ AbstractFloatingView.closeAllOpenViews(mTarget);
if (mItemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo;
SystemUiProxy.INSTANCE.get(mTarget).startShortcut(
@@ -272,7 +286,8 @@
workspaceItemInfo.getDeepShortcutId(),
mPosition.stagePosition,
null,
- workspaceItemInfo.user);
+ workspaceItemInfo.user,
+ instanceIds.first);
} else {
SystemUiProxy.INSTANCE.get(mTarget).startIntent(
mTarget.getSystemService(LauncherApps.class).getMainActivityLaunchIntent(
@@ -281,7 +296,8 @@
mItemInfo.user),
new Intent(),
mPosition.stagePosition,
- null);
+ null,
+ instanceIds.first);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index f131595..c10b57a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -17,24 +17,29 @@
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DEEP_SHORTCUTS;
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.SHORTCUTS_AND_NOTIFICATIONS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
import android.content.pm.LauncherApps;
+import android.util.Pair;
import android.view.KeyEvent;
import android.view.View;
+import com.android.internal.logging.InstanceId;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ShortcutUtil;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LogUtils;
import java.util.List;
@@ -49,10 +54,12 @@
public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
private final LauncherApps mLauncherApps;
+ private final StatsLogManager mStatsLogManager;
public TaskbarShortcutMenuAccessibilityDelegate(TaskbarActivityContext context) {
super(context);
mLauncherApps = context.getSystemService(LauncherApps.class);
+ mStatsLogManager = context.getStatsLogManager();
mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
@@ -82,7 +89,14 @@
&& (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
int side = action == MOVE_TO_TOP_OR_LEFT
- ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
+ ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
+
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
+ mStatsLogManager.logger()
+ .withItemInfo(item)
+ .withInstanceId(instanceIds.second)
+ .log(getLogEventForPosition(side));
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
SystemUiProxy.INSTANCE.get(mContext).startShortcut(
@@ -90,14 +104,15 @@
info.getDeepShortcutId(),
side,
/* bundleOpts= */ null,
- info.user);
+ info.user,
+ instanceIds.first);
} else {
SystemUiProxy.INSTANCE.get(mContext).startIntent(
mLauncherApps.getMainActivityLaunchIntent(
item.getIntent().getComponent(),
/* startActivityOptions= */null,
item.user),
- new Intent(), side, null);
+ new Intent(), side, null, instanceIds.first);
}
return true;
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index d9d55e7..4b0adb1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -32,6 +32,7 @@
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.Log;
+import android.view.InsetsController;
import android.view.View;
import android.view.ViewConfiguration;
@@ -46,11 +47,8 @@
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.IntPredicate;
@@ -96,7 +94,7 @@
* How long to stash/unstash when manually invoked via long press.
*/
public static final long TASKBAR_STASH_DURATION =
- WindowManagerWrapper.ANIMATION_DURATION_RESIZE;
+ InsetsController.ANIMATION_DURATION_RESIZE;
/**
* How long to stash/unstash when keyboard is appearing/disappearing.
@@ -162,7 +160,7 @@
private boolean mIsImeShowing;
private boolean mIsImeSwitcherShowing;
- private boolean mEnableManualStashingForTests = false;
+ private boolean mEnableManualStashingDuringTests = false;
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
@@ -242,15 +240,15 @@
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingForTests);
+ && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingDuringTests);
}
/**
* Enables support for manual stashing. This should only be used to add this functionality
* to Launcher specific tests.
*/
- public void enableManualStashingForTests(boolean enableManualStashing) {
- mEnableManualStashingForTests = enableManualStashing;
+ public void enableManualStashingDuringTests(boolean enableManualStashing) {
+ mEnableManualStashingDuringTests = enableManualStashing;
}
/**
@@ -546,13 +544,7 @@
}
private void addJankMonitorListener(AnimatorSet animator, boolean expanding) {
- Optional<View> optionalView =
- Arrays.stream(mControllers.taskbarViewController.getIconViews()).findFirst();
- if (optionalView.isEmpty()) {
- Log.wtf(TAG, "No views to start Interaction jank monitor with.", new Exception());
- return;
- }
- View v = optionalView.get();
+ View v = mControllers.taskbarActivityContext.getDragLayer();
int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND :
InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE;
animator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index dbf9759..bb82d19 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,10 +16,13 @@
package com.android.launcher3.taskbar;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -53,6 +56,7 @@
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable {
+ private static final String TAG = TaskbarView.class.getSimpleName();
private static final float TASKBAR_BACKGROUND_LUMINANCE = 0.30f;
public int mThemeIconsBackground;
@@ -81,6 +85,12 @@
private View mQsb;
+ // Only non-null when device supports having a floating task.
+ private @Nullable BubbleTextView mFloatingTaskButton;
+ private @Nullable Intent mFloatingTaskIntent;
+ private static final boolean FLOATING_TASKS_ENABLED =
+ SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -123,6 +133,19 @@
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+
+ if (FLOATING_TASKS_ENABLED) {
+ mFloatingTaskIntent = FloatingTaskIntentResolver.getIntent(context);
+ if (mFloatingTaskIntent != null) {
+ mFloatingTaskButton = new LaunchFloatingTaskButton(context);
+ mFloatingTaskButton.setLayoutParams(
+ new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
+ mFloatingTaskButton.setPadding(mItemPadding, mItemPadding, mItemPadding,
+ mItemPadding);
+ } else {
+ Log.d(TAG, "Floating tasks is enabled but no intent was found!");
+ }
+ }
}
private int getColorWithGivenLuminance(int color, float luminance) {
@@ -150,6 +173,10 @@
if (mAllAppsButton != null) {
mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
}
+ if (mFloatingTaskButton != null) {
+ mFloatingTaskButton.setOnClickListener(
+ mControllerCallbacks.getFloatingTaskButtonListener(mFloatingTaskIntent));
+ }
}
private void removeAndRecycle(View view) {
@@ -174,6 +201,10 @@
}
removeView(mQsb);
+ if (mFloatingTaskButton != null) {
+ removeView(mFloatingTaskButton);
+ }
+
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
if (hotseatItemInfo == null) {
@@ -255,6 +286,11 @@
mQsb.setVisibility(View.INVISIBLE);
}
+ if (mFloatingTaskButton != null) {
+ int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
+ addView(mFloatingTaskButton, index);
+ }
+
mThemeIconsBackground = calculateThemeIconsBackground();
setThemedIconsBackgroundColor(mThemeIconsBackground);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 31775e2..16dd90d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -24,6 +24,7 @@
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.annotation.NonNull;
+import android.content.Intent;
import android.graphics.Rect;
import android.util.FloatProperty;
import android.util.Log;
@@ -51,6 +52,7 @@
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -433,6 +435,13 @@
};
}
+ public View.OnClickListener getFloatingTaskButtonListener(@NonNull Intent intent) {
+ return v -> {
+ SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(v.getContext());
+ proxy.showFloatingTask(intent);
+ };
+ }
+
public View.OnLongClickListener getIconOnLongClickListener() {
return mControllers.taskbarDragController::startDragOnLongClick;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index 81acda3..076900c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -77,7 +77,7 @@
fadeStashedHandle.end()
}
- moveTaskbarBackgroundToAppropriateLayer()
+ moveTaskbarBackgroundToAppropriateLayer(skipAnim)
}
/**
@@ -86,24 +86,28 @@
* OR
* Removes the temporary window and show the TaskbarDragLayer background again.
*/
- private fun moveTaskbarBackgroundToAppropriateLayer() {
+ private fun moveTaskbarBackgroundToAppropriateLayer(skipAnim: Boolean) {
val taskbarBackgroundOverride = controllers.taskbarDragLayerController
.overrideBackgroundAlpha
val moveToLowerLayer = isVoiceInteractionWindowVisible
- if (moveToLowerLayer) {
+ val onWindowsSynchronized = if (moveToLowerLayer) {
// First add the temporary window, then hide the overlapping taskbar background.
- context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams)
- ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
- ) {
- taskbarBackgroundOverride.updateValue(0f)
- }
+ context.addWindowView(separateWindowForTaskbarBackground, separateWindowLayoutParams);
+ { taskbarBackgroundOverride.updateValue(0f) }
} else {
// First reapply the original taskbar background, then remove the temporary window.
- taskbarBackgroundOverride.updateValue(1f)
- ViewRootSync.synchronizeNextDraw(separateWindowForTaskbarBackground, context.dragLayer
- ) {
- context.removeWindowView(separateWindowForTaskbarBackground)
- }
+ taskbarBackgroundOverride.updateValue(1f);
+ { context.removeWindowView(separateWindowForTaskbarBackground) }
+ }
+
+ if (skipAnim) {
+ onWindowsSynchronized()
+ } else {
+ ViewRootSync.synchronizeNextDraw(
+ separateWindowForTaskbarBackground,
+ context.dragLayer,
+ onWindowsSynchronized
+ )
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
index c76180e..0372f67 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
@@ -17,15 +17,16 @@
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 static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.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.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
import com.android.launcher3.AbstractFloatingView;
@@ -40,6 +41,7 @@
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;
@@ -47,9 +49,6 @@
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
-import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
/**
* Window context for the taskbar all apps overlay.
@@ -74,7 +73,7 @@
TaskbarAllAppsContext(
TaskbarActivityContext taskbarContext,
TaskbarAllAppsController windowController,
- TaskbarStashController taskbarStashController) {
+ TaskbarControllers taskbarControllers) {
super(taskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null));
mTaskbarContext = taskbarContext;
mWindowController = windowController;
@@ -88,9 +87,10 @@
this,
slideInView,
windowController,
- taskbarStashController);
+ taskbarControllers);
mAppsView = slideInView.getAppsView();
+ TaskbarStashController taskbarStashController = taskbarControllers.taskbarStashController;
mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing();
mStashedTaskbarHeight = taskbarStashController.getStashedHeight();
}
@@ -163,7 +163,7 @@
/** Root drag layer for this context. */
private static class TaskbarAllAppsDragLayer extends
- BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInsetsListener {
+ BaseDragLayer<TaskbarAllAppsContext> implements OnComputeInternalInsetsListener {
private TaskbarAllAppsDragLayer(Context context) {
super(context, null, 1);
@@ -174,14 +174,13 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- ViewTreeObserverWrapper.addOnComputeInsetsListener(
- getViewTreeObserver(), this);
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- ViewTreeObserverWrapper.removeOnComputeInsetsListener(this);
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
@Override
@@ -207,7 +206,7 @@
}
@Override
- public void onComputeInsets(InsetsInfo inoutInfo) {
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
if (mActivity.mDragController.isSystemDragInProgress()) {
inoutInfo.touchableRegion.setEmpty();
inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 6c43e50..1671a0f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -144,9 +144,7 @@
// to catch invalid states.
mControllers.getSharedState().allAppsVisible = true;
- mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext,
- this,
- mControllers.taskbarStashController);
+ mAllAppsContext = new TaskbarAllAppsContext(mTaskbarContext, this, mControllers);
mAllAppsContext.getDragController().init(mControllers);
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
Optional.ofNullable(mAllAppsContext.getSystemService(WindowManager.class))
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 0e62da3..9d48c8d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -30,13 +30,10 @@
import com.android.launcher3.R;
import com.android.launcher3.views.AbstractSlideInView;
-import java.util.Optional;
-
/** Wrapper for taskbar all apps with slide-in behavior. */
public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarAllAppsContext>
implements Insettable, DeviceProfile.OnDeviceProfileChangeListener {
private TaskbarAllAppsContainerView mAppsView;
- private OnCloseListener mOnCloseBeginListener;
private float mShiftRange;
public TaskbarAllAppsSlideInView(Context context, AttributeSet attrs) {
@@ -72,14 +69,8 @@
return mAppsView;
}
- /** Callback invoked when the view is beginning to close (e.g. close animation is started). */
- void setOnCloseBeginListener(OnCloseListener onCloseBeginListener) {
- mOnCloseBeginListener = onCloseBeginListener;
- }
-
@Override
protected void handleClose(boolean animate) {
- Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
handleClose(animate,
ALL_APPS.getTransitionDuration(mActivityContext, false /* isToState */));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index a39e872..b0d3528 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -22,6 +22,8 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.appprediction.AppsDividerView;
import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.taskbar.NavbarButtonsViewController;
+import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarStashController;
/**
@@ -34,17 +36,19 @@
private final TaskbarAllAppsSlideInView mSlideInView;
private final TaskbarAllAppsContainerView mAppsView;
private final TaskbarStashController mTaskbarStashController;
+ private final NavbarButtonsViewController mNavbarButtonsViewController;
TaskbarAllAppsViewController(
TaskbarAllAppsContext context,
TaskbarAllAppsSlideInView slideInView,
TaskbarAllAppsController windowController,
- TaskbarStashController taskbarStashController) {
+ TaskbarControllers taskbarControllers) {
mContext = context;
mSlideInView = slideInView;
mAppsView = mSlideInView.getAppsView();
- mTaskbarStashController = taskbarStashController;
+ mTaskbarStashController = taskbarControllers.taskbarStashController;
+ mNavbarButtonsViewController = taskbarControllers.navbarButtonsViewController;
setUpIconLongClick();
setUpAppDivider();
@@ -83,7 +87,9 @@
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_APP_ALL_APPS, true);
mTaskbarStashController.applyState(
ALL_APPS.getTransitionDuration(mContext, true /* isToState */));
+ mNavbarButtonsViewController.setSlideInViewVisible(true);
mSlideInView.setOnCloseBeginListener(() -> {
+ mNavbarButtonsViewController.setSlideInViewVisible(false);
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
// Post in case view is closing due to gesture navigation. If a gesture is in progress,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 2e4e739..8fb7030 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -88,6 +88,11 @@
return;
}
setStateWithAnimationInternal(toState, config, builder);
+ builder.addEndListener(success -> {
+ if (!success) {
+ mRecentsView.reset();
+ }
+ });
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 12d7a98..192ac62 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -121,7 +121,6 @@
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.ViewCapture;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.RecentsModel;
@@ -135,11 +134,11 @@
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TISBindHelper;
+import com.android.quickstep.util.ViewCapture;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -192,7 +191,7 @@
RecentsView overviewPanel = (RecentsView) getOverviewPanel();
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
- getDepthController());
+ getDepthController(), getStatsLogManager());
overviewPanel.init(mActionsView, controller);
mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));
@@ -587,6 +586,8 @@
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) {
WIDGET_DEPTH.set(getDepthController(),
Utilities.mapToRange(progress, 0f, 1f, 0f, getDeviceProfile().bottomSheetDepth,
@@ -800,8 +801,8 @@
? mAppTransitionManager.getActivityLaunchOptions(v)
: super.getActivityLaunchOptions(v, item);
if (mLastTouchUpTime > 0) {
- ActivityOptionsCompat.setLauncherSourceInfo(
- activityOptions.options, mLastTouchUpTime);
+ activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER,
+ mLastTouchUpTime);
}
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
activityOptions.options.setLaunchDisplayId(
@@ -894,13 +895,13 @@
// load in, and then proceed to OverviewSplitSelect.
if (isInState(OVERVIEW_SPLIT_SELECT)) {
SplitSelectStateController splitSelectStateController =
- ((RecentsView) getOverviewPanel()).getSplitPlaceholder();
+ ((RecentsView) getOverviewPanel()).getSplitSelectController();
// Launcher will restart in Overview and then transition to OverviewSplitSelect.
outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
new PendingSplitSelectInfo(
splitSelectStateController.getInitialTaskId(),
- splitSelectStateController.getActiveSplitStagePosition()
- )
+ splitSelectStateController.getActiveSplitStagePosition(),
+ splitSelectStateController.getSplitEvent())
));
outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 5cddd07..910b99b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -27,13 +27,13 @@
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
+import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
import android.util.Pair;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorListeners;
@@ -42,6 +42,8 @@
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.util.AnimUtils;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
@@ -71,7 +73,10 @@
// DepthController to prevent optimizations which might occlude the layers behind
mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
- handleSplitSelectionState(state, null);
+ PendingAnimation builder =
+ new PendingAnimation(state.getTransitionDuration(mLauncher, true));
+
+ handleSplitSelectionState(state, builder, /* animate */false);
}
@Override
@@ -92,7 +97,7 @@
builder.addListener(AnimatorListeners.forSuccessCallback(() ->
mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
- handleSplitSelectionState(toState, builder);
+ handleSplitSelectionState(toState, builder, /* animate */true);
setAlphas(builder, config, toState);
builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
@@ -105,8 +110,14 @@
* will add animations to builder.
*/
private void handleSplitSelectionState(@NonNull LauncherState toState,
- @Nullable PendingAnimation builder) {
- boolean animate = builder != null;
+ @NonNull PendingAnimation builder, boolean animate) {
+ if (toState != OVERVIEW_SPLIT_SELECT) {
+ // Not going to split, nothing to do but ensure taskviews are at correct offset
+ mRecentsView.resetSplitPrimaryScrollOffset();
+ return;
+ }
+
+ // Create transition animations to split select
PagedOrientationHandler orientationHandler =
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
Pair<FloatProperty, FloatProperty> taskViewsFloat =
@@ -114,25 +125,27 @@
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mLauncher.getDeviceProfile());
- if (toState == OVERVIEW_SPLIT_SELECT) {
- // Animation to "dismiss" selected taskView
- PendingAnimation splitSelectInitAnimation = mRecentsView.createSplitSelectInitAnimation(
- toState.getTransitionDuration(mLauncher, true /* isToState */));
- // Add properties to shift remaining taskViews to get out of placeholder view
- splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
- toState.getSplitSelectTranslation(mLauncher), LINEAR);
- splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
+ SplitAnimationTimings timings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mLauncher.getDeviceProfile().isTablet);
- if (!animate) {
- splitSelectInitAnimation.buildAnim().start();
- } else {
- builder.add(splitSelectInitAnimation.buildAnim());
- }
+ mRecentsView.createSplitSelectInitAnimation(builder,
+ toState.getTransitionDuration(mLauncher, true /* isToState */));
+ // Shift tasks vertically downward to get out of placeholder view
+ builder.setFloat(mRecentsView, taskViewsFloat.first,
+ toState.getSplitSelectTranslation(mLauncher),
+ timings.getGridSlidePrimaryInterpolator());
+ // Zero out horizontal translation
+ builder.setFloat(mRecentsView, taskViewsFloat.second,
+ 0,
+ timings.getGridSlideSecondaryInterpolator());
- mRecentsView.applySplitPrimaryScrollOffset();
- } else {
- mRecentsView.resetSplitPrimaryScrollOffset();
+ if (!animate) {
+ AnimatorSet as = builder.buildAnim();
+ as.start();
+ as.end();
}
+
+ mRecentsView.applySplitPrimaryScrollOffset();
}
private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 872e64a..da07edf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -34,6 +34,7 @@
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -62,6 +63,7 @@
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.RecentsView;
/**
@@ -188,6 +190,22 @@
AllAppsSwipeController.applyAllAppsToNormalConfig(mActivity, config);
} else if (fromState == NORMAL && toState == ALL_APPS) {
AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mActivity, config);
+ } else if (fromState == OVERVIEW && toState == OVERVIEW_SPLIT_SELECT) {
+ SplitAnimationTimings timings = mActivity.getDeviceProfile().isTablet
+ ? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT
+ : SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
+ config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR,
+ timings.getActionsFadeStartOffset(),
+ timings.getActionsFadeEndOffset()));
+ } else if ((fromState == NORMAL || fromState == ALL_APPS)
+ && toState == OVERVIEW_SPLIT_SELECT) {
+ // Splitting from Home is currently only available on tablets
+ SplitAnimationTimings timings = SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+ config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR,
+ timings.getScrimFadeInStartOffset(),
+ timings.getScrimFadeInEndOffset()));
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_0_75);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_0_75);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index e79d56b..8babd34 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -16,7 +16,10 @@
package com.android.launcher3.uioverrides.states;
+import android.content.Context;
+
import com.android.launcher3.Launcher;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.RecentsView;
/**
@@ -38,4 +41,16 @@
RecentsView recentsView = launcher.getOverviewPanel();
return recentsView.getSplitSelectTranslation();
}
+
+ @Override
+ public int getTransitionDuration(Context context, boolean isToState) {
+ boolean isTablet = ((Launcher) context).getDeviceProfile().isTablet;
+ if (isToState && isTablet) {
+ return SplitAnimationTimings.TABLET_ENTER_DURATION;
+ } else if (isToState && !isTablet) {
+ return SplitAnimationTimings.PHONE_ENTER_DURATION;
+ } else {
+ return SplitAnimationTimings.ABORT_DURATION;
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index ca7f633..c49848a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -236,7 +236,8 @@
PendingAnimation pa;
if (goingUp) {
currentInterpolator = Interpolators.LINEAR;
- pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+ pa = new PendingAnimation(maxDuration);
+ mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration,
false /* dismissingForSplitSelection*/);
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 81a5c1c..f3630c1 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -50,7 +50,6 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -87,6 +86,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -127,7 +127,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -622,8 +621,8 @@
Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
TraceHelper.FLAG_IGNORE_BINDERS);
- LatencyTrackerCompat.logToggleRecents(
- mContext, (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+ LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
+ (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
TraceHelper.INSTANCE.endSection(traceToken);
// This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
@@ -827,10 +826,6 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- ActiveGestureLog.INSTANCE.addLog(
- /* event= */ "startRecentsAnimationCallback",
- /* extras= */ targets.apps.length,
- /* gestureEvent= */ START_RECENTS_ANIMATION);
mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets);
mRecentsAnimationController = controller;
mRecentsAnimationTargets = targets;
@@ -881,11 +876,6 @@
// properly cleaned up the screenshot without accidentally using it.
mDeferredCleanupRecentsAnimationController = mRecentsAnimationController;
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
-
- if (mRecentsAnimationTargets != null) {
- setDividerShown(true /* shown */, false /* immediate */);
- }
-
// Defer clearing the controller and the targets until after we've updated the state
mRecentsAnimationController = null;
mRecentsAnimationTargets = null;
@@ -1454,6 +1444,7 @@
}
}
+ @Nullable
private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory,
RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
// Directly animate the app to PiP (picture-in-picture) mode
@@ -1479,6 +1470,10 @@
runningTaskTarget.taskInfo.pictureInPictureParams,
homeRotation,
hotseatKeepClearArea);
+ if (destinationBounds == null) {
+ // No destination bounds returned from SystemUI, bail early.
+ return null;
+ }
final Rect appBounds = new Rect();
final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
// Adjust the appBounds for TaskBar by using the calculated window crop Rect
@@ -1783,10 +1778,6 @@
boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
- if (mRecentsAnimationTargets != null && wasVisible) {
- setDividerShown(true /* shown */, true /* immediate */);
- }
-
// Leave the pending invisible flag, as it may be used by wallpaper open animation.
if (mActivity != null) {
mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
@@ -2072,7 +2063,6 @@
if (handleTaskAppeared(appearedTaskTargets)) {
mRecentsAnimationController.finish(false /* toRecents */,
null /* onFinishComplete */);
- mActivityInterface.onLaunchTaskSuccess();
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "finishRecentsAnimation",
/* extras= */ false,
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index a166553..b06b894 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -135,6 +135,13 @@
}
/**
+ * Returns whether we are currently not animating, and the animation's value matches the given.
+ */
+ public boolean isSettledOnValue(float endValue) {
+ return !isAnimating() && value == endValue;
+ }
+
+ /**
* Returns the value we are animating to, or {@code null} if we are not currently animating.
*/
public Float getEndValue() {
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 315a91e..226b173 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -183,14 +183,6 @@
public abstract void onLaunchTaskFailed();
- public void onLaunchTaskSuccess() {
- ACTIVITY_TYPE activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
- activity.getStateManager().moveToRestState();
- }
-
/**
* Closes any overlays.
*/
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index e55e966..99f7bdd 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(SurfaceProperties builder,
+ private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder,
RemoteAnimationTargetCompat app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mCurrentShift.value,
Utilities.boundToRange(1 - mCurrentShift.value, 0, 1));
}
- private void setHomeScaleAndAlpha(SurfaceProperties builder,
+ private void setHomeScaleAndAlpha(SurfaceParams.Builder 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.setMatrix(mTmpMatrix).setAlpha(alpha);
+ builder.withMatrix(mTmpMatrix).withAlpha(alpha);
}
@Override
@@ -279,12 +279,12 @@
return mTargetRect;
}
- private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
RemoteAnimationTargetCompat app, TransformParams params) {
- builder.setAlpha(mRecentsAlpha.value);
+ builder.withAlpha(mRecentsAlpha.value);
}
- private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder,
+ private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
RemoteAnimationTargetCompat app, TransformParams params) {
setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value);
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 703db07..bcd9687 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -21,6 +21,8 @@
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_LAST_TASK;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
import android.annotation.Nullable;
import android.annotation.TargetApi;
@@ -335,9 +337,13 @@
case HOME:
ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_HOME);
break;
- case RECENTS:
case NEW_TASK:
+ ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
+ break;
case LAST_TASK:
+ ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_LAST_TASK);
+ break;
+ case RECENTS:
default:
// No-Op
}
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index ee3b075..7a281dd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -46,6 +46,7 @@
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.
@@ -241,17 +242,20 @@
/** 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 (mBackTarget.leash.isValid()) {
- mTransaction.setMatrix(mBackTarget.leash, mTransformMatrix, new float[9]);
- mTransaction.setWindowCrop(mBackTarget.leash, mStartRect);
- mTransaction.setCornerRadius(mBackTarget.leash, cornerRadius);
+ if (surfaceParams.surface.isValid()) {
+ surfaceParams.applyTo(mTransaction);
}
-
mTransaction.apply();
}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 36ca993..d1533f0 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -84,7 +84,8 @@
final View workspaceView = findWorkspaceView(launchCookies,
mRecentsView.getRunningTaskView());
- boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
+ boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow()
+ && workspaceView.getHeight() > 0;
mActivity.getRootView().setForceHideBackArrow(true);
if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 57a26ee..875b72c 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -240,8 +240,11 @@
interactionHandler.onGestureCancelled();
cmd.removeListener(this);
- RecentsView createdRecents =
- activityInterface.getCreatedActivity().getOverviewPanel();
+ T createdActivity = activityInterface.getCreatedActivity();
+ if (createdActivity == null) {
+ return;
+ }
+ RecentsView createdRecents = createdActivity.getOverviewPanel();
if (createdRecents != null) {
createdRecents.onRecentsAnimationComplete();
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 9bd0cb9..b7cdecd 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,16 +1,25 @@
package com.android.quickstep;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.app.Activity;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
import androidx.annotation.Nullable;
+import com.android.launcher3.R;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.TISBindHelper;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
public class QuickstepTestInformationHandler extends TestInformationHandler {
@@ -72,6 +81,37 @@
TestProtocol.REQUEST_HAS_TIS, true);
return response;
}
+
+ case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
+ runOnTISBinder(tisBinder -> {
+ enableManualTaskbarStashing(tisBinder, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
+ runOnTISBinder(tisBinder -> {
+ enableManualTaskbarStashing(tisBinder, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
+ runOnTISBinder(tisBinder -> {
+ enableManualTaskbarStashing(tisBinder, true);
+
+ // Allow null-pointer to catch illegal states.
+ tisBinder.getTaskbarManager().getCurrentActivityContext()
+ .unstashTaskbarIfStashed();
+
+ enableManualTaskbarStashing(tisBinder, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
+ final Resources resources = mContext.getResources();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
+ return response;
+ }
}
return super.call(method, arg, extras);
@@ -93,4 +133,30 @@
protected boolean isLauncherInitialized() {
return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
}
+
+ private void enableManualTaskbarStashing(
+ TouchInteractionService.TISBinder tisBinder, boolean enable) {
+ // Allow null-pointer to catch illegal states.
+ tisBinder.getTaskbarManager().getCurrentActivityContext().enableManualStashingDuringTests(
+ enable);
+ }
+
+ /**
+ * Runs the given command on the UI thread, after ensuring we are connected to
+ * TouchInteractionService.
+ */
+ protected void runOnTISBinder(Consumer<TouchInteractionService.TISBinder> connectionCallback) {
+ try {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ TISBindHelper helper = MAIN_EXECUTOR.submit(() ->
+ new TISBindHelper(mContext, tisBinder -> {
+ connectionCallback.accept(tisBinder);
+ countDownLatch.countDown();
+ })).get();
+ countDownLatch.await();
+ MAIN_EXECUTOR.execute(helper::onDestroy);
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 5a9862c..6b616b1 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,6 +20,7 @@
import android.annotation.TargetApi;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -31,7 +32,6 @@
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -49,7 +49,7 @@
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
- private final KeyguardManagerCompat mKeyguardManager;
+ private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
@@ -66,8 +66,8 @@
// Tasks are stored in order of least recently launched to most recently launched.
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
- public RecentTasksList(LooperExecutor mainThreadExecutor,
- KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
+ public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keyguardManager,
+ SystemUiProxy sysUiProxy) {
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
@@ -255,7 +255,8 @@
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
for (GroupedRecentTaskInfo rawTask : rawTasks) {
if (rawTask.getType() == GroupedRecentTaskInfo.TYPE_FREEFORM) {
- // TODO: add entry for freeform tasks
+ GroupTask desktopTask = createDesktopTask(rawTask);
+ allTasks.add(desktopTask);
continue;
}
ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
@@ -283,6 +284,16 @@
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 SplitConfigurationOptions.SplitBounds convertSplitBounds(
SplitBounds shellSplitBounds) {
return shellSplitBounds == null ?
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3e3a431..c879494 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -30,6 +30,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.app.ActivityOptions;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
@@ -77,7 +78,6 @@
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -131,7 +131,7 @@
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
- null /* depthController */);
+ /* depthController */ null, getStatsLogManager());
mDragLayer.recreateControllers();
mFallbackRecentsView.init(mActionsView, controller);
@@ -263,8 +263,10 @@
wrapper, RECENTS_LAUNCH_DURATION,
RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY, getIApplicationThread());
- final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
- ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
+ final ActivityOptions options = ActivityOptions.makeRemoteAnimation(
+ adapterCompat.getWrapped(),
+ adapterCompat.getRemoteTransition().getTransition());
+ final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options,
onEndCallback);
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
activityOptions.options.setLaunchDisplayId(
@@ -406,8 +408,10 @@
RemoteAnimationAdapterCompat adapterCompat =
new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
getIApplicationThread());
- startHomeIntentSafely(this,
- ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
+ ActivityOptions options = ActivityOptions.makeRemoteAnimation(
+ adapterCompat.getWrapped(),
+ adapterCompat.getRemoteTransition().getTransition());
+ startHomeIntentSafely(this, options.toBundle());
}
private final RemoteAnimationFactory mAnimationToHomeFactory =
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 887fd54..b233521 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION;
import android.graphics.Rect;
import android.util.ArraySet;
@@ -28,6 +29,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -113,6 +115,10 @@
homeContentInsets, minimizedHomeBounds);
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
+ ActiveGestureLog.INSTANCE.addLog(
+ /* event= */ "RecentsAnimationCallbacks.onAnimationStart",
+ /* extras= */ targets.apps.length,
+ /* gestureEvent= */ START_RECENTS_ANIMATION);
for (RecentsAnimationListener listener : getListeners()) {
listener.onRecentsAnimationStart(mController, targets);
}
@@ -137,7 +143,8 @@
@Override
public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
- ActiveGestureLog.INSTANCE.addLog("onTasksAppeared");
+ ActiveGestureLog.INSTANCE.addLog("onTasksAppeared",
+ ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED);
for (RecentsAnimationListener listener : getListeners()) {
listener.onTasksAppeared(apps);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 3074dbb..3053474 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -22,6 +22,7 @@
import android.annotation.TargetApi;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
@@ -40,7 +41,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -75,7 +75,8 @@
private RecentsModel(Context context) {
mContext = context;
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
- new KeyguardManagerCompat(context), SystemUiProxy.INSTANCE.get(context));
+ context.getSystemService(KeyguardManager.class),
+ SystemUiProxy.INSTANCE.get(context));
IconProvider iconProvider = new IconProvider(context);
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 71e8a77..baeb514 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(SurfaceProperties builder, RemoteAnimationTargetCompat app,
- TransformParams params) {
- builder.setMatrix(mMatrix)
- .setWindowCrop(mCropRect)
- .setCornerRadius(params.getCornerRadius());
+ public void onBuildTargetParams(
+ Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mCropRect)
+ .withCornerRadius(params.getCornerRadius());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 3a7ade3..3b2df31 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -45,8 +45,10 @@
import android.view.SurfaceControl;
import android.window.IOnBackInvokedCallback;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.internal.logging.InstanceId;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -56,6 +58,8 @@
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.back.IBackAnimation;
+import com.android.wm.shell.desktopmode.IDesktopMode;
+import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -86,11 +90,13 @@
private IPip mPip;
private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
private ISplitScreen mSplitScreen;
+ private IFloatingTasks mFloatingTasks;
private IOneHanded mOneHanded;
private IShellTransitions mShellTransitions;
private IStartingWindow mStartingWindow;
private IRecentTasks mRecentTasks;
private IBackAnimation mBackAnimation;
+ private IDesktopMode mDesktopMode;
private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
MAIN_EXECUTOR.execute(() -> clearProxy());
};
@@ -162,20 +168,22 @@
}
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
- IOneHanded oneHanded, IShellTransitions shellTransitions,
+ IFloatingTasks floatingTasks, IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
- IBackAnimation backAnimation) {
+ IBackAnimation backAnimation, IDesktopMode desktopMode) {
unlinkToDeath();
mSystemUiProxy = proxy;
mPip = pip;
mSplitScreen = splitScreen;
+ mFloatingTasks = floatingTasks;
mOneHanded = oneHanded;
mShellTransitions = shellTransitions;
mStartingWindow = startingWindow;
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
mRecentTasks = recentTasks;
mBackAnimation = backAnimation;
+ mDesktopMode = desktopMode;
linkToDeath();
// re-attach the listeners once missing due to setProxy has not been initialized yet.
if (mPipAnimationListener != null && mPip != null) {
@@ -202,7 +210,7 @@
}
public void clearProxy() {
- setProxy(null, null, null, null, null, null, null, null, null);
+ setProxy(null, null, null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -480,6 +488,10 @@
mPipAnimationListener = listener;
}
+ /**
+ * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
+ */
+ @Nullable
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams, int launcherRotation,
Rect hotseatKeepClearArea) {
@@ -539,11 +551,11 @@
/** Start multiple tasks in split-screen simultaneously. */
public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
@SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
- RemoteTransitionCompat remoteTransition) {
+ RemoteTransitionCompat remoteTransition, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
- sidePosition, splitRatio, remoteTransition.getTransition());
+ sidePosition, splitRatio, remoteTransition.getTransition(), instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTask");
}
@@ -555,11 +567,11 @@
*/
public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
- sideOptions, sidePosition, splitRatio, adapter);
+ sideOptions, sidePosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startTasksWithLegacyTransition");
}
@@ -569,11 +581,12 @@
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
@SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter) {
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
- taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+ taskId, mainOptions, sideOptions, sidePosition, splitRatio, adapter,
+ instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntentAndTaskWithLegacyTransition");
}
@@ -583,11 +596,11 @@
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, int taskId,
Bundle mainOptions, Bundle sideOptions,
@SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter) {
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
if (mSystemUiProxy != null) {
try {
mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId,
- mainOptions, sideOptions, sidePosition, splitRatio, adapter);
+ mainOptions, sideOptions, sidePosition, splitRatio, adapter, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcutAndTaskWithLegacyTransition");
}
@@ -595,11 +608,11 @@
}
public void startShortcut(String packageName, String shortcutId, int position,
- Bundle options, UserHandle user) {
+ Bundle options, UserHandle user, InstanceId instanceId) {
if (mSplitScreen != null) {
try {
mSplitScreen.startShortcut(packageName, shortcutId, position, options,
- user);
+ user, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startShortcut");
}
@@ -607,10 +620,10 @@
}
public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
- Bundle options) {
+ Bundle options, InstanceId instanceId) {
if (mSplitScreen != null) {
try {
- mSplitScreen.startIntent(intent, fillInIntent, position, options);
+ mSplitScreen.startIntent(intent, fillInIntent, position, options, instanceId);
} catch (RemoteException e) {
Log.w(TAG, "Failed call startIntent");
}
@@ -656,6 +669,20 @@
}
//
+ // Floating tasks
+ //
+
+ public void showFloatingTask(Intent intent) {
+ if (mFloatingTasks != null) {
+ try {
+ mFloatingTasks.showTask(intent);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Launcher: Failed call showFloatingTask", e);
+ }
+ }
+ }
+
+ //
// One handed
//
@@ -884,4 +911,19 @@
return false;
}
+
+ //
+ // Desktop Mode
+ //
+
+ /** Call shell to show all apps active on the desktop */
+ public void showDesktopApps() {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.showDesktopApps();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showDesktopApps", e);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 5fb806d..b9a1b06 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -36,12 +36,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
-import com.android.quickstep.util.ActiveGestureErrorDetector;
-import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -138,8 +135,6 @@
// handling this call entirely
return;
}
- ActiveGestureLog.INSTANCE.addLog("TaskAnimationManager.startRecentsAnimation",
- ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION);
mController = controller;
mTargets = targets;
mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId());
@@ -233,7 +228,8 @@
RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
mController != null ? mController.getController() : null,
mCtx.getIApplicationThread());
- final ActivityOptions options = ActivityOptionsCompat.makeRemoteTransition(transition);
+ final ActivityOptions options = ActivityOptions.makeRemoteTransition(
+ transition.getTransition());
// Allowing to pause Home if Home is top activity and Recents is not Home. So when user
// start home when recents animation is playing, the home activity can be resumed again
// to let the transition controller collect Home activity.
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index a0860ee..eae79df 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
@@ -26,9 +28,12 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.os.SystemProperties;
+import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
+import android.view.WindowManagerGlobal;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -52,8 +57,6 @@
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.Collections;
import java.util.List;
@@ -121,6 +124,7 @@
}
class FreeformSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
+ private static final String TAG = "FreeformSystemShortcut";
private Handler mHandler;
@@ -193,7 +197,7 @@
taskId, thumbnail, taskBounds));
}
};
- WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+ overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
@@ -201,8 +205,27 @@
}
}
+ /**
+ * Overrides a pending app transition.
+ */
+ private void overridePendingAppTransitionMultiThumbFuture(
+ AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
+ Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(
+ animationSpecFuture.getFuture(),
+ RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
+ animStartedCallback), scaleUp, displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ",
+ e);
+ }
+ }
+
private ActivityOptions makeLaunchOptions(Activity activity) {
- ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
+ ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
// Arbitrary bounds only because freeform is in dev mode right now
final View decorView = activity.getWindow().getDecorView();
final WindowInsets insets = decorView.getRootWindowInsets();
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 7e1d181..a96524e 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -72,8 +72,6 @@
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;
@@ -84,6 +82,7 @@
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;
@@ -253,24 +252,21 @@
@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.setMatrix(taskViewSimulator.getCurrentMatrix())
- .setWindowCrop(cropRect)
- .setAlpha(mNavFadeIn.value);
+ navBuilder.withMatrix(taskViewSimulator.getCurrentMatrix())
+ .withWindowCrop(cropRect)
+ .withAlpha(mNavFadeIn.value);
} else {
- navBuilder.setAlpha(mNavFadeOut.value);
+ navBuilder.withAlpha(mNavFadeOut.value);
}
- handle.getTransformParams().applySurfaceParams(transaction);
+ handle.getTransformParams().applySurfaceParams(navBuilder.build());
}
}
});
@@ -478,7 +474,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
@@ -672,7 +668,7 @@
for (int i = 0; i < nonApps.length; ++i) {
final RemoteAnimationTargetCompat targ = nonApps[i];
final SurfaceControl leash = targ.leash;
- if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null) {
+ if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null && leash.isValid()) {
auxiliarySurfaces.add(leash);
hasSurfaceToAnimate = true;
}
@@ -685,7 +681,9 @@
dockFadeAnimator.addUpdateListener(valueAnimator -> {
float progress = valueAnimator.getAnimatedFraction();
for (SurfaceControl leash : auxiliarySurfaces) {
- t.setAlpha(leash, shown ? progress : 1 - progress);
+ if (leash != null && leash.isValid()) {
+ t.setAlpha(leash, shown ? progress : 1 - progress);
+ }
}
t.apply();
});
@@ -706,7 +704,9 @@
public void onAnimationEnd(Animator animation) {
if (!shown) {
for (SurfaceControl leash : auxiliarySurfaces) {
- t.hide(leash);
+ if (leash != null && leash.isValid()) {
+ t.hide(leash);
+ }
}
t.apply();
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 040c55b..1999701 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -28,6 +28,8 @@
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -82,7 +84,6 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.ViewCapture;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -101,6 +102,7 @@
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.util.ViewCapture;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -110,6 +112,8 @@
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.back.IBackAnimation;
+import com.android.wm.shell.desktopmode.IDesktopMode;
+import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -167,6 +171,8 @@
IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
KEY_EXTRA_SHELL_SPLIT_SCREEN));
+ IFloatingTasks floatingTasks = IFloatingTasks.Stub.asInterface(bundle.getBinder(
+ KEY_EXTRA_SHELL_FLOATING_TASKS));
IOneHanded onehanded = IOneHanded.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
@@ -180,10 +186,12 @@
bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
IBackAnimation backAnimation = IBackAnimation.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
+ IDesktopMode desktopMode = IDesktopMode.Stub.asInterface(
+ bundle.getBinder(KEY_EXTRA_SHELL_DESKTOP_MODE));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
- splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
- launcherUnlockAnimationController, backAnimation);
+ splitscreen, floatingTasks, onehanded, shellTransitions, startingWindow,
+ recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode);
TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
preloadOverview(true /* fromInit */);
});
@@ -709,16 +717,19 @@
}
public GestureState createGestureState(GestureState previousGestureState) {
- GestureState gestureState = new GestureState(mOverviewComponentObserver,
- ActiveGestureLog.INSTANCE.incrementLogId());
+ final GestureState gestureState;
TopTaskTracker.CachedTaskInfo taskInfo;
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+ gestureState = new GestureState(mOverviewComponentObserver,
+ ActiveGestureLog.INSTANCE.getLogId());
taskInfo = previousGestureState.getRunningTask();
gestureState.updateRunningTask(taskInfo);
gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
gestureState.updatePreviouslyAppearedTaskIds(
previousGestureState.getPreviouslyAppearedTaskIds());
} else {
+ gestureState = new GestureState(mOverviewComponentObserver,
+ ActiveGestureLog.INSTANCE.incrementLogId());
taskInfo = TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false);
gestureState.updateRunningTask(taskInfo);
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 7337132..19a6c38 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -78,6 +78,11 @@
}
// While animating into recents, update the visible task data as needed
setter.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
+ setter.addEndListener(success -> {
+ if (!success) {
+ mRecentsView.reset();
+ }
+ });
mRecentsView.updateEmptyMessage();
setProperties(toState, config, setter);
@@ -114,8 +119,11 @@
RecentsState currentState = mActivity.getStateManager().getState();
if (isSplitSelectionState(state) && !isSplitSelectionState(currentState)) {
- setter.add(mRecentsView.createSplitSelectInitAnimation(
- state.getTransitionDuration(mActivity, true /* isToState */)).buildAnim());
+ int duration = state.getTransitionDuration(mActivity, true /* isToState */);
+ // TODO (b/246851887): Pass in setter as a NO_ANIM PendingAnimation instead
+ PendingAnimation pa = new PendingAnimation(duration);
+ mRecentsView.createSplitSelectInitAnimation(pa, duration);
+ setter.add(pa.buildAnim());
}
Pair<FloatProperty, FloatProperty> taskViewsFloat =
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 7c96bf8..e32aaee 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -33,6 +33,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.SplitConfigurationOptions;
@@ -54,6 +55,8 @@
public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
implements StateListener<RecentsState> {
+ private static final int TASK_DISMISS_DURATION = 150;
+
@Nullable
private Task mHomeTask;
@@ -105,8 +108,9 @@
if (mHomeTask != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskViewByTaskId(mHomeTask.key.id);
if (tv != null) {
- PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150,
- false /* dismissingForSplitSelection*/);
+ PendingAnimation pa = new PendingAnimation(TASK_DISMISS_DURATION);
+ createTaskDismissAnimation(pa, tv, true, false,
+ TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/);
pa.addEndListener(e -> setCurrentTask(-1));
AnimatorPlaybackController controller = pa.createPlaybackController();
controller.dispatchOnStart();
@@ -210,8 +214,9 @@
@Override
public void initiateSplitSelect(TaskView taskView,
- @SplitConfigurationOptions.StagePosition int stagePosition) {
- super.initiateSplitSelect(taskView, stagePosition);
+ @SplitConfigurationOptions.StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent) {
+ super.initiateSplitSelect(taskView, stagePosition, splitEvent);
mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
}
@@ -227,11 +232,6 @@
}
@Override
- public void onStateTransitionFailed(RecentsState toState) {
- reset();
- }
-
- @Override
public void onStateTransitionComplete(RecentsState finalState) {
if (finalState == HOME) {
// Clean-up logic that occurs when recents is no longer in use/visible.
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index c131c05..6bc24f2 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(
- SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
- builder.setMatrix(mMatrix);
+ builder.withMatrix(mMatrix);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 401df24..fa7bc04 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -33,6 +33,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -52,11 +53,9 @@
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.RecordingSurfaceTransaction.MockProperties;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.TransformParams;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
@@ -416,23 +415,21 @@
private class FakeTransformParams extends TransformParams {
@Override
- public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
- RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
- proxy.onBuildTargetParams(transaction.mockProperties, null, this);
- return transaction;
+ public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
+ SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
+ proxy.onBuildTargetParams(builder, null, this);
+ return new SurfaceParams[] {builder.build()};
}
@Override
- 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();
- }
+ 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();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 45c8036..37a28e5 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -336,8 +336,9 @@
appState.getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel,
- AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
}
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index d2d1233..53e0c2b 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -33,10 +33,10 @@
* Enums associated to gesture navigation events.
*/
public enum GestureEvent {
- MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, ON_SETTLED_ON_END_TARGET,
- START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION,
- SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT,
- SCROLLER_ANIMATION_ABORTED,
+ MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_LAST_TASK,
+ SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION,
+ FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK,
+ CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED,
/**
* These GestureEvents are specifically associated to state flags that get set in
@@ -134,6 +134,15 @@
+ " settling on end target.",
writer);
break;
+ case TASK_APPEARED:
+ errorDetected |= printErrorIfTrue(
+ !encounteredEvents.contains(GestureEvent.SET_END_TARGET_LAST_TASK)
+ && !encounteredEvents.contains(
+ GestureEvent.SET_END_TARGET_NEW_TASK),
+ /* errorMessage= */ prefix + "\t\tonTasksAppeared called "
+ + "before/without setting end target to last or new task",
+ writer);
+ break;
case STATE_GESTURE_COMPLETED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),
@@ -289,6 +298,21 @@
+ "the task screenshot wasn't cleaned up.",
writer);
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.SET_END_TARGET_LAST_TASK)
+ && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
+ /* errorMessage= */ prefix + "\t\tend target set to last task, but "
+ + "onTaskAppeared wasn't called.",
+ writer);
+ errorDetected |= printErrorIfTrue(
+ /* condition= */ encounteredEvents.contains(
+ GestureEvent.SET_END_TARGET_NEW_TASK)
+ && !encounteredEvents.contains(GestureEvent.TASK_APPEARED),
+ /* errorMessage= */ prefix + "\t\tend target set to new task, but "
+ + "onTaskAppeared wasn't called.",
+ writer);
+
if (!errorDetected) {
writer.println(prefix + "\t\tNo errors detected.");
}
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
index 40eb31b..23fdd58 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
@@ -161,16 +161,14 @@
ArrayList<EventLog> eventLogs = new ArrayList<>();
for (int i = 0; i < logs.length; i++) {
- EventLog eventLog = logs[(nextIndex + logs.length - i - 1) % logs.length];
+ EventLog eventLog = logs[(nextIndex + i) % logs.length];
if (eventLog == null) {
continue;
}
eventLogs.add(eventLog);
writer.println(prefix + "\tLogs for logId: " + eventLog.logId);
- List<EventEntry> eventEntries = eventLog.eventEntries;
- for (int j = eventEntries.size() - 1; j >= 0; j--) {
- EventEntry eventEntry = eventEntries.get(j);
+ for (EventEntry eventEntry : eventLog.eventEntries) {
date.setTime(eventEntry.time);
StringBuilder msg = new StringBuilder(prefix + "\t\t").append(sdf.format(date))
@@ -215,6 +213,11 @@
return mCurrentLogId++;
}
+ /** Returns the current log ID. This should be used when a log trace is being reused. */
+ public int getLogId() {
+ return mCurrentLogId;
+ }
+
private boolean isEntrySame(
EventEntry entry,
int type,
diff --git a/quickstep/src/com/android/quickstep/util/AnimUtils.java b/quickstep/src/com/android/quickstep/util/AnimUtils.java
new file mode 100644
index 0000000..b7b7825
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AnimUtils.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+/**
+ * Utility class containing methods to help manage animations, interpolators, and timings.
+ */
+public class AnimUtils {
+ /**
+ * Fetches device-specific timings for the Overview > Split animation
+ * (splitscreen initiated from Overview).
+ */
+ public static SplitAnimationTimings getDeviceOverviewToSplitTimings(boolean isTablet) {
+ return isTablet
+ ? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT
+ : SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
+ }
+
+ /**
+ * Fetches device-specific timings for the Split > Confirm animation
+ * (splitscreen confirmed by selecting a second app).
+ */
+ public static SplitAnimationTimings getDeviceSplitToConfirmTimings(boolean isTablet) {
+ return isTablet
+ ? SplitAnimationTimings.TABLET_SPLIT_TO_CONFIRM
+ : SplitAnimationTimings.PHONE_SPLIT_TO_CONFIRM;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LogUtils.kt b/quickstep/src/com/android/quickstep/util/LogUtils.kt
new file mode 100644
index 0000000..bad8506
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LogUtils.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.util.Pair
+import com.android.internal.logging.InstanceIdSequence
+import com.android.launcher3.logging.InstanceId
+
+object LogUtils {
+ /**
+ * @return a [Pair] of two InstanceIds but with different types, one that can be used by framework
+ * (if needing to pass through an intent or such) and one used in Launcher
+ */
+ @JvmStatic
+ fun getShellShareableInstanceId():
+ Pair<com.android.internal.logging.InstanceId, InstanceId> {
+ val internalInstanceId = InstanceIdSequence(InstanceId.INSTANCE_ID_MAX).newInstanceId()
+ val launcherInstanceId = InstanceId(internalInstanceId.id)
+ return Pair(internalInstanceId, launcherInstanceId)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
new file mode 100644
index 0000000..e189a66
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/OverviewToSplitTimings.java
@@ -0,0 +1,98 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Overview > OverviewSplitSelect animation.
+ */
+abstract class OverviewToSplitTimings implements SplitAnimationTimings {
+ // Overwritten by device-specific timings
+ abstract public int getPlaceholderFadeInStart();
+ abstract public int getPlaceholderFadeInEnd();
+ abstract public int getPlaceholderIconFadeInStart();
+ abstract public int getPlaceholderIconFadeInEnd();
+ abstract public int getStagedRectSlideStart();
+ abstract public int getStagedRectSlideEnd();
+ abstract public int getGridSlideStart();
+ abstract public int getGridSlideStagger();
+ abstract public int getGridSlideDuration();
+
+ // Common timings
+ public int getIconFadeStart() { return 0; }
+ public int getIconFadeEnd() { return 83; }
+ public int getActionsFadeStart() { return 0; }
+ public int getActionsFadeEnd() { return 83; }
+ public int getInstructionsContainerFadeInStart() { return 167; }
+ public int getInstructionsContainerFadeInEnd() { return 250; }
+ public int getInstructionsTextFadeInStart() { return 217; }
+ public int getInstructionsTextFadeInEnd() { return 300; }
+ public int getInstructionsUnfoldStart() { return 167; }
+ public int getInstructionsUnfoldEnd() { return 500; }
+ public Interpolator getGridSlidePrimaryInterpolator() { return EMPHASIZED; }
+ public Interpolator getGridSlideSecondaryInterpolator() { return INSTANT; }
+
+ abstract public int getDuration();
+ abstract public Interpolator getStagedRectXInterpolator();
+ abstract public Interpolator getStagedRectYInterpolator();
+ abstract public Interpolator getStagedRectScaleXInterpolator();
+ abstract public Interpolator getStagedRectScaleYInterpolator();
+
+ public float getGridSlideStartOffset() {
+ return (float) getGridSlideStart() / getDuration();
+ }
+ public float getGridSlideStaggerOffset() {
+ return (float) getGridSlideStagger() / getDuration();
+ }
+ public float getGridSlideDurationOffset() {
+ return (float) getGridSlideDuration() / getDuration();
+ }
+ public float getActionsFadeStartOffset() {
+ return (float) getActionsFadeStart() / getDuration();
+ }
+ public float getActionsFadeEndOffset() {
+ return (float) getActionsFadeEnd() / getDuration();
+ }
+ public float getIconFadeStartOffset() {
+ return (float) getIconFadeStart() / getDuration();
+ }
+ public float getIconFadeEndOffset() {
+ return (float) getIconFadeEnd() / getDuration();
+ }
+ public float getInstructionsContainerFadeInStartOffset() {
+ return (float) getInstructionsContainerFadeInStart() / getDuration();
+ }
+ public float getInstructionsContainerFadeInEndOffset() {
+ return (float) getInstructionsContainerFadeInEnd() / getDuration();
+ }
+ public float getInstructionsTextFadeInStartOffset() {
+ return (float) getInstructionsTextFadeInStart() / getDuration();
+ }
+ public float getInstructionsTextFadeInEndOffset() {
+ return (float) getInstructionsTextFadeInEnd() / getDuration();
+ }
+ public float getInstructionsUnfoldStartOffset() {
+ return (float) getInstructionsUnfoldStart() / getDuration();
+ }
+ public float getInstructionsUnfoldEndOffset() {
+ return (float) getInstructionsUnfoldEnd() / getDuration();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
new file mode 100644
index 0000000..f1dde53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/PhoneOverviewToSplitTimings.java
@@ -0,0 +1,43 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Overview > OverviewSplitSelect animation on phones.
+ */
+public class PhoneOverviewToSplitTimings
+ extends OverviewToSplitTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 83; }
+ public int getPlaceholderIconFadeInEnd() { return 167; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 333; }
+ public int getGridSlideStart() { return 100; }
+ public int getGridSlideStagger() { return 0; }
+ public int getGridSlideDuration() { return 417; }
+
+ public int getDuration() { return PHONE_ENTER_DURATION; }
+ public Interpolator getStagedRectXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
new file mode 100644
index 0000000..9e7351a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/PhoneSplitToConfirmTimings.java
@@ -0,0 +1,40 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.EMPHASIZED;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the OverviewSplitSelect > confirmed animation on phones.
+ */
+public class PhoneSplitToConfirmTimings
+ extends SplitToConfirmTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 50; }
+ public int getPlaceholderIconFadeInEnd() { return 133; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 333; }
+
+ public int getDuration() { return PHONE_CONFIRM_DURATION; }
+ public Interpolator getStagedRectXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectYInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleXInterpolator() { return EMPHASIZED; }
+ public Interpolator getStagedRectScaleYInterpolator() { return EMPHASIZED; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
deleted file mode 100644
index a56c851..0000000
--- a/quickstep/src/com/android/quickstep/util/RecordingSurfaceTransaction.java
+++ /dev/null
@@ -1,81 +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.quickstep.util;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-
-/**
- * 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();
-
- /**
- * 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;
-
- 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/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index b2e159e..81c124f 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) {
- Transaction t = new Transaction();
+ TransactionCompat t = new TransactionCompat();
if (mFirstFrame) {
for (RemoteAnimationTargetCompat target : mTarget.unfilteredApps) {
t.show(target.leash);
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
new file mode 100644
index 0000000..2966fbb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationTimings.java
@@ -0,0 +1,98 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.view.animation.Interpolator;
+
+/**
+ * An interface that supports the centralization of timing information for splitscreen animations.
+ */
+public interface SplitAnimationTimings {
+ int TABLET_ENTER_DURATION = 866;
+ int TABLET_CONFIRM_DURATION = 383;
+
+ int PHONE_ENTER_DURATION = 517;
+ int PHONE_CONFIRM_DURATION = 333;
+
+ int ABORT_DURATION = 500;
+
+ SplitAnimationTimings TABLET_OVERVIEW_TO_SPLIT = new TabletOverviewToSplitTimings();
+ SplitAnimationTimings TABLET_HOME_TO_SPLIT = new TabletHomeToSplitTimings();
+ SplitAnimationTimings TABLET_SPLIT_TO_CONFIRM = new TabletSplitToConfirmTimings();
+
+ SplitAnimationTimings PHONE_OVERVIEW_TO_SPLIT = new PhoneOverviewToSplitTimings();
+ SplitAnimationTimings PHONE_SPLIT_TO_CONFIRM = new PhoneSplitToConfirmTimings();
+
+ // Shared methods
+ int getDuration();
+ int getPlaceholderFadeInStart();
+ int getPlaceholderFadeInEnd();
+ int getPlaceholderIconFadeInStart();
+ int getPlaceholderIconFadeInEnd();
+ int getStagedRectSlideStart();
+ int getStagedRectSlideEnd();
+ Interpolator getStagedRectXInterpolator();
+ Interpolator getStagedRectYInterpolator();
+ Interpolator getStagedRectScaleXInterpolator();
+ Interpolator getStagedRectScaleYInterpolator();
+ default float getPlaceholderFadeInStartOffset() {
+ return (float) getPlaceholderFadeInStart() / getDuration();
+ }
+ default float getPlaceholderFadeInEndOffset() {
+ return (float) getPlaceholderFadeInEnd() / getDuration();
+ }
+ default float getPlaceholderIconFadeInStartOffset() {
+ return (float) getPlaceholderIconFadeInStart() / getDuration();
+ }
+ default float getPlaceholderIconFadeInEndOffset() {
+ return (float) getPlaceholderIconFadeInEnd() / getDuration();
+ }
+ default float getStagedRectSlideStartOffset() {
+ return (float) getStagedRectSlideStart() / getDuration();
+ }
+ default float getStagedRectSlideEndOffset() {
+ return (float) getStagedRectSlideEnd() / getDuration();
+ }
+
+ // Defaults for OverviewToSplit
+ default float getGridSlideStartOffset() { return 0; }
+ default float getGridSlideStaggerOffset() { return 0; }
+ default float getGridSlideDurationOffset() { return 0; }
+ default float getActionsFadeStartOffset() { return 0; }
+ default float getActionsFadeEndOffset() { return 0; }
+ default float getIconFadeStartOffset() { return 0; }
+ default float getIconFadeEndOffset() { return 0; }
+ default float getInstructionsContainerFadeInStartOffset() { return 0; }
+ default float getInstructionsContainerFadeInEndOffset() { return 0; }
+ default float getInstructionsTextFadeInStartOffset() { return 0; }
+ default float getInstructionsTextFadeInEndOffset() { return 0; }
+ default float getInstructionsUnfoldStartOffset() { return 0; }
+ default float getInstructionsUnfoldEndOffset() { return 0; }
+ default Interpolator getGridSlidePrimaryInterpolator() { return LINEAR; }
+ default Interpolator getGridSlideSecondaryInterpolator() { return LINEAR; }
+
+ // Defaults for HomeToSplit
+ default float getScrimFadeInStartOffset() { return 0; }
+ default float getScrimFadeInEndOffset() { return 0; }
+
+ // Defaults for SplitToConfirm
+ default float getInstructionsFadeStartOffset() { return 0; }
+ default float getInstructionsFadeEndOffset() { return 0; }
+}
+
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 7efb1a5..efbe783 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -38,12 +38,16 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.view.RemoteAnimationAdapter;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import androidx.annotation.Nullable;
+import com.android.internal.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
@@ -74,10 +78,12 @@
private final Context mContext;
private final Handler mHandler;
+ private StatsLogManager mStatsLogManager;
private final SystemUiProxy mSystemUiProxy;
private final StateManager mStateManager;
private final DepthController mDepthController;
private @StagePosition int mStagePosition;
+ private ItemInfo mItemInfo;
private Intent mInitialTaskIntent;
private int mInitialTaskId = INVALID_TASK_ID;
private int mSecondTaskId = INVALID_TASK_ID;
@@ -88,11 +94,14 @@
/** If not null, this is the TaskView we want to launch from */
@Nullable
private GroupedTaskView mLaunchingTaskView;
+ /** Represents where split is intended to be invoked from. */
+ private StatsLogManager.EventEnum mSplitEvent;
public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
- DepthController depthController) {
+ DepthController depthController, StatsLogManager statsLogManager) {
mContext = context;
mHandler = handler;
+ mStatsLogManager = statsLogManager;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
mStateManager = stateManager;
mDepthController = depthController;
@@ -101,19 +110,25 @@
/**
* To be called after first task selected
*/
- public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition) {
+ public void setInitialTaskSelect(int taskId, @StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
mInitialTaskId = taskId;
- mStagePosition = stagePosition;
- mInitialTaskIntent = null;
- mUser = null;
+ setInitialData(stagePosition, splitEvent, itemInfo);
}
public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
- @Nullable UserHandle user) {
+ @NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent) {
mInitialTaskIntent = intent;
+ mUser = itemInfo.user;
+ mItemInfo = itemInfo;
+ setInitialData(stagePosition, splitEvent, itemInfo);
+ }
+
+ private void setInitialData(@StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
+ mItemInfo = itemInfo;
mStagePosition = stagePosition;
- mInitialTaskId = INVALID_TASK_ID;
- mUser = user;
+ mSplitEvent = splitEvent;
}
/**
@@ -137,8 +152,16 @@
FLAG_MUTABLE, null /* options */, mUser)
: PendingIntent.getActivity(mContext, 0, mInitialTaskIntent, FLAG_MUTABLE));
+ Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
+ LogUtils.getShellShareableInstanceId();
launchTasks(mInitialTaskId, pendingIntent, fillInIntent, mSecondTaskId, mStagePosition,
- callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
+ callback, false /* freezeTaskList */, DEFAULT_SPLIT_RATIO,
+ instanceIds.first);
+
+ mStatsLogManager.logger()
+ .withItemInfo(mItemInfo)
+ .withInstanceId(instanceIds.second)
+ .log(mSplitEvent);
}
@@ -174,7 +197,7 @@
public void launchTasks(int taskId1, int taskId2, @StagePosition int stagePosition,
Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
launchTasks(taskId1, null /* taskPendingIntent */, null /* fillInIntent */, taskId2,
- stagePosition, callback, freezeTaskList, splitRatio);
+ stagePosition, callback, freezeTaskList, splitRatio, null);
}
/**
@@ -183,10 +206,14 @@
* fill in intent with a taskId2 are set.
* @param taskPendingIntent is null when split is initiated from Overview
* @param stagePosition representing location of task1
+ * @param shellInstanceId loggingId to be used by shell, will be non-null for actions that create
+ * a split instance, null for cases that bring existing instaces to the
+ * foreground (quickswitch, launching previous pairs from overview)
*/
public void launchTasks(int taskId1, @Nullable PendingIntent taskPendingIntent,
@Nullable Intent fillInIntent, int taskId2, @StagePosition int stagePosition,
- Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+ Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio,
+ @Nullable InstanceId shellInstanceId) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "launchSplitTasks");
// Assume initial task is for top/left part of screen
@@ -200,7 +227,8 @@
mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
- ActivityThread.currentActivityThread().getApplicationThread()));
+ ActivityThread.currentActivityThread().getApplicationThread()),
+ shellInstanceId);
// TODO(b/237635859): handle intent/shortcut + task with shell transition
} else {
RemoteSplitLaunchAnimationRunner animationRunner =
@@ -218,18 +246,18 @@
if (taskPendingIntent == null) {
mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
- splitRatio, adapter);
+ splitRatio, adapter, shellInstanceId);
} else {
final ShortcutInfo shortcutInfo = getShortcutInfo(mInitialTaskIntent,
taskPendingIntent.getCreatorUserHandle());
if (shortcutInfo != null) {
mSystemUiProxy.startShortcutAndTaskWithLegacyTransition(shortcutInfo, taskId2,
mainOpts.toBundle(), null /* sideOptions */, stagePosition, splitRatio,
- adapter);
+ adapter, shellInstanceId);
} else {
mSystemUiProxy.startIntentAndTaskWithLegacyTransition(taskPendingIntent,
fillInIntent, taskId2, mainOpts.toBundle(), null /* sideOptions */,
- stagePosition, splitRatio, adapter);
+ stagePosition, splitRatio, adapter, shellInstanceId);
}
}
}
@@ -239,6 +267,10 @@
return mStagePosition;
}
+ public StatsLogManager.EventEnum getSplitEvent() {
+ return mSplitEvent;
+ }
+
public void setRecentsAnimationRunning(boolean running) {
this.mRecentsAnimationRunning = running;
}
@@ -358,6 +390,8 @@
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
+ mItemInfo = null;
+ mSplitEvent = null;
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
new file mode 100644
index 0000000..3026e98
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitToConfirmTimings.java
@@ -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.quickstep.util;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the OverviewSplitSelect > confirmed animation.
+ */
+abstract class SplitToConfirmTimings implements SplitAnimationTimings {
+ // Overwritten by device-specific timings
+ abstract public int getPlaceholderFadeInStart();
+ abstract public int getPlaceholderFadeInEnd();
+ abstract public int getPlaceholderIconFadeInStart();
+ abstract public int getPlaceholderIconFadeInEnd();
+ abstract public int getStagedRectSlideStart();
+ abstract public int getStagedRectSlideEnd();
+
+ // Common timings
+ public int getInstructionsFadeStart() { return 0; }
+ public int getInstructionsFadeEnd() { return 67; }
+
+ abstract public int getDuration();
+ abstract public Interpolator getStagedRectXInterpolator();
+ abstract public Interpolator getStagedRectYInterpolator();
+ abstract public Interpolator getStagedRectScaleXInterpolator();
+ abstract public Interpolator getStagedRectScaleYInterpolator();
+
+ public float getInstructionsFadeStartOffset() {
+ return (float) getInstructionsFadeStart() / getDuration();
+ }
+ public float getInstructionsFadeEndOffset() {
+ return (float) getInstructionsFadeEnd() / getDuration();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
deleted file mode 100644
index 6bdcf4d..0000000
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java
+++ /dev/null
@@ -1,110 +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.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 new SurfaceProperties(surface);
- }
-
- /**
- * 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;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 95473dc..1200208 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -25,6 +25,7 @@
import android.view.ViewRootImpl;
import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.function.Consumer;
@@ -69,12 +70,18 @@
* @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
* this method to avoid synchronization issues.
*/
- public void scheduleApply(SurfaceTransaction params) {
+ public void scheduleApply(final SurfaceParams... params) {
View view = mTargetViewRootImpl.getView();
if (view == null) {
return;
}
- Transaction t = params.getTransaction();
+ Transaction t = new Transaction();
+ for (int i = params.length - 1; i >= 0; i--) {
+ SurfaceParams surfaceParams = params[i];
+ if (surfaceParams.surface.isValid()) {
+ surfaceParams.applyTo(t);
+ }
+ }
mLastSequenceNumber++;
final int toApplySeqNo = mLastSequenceNumber;
@@ -95,7 +102,7 @@
}
/**
- * Creates an instance of SurfaceTransactionApplier, deferring until the target view is
+ * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
* attached if necessary.
*/
public static void create(
diff --git a/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
new file mode 100644
index 0000000..bf8612a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TabletHomeToSplitTimings.java
@@ -0,0 +1,44 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Home > OverviewSplitSelect animation on tablets.
+ */
+public class TabletHomeToSplitTimings
+ extends TabletOverviewToSplitTimings implements SplitAnimationTimings {
+ @Override
+ public Interpolator getStagedRectXInterpolator() { return LINEAR; }
+ @Override
+ public Interpolator getStagedRectScaleXInterpolator() { return LINEAR; }
+ @Override
+ public Interpolator getStagedRectScaleYInterpolator() { return LINEAR; }
+
+ public int getScrimFadeInStart() { return 0; }
+ public int getScrimFadeInEnd() { return 167; }
+
+ public float getScrimFadeInStartOffset() {
+ return (float) getScrimFadeInStart() / getDuration();
+ }
+ public float getScrimFadeInEndOffset() {
+ return (float) getScrimFadeInEnd() / getDuration();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
new file mode 100644
index 0000000..cbf46bf
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TabletOverviewToSplitTimings.java
@@ -0,0 +1,43 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the Overview > OverviewSplitSelect animation on tablets.
+ */
+public class TabletOverviewToSplitTimings
+ extends OverviewToSplitTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 167; }
+ public int getPlaceholderIconFadeInEnd() { return 250; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 417; }
+ public int getGridSlideStart() { return 67; }
+ public int getGridSlideStagger() { return 16; }
+ public int getGridSlideDuration() { return 500; }
+
+ public int getDuration() { return TABLET_ENTER_DURATION; }
+ public Interpolator getStagedRectXInterpolator() { return DEACCEL_2; }
+ public Interpolator getStagedRectYInterpolator() { return DEACCEL_2; }
+ public Interpolator getStagedRectScaleXInterpolator() { return DEACCEL_2; }
+ public Interpolator getStagedRectScaleYInterpolator() { return DEACCEL_2; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
new file mode 100644
index 0000000..3ea8466
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TabletSplitToConfirmTimings.java
@@ -0,0 +1,40 @@
+/*
+ * 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 static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.view.animation.Interpolator;
+
+/**
+ * Timings for the OverviewSplitSelect > confirmed animation on tablets.
+ */
+public class TabletSplitToConfirmTimings
+ extends SplitToConfirmTimings implements SplitAnimationTimings {
+ public int getPlaceholderFadeInStart() { return 0; }
+ public int getPlaceholderFadeInEnd() { return 133; }
+ public int getPlaceholderIconFadeInStart() { return 167; }
+ public int getPlaceholderIconFadeInEnd() { return 250; }
+ public int getStagedRectSlideStart() { return 0; }
+ public int getStagedRectSlideEnd() { return 383; }
+
+ public int getDuration() { return TABLET_CONFIRM_DURATION; }
+ public Interpolator getStagedRectXInterpolator() { return LINEAR; }
+ public Interpolator getStagedRectYInterpolator() { return LINEAR; }
+ public Interpolator getStagedRectScaleXInterpolator() { return LINEAR; }
+ public Interpolator getStagedRectScaleYInterpolator() { return LINEAR; }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index cca38b7..1a026fc 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.util;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
@@ -25,7 +27,6 @@
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.animation.TimeInterpolator;
import android.content.Context;
@@ -46,11 +47,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.TaskThumbnailView.PreviewPositionHelper;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
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
@@ -386,10 +387,10 @@
@Override
public void onBuildTargetParams(
- SurfaceProperties builder, RemoteAnimationTargetCompat app, TransformParams params) {
- builder.setMatrix(mMatrix)
- .setWindowCrop(mTmpCropRect)
- .setCornerRadius(getCurrentCornerRadius());
+ Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mTmpCropRect)
+ .withCornerRadius(getCurrentCornerRadius());
// If mDrawsBelowRecents is unset, no reordering will be enforced.
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && 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.setLayer(mDrawsBelowRecents
+ builder.withLayer(mDrawsBelowRecents
? Integer.MIN_VALUE + 1
: ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 3d505c6..a7f25d4 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -21,8 +21,10 @@
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 {
@@ -111,7 +113,8 @@
* 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;
}
@@ -134,14 +137,16 @@
return this;
}
- public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
+ public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
- SurfaceTransaction transaction = new SurfaceTransaction();
+ final int appLength = targets.unfilteredApps.length;
+ final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
+ SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
mRecentsSurface = getRecentsSurface(targets);
- for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ for (int i = 0; i < appLength; i++) {
RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
- SurfaceProperties builder = transaction.forSurface(app.leash);
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
if (app.mode == targets.targetMode) {
if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
@@ -151,9 +156,9 @@
if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT
&& app.isNotInRecents) {
float progress = Utilities.boundToRange(getProgress(), 0, 1);
- builder.setAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
+ builder.withAlpha(1 - Interpolators.DEACCEL_2_5.getInterpolation(progress));
} else {
- builder.setAlpha(getTargetAlpha());
+ builder.withAlpha(getTargetAlpha());
}
proxy.onBuildTargetParams(builder, app, this);
@@ -161,15 +166,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];
- transaction.forSurface(wallpaper.leash).setLayer(Integer.MIN_VALUE);
+ surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
+ .withLayer(Integer.MIN_VALUE).build();
}
- return transaction;
+ return surfaceParams;
}
private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
@@ -208,11 +213,15 @@
return mTargetSet;
}
- public void applySurfaceParams(SurfaceTransaction builder) {
+ public void applySurfaceParams(SurfaceParams... params) {
if (mSyncTransactionApplier != null) {
- mSyncTransactionApplier.scheduleApply(builder);
+ mSyncTransactionApplier.scheduleApply(params);
} else {
- builder.getTransaction().apply();
+ TransactionCompat t = new TransactionCompat();
+ for (SurfaceParams param : params) {
+ SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
+ }
+ t.apply();
}
}
@@ -220,9 +229,9 @@
public interface BuilderProxy {
BuilderProxy NO_OP = (builder, app, params) -> { };
- BuilderProxy ALWAYS_VISIBLE = (builder, app, params) -> builder.setAlpha(1);
+ BuilderProxy ALWAYS_VISIBLE = (builder, app, params) ->builder.withAlpha(1);
- void onBuildTargetParams(SurfaceProperties builder,
+ void onBuildTargetParams(SurfaceParams.Builder builder,
RemoteAnimationTargetCompat app, TransformParams params);
}
}
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
similarity index 73%
rename from src/com/android/launcher3/util/ViewCapture.java
rename to quickstep/src/com/android/quickstep/util/ViewCapture.java
index 0cf3ad7..6171c5d 100644
--- a/src/com/android/launcher3/util/ViewCapture.java
+++ b/quickstep/src/com/android/quickstep/util/ViewCapture.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.util;
+package com.android.quickstep.util;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -44,6 +44,9 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.view.ViewCaptureData.ExportedData;
import com.android.launcher3.view.ViewCaptureData.FrameData;
import com.android.launcher3.view.ViewCaptureData.ViewNode;
@@ -64,6 +67,10 @@
private static final String TAG = "ViewCapture";
+ // These flags are copies of two private flags in the View class.
+ private static final int PFLAG_INVALIDATED = 0x80000000;
+ private static final int PFLAG_DIRTY_MASK = 0x00200000;
+
// Number of frames to keep in memory
private static final int MEMORY_SIZE = 2000;
// Initial size of the reference pool. This is at least be 5 * total number of views in
@@ -184,6 +191,7 @@
private final ViewRef mViewRef = new ViewRef();
private int mFrameIndexBg = -1;
+ private boolean mIsFirstFrame = true;
private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
@@ -202,6 +210,7 @@
Message m = Message.obtain(mHandler);
m.obj = mViewRef.next;
mHandler.sendMessage(m);
+ mIsFirstFrame = false;
Trace.endSection();
}
@@ -211,9 +220,9 @@
*/
@WorkerThread
private boolean captureViewPropertiesBg(Message msg) {
- ViewRef start = (ViewRef) msg.obj;
+ ViewRef viewRefStart = (ViewRef) msg.obj;
long time = msg.getWhen();
- if (start == null) {
+ if (viewRefStart == null) {
return false;
}
mFrameIndexBg++;
@@ -224,12 +233,11 @@
ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
- ViewPropertyRef result = null;
+ ViewPropertyRef resultStart = null;
ViewPropertyRef resultEnd = null;
- ViewRef current = start;
- ViewRef last = start;
- while (current != null) {
+ ViewRef viewRefEnd = viewRefStart;
+ while (viewRefEnd != null) {
ViewPropertyRef propertyRef = recycle;
if (propertyRef == null) {
propertyRef = new ViewPropertyRef();
@@ -238,24 +246,64 @@
propertyRef.next = null;
}
- propertyRef.transfer(current);
- last = current;
- current = current.next;
+ ViewPropertyRef copy = null;
+ if (viewRefEnd.childCount < 0) {
+ copy = findInLastFrame(viewRefEnd.view.hashCode());
+ viewRefEnd.childCount = (copy != null) ? copy.childCount : 0;
+ }
+ viewRefEnd.transferTo(propertyRef);
- if (result == null) {
- result = propertyRef;
- resultEnd = result;
+ if (resultStart == null) {
+ resultStart = propertyRef;
+ resultEnd = resultStart;
} else {
resultEnd.next = propertyRef;
- resultEnd = propertyRef;
+ resultEnd = resultEnd.next;
}
+
+ if (copy != null) {
+ int pending = copy.childCount;
+ while (pending > 0) {
+ copy = copy.next;
+ pending = pending - 1 + copy.childCount;
+
+ propertyRef = recycle;
+ if (propertyRef == null) {
+ propertyRef = new ViewPropertyRef();
+ } else {
+ recycle = recycle.next;
+ propertyRef.next = null;
+ }
+
+ copy.transferTo(propertyRef);
+
+ resultEnd.next = propertyRef;
+ resultEnd = resultEnd.next;
+ }
+ }
+
+ if (viewRefEnd.next == null) {
+ // The compiler will complain about using a non-final variable from
+ // an outer class in a lambda if we pass in viewRefEnd directly.
+ final ViewRef finalViewRefEnd = viewRefEnd;
+ MAIN_EXECUTOR.execute(() -> addToPool(viewRefStart, finalViewRefEnd));
+ break;
+ }
+ viewRefEnd = viewRefEnd.next;
}
- mNodesBg[mFrameIndexBg] = result;
- ViewRef end = last;
- MAIN_EXECUTOR.execute(() -> addToPool(start, end));
+ mNodesBg[mFrameIndexBg] = resultStart;
return true;
}
+ private ViewPropertyRef findInLastFrame(int hashCode) {
+ int lastFrameIndex = (mFrameIndexBg == 0) ? MEMORY_SIZE - 1 : mFrameIndexBg - 1;
+ ViewPropertyRef viewPropertyRef = mNodesBg[lastFrameIndex];
+ while (viewPropertyRef != null && viewPropertyRef.hashCode != hashCode) {
+ viewPropertyRef = viewPropertyRef.next;
+ }
+ return viewPropertyRef;
+ }
+
void attachToRoot() {
if (mRoot.isAttachedToWindow()) {
mRoot.getViewTreeObserver().addOnDrawListener(this);
@@ -311,8 +359,16 @@
ref.view = view;
start.next = ref;
if (view instanceof ViewGroup) {
- ViewRef result = ref;
ViewGroup parent = (ViewGroup) view;
+ // If a view has not changed since the last frame, we will copy
+ // its children from the last processed frame's data.
+ if ((view.mPrivateFlags & (PFLAG_INVALIDATED | PFLAG_DIRTY_MASK)) == 0
+ && !mIsFirstFrame) {
+ // A negative child count is the signal to copy this view from the last frame.
+ ref.childCount = -parent.getChildCount();
+ return ref;
+ }
+ ViewRef result = ref;
int childCount = ref.childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
result = captureViewTree(parent.getChildAt(i), result);
@@ -346,31 +402,26 @@
public ViewPropertyRef next;
- public void transfer(ViewRef viewRef) {
- childCount = viewRef.childCount;
-
- View view = viewRef.view;
- viewRef.view = null;
-
- clazz = view.getClass();
- hashCode = view.hashCode();
- id = view.getId();
- left = view.getLeft();
- top = view.getTop();
- right = view.getRight();
- bottom = view.getBottom();
- scrollX = view.getScrollX();
- scrollY = view.getScrollY();
-
- translateX = view.getTranslationX();
- translateY = view.getTranslationY();
- scaleX = view.getScaleX();
- scaleY = view.getScaleY();
- alpha = view.getAlpha();
-
- visibility = view.getVisibility();
- willNotDraw = view.willNotDraw();
- elevation = view.getElevation();
+ public void transferTo(ViewPropertyRef out) {
+ out.clazz = this.clazz;
+ out.hashCode = this.hashCode;
+ out.childCount = this.childCount;
+ out.id = this.id;
+ out.left = this.left;
+ out.top = this.top;
+ out.right = this.right;
+ out.bottom = this.bottom;
+ out.scrollX = this.scrollX;
+ out.scrollY = this.scrollY;
+ out.scaleX = this.scaleX;
+ out.scaleY = this.scaleY;
+ out.translateX = this.translateX;
+ out.translateY = this.translateY;
+ out.alpha = this.alpha;
+ out.visibility = this.visibility;
+ out.willNotDraw = this.willNotDraw;
+ out.clipChildren = this.clipChildren;
+ out.elevation = this.elevation;
}
/**
@@ -417,6 +468,33 @@
public View view;
public int childCount = 0;
public ViewRef next;
+
+ public void transferTo(ViewPropertyRef out) {
+ out.childCount = this.childCount;
+
+ View view = this.view;
+ this.view = null;
+
+ out.clazz = view.getClass();
+ out.hashCode = view.hashCode();
+ out.id = view.getId();
+ out.left = view.getLeft();
+ out.top = view.getTop();
+ out.right = view.getRight();
+ out.bottom = view.getBottom();
+ out.scrollX = view.getScrollX();
+ out.scrollY = view.getScrollY();
+
+ out.translateX = view.getTranslationX();
+ out.translateY = view.getTranslationY();
+ out.scaleX = view.getScaleX();
+ out.scaleY = view.getScaleY();
+ out.alpha = view.getAlpha();
+ out.elevation = view.getElevation();
+
+ out.visibility = view.getVisibility();
+ out.willNotDraw = view.willNotDraw();
+ }
}
private static final class ViewIdProvider {
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 76552a3..96504af 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -385,4 +385,12 @@
mBanner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
mBanner.setLayerPaint(layerPaint);
}
+
+ void setBannerVisibility(int visibility) {
+ if (mBanner == null) {
+ return;
+ }
+
+ mBanner.setVisibility(visibility);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index c3bf041..dc1ae52 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -1,9 +1,8 @@
package com.android.quickstep.views;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -24,7 +23,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
@@ -32,7 +30,9 @@
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.systemui.shared.system.QuickStepContract;
@@ -42,7 +42,8 @@
* which will have the thumbnail from the provided existing TaskView overlaying the taskview itself.
*
* Can then animate the taskview using
- * {@link #addAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}
+ * {@link #addStagingAnimation(PendingAnimation, RectF, Rect, boolean, boolean)} or
+ * {@link #addConfirmAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}
* giving a starting and ending bounds. Currently this is set to use the split placeholder view,
* but it could be generified.
*
@@ -124,7 +125,7 @@
RecentsView recentsView = launcher.getOverviewPanel();
mOrientationHandler = recentsView.getPagedOrientationHandler();
- mStagePosition = recentsView.getSplitPlaceholder().getActiveSplitStagePosition();
+ mStagePosition = recentsView.getSplitSelectController().getActiveSplitStagePosition();
mSplitPlaceholderView.setIcon(icon,
mContext.getResources().getDimensionPixelSize(R.dimen.split_placeholder_icon_size));
mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
@@ -209,8 +210,50 @@
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
}
- public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
- boolean fadeWithThumbnail, boolean isStagedTask) {
+ /**
+ * Animates a FloatingTaskThumbnailView and its overlapping SplitPlaceholderView when a split
+ * is staged.
+ */
+ public void addStagingAnimation(PendingAnimation animation, RectF startingBounds,
+ Rect endBounds, boolean fadeWithThumbnail, boolean isStagedTask) {
+ boolean isTablet = mActivity.getDeviceProfile().isTablet;
+ boolean splittingFromOverview = fadeWithThumbnail;
+ SplitAnimationTimings timings;
+
+ if (isTablet && splittingFromOverview) {
+ timings = SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT;
+ } else if (!isTablet && splittingFromOverview) {
+ timings = SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
+ } else {
+ // Splitting from Home is currently only available on tablets
+ timings = SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+ }
+
+ addAnimation(animation, startingBounds, endBounds, fadeWithThumbnail, isStagedTask,
+ timings);
+ }
+
+ /**
+ * Animates the FloatingTaskThumbnailView and SplitPlaceholderView for the two thumbnails
+ * when a split is confirmed.
+ */
+ public void addConfirmAnimation(PendingAnimation animation, RectF startingBounds,
+ Rect endBounds, boolean fadeWithThumbnail, boolean isStagedTask) {
+ SplitAnimationTimings timings =
+ AnimUtils.getDeviceSplitToConfirmTimings(mActivity.getDeviceProfile().isTablet);
+
+ addAnimation(animation, startingBounds, endBounds, fadeWithThumbnail, isStagedTask,
+ timings);
+ }
+
+ /**
+ * Sets up and builds a split staging animation.
+ * Called by {@link #addStagingAnimation(PendingAnimation, RectF, Rect, boolean, boolean)} and
+ * {@link #addConfirmAnimation(PendingAnimation, RectF, Rect, boolean, boolean)}.
+ */
+ public void addAnimation(PendingAnimation animation, RectF startingBounds,
+ Rect endBounds, boolean fadeWithThumbnail, boolean isStagedTask,
+ SplitAnimationTimings timings) {
mFullscreenParams.setIsStagedTask(isStagedTask);
final BaseDragLayer dragLayer = mActivity.getDragLayer();
int[] dragLayerBounds = new int[2];
@@ -224,26 +267,47 @@
RectF floatingTaskViewBounds = new RectF();
if (fadeWithThumbnail) {
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
- 0, 1, ACCEL);
- animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA,
- 1, 0, DEACCEL_3);
+ // This code block runs for the placeholder view during Overview > OverviewSplitSelect
+ // and for the selected (secondary) thumbnail during OverviewSplitSelect > Confirmed
+
+ // FloatingTaskThumbnailView: thumbnail fades out to transparent
+ animation.setViewAlpha(mThumbnailView, 0, clampToProgress(LINEAR,
+ timings.getPlaceholderFadeInStartOffset(),
+ timings.getPlaceholderFadeInEndOffset()));
+
+ // SplitPlaceholderView: gray background fades in at same time, then new icon fades in
+ fadeInSplitPlaceholder(animation, timings);
} else if (isStagedTask) {
- // Fade in the placeholder view when split is initiated from homescreen / all apps
- // icons.
+ // This code block runs for the placeholder view during Normal > OverviewSplitSelect
+ // and for the placeholder (primary) thumbnail during OverviewSplitSelect > Confirmed
+
+ // Fade in the placeholder view during Normal > OverviewSplitSelect
if (mSplitPlaceholderView.getAlpha() == 0) {
- animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
- 0.3f, 1, ACCEL);
+ mSplitPlaceholderView.getIconView().setAlpha(0);
+ fadeInSplitPlaceholder(animation, timings);
}
+
+ // No-op for placeholder during OverviewSplitSelect > Confirmed, alpha should be set
}
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
- final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
- final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
+ // SplitPlaceholderView: rectangle translates and stretches to new position
+ final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration,
+ clampToProgress(timings.getStagedRectXInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
+ final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration,
+ clampToProgress(timings.getStagedRectYInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0,
- animDuration, LINEAR);
+ animDuration, clampToProgress(timings.getStagedRectScaleXInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
final FloatProp mTaskViewScaleY = new FloatProp(1f, prop.finalTaskViewScaleY, 0,
- animDuration, LINEAR);
+ animDuration, clampToProgress(timings.getStagedRectScaleYInterpolator(),
+ timings.getStagedRectSlideStartOffset(),
+ timings.getStagedRectSlideEndOffset()));
@Override
public void onUpdate(float percent, boolean initOnly) {
// Calculate the icon position.
@@ -255,9 +319,19 @@
update(floatingTaskViewBounds, percent);
}
};
+
transitionAnimator.addUpdateListener(listener);
}
+ void fadeInSplitPlaceholder(PendingAnimation animation, SplitAnimationTimings timings) {
+ animation.setViewAlpha(mSplitPlaceholderView, 1, clampToProgress(LINEAR,
+ timings.getPlaceholderFadeInStartOffset(),
+ timings.getPlaceholderFadeInEndOffset()));
+ animation.setViewAlpha(mSplitPlaceholderView.getIconView(), 1, clampToProgress(LINEAR,
+ timings.getPlaceholderIconFadeInStartOffset(),
+ timings.getPlaceholderIconFadeInEndOffset()));
+ }
+
void drawRoundedRect(Canvas canvas, Paint paint) {
if (mFullscreenParams == null) {
return;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index 3920c56..3a5f606 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -167,7 +167,7 @@
@Override
protected boolean showTaskMenuWithContainer(IconView iconView) {
boolean showedTaskMenu = super.showTaskMenuWithContainer(iconView);
- if (iconView == mIconView2 && showedTaskMenu && !isGridTask()) {
+ if (iconView == mIconView2 && showedTaskMenu && !mActivity.getDeviceProfile().isTablet) {
// Adjust the position of the secondary task's menu view (only on phones)
TaskMenuView taskMenuView = getAnyView(mActivity, AbstractFloatingView.TYPE_TASK_MENU);
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
@@ -191,7 +191,7 @@
// Callbacks run from remote animation when recents animation not currently running
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Enter form GroupedTaskView");
- recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
+ recentsView.getSplitSelectController().launchTasks(this /*groupedTaskView*/,
success -> {
endCallback.executeAllAndDestroy();
InteractionJankMonitorWrapper.end(
@@ -206,7 +206,7 @@
@Override
public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
- getRecentsView().getSplitPlaceholder().launchTasks(mTask.key.id, mSecondaryTask.key.id,
+ getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id,
STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, getSplitRatio());
}
@@ -230,11 +230,12 @@
}
@Override
- protected int getChildTaskIndexAtPosition(PointF position) {
- if (isCoordInView(mIconView2, position) || isCoordInView(mSnapshotView2, position)) {
+ protected int getLastSelectedChildTaskIndex() {
+ if (isCoordInView(mIconView2, mLastTouchDownPosition)
+ || isCoordInView(mSnapshotView2, mLastTouchDownPosition)) {
return 1;
}
- return super.getChildTaskIndexAtPosition(position);
+ return super.getLastSelectedChildTaskIndex();
}
private boolean isCoordInView(View v, PointF position) {
@@ -336,9 +337,26 @@
mSnapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha);
}
+ /**
+ * Sets visibility for thumbnails and associated elements (DWB banners).
+ * IconView is unaffected.
+ *
+ * When setting INVISIBLE, sets the visibility for the last selected child task.
+ * When setting VISIBLE (as a reset), sets the visibility for both tasks.
+ */
@Override
void setThumbnailVisibility(int visibility) {
- super.setThumbnailVisibility(visibility);
- mSnapshotView2.setVisibility(visibility);
+ if (visibility == VISIBLE) {
+ mSnapshotView.setVisibility(visibility);
+ mDigitalWellBeingToast.setBannerVisibility(visibility);
+ mSnapshotView2.setVisibility(visibility);
+ mDigitalWellBeingToast2.setBannerVisibility(visibility);
+ } else if (getLastSelectedChildTaskIndex() == 0) {
+ mSnapshotView.setVisibility(visibility);
+ mDigitalWellBeingToast.setBannerVisibility(visibility);
+ } else {
+ mSnapshotView2.setVisibility(visibility);
+ mDigitalWellBeingToast2.setBannerVisibility(visibility);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index de7ccad..bb8506d 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -33,6 +33,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
@@ -94,7 +95,7 @@
if (recoveryData.getStagedTaskId() == taskId) {
initiateSplitSelect(
getTaskViewByTaskId(recoveryData.getStagedTaskId()),
- recoveryData.getStagePosition()
+ recoveryData.getStagePosition(), recoveryData.getSource()
);
mActivity.finishSplitSelectRecovery();
}
@@ -104,7 +105,6 @@
@Override
public void reset() {
super.reset();
-
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
}
@@ -120,11 +120,6 @@
}
@Override
- public void onStateTransitionFailed(LauncherState toState) {
- reset();
- }
-
- @Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == NORMAL || finalState == SPRING_LOADED) {
// Clean-up logic that occurs when recents is no longer in use/visible.
@@ -192,8 +187,9 @@
@Override
public void initiateSplitSelect(TaskView taskView,
- @SplitConfigurationOptions.StagePosition int stagePosition) {
- super.initiateSplitSelect(taskView, stagePosition);
+ @SplitConfigurationOptions.StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent) {
+ super.initiateSplitSelect(taskView, stagePosition, splitEvent);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ef98e92..31e165d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -35,11 +35,14 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_0_75;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_ACTIONS_SPLIT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -132,6 +135,7 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
@@ -167,12 +171,13 @@
import com.android.quickstep.ViewUtils;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.AnimUtils;
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;
@@ -185,6 +190,7 @@
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;
@@ -650,7 +656,7 @@
@Nullable
private TaskView mSplitHiddenTaskView;
@Nullable
- private View mSecondSplitHiddenView;
+ private TaskView mSecondSplitHiddenView;
@Nullable
private SplitBounds mSplitBoundsConfig;
private final Toast mSplitToast = Toast.makeText(getContext(),
@@ -907,7 +913,7 @@
mSplitSelectStateController = splitController;
}
- public SplitSelectStateController getSplitPlaceholder() {
+ public SplitSelectStateController getSplitSelectController() {
return mSplitSelectStateController;
}
@@ -1064,15 +1070,14 @@
appAnimator.setInterpolator(ACCEL_DEACCEL);
appAnimator.addUpdateListener(valueAnimator -> {
float percent = valueAnimator.getAnimatedFraction();
- SurfaceTransaction transaction = new SurfaceTransaction();
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(
+ apps[apps.length - 1].leash);
Matrix matrix = new Matrix();
matrix.postScale(percent, percent);
matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
- transaction.forSurface(apps[apps.length - 1].leash)
- .setAlpha(percent)
- .setMatrix(matrix);
- surfaceApplier.scheduleApply(transaction);
+ builder.withAlpha(percent).withMatrix(matrix);
+ surfaceApplier.scheduleApply(builder.build());
});
anim.play(appAnimator);
anim.addListener(new AnimatorListenerAdapter() {
@@ -1240,7 +1245,7 @@
protected void onPageEndTransition() {
super.onPageEndTransition();
ActiveGestureLog.INSTANCE.addLog(
- "onPageEndTransition: current page index updated", mCurrentPage);
+ "onPageEndTransition: current page index updated", getNextPage());
if (isClearAllHidden() && !mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
@@ -2771,7 +2776,7 @@
anim.setFloat(taskView, VIEW_ALPHA, 0,
clampToProgress(isOnGridBottomRow(taskView) ? ACCEL : FINAL_FRAME, 0, 0.5f));
FloatProperty<TaskView> secondaryViewTranslate =
- taskView.getSecondaryDissmissTranslationProperty();
+ taskView.getSecondaryDismissTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
@@ -2805,33 +2810,48 @@
mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
mSplitPlaceholderInset, mActivity.getDeviceProfile(),
mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
+ SplitAnimationTimings timings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet);
RectF startingTaskRect = new RectF();
+ safeRemoveDragLayerView(mFirstFloatingTaskView);
if (mSplitHiddenTaskView != null) {
- // Split staging is initiated, hide the original TaskView thumbnail.
- // Toggled back on in resetFromSplitSelectionState().
+ // Create the split select animation from Overview
mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
- anim.addFloat(mSplitHiddenTaskView, TaskView.ICON_ALPHA, 1, 0,
- clampToProgress(LINEAR, 0, 0.167f));
+ anim.setViewAlpha(mSplitHiddenTaskView.getIconView(), 0, clampToProgress(LINEAR,
+ timings.getIconFadeStartOffset(),
+ timings.getIconFadeEndOffset()));
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitHiddenTaskView.getThumbnail(),
mSplitHiddenTaskView.getThumbnail().getThumbnail(),
mSplitHiddenTaskView.getIconView().getDrawable(), startingTaskRect);
mFirstFloatingTaskView.setAlpha(1);
- mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
+ mFirstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
true /* fadeWithThumbnail */, true /* isStagedTask */);
} else {
+ // Create the split select animation from Home
mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
mSplitSelectSource.view, null /* thumbnail */,
mSplitSelectSource.drawable, startingTaskRect);
mFirstFloatingTaskView.setAlpha(1);
- mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
+ mFirstFloatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
}
+ // SplitInstructionsView: animate in
+ safeRemoveDragLayerView(mSplitInstructionsView);
mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity);
mSplitInstructionsView.setAlpha(0);
- anim.addFloat(mSplitInstructionsView, SplitInstructionsView.ALPHA_FLOAT, 0, 1, ACCEL);
+ anim.setViewAlpha(mSplitInstructionsView, 1, clampToProgress(LINEAR,
+ timings.getInstructionsContainerFadeInStartOffset(),
+ timings.getInstructionsContainerFadeInEndOffset()));
+ anim.setViewAlpha(mSplitInstructionsView.getTextView(), 1, clampToProgress(LINEAR,
+ timings.getInstructionsTextFadeInStartOffset(),
+ timings.getInstructionsTextFadeInEndOffset()));
+ anim.addFloat(mSplitInstructionsView, mSplitInstructionsView.UNFOLD, 0.1f, 1,
+ clampToProgress(EMPHASIZED_DECELERATE,
+ timings.getInstructionsUnfoldStartOffset(),
+ timings.getInstructionsUnfoldEndOffset()));
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
@@ -2858,17 +2878,16 @@
* @param dismissingForSplitSelection task dismiss animation is used for entering split
* selection state from app icon
*/
- public PendingAnimation createTaskDismissAnimation(TaskView dismissedTaskView,
+ public void createTaskDismissAnimation(PendingAnimation anim, TaskView dismissedTaskView,
boolean animateTaskView, boolean shouldRemoveTask, long duration,
boolean dismissingForSplitSelection) {
if (mPendingAnimation != null) {
mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
}
- PendingAnimation anim = new PendingAnimation(duration);
int count = getPageCount();
if (count == 0) {
- return anim;
+ return;
}
boolean showAsGrid = showAsGrid();
@@ -3036,6 +3055,9 @@
}
}
+ SplitAnimationTimings splitTimings =
+ AnimUtils.getDeviceOverviewToSplitTimings(mActivity.getDeviceProfile().isTablet);
+
int distanceFromDismissedTask = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
@@ -3078,9 +3100,30 @@
float additionalDismissDuration =
ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
i - dismissedIndex);
- anim.setFloat(child, translationProperty, scrollDiff, clampToProgress(LINEAR,
- Utilities.boundToRange(INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- + additionalDismissDuration, 0f, 1f), 1));
+
+ // We are in non-grid layout.
+ // If dismissing for split select, use split timings.
+ // If not, use dismiss timings.
+ float animationStartProgress = isSplitSelectionActive()
+ ? Utilities.boundToRange(splitTimings.getGridSlideStartOffset(), 0f, 1f)
+ : Utilities.boundToRange(
+ INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + additionalDismissDuration, 0f, 1f);
+
+ float animationEndProgress = isSplitSelectionActive()
+ ? Utilities.boundToRange(splitTimings.getGridSlideStartOffset()
+ + splitTimings.getGridSlideDurationOffset(), 0f, 1f)
+ : 1f;
+
+ // Slide tiles in horizontally to fill dismissed area
+ anim.setFloat(child, translationProperty, scrollDiff,
+ clampToProgress(
+ splitTimings.getGridSlidePrimaryInterpolator(),
+ animationStartProgress,
+ animationEndProgress
+ )
+ );
+
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& child instanceof TaskView
&& ((TaskView) child).isRunningTask()) {
@@ -3113,11 +3156,35 @@
// Animate task with index >= dismissed index and in the same row as the
// dismissed index or next focused index. Offset successive task dismissal
// durations for a staggered effect.
- float animationStartProgress = Utilities.boundToRange(
- INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- * ++distanceFromDismissedTask, 0f,
- dismissTranslationInterpolationEnd);
+ distanceFromDismissedTask++;
+ boolean isStagingFocusedTask =
+ isFocusedTaskDismissed && nextFocusedTaskView == null;
+ int staggerColumn = isStagingFocusedTask
+ ? (int) Math.ceil(distanceFromDismissedTask / 2f)
+ : distanceFromDismissedTask;
+ // Set timings based on if user is initiating splitscreen on the focused task,
+ // or splitting/dismissing some other task.
+ float animationStartProgress = isStagingFocusedTask
+ ? Utilities.boundToRange(
+ splitTimings.getGridSlideStartOffset()
+ + (splitTimings.getGridSlideStaggerOffset()
+ * staggerColumn),
+ 0f,
+ dismissTranslationInterpolationEnd)
+ : Utilities.boundToRange(
+ INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ * staggerColumn, 0f, dismissTranslationInterpolationEnd);
+ float animationEndProgress = isStagingFocusedTask
+ ? Utilities.boundToRange(
+ splitTimings.getGridSlideStartOffset()
+ + (splitTimings.getGridSlideStaggerOffset() * staggerColumn)
+ + splitTimings.getGridSlideDurationOffset(),
+ 0f,
+ dismissTranslationInterpolationEnd)
+ : dismissTranslationInterpolationEnd;
+ Interpolator dismissInterpolator = isStagingFocusedTask ? OVERSHOOT_0_75 : LINEAR;
+
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
@@ -3132,7 +3199,7 @@
if (!nextFocusedTaskFromTop) {
secondaryTranslation -= mTopBottomRowHeightDiff;
}
- anim.setFloat(taskView, taskView.getSecondaryDissmissTranslationProperty(),
+ anim.setFloat(taskView, taskView.getSecondaryDismissTranslationProperty(),
secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
anim.setFloat(taskView, TaskView.FOCUS_TRANSITION, 0f,
@@ -3140,7 +3207,7 @@
} else {
float primaryTranslation =
nextFocusedTaskView != null ? nextFocusedTaskWidth : dismissedTaskWidth;
- if (isFocusedTaskDismissed && nextFocusedTaskView == null) {
+ if (isStagingFocusedTask) {
// Moves less if focused task is not in scroll position.
int focusedTaskScroll = getScrollForPage(dismissedIndex);
int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
@@ -3156,8 +3223,8 @@
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? primaryTranslation : -primaryTranslation,
- clampToProgress(LINEAR, animationStartProgress,
- dismissTranslationInterpolationEnd));
+ clampToProgress(dismissInterpolator, animationStartProgress,
+ animationEndProgress));
}
}
}
@@ -3389,7 +3456,6 @@
mPendingAnimation = null;
}
});
- return anim;
}
/**
@@ -3557,8 +3623,10 @@
}
public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
- runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
- DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/));
+ PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
+ createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask, DISMISS_TASK_DURATION,
+ false /* dismissingForSplitSelection*/);
+ runDismissAnimation(pa);
}
@SuppressWarnings("unused")
@@ -4067,16 +4135,21 @@
}
}
+ /**
+ * Primarily used by overview actions to initiate split from focused task, logs the source
+ * of split invocation as such.
+ */
public void initiateSplitSelect(TaskView taskView) {
int defaultSplitPosition = mOrientationHandler
.getDefaultSplitPosition(mActivity.getDeviceProfile());
- initiateSplitSelect(taskView, defaultSplitPosition);
+ initiateSplitSelect(taskView, defaultSplitPosition, LAUNCHER_OVERVIEW_ACTIONS_SPLIT);
}
- public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition) {
+ public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
+ StatsLogManager.EventEnum splitEvent) {
mSplitHiddenTaskView = taskView;
mSplitSelectStateController.setInitialTaskSelect(taskView.getTask().key.id,
- stagePosition);
+ stagePosition, splitEvent, taskView.getItemInfo());
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
@@ -4084,20 +4157,27 @@
}
}
+ /**
+ * Called when staging a split from Home/AllApps, using the icon long-press menu.
+ */
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
mSplitSelectSource = splitSelectSource;
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
- splitSelectSource.position.stagePosition, splitSelectSource.user);
+ splitSelectSource.position.stagePosition, splitSelectSource.mItemInfo,
+ splitSelectSource.splitEvent);
}
- public PendingAnimation createSplitSelectInitAnimation(int duration) {
+ /**
+ * Modifies a PendingAnimation with the animations for entering split staging
+ */
+ public void createSplitSelectInitAnimation(PendingAnimation builder, int duration) {
if (mSplitHiddenTaskView != null) {
- return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
+ // Splitting from Overview
+ createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
true /* dismissingForSplitSelection*/);
} else {
- PendingAnimation anim = new PendingAnimation(duration);
- createInitialSplitSelectAnimation(anim);
- return anim;
+ // Splitting from Home
+ createInitialSplitSelectAnimation(builder);
}
}
@@ -4128,9 +4208,13 @@
// TODO(194414938) starting bounds seem slightly off, investigate
Rect firstTaskStartingBounds = new Rect();
Rect firstTaskEndingBounds = mTempRect;
- int duration = mActivity.getStateManager().getState().getTransitionDuration(mActivity,
- false /* isToState */);
+
+ boolean isTablet = mActivity.getDeviceProfile().isTablet;
+ int duration = isTablet
+ ? SplitAnimationTimings.TABLET_CONFIRM_DURATION
+ : SplitAnimationTimings.PHONE_CONFIRM_DURATION;
PendingAnimation pendingAnimation = new PendingAnimation(duration);
+ SplitAnimationTimings timings = AnimUtils.getDeviceSplitToConfirmTimings(isTablet);
int halfDividerSize = getResources()
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
@@ -4140,28 +4224,31 @@
secondTaskEndingBounds);
mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
- mFirstFloatingTaskView.addAnimation(pendingAnimation,
+ mFirstFloatingTaskView.addConfirmAnimation(pendingAnimation,
new RectF(firstTaskStartingBounds), firstTaskEndingBounds,
false /* fadeWithThumbnail */, true /* isStagedTask */);
+ safeRemoveDragLayerView(mSecondFloatingTaskView);
mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
thumbnailView, thumbnailView.getThumbnail(),
iconView.getDrawable(), secondTaskStartingBounds);
mSecondFloatingTaskView.setAlpha(1);
- mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
+ mSecondFloatingTaskView.addConfirmAnimation(pendingAnimation, secondTaskStartingBounds,
secondTaskEndingBounds, true /* fadeWithThumbnail */, false /* isStagedTask */);
+
+ pendingAnimation.setViewAlpha(mSplitInstructionsView, 0, clampToProgress(LINEAR,
+ timings.getInstructionsFadeStartOffset(),
+ timings.getInstructionsFadeEndOffset()));
+
pendingAnimation.addEndListener(aBoolean -> {
mSplitSelectStateController.launchSplitTasks(
aBoolean1 -> RecentsView.this.resetFromSplitSelectionState());
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
});
- if (containerTaskView.containsMultipleTasks()) {
- // If we are launching from a child task, then only hide the thumbnail itself
- mSecondSplitHiddenView = thumbnailView;
- } else {
- mSecondSplitHiddenView = containerTaskView;
- }
- mSecondSplitHiddenView.setVisibility(INVISIBLE);
+
+ mSecondSplitHiddenView = containerTaskView;
+ mSecondSplitHiddenView.setThumbnailVisibility(INVISIBLE);
+
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
@@ -4176,23 +4263,20 @@
@SuppressLint("WrongCall")
protected void resetFromSplitSelectionState() {
if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
- if (mSplitInstructionsView != null) {
- mActivity.getDragLayer().removeView(mSplitInstructionsView);
- mSplitInstructionsView = null;
- }
- if (mFirstFloatingTaskView != null) {
- mActivity.getDragLayer().removeView(mFirstFloatingTaskView);
- mFirstFloatingTaskView = null;
- }
- if (mSecondFloatingTaskView != null) {
- mActivity.getDragLayer().removeView(mSecondFloatingTaskView);
- mSecondFloatingTaskView = null;
- mSecondSplitHiddenView.setVisibility(VISIBLE);
- mSecondSplitHiddenView = null;
- }
+ safeRemoveDragLayerView(mFirstFloatingTaskView);
+ safeRemoveDragLayerView(mSecondFloatingTaskView);
+ safeRemoveDragLayerView(mSplitInstructionsView);
+ mFirstFloatingTaskView = null;
+ mSecondFloatingTaskView = null;
+ mSplitInstructionsView = null;
mSplitSelectSource = null;
}
+ if (mSecondSplitHiddenView != null) {
+ mSecondSplitHiddenView.setThumbnailVisibility(VISIBLE);
+ mSecondSplitHiddenView = null;
+ }
+
if (mSplitHiddenTaskViewIndex == -1) {
return;
}
@@ -4206,22 +4290,32 @@
snapToPageImmediately(pageToSnapTo);
}
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
+
+ // We are leaving split selection state, so it is safe to reset thumbnail translations for
+ // the next time split is invoked.
+ setTaskViewsPrimarySplitTranslation(0);
+ setTaskViewsSecondarySplitTranslation(0);
+
resetTaskVisuals();
mSplitHiddenTaskViewIndex = -1;
if (mSplitHiddenTaskView != null) {
- // Toggle thumbnail visibility back on (turned off in
- // createInitialSplitSelectAnimation()).
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE);
mSplitHiddenTaskView = null;
}
}
+ private void safeRemoveDragLayerView(@Nullable View viewToRemove) {
+ if (viewToRemove != null) {
+ mActivity.getDragLayer().removeView(viewToRemove);
+ }
+ }
+
/**
* Returns how much additional translation there should be for each of the child TaskViews.
* Note that the translation can be its primary or secondary dimension.
*/
public float getSplitSelectTranslation() {
- int splitPosition = getSplitPlaceholder().getActiveSplitStagePosition();
+ int splitPosition = getSplitSelectController().getActiveSplitStagePosition();
if (!shouldShiftThumbnailsForSplitSelect()) {
return 0f;
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
index bcaa462..27ec01a 100644
--- a/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitInstructionsView.java
@@ -25,6 +25,7 @@
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
@@ -40,18 +41,18 @@
*/
public class SplitInstructionsView extends FrameLayout {
private final StatefulActivity mLauncher;
+ private AppCompatTextView mTextView;
- public static final FloatProperty<SplitInstructionsView> ALPHA_FLOAT =
- new FloatProperty<SplitInstructionsView>("SplitInstructionsAlpha") {
+ public static final FloatProperty<SplitInstructionsView> UNFOLD =
+ new FloatProperty<SplitInstructionsView>("SplitInstructionsUnfold") {
@Override
public void setValue(SplitInstructionsView splitInstructionsView, float v) {
- splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
- splitInstructionsView.setAlpha(v);
+ splitInstructionsView.setScaleY(v);
}
@Override
public Float get(SplitInstructionsView splitInstructionsView) {
- return splitInstructionsView.getAlpha();
+ return splitInstructionsView.getScaleY();
}
};
@@ -77,6 +78,14 @@
false
);
+ splitInstructionsView.mTextView = splitInstructionsView.findViewById(
+ R.id.split_instructions_text);
+
+ // Since textview overlays base view, and we sometimes manipulate the alpha of each
+ // simultaneously, force overlapping rendering to false prevents redrawing of pixels,
+ // improving performance at the cost of some accuracy.
+ splitInstructionsView.forceHasOverlappingRendering(false);
+
dragLayer.addView(splitInstructionsView);
return splitInstructionsView;
}
@@ -120,4 +129,8 @@
return 0;
}
}
+
+ public AppCompatTextView getTextView() {
+ return mTextView;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index 28080d4..08004dc 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -22,7 +22,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.util.TypedValue;
import android.widget.FrameLayout;
@@ -33,20 +32,6 @@
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect mTempRect = new Rect();
- public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
- new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
- @Override
- public void setValue(SplitPlaceholderView splitPlaceholderView, float v) {
- splitPlaceholderView.setVisibility(v != 0 ? VISIBLE : GONE);
- splitPlaceholderView.setAlpha(v);
- }
-
- @Override
- public Float get(SplitPlaceholderView splitPlaceholderView) {
- return splitPlaceholderView.getAlpha();
- }
- };
-
@Nullable
private IconView mIconView;
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index f4b3d98..d7a8599 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,11 +16,11 @@
package com.android.quickstep.views;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.content.Context;
import android.graphics.Bitmap;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 26bae35..a0f195c 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -32,6 +32,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -48,6 +49,7 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Handler;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@@ -86,6 +88,7 @@
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;
@@ -93,13 +96,13 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -323,19 +326,6 @@
}
};
- public static final FloatProperty<TaskView> ICON_ALPHA =
- new FloatProperty<TaskView>("iconAlpha") {
- @Override
- public void setValue(TaskView taskView, float v) {
- taskView.mIconView.setAlpha(v);
- }
-
- @Override
- public Float get(TaskView taskView) {
- return taskView.mIconView.getAlpha();
- }
- };
-
@Nullable
protected Task mTask;
protected TaskThumbnailView mSnapshotView;
@@ -399,7 +389,7 @@
private final float[] mIconCenterCoords = new float[2];
- private final PointF mLastTouchDownPosition = new PointF();
+ protected final PointF mLastTouchDownPosition = new PointF();
private boolean mIsClickableAsLiveTile = true;
@@ -573,6 +563,18 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ RecentsView recentsView = getRecentsView();
+ if (recentsView == null || mTask == null) {
+ return false;
+ }
+ SplitSelectStateController splitSelectStateController =
+ recentsView.getSplitSelectController();
+ if (splitSelectStateController.isSplitSelectActive() &&
+ splitSelectStateController.getInitialTaskId() == mTask.key.id) {
+ // Prevent taps on the this taskview if it's being animated into split select state
+ return false;
+ }
+
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mLastTouchDownPosition.set(ev.getX(), ev.getY());
}
@@ -596,16 +598,16 @@
* second app. {@code false} otherwise
*/
private boolean confirmSecondSplitSelectApp() {
- int index = getChildTaskIndexAtPosition(mLastTouchDownPosition);
+ int index = getLastSelectedChildTaskIndex();
TaskIdAttributeContainer container = mTaskIdAttributeContainer[index];
return getRecentsView().confirmSplitSelect(this, container.getTask(),
container.getIconView(), container.getThumbnailView());
}
/**
- * Returns the task under the given position in the local coordinates of this task view.
+ * Returns the task index of the last selected child task (0 or 1).
*/
- protected int getChildTaskIndexAtPosition(PointF position) {
+ protected int getLastSelectedChildTaskIndex() {
return 0;
}
@@ -659,12 +661,12 @@
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
// Indicate success once the system has indicated that the transition has started
- ActivityOptions opts = ActivityOptionsCompat.makeCustomAnimation(
- getContext(), 0, 0, () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
+ ActivityOptions opts = makeCustomAnimation(getContext(), 0, 0,
+ () -> callback.accept(true), MAIN_EXECUTOR.getHandler());
opts.setLaunchDisplayId(
getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
if (freezeTaskList) {
- ActivityOptionsCompat.setFreezeRecentTasksList(opts);
+ opts.setFreezeRecentTasksReordering();
}
// TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow.
opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView()
@@ -687,12 +689,31 @@
}
/**
+ * Returns ActivityOptions for overriding task transition animation.
+ */
+ private ActivityOptions makeCustomAnimation(Context context, int enterResId,
+ int exitResId, final Runnable callback, final Handler callbackHandler) {
+ return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+ callbackHandler,
+ elapsedRealTime -> {
+ if (callback != null) {
+ callbackHandler.post(callback);
+ }
+ }, null /* finishedListener */);
+ }
+
+ /**
* Launch of the current task (both live and inactive tasks) with an animation.
*/
public RunnableList launchTasks() {
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 (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask() && remoteTargetHandles != null) {
if (!mIsClickableAsLiveTile) {
return runnableList;
@@ -1240,7 +1261,7 @@
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
- public FloatProperty<TaskView> getSecondaryDissmissTranslationProperty() {
+ public FloatProperty<TaskView> getSecondaryDismissTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
@@ -1495,7 +1516,8 @@
}
public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
- getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
+ getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition,
+ getLogEventForPosition(splitPositionOption.stagePosition));
}
/**
@@ -1513,8 +1535,17 @@
return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
}
+ /**
+ * Sets visibility for the thumbnail and associated elements (DWB banners and action chips).
+ * IconView is unaffected.
+ */
void setThumbnailVisibility(int visibility) {
- mSnapshotView.setVisibility(visibility);
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child != mIconView) {
+ child.setVisibility(visibility);
+ }
+ }
}
/**
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index b903691..c1b3beb 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,6 +36,7 @@
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;
@@ -110,6 +111,7 @@
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/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index 262dc37..ed5526f 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -26,12 +26,12 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import androidx.test.filters.SmallTest;
import com.android.launcher3.util.LooperExecutor;
import com.android.quickstep.util.GroupTask;
-import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import org.junit.Before;
@@ -56,8 +56,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
- KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
- mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
+ KeyguardManager mockKeyguardManager = mock(KeyguardManager.class);
+ mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManager,
mockSystemUiProxy);
}
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index c2667b8..190b002 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -24,6 +24,7 @@
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;
@@ -41,8 +42,8 @@
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.RecordingSurfaceTransaction.MockProperties;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@@ -206,21 +207,17 @@
}
@Override
- public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) {
- RecordingSurfaceTransaction transaction = new RecordingSurfaceTransaction();
- proxy.onBuildTargetParams(
- transaction.mockProperties, mock(RemoteAnimationTargetCompat.class), this);
- return transaction;
+ 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()};
}
@Override
- public void applySurfaceParams(SurfaceTransaction params) {
- Assert.assertTrue(params instanceof RecordingSurfaceTransaction);
- MockProperties p = ((RecordingSurfaceTransaction) params).mockProperties;
-
+ public void applySurfaceParams(SurfaceParams[] params) {
// Verify that the task position remains the same
RectF newAppBounds = new RectF(mAppBounds);
- p.matrix.mapRect(newAppBounds);
+ params[0].matrix.mapRect(newAppBounds);
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
diff --git a/res/drawable/page_indicator.xml b/res/drawable/page_indicator.xml
new file mode 100644
index 0000000..c0ccc49
--- /dev/null
+++ b/res/drawable/page_indicator.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?attr/folderPaginationColor"/>
+ <size android:width="@dimen/page_indicator_size" android:height="@dimen/page_indicator_size"/>
+</shape>
\ No newline at end of file
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index d15b906..4459c87 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -22,6 +22,7 @@
android:layout_gravity="center_horizontal"
android:paddingTop="@dimen/all_apps_tabs_vertical_padding"
android:paddingBottom="@dimen/all_apps_tabs_vertical_padding"
+ android:layout_marginTop="@dimen/all_apps_tabs_margin_top"
android:orientation="horizontal"
style="@style/TextHeadline">
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 709160d..3a58b85 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -85,7 +85,7 @@
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> телефон чалууларды аткарууга уруксаты жок"</string>
<string name="gadget_error_text" msgid="740356548025791839">"Виджет жүктөлбөй жатат"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"Виджеттин жөндөөлөрү"</string>
- <string name="gadget_complete_setup_text" msgid="309040266978007925">"Жөндөп бүтүү үчүн таптап коюңуз"</string>
+ <string name="gadget_complete_setup_text" msgid="309040266978007925">"Аягына чейин тууралоо үчүн басып коюңуз"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
<string name="folder_hint_text" msgid="5174843001373488816">"Аталышын түзөтүү"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 48f7355..b553c90 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -120,7 +120,7 @@
<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_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/config.xml b/res/values/config.xml
index d3f5033..11b6e8c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -204,4 +204,7 @@
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">0</item>
+
+ <string name="floating_task_package" translatable="false"></string>
+ <string name="floating_task_action" translatable="false"></string>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2e886db..47584e2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -121,6 +121,7 @@
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<dimen name="all_apps_tabs_button_horizontal_padding">4dp</dimen>
<dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
+ <dimen name="all_apps_tabs_margin_top">8dp</dimen>
<dimen name="all_apps_divider_height">2dp</dimen>
<dimen name="all_apps_divider_width">128dp</dimen>
<dimen name="all_apps_content_fade_in_offset">150dp</dimen>
@@ -128,9 +129,9 @@
<dimen name="all_apps_height_extra">6dp</dimen>
<dimen name="all_apps_bottom_sheet_horizontal_padding">0dp</dimen>
<dimen name="all_apps_paged_view_top_padding">40dp</dimen>
- <dimen name="all_apps_personal_work_tabs_vertical_margin">16dp</dimen>
<dimen name="all_apps_icon_drawable_padding">8dp</dimen>
+ <dimen name="all_apps_predicted_icon_vertical_padding">8dp</dimen>
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
<dimen name="arrow_toast_elevation">2dp</dimen>
@@ -247,6 +248,8 @@
<!-- Folders -->
<dimen name="page_indicator_dot_size">8dp</dimen>
+ <dimen name="page_indicator_size">10dp</dimen>
+
<dimen name="folder_cell_x_padding">9dp</dimen>
<dimen name="folder_cell_y_padding">6dp</dimen>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index d34f535..83ff084 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -25,12 +25,16 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Bundle;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.IntDef;
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.AppLauncher;
@@ -172,6 +176,19 @@
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (Utilities.ATLEAST_T) {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ () -> {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ });
+ }
+ }
+
+ @Override
protected void onStart() {
addActivityFlags(ACTIVITY_STATE_STARTED);
super.onStart();
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 747b755..2f927d3 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -24,7 +24,6 @@
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;
@@ -92,8 +91,7 @@
protected int getAvailableScrollHeight() {
// AvailableScrollHeight = Total height of the all items - first page height
int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
- int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ getAdapter().getItemCount());
- int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+ int availableScrollHeight = computeVerticalScrollRange() - firstPageHeight;
return Math.max(0, availableScrollHeight);
}
@@ -146,10 +144,7 @@
// 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.
- if (getCurrentScrollY() == 0) {
- return true;
- }
- return getAdapter() == null || getAdapter().getItemCount() == 0;
+ return computeVerticalScrollOffset() == 0;
}
/**
@@ -160,53 +155,6 @@
}
/**
- * @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 ce95b15..a6831aa 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -480,7 +480,6 @@
mAppWidgetHost = createAppWidgetHost();
mAppWidgetHost.startListening();
- inflateRootView(R.layout.launcher);
setupViews();
crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
@@ -1259,6 +1258,7 @@
* Finds all the views we need and configure them properly.
*/
protected void setupViews() {
+ inflateRootView(R.layout.launcher);
mDragLayer = findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
@@ -2780,7 +2780,7 @@
View v = getFirstMatch(Collections.singletonList(activeRecyclerView),
preferredItem, packageAndUserAndApp);
- if (v != null && activeRecyclerView.getCurrentScrollY() > 0) {
+ if (v != null && activeRecyclerView.computeVerticalScrollOffset() > 0) {
RectF locationBounds = new RectF();
FloatingIconView.getLocationBoundsForView(this, v, false, locationBounds,
new Rect());
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 68b7701..20df897 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -33,6 +33,7 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -89,9 +90,11 @@
static final String TAG = "Launcher.Model";
+ @NonNull
private final LauncherAppState mApp;
+ @NonNull
private final Object mLock = new Object();
-
+ @Nullable
private LoaderTask mLoaderTask;
private boolean mIsLoaderTaskRunning;
@@ -107,20 +110,25 @@
}
}
+ @NonNull
private final ArrayList<Callbacks> mCallbacksList = new ArrayList<>(1);
// < only access in worker thread >
+ @NonNull
private final AllAppsList mBgAllAppsList;
/**
* All the static data should be accessed on the background thread, A lock should be acquired
* on this object when accessing any data from this model.
*/
+ @NonNull
private final BgDataModel mBgDataModel = new BgDataModel();
+ @NonNull
private final ModelDelegate mModelDelegate;
// Runnable to check if the shortcuts permission has changed.
+ @NonNull
private final Runnable mDataValidationCheck = new Runnable() {
@Override
public void run() {
@@ -130,14 +138,16 @@
}
};
- LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter,
- boolean isPrimaryInstance) {
+ LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
+ @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
+ final boolean isPrimaryInstance) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
isPrimaryInstance);
}
+ @NonNull
public ModelDelegate getModelDelegate() {
return mModelDelegate;
}
@@ -145,52 +155,57 @@
/**
* Adds the provided items to the workspace.
*/
- public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
+ public void addAndBindAddedWorkspaceItems(
+ @NonNull final List<Pair<ItemInfo, Object>> itemList) {
for (Callbacks cb : getCallbacks()) {
cb.preAddApps();
}
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
- public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges,
- @Nullable Callbacks owner) {
+ @NonNull
+ public ModelWriter getWriter(final boolean hasVerticalHotseat, final boolean verifyChanges,
+ @Nullable final Callbacks owner) {
return new ModelWriter(mApp.getContext(), this, mBgDataModel,
hasVerticalHotseat, verifyChanges, owner);
}
@Override
- public void onPackageChanged(String packageName, UserHandle user) {
+ public void onPackageChanged(
+ @NonNull final String packageName, @NonNull final UserHandle user) {
int op = PackageUpdatedTask.OP_UPDATE;
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
@Override
- public void onPackageRemoved(String packageName, UserHandle user) {
+ public void onPackageRemoved(
+ @NonNull final String packageName, @NonNull final UserHandle user) {
onPackagesRemoved(user, packageName);
}
- public void onPackagesRemoved(UserHandle user, String... packages) {
+ public void onPackagesRemoved(
+ @NonNull final UserHandle user, @NonNull final String... packages) {
int op = PackageUpdatedTask.OP_REMOVE;
FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages));
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
}
@Override
- public void onPackageAdded(String packageName, UserHandle user) {
+ public void onPackageAdded(@NonNull final String packageName, @NonNull final UserHandle user) {
int op = PackageUpdatedTask.OP_ADD;
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
}
@Override
- public void onPackagesAvailable(String[] packageNames, UserHandle user,
- boolean replacing) {
+ public void onPackagesAvailable(@NonNull final String[] packageNames,
+ @NonNull final UserHandle user, final boolean replacing) {
enqueueModelUpdateTask(
new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, user, packageNames));
}
@Override
- public void onPackagesUnavailable(String[] packageNames, UserHandle user,
- boolean replacing) {
+ public void onPackagesUnavailable(@NonNull final String[] packageNames,
+ @NonNull final UserHandle user, final boolean replacing) {
if (!replacing) {
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNAVAILABLE, user, packageNames));
@@ -198,20 +213,22 @@
}
@Override
- public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+ public void onPackagesSuspended(
+ @NonNull final String[] packageNames, @NonNull final UserHandle user) {
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_SUSPEND, user, packageNames));
}
@Override
- public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+ public void onPackagesUnsuspended(
+ @NonNull final String[] packageNames, @NonNull final UserHandle user) {
enqueueModelUpdateTask(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNSUSPEND, user, packageNames));
}
@Override
- public void onPackageLoadingProgressChanged(
- String packageName, UserHandle user, float progress) {
+ public void onPackageLoadingProgressChanged(@NonNull final String packageName,
+ @NonNull final UserHandle user, final float progress) {
if (Utilities.ATLEAST_S) {
enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
packageName, user, progress));
@@ -219,8 +236,8 @@
}
@Override
- public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user) {
+ public void onShortcutsChanged(@NonNull final String packageName,
+ @NonNull final List<ShortcutInfo> shortcuts, @NonNull final UserHandle user) {
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
}
@@ -228,7 +245,8 @@
* Called when the icon for an app changes, outside of package event
*/
@WorkerThread
- public void onAppIconChanged(String packageName, UserHandle user) {
+ public void onAppIconChanged(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
// Update the icon for the calendar package
Context context = mApp.getContext();
onPackageChanged(packageName, user);
@@ -256,7 +274,7 @@
MODEL_EXECUTOR.execute(mModelDelegate::destroy);
}
- public void onBroadcastIntent(Intent intent) {
+ public void onBroadcastIntent(@NonNull final Intent intent) {
if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
final String action = intent.getAction();
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
@@ -322,7 +340,7 @@
/**
* Removes an existing callback
*/
- public void removeCallbacks(Callbacks callbacks) {
+ public void removeCallbacks(@NonNull final Callbacks callbacks) {
synchronized (mCallbacksList) {
Preconditions.assertUIThread();
if (mCallbacksList.remove(callbacks)) {
@@ -338,7 +356,7 @@
* Adds a callbacks to receive model updates
* @return true if workspace load was performed synchronously
*/
- public boolean addCallbacksAndLoad(Callbacks callbacks) {
+ public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
synchronized (mLock) {
addCallbacks(callbacks);
return startLoader(new Callbacks[] { callbacks });
@@ -349,7 +367,7 @@
/**
* Adds a callbacks to receive model updates
*/
- public void addCallbacks(Callbacks callbacks) {
+ public void addCallbacks(@NonNull final Callbacks callbacks) {
Preconditions.assertUIThread();
synchronized (mCallbacksList) {
if (TestProtocol.sDebugTracing) {
@@ -370,7 +388,7 @@
return startLoader(new Callbacks[0]);
}
- private boolean startLoader(Callbacks[] newCallbacks) {
+ private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
@@ -433,7 +451,7 @@
* Loads the model if not loaded
* @param callback called with the data model upon successful load or null on model thread.
*/
- public void loadAsync(Consumer<BgDataModel> callback) {
+ public void loadAsync(@NonNull final Consumer<BgDataModel> callback) {
synchronized (mLock) {
if (!mModelLoaded && !mIsLoaderTaskRunning) {
startLoader();
@@ -443,11 +461,12 @@
}
@Override
- public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+ public void onInstallSessionCreated(@NonNull final PackageInstallInfo sessionInfo) {
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
apps.addPromiseApp(app.getContext(), sessionInfo);
bindApplicationsIfNeeded();
}
@@ -456,10 +475,12 @@
}
@Override
- public void onSessionFailure(String packageName, UserHandle user) {
+ public void onSessionFailure(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
final IntSet removedIds = new IntSet();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
@@ -483,7 +504,7 @@
}
@Override
- public void onPackageStateChanged(PackageInstallInfo installInfo) {
+ public void onPackageStateChanged(@NonNull final PackageInstallInfo installInfo) {
enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
}
@@ -491,7 +512,8 @@
* Updates the icons and label of all pending icons for the provided package name.
*/
@Override
- public void onUpdateSessionDisplay(PackageUserKey key, PackageInstaller.SessionInfo info) {
+ public void onUpdateSessionDisplay(@NonNull final PackageUserKey key,
+ @NonNull final PackageInstaller.SessionInfo info) {
mApp.getIconCache().updateSessionCache(key, info);
HashSet<String> packages = new HashSet<>();
@@ -502,9 +524,10 @@
public class LoaderTransaction implements AutoCloseable {
+ @NonNull
private final LoaderTask mTask;
- private LoaderTransaction(LoaderTask task) throws CancellationException {
+ private LoaderTransaction(@NonNull final LoaderTask task) throws CancellationException {
synchronized (mLock) {
if (mLoaderTask != task) {
throw new CancellationException("Loader already stopped");
@@ -534,7 +557,8 @@
}
}
- public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+ public LoaderTransaction beginLoader(@NonNull final LoaderTask task)
+ throws CancellationException {
return new LoaderTransaction(task);
}
@@ -551,7 +575,8 @@
/**
* Called when the icons for packages have been updated in the icon cache.
*/
- public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user) {
+ public void onPackageIconsUpdated(@NonNull final HashSet<String> updatedPackages,
+ @NonNull final UserHandle user) {
// If any package icon has changed (app was updated while launcher was dead),
// update the corresponding shortcuts.
enqueueModelUpdateTask(new CacheDataUpdatedTask(
@@ -561,17 +586,19 @@
/**
* Called when the labels for the widgets has updated in the icon cache.
*/
- public void onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user) {
+ public void onWidgetLabelsUpdated(@NonNull final HashSet<String> updatedPackages,
+ @NonNull final UserHandle user) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, app);
bindUpdatedWidgets(dataModel);
}
});
}
- public void enqueueModelUpdateTask(ModelUpdateTask task) {
+ public void enqueueModelUpdateTask(@NonNull final ModelUpdateTask task) {
if (mModelDestroyed) {
return;
}
@@ -585,7 +612,7 @@
*/
public interface CallbackTask {
- void execute(Callbacks callbacks);
+ void execute(@NonNull Callbacks callbacks);
}
/**
@@ -596,12 +623,14 @@
/**
* Called before the task is posted to initialize the internal state.
*/
- void init(LauncherAppState app, LauncherModel model,
- BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor);
+ void init(@NonNull LauncherAppState app, @NonNull LauncherModel model,
+ @NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList,
+ @NonNull Executor uiExecutor);
}
- public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
+ public void updateAndBindWorkspaceItem(@NonNull final WorkspaceItemInfo si,
+ @NonNull final ShortcutInfo info) {
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
mApp.getIconCache().getShortcutIcon(si, info);
@@ -612,10 +641,12 @@
/**
* Utility method to update a shortcut on the background thread.
*/
- public void updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider) {
+ public void updateAndBindWorkspaceItem(
+ @NonNull final Supplier<WorkspaceItemInfo> itemProvider) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
WorkspaceItemInfo info = itemProvider.get();
getModelWriter().updateItemInDatabase(info);
ArrayList<WorkspaceItemInfo> update = new ArrayList<>();
@@ -628,14 +659,16 @@
public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
dataModel.widgetsModel.update(app, packageUser);
bindUpdatedWidgets(dataModel);
}
});
}
- public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ public void dumpState(@Nullable final String prefix, @Nullable final FileDescriptor fd,
+ @NonNull final PrintWriter writer, @NonNull final String[] args) {
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
for (AppInfo info : mBgAllAppsList.data) {
@@ -661,6 +694,7 @@
/**
* Returns an array of currently attached callbacks
*/
+ @NonNull
public Callbacks[] getCallbacks() {
synchronized (mCallbacksList) {
return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index aa9cfd1..08b42cd 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -84,6 +84,7 @@
/** Invoke when the current search session is finished. */
public void onClearSearchResult() {
+ getMainAdapterProvider().clearHighlightedItem();
animateToSearchState(false);
rebindAdapters();
}
@@ -92,6 +93,7 @@
* Sets results list for search
*/
public void setSearchResults(ArrayList<AdapterItem> results) {
+ getMainAdapterProvider().clearHighlightedItem();
if (getSearchResultList().setSearchResults(results)) {
getSearchRecyclerView().onSearchResultsChanged();
}
@@ -179,11 +181,6 @@
}
@Override
- protected boolean shouldShowTabs() {
- return super.shouldShowTabs() && !isSearching();
- }
-
- @Override
public boolean isSearching() {
return mIsSearching;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 33d2f2b..368a373 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -26,7 +26,9 @@
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;
@@ -40,32 +42,36 @@
BaseAllAppsAdapter<T> {
public static final String TAG = "AppsGridAdapter";
- private final GridLayoutManager mGridLayoutMgr;
- private final GridSpanSizer mGridSizer;
+ private final AppsGridLayoutManager mGridLayoutMgr;
public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
super(activityContext, inflater, apps, adapterProviders);
- mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
- mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
+ mGridLayoutMgr.setSpanSizeLookup(new GridSpanSizer());
setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
}
/**
* Returns the grid layout manager.
*/
- public RecyclerView.LayoutManager getLayoutManager() {
+ public AppsGridLayoutManager getLayoutManager() {
return mGridLayoutMgr;
}
+ /** @return the column index that the given adapter index falls. */
+ public int getSpanIndex(int adapterIndex) {
+ AppsGridLayoutManager lm = getLayoutManager();
+ return lm.getSpanSizeLookup().getSpanIndex(adapterIndex, lm.getSpanCount());
+ }
+
/**
* A subclass of GridLayoutManager that overrides accessibility values during app search.
*/
- public class AppsGridLayoutManager extends GridLayoutManager {
+ public class AppsGridLayoutManager extends ScrollableLayoutManager {
public AppsGridLayoutManager(Context context) {
- super(context, 1, GridLayoutManager.VERTICAL, false);
+ super(context);
}
@Override
@@ -125,6 +131,15 @@
}
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 21a7dfb..3d06fb5 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.allapps;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
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;
@@ -26,7 +24,6 @@
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.SparseIntArray;
import androidx.recyclerview.widget.RecyclerView;
@@ -49,40 +46,10 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_LATENCY = Utilities.isPropertyEnabled(SEARCH_LOGGING);
- 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 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();
- }
- };
+ protected AlphabeticalAppsList<?> mApps;
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -122,12 +89,8 @@
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) {
@@ -200,17 +163,6 @@
}
@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;
}
@@ -231,13 +183,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) {
+ if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
mScrollbar.setThumbOffsetY(-1);
return;
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -292,51 +244,6 @@
}
}
- @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);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 22e8bcf..872c4fd 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -228,6 +228,11 @@
public void setStateWithAnimation(LauncherState toState,
StateAnimationConfig config, PendingAnimation builder) {
if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) {
+ // For atomic animations, we close the keyboard immediately.
+ if (!config.userControlled && !FeatureFlags.ENABLE_KEYBOARD_TRANSITION_SYNC.get()) {
+ mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
+ }
+
builder.addEndListener(success -> {
// Reset pull back progress and alpha after switching states.
ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
@@ -238,7 +243,7 @@
// the keyboard open and then changes their mind and swipes back up, we want the
// keyboard to remain open. However an onCancel signal is sent to the listeners
// (success = false), so we need to check for that.
- if (success) {
+ if (config.userControlled && success) {
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
}
});
@@ -314,7 +319,6 @@
* TODO: This logic should go in {@link LauncherState}
*/
private void onProgressAnimationEnd() {
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 879a159..f082542 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -88,6 +88,9 @@
public static final float PULL_MULTIPLIER = .02f;
public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
+ // Render the header protection at all times to debug clipping issues.
+ private static final boolean DEBUG_HEADER_PROTECTION = false;
+
private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Rect mInsets = new Rect();
@@ -103,7 +106,8 @@
new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
+ updateHeaderScroll(
+ ((AllAppsRecyclerView) recyclerView).computeVerticalScrollOffset());
}
};
@@ -128,7 +132,6 @@
private final int mScrimColor;
private final int mHeaderProtectionColor;
protected final float mHeaderThreshold;
- private int mHeaderBottomAdjustment;
private ScrimView mScrimView;
private int mHeaderColor;
private int mTabsProtectionAlpha;
@@ -141,8 +144,6 @@
mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
mHeaderThreshold = getResources().getDimensionPixelSize(
R.dimen.dynamic_grid_cell_border_spacing);
- mHeaderBottomAdjustment = getResources().getDimensionPixelSize(
- R.dimen.all_apps_header_bottom_adjustment);
mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
mWorkManager = new WorkProfileManager(
@@ -491,6 +492,7 @@
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+ mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
if (mUsingTabs) {
mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher);
@@ -523,6 +525,7 @@
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+ mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
}
protected void updateSearchResultsVisibility() {
@@ -725,17 +728,29 @@
if (!mHeader.isHeaderProtectionSupported()) {
return;
}
- mHeaderPaint.setColor(mHeaderColor);
- mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+ if (DEBUG_HEADER_PROTECTION) {
+ mHeaderPaint.setColor(Color.MAGENTA);
+ mHeaderPaint.setAlpha(255);
+ } else {
+ mHeaderPaint.setColor(mHeaderColor);
+ mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+ }
if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
int bottom = getHeaderBottom();
+ FloatingHeaderView headerView = getFloatingHeaderView();
if (!mUsingTabs) {
- bottom += getFloatingHeaderView().getPaddingBottom() - mHeaderBottomAdjustment;
+ // Add protection which is otherwise added when tabs scroll up.
+ bottom += headerView.getTabsAdditionalPaddingTop();
}
canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
- int tabsHeight = getFloatingHeaderView().getPeripheralProtectionHeight();
+ int tabsHeight = headerView.getPeripheralProtectionHeight();
if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
- mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ if (DEBUG_HEADER_PROTECTION) {
+ mHeaderPaint.setColor(Color.BLUE);
+ mHeaderPaint.setAlpha(255);
+ } else {
+ mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ }
canvas.drawRect(0, bottom, canvas.getWidth(), bottom + tabsHeight, mHeaderPaint);
}
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 8ec2aeb..1cbb0f9 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.getCurrentScrollY();
+ int current = -mCurrentRV.computeVerticalScrollOffset();
boolean headerCollapsed = mHeaderCollapsed;
moved(current);
applyVerticalMove();
@@ -272,11 +272,12 @@
}
mMaxTranslation += mFloatingRowsHeight;
if (!mTabsHidden) {
- mMaxTranslation += mTabsAdditionalPaddingBottom;
+ mMaxTranslation += mTabsAdditionalPaddingBottom
+ + getResources().getDimensionPixelSize(R.dimen.all_apps_tabs_margin_top);
}
}
- public int getMaxTranslation() {
+ int getMaxTranslation() {
if (mMaxTranslation == 0 && (mTabsHidden || mFloatingRowsCollapsed)) {
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
} else if (mMaxTranslation > 0 && mTabsHidden) {
@@ -333,7 +334,8 @@
int clipTop = getPaddingTop() - mTabsAdditionalPaddingTop;
if (mTabsHidden) {
- clipTop += getPaddingBottom() - mTabsAdditionalPaddingBottom;
+ // Add back spacing that is otherwise covered by the tabs.
+ clipTop += mTabsAdditionalPaddingTop;
}
mRVClip.top = mTabsHidden || mFloatingRowsCollapsed ? clipTop : 0;
mHeaderClip.top = clipTop;
@@ -404,6 +406,10 @@
return mFloatingRowsHeight;
}
+ int getTabsAdditionalPaddingTop() {
+ return mTabsAdditionalPaddingTop;
+ }
+
int getTabsAdditionalPaddingBottom() {
return mTabsAdditionalPaddingBottom;
}
@@ -472,7 +478,7 @@
/**
* Returns visible height of FloatingHeaderView contents requiring header protection
*/
- public int getPeripheralProtectionHeight() {
+ int getPeripheralProtectionHeight() {
if (!mHeaderProtectionSupported) {
return 0;
}
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index 03529af..a1f5bc6 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -23,27 +23,32 @@
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import android.animation.ObjectAnimator;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
+import android.util.Log;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
/** Coordinates the transition between Search and A-Z in All Apps. */
public class SearchTransitionController {
+ private static final String LOG_TAG = "SearchTransitionCtrl";
+
// Interpolator when the user taps the QSB while already in All Apps.
- private static final Interpolator DEFAULT_INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_1_7;
+ private static final Interpolator INTERPOLATOR_WITHIN_ALL_APPS = DEACCEL_1_7;
// Interpolator when the user taps the QSB from home screen, so transition to all apps is
// happening simultaneously.
- private static final Interpolator DEFAULT_INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = LINEAR;
+ private static final Interpolator INTERPOLATOR_TRANSITIONING_TO_ALL_APPS = INSTANT;
/**
* These values represent points on the [0, 1] animation progress spectrum. They are used to
@@ -103,9 +108,11 @@
boolean inAllApps = Launcher.getLauncher(
mAllAppsContainerView.getContext()).getStateManager().isInStableState(
LauncherState.ALL_APPS);
+ if (!inAllApps) {
+ duration = 0; // Don't want to animate when coming from QSB.
+ }
mSearchToAzAnimator.setDuration(duration).setInterpolator(
- inAllApps ? DEFAULT_INTERPOLATOR_WITHIN_ALL_APPS
- : DEFAULT_INTERPOLATOR_TRANSITIONING_TO_ALL_APPS);
+ inAllApps ? INTERPOLATOR_WITHIN_ALL_APPS : INTERPOLATOR_TRANSITIONING_TO_ALL_APPS);
mSearchToAzAnimator.addListener(forEndCallback(() -> mSearchToAzAnimator = null));
if (!goingToSearch) {
mSearchToAzAnimator.addListener(forSuccessCallback(() -> {
@@ -144,8 +151,11 @@
headerView.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
// Account for the additional padding added for the tabs.
- appsTranslationY -=
- headerView.getPaddingTop() - headerView.getTabsAdditionalPaddingBottom();
+ appsTranslationY +=
+ headerView.getTabsAdditionalPaddingBottom()
+ + mAllAppsContainerView.getResources().getDimensionPixelOffset(
+ R.dimen.all_apps_tabs_margin_top)
+ - headerView.getPaddingTop();
}
View appsContainer = mAllAppsContainerView.getAppsRecyclerViewContainer();
@@ -165,6 +175,7 @@
int appRowHeight = 0;
Integer top = null;
SearchRecyclerView searchRecyclerView = getSearchRecyclerView();
+
for (int i = 0; i < searchRecyclerView.getChildCount(); i++) {
View searchResultView = searchRecyclerView.getChildAt(i);
if (searchResultView == null) {
@@ -220,19 +231,48 @@
float scaleY = 1 - mSearchToAzProgress;
int scaledHeight = (int) (searchResultView.getHeight() * scaleY);
searchResultView.setScaleY(scaleY);
- searchResultView.setY(top + totalHeight);
- numSearchResultsAnimated++;
- totalHeight += scaledHeight;
+ // 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++;
+ totalHeight += scaledHeight;
+ }
+ searchResultView.setY(y);
}
return totalHeight - appRowHeight;
}
+ /** @return the column that the view at this position is found (0 assumed if indeterminate). */
+ private int getSpanIndex(SearchRecyclerView searchRecyclerView, int adapterPosition) {
+ if (adapterPosition == NO_POSITION) {
+ Log.w(LOG_TAG, "Can't determine span index - child not found in adapter");
+ return 0;
+ }
+ if (!(searchRecyclerView.getAdapter() instanceof AllAppsGridAdapter<?>)) {
+ Log.e(LOG_TAG, "Search RV doesn't have an AllAppsGridAdapter?");
+ // This case shouldn't happen, but for debug devices we will continue to create a more
+ // visible crash.
+ if (!Utilities.IS_DEBUG_DEVICE) {
+ return 0;
+ }
+ }
+ AllAppsGridAdapter<?> adapter = (AllAppsGridAdapter<?>) searchRecyclerView.getAdapter();
+ return adapter.getSpanIndex(adapterPosition);
+ }
+
/** Called just before a child is attached to the SearchRecyclerView. */
private void onSearchChildAttached(View child) {
// Avoid allocating hardware layers for alpha changes.
child.forceHasOverlappingRendering(false);
+ child.setPivotY(0);
if (mSearchToAzProgress > 0) {
// Before the child is rendered, apply the animation including it to avoid flicker.
updateSearchRecyclerViewProgress();
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index a589448..15fb77c 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -97,12 +97,10 @@
bottomMargin += dp.hotseatQsbHeight;
}
- if (!dp.isGestureMode) {
- if (dp.isTaskbarPresent) {
- bottomMargin += dp.taskbarSize;
- } else {
- bottomMargin += insets.bottom;
- }
+ if (!dp.isGestureMode && dp.isTaskbarPresent) {
+ bottomMargin += dp.taskbarSize;
+ } else {
+ bottomMargin += insets.bottom;
}
lp.bottomMargin = bottomMargin;
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index d2d7a6c..ab47097 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
@@ -68,7 +69,8 @@
public void doSearch(String query, SearchCallback<AdapterItem> callback) {
mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
ArrayList<AdapterItem> result = getTitleMatchResult(apps.data, query);
if (mAddNoResultsMessage && result.isEmpty()) {
result.add(getEmptyMessageAdapterItem(query));
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index a95bd51..4fb732d 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -84,4 +84,9 @@
public RecyclerView.ItemDecoration getDecorator() {
return mDecoration;
}
+
+ @Override
+ public void clearHighlightedItem() {
+ mHighlightedView = null;
+ }
}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index bc52784..3890741 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -58,4 +58,9 @@
* Returns the item decorator.
*/
public abstract RecyclerView.ItemDecoration getDecorator();
+
+ /**
+ * Clear the highlighted view.
+ */
+ public abstract void clearHighlightedItem();
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 12b4223..b55a1e4 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -83,6 +83,7 @@
EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
}
+ public static final Interpolator OVERSHOOT_0_75 = new OvershootInterpolator(0.75f);
public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 0e36dc0..50cf8d6 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -94,12 +94,12 @@
getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false,
"Keep All Apps search bar at the bottom (but above keyboard if open)");
- public static final BooleanFlag ENABLE_QUICK_SEARCH = new DeviceFlag("ENABLE_QUICK_SEARCH",
- true, "Use quick search behavior.");
-
public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
true, "Hide header on keyboard before typing in all apps");
+ public static final BooleanFlag ENABLE_HIDE_HEADER_STATIC = new DeviceFlag(
+ "ENABLE_HIDE_HEADER_STATIC", false, "Hide keyboard suggestion strip");
+
public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
"COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
@@ -281,18 +281,16 @@
"FOLDABLE_WORKSPACE_REORDER", true,
"In foldables, when reordering the icons and widgets, is now going to use both sides");
- public static final BooleanFlag SHOW_SEARCH_EDUCARD_QSB = new DeviceFlag(
- "SHOW_SEARCH_EDUCARD_QSB", false, "Shows Search Educard for QSB entry in OneSearch.");
-
- public static final BooleanFlag ENABLE_IME_LATENCY_LOGGER = getDebugFlag(
- "ENABLE_IME_LATENCY_LOGGER", false,
- "Enable option to log the keyboard latency for both atomic and controlled keyboard "
- + "animations on an EditText");
-
- // Change of wallpaper depth in widget picker is disabled for tests as it causes flakiness on
- // very slow cuttlefish devices.
public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
- "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
+ "ENABLE_WIDGET_PICKER_DEPTH", false, "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 POPUP_MATERIAL_U = new DeviceFlag(
+ "POPUP_MATERIAL_U", false, "Switch popup UX to use material U");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 466b268..05b1984 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -31,6 +31,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
@@ -62,6 +63,8 @@
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.views.AbstractSlideInView;
import com.android.launcher3.views.BaseDragLayer;
@@ -136,13 +139,25 @@
mAccessibilityManager =
getApplicationContext().getSystemService(AccessibilityManager.class);
- if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
- setupShortcut();
- } else {
- if (!setupWidget()) {
- // TODO: show error toast?
- finish();
- }
+ PackageUserKey targetApp = null;
+ switch (mRequest.getRequestType()) {
+ case PinItemRequest.REQUEST_TYPE_SHORTCUT:
+ targetApp = setupShortcut();
+ break;
+ case PinItemRequest.REQUEST_TYPE_APPWIDGET:
+ targetApp = setupWidget();
+ break;
+ }
+ if (targetApp == null) {
+ // TODO: show error toast?
+ finish();
+ return;
+ }
+ ApplicationInfo info = new PackageManagerHelper(this)
+ .getApplicationInfo(targetApp.mPackageName, targetApp.mUser, 0);
+ if (info == null) {
+ finish();
+ return;
}
WidgetCellPreview previewContainer = mWidgetCell.findViewById(
@@ -156,8 +171,10 @@
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
}
+ // Set the label synchronously instead of via IconCache as this is the first thing
+ // user sees
TextView widgetAppName = findViewById(R.id.widget_appName);
- widgetAppName.setText(getApplicationInfo().labelRes);
+ widgetAppName.setText(info.loadLabel(getPackageManager()));
mSlideInView = findViewById(R.id.add_item_bottom_sheet);
mSlideInView.addOnCloseListener(this);
@@ -246,20 +263,23 @@
}
}
- private void setupShortcut() {
+ private PackageUserKey setupShortcut() {
PinShortcutRequestActivityInfo shortcutInfo =
new PinShortcutRequestActivityInfo(mRequest, this);
mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
applyWidgetItemAsync(
() -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
+ return new PackageUserKey(
+ mRequest.getShortcutInfo().getPackage(),
+ mRequest.getShortcutInfo().getUserHandle());
}
- private boolean setupWidget() {
+ private PackageUserKey setupWidget() {
LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
.fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this));
if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) {
// Cannot add widget
- return false;
+ return null;
}
mWidgetCell.setRemoteViewsPreview(PinItemDragListener.getPreview(mRequest));
@@ -274,7 +294,7 @@
mWidgetCell.getWidgetView().setTag(pendingInfo);
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
- return true;
+ return new PackageUserKey(widgetInfo.provider.getPackageName(), widgetInfo.getUser());
}
private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 5021644..bf59594 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherAppState;
@@ -192,7 +193,8 @@
private class FolderNameWorker extends BaseModelUpdateTask {
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
mFolderInfos = dataModel.folders.clone();
mAppInfos = Arrays.asList(apps.copyData());
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 651372f..22627b4 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -593,6 +593,12 @@
@UiEvent(doc = "User tapped on Share app system shortcut.")
LAUNCHER_SYSTEM_SHORTCUT_APP_SHARE_TAP(1075),
+
+ @UiEvent(doc = "User has invoked split to right half from an app icon menu")
+ LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM(1199),
+
+ @UiEvent(doc = "User has invoked split to left half from an app icon menu")
+ LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP(1200)
;
// ADD MORE
@@ -738,8 +744,9 @@
HOT(2),
TIMEOUT(3),
FAIL(4),
- COLD_USERWAITING(5);
-
+ COLD_USERWAITING(5),
+ ATOMIC(6),
+ CONTROLLED(7);
private final int mId;
LatencyType(int id) {
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 4c0f1ae..0d978e1 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -23,6 +23,9 @@
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
@@ -42,6 +45,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Task to add auto-created workspace items.
@@ -50,14 +54,16 @@
private static final String LOG = "AddWorkspaceItemsTask";
+ @NonNull
private final List<Pair<ItemInfo, Object>> mItemList;
+ @NonNull
private final WorkspaceItemSpaceFinder mItemSpaceFinder;
/**
* @param itemList items to add on the workspace
*/
- public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList) {
+ public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList) {
this(itemList, new WorkspaceItemSpaceFinder());
}
@@ -65,14 +71,15 @@
* @param itemList items to add on the workspace
* @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing
*/
- public AddWorkspaceItemsTask(List<Pair<ItemInfo, Object>> itemList,
- WorkspaceItemSpaceFinder itemSpaceFinder) {
+ public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList,
+ @NonNull final WorkspaceItemSpaceFinder itemSpaceFinder) {
mItemList = itemList;
mItemSpaceFinder = itemSpaceFinder;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
if (mItemList.isEmpty()) {
return;
}
@@ -98,7 +105,8 @@
}
// b/139663018 Short-circuit this logic if the icon is a system app
- if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) {
+ if (PackageManagerHelper.isSystemApp(app.getContext(),
+ Objects.requireNonNull(item.getIntent()))) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON,
LOG + " Item is a system app.");
@@ -159,7 +167,7 @@
continue;
}
- List<LauncherActivityInfo> activities = launcherApps
+ List<LauncherActivityInfo> activities = Objects.requireNonNull(launcherApps)
.getActivityList(packageName, item.user);
boolean hasActivity = activities != null && !activities.isEmpty();
@@ -218,7 +226,7 @@
if (!addedItemsFinal.isEmpty()) {
scheduleCallbackTask(new CallbackTask() {
@Override
- public void execute(Callbacks callbacks) {
+ public void execute(@NonNull Callbacks callbacks) {
final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
if (!addedItemsFinal.isEmpty()) {
@@ -243,7 +251,8 @@
* Returns true if the shortcuts already exists on the workspace. This must be called after
* the workspace has been loaded. We identify a shortcut by its intent.
*/
- protected boolean shortcutExists(BgDataModel dataModel, Intent intent, UserHandle user) {
+ protected boolean shortcutExists(@NonNull final BgDataModel dataModel,
+ @Nullable final Intent intent, @NonNull final UserHandle user) {
final String compPkgName, intentWithPkg, intentWithoutPkg;
if (intent == null) {
// Skip items with null intents
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 95150dc..6da948c 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -66,7 +66,10 @@
/** The list off all apps. */
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
+ @NonNull
private IconCache mIconCache;
+
+ @NonNull
private AppFilter mAppFilter;
private boolean mDataChanged = false;
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 2a6a691..5b6f9f6 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,6 +17,7 @@
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
@@ -47,14 +48,17 @@
private static final boolean DEBUG_TASKS = false;
private static final String TAG = "BaseModelUpdateTask";
+ // Nullabilities are explicitly omitted here because these are late-init fields,
+ // They will be non-null after init(), which is always the case in enqueueModelUpdateTask().
private LauncherAppState mApp;
private LauncherModel mModel;
private BgDataModel mDataModel;
private AllAppsList mAllAppsList;
private Executor mUiExecutor;
- public void init(LauncherAppState app, LauncherModel model,
- BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor) {
+ public void init(@NonNull final LauncherAppState app, @NonNull final LauncherModel model,
+ @NonNull final BgDataModel dataModel, @NonNull final AllAppsList allAppsList,
+ @NonNull final Executor uiExecutor) {
mApp = app;
mModel = model;
mDataModel = dataModel;
@@ -64,7 +68,7 @@
@Override
public final void run() {
- if (!mModel.isModelLoaded()) {
+ if (!Objects.requireNonNull(mModel).isModelLoaded()) {
if (DEBUG_TASKS) {
Log.d(TAG, "Ignoring model task since loader is pending=" + this);
}
@@ -77,13 +81,13 @@
/**
* Execute the actual task. Called on the worker thread.
*/
- public abstract void execute(
- LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+ public abstract void execute(@NonNull LauncherAppState app,
+ @NonNull BgDataModel dataModel, @NonNull AllAppsList apps);
/**
* Schedules a {@param task} to be executed on the current callbacks.
*/
- public final void scheduleCallbackTask(final CallbackTask task) {
+ public final void scheduleCallbackTask(@NonNull final CallbackTask task) {
for (final Callbacks cb : mModel.getCallbacks()) {
mUiExecutor.execute(() -> task.execute(cb));
}
@@ -95,7 +99,7 @@
return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, null);
}
- public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
+ public void bindUpdatedWorkspaceItems(@NonNull final List<WorkspaceItemInfo> allUpdates) {
// Bind workspace items
List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream()
.filter(info -> info.id != ItemInfo.NO_ID)
@@ -113,18 +117,18 @@
.forEach(this::bindExtraContainerItems);
}
- public void bindExtraContainerItems(FixedContainerItems item) {
+ public void bindExtraContainerItems(@NonNull final FixedContainerItems item) {
FixedContainerItems copy = item.clone();
scheduleCallbackTask(c -> c.bindExtraContainerItems(copy));
}
- public void bindDeepShortcuts(BgDataModel dataModel) {
+ public void bindDeepShortcuts(@NonNull final BgDataModel dataModel) {
final HashMap<ComponentKey, Integer> shortcutMapCopy =
new HashMap<>(dataModel.deepShortcutMap);
scheduleCallbackTask(callbacks -> callbacks.bindDeepShortcutMap(shortcutMapCopy));
}
- public void bindUpdatedWidgets(BgDataModel dataModel) {
+ public void bindUpdatedWidgets(@NonNull final BgDataModel dataModel) {
final ArrayList<WidgetsListBaseEntry> widgets =
dataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index f644d49..57fefaa 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -18,6 +18,8 @@
import android.content.ComponentName;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.IconCache;
@@ -35,17 +37,23 @@
public static final int OP_SESSION_UPDATE = 2;
private final int mOp;
+
+ @NonNull
private final UserHandle mUser;
+
+ @NonNull
private final HashSet<String> mPackages;
- public CacheDataUpdatedTask(int op, UserHandle user, HashSet<String> packages) {
+ public CacheDataUpdatedTask(final int op, @NonNull final UserHandle user,
+ @NonNull final HashSet<String> packages) {
mOp = op;
mUser = user;
mPackages = packages;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
IconCache iconCache = app.getIconCache();
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
@@ -65,7 +73,7 @@
bindApplicationsIfNeeded();
}
- public boolean isValidShortcut(WorkspaceItemInfo si) {
+ public boolean isValidShortcut(@NonNull final WorkspaceItemInfo si) {
switch (mOp) {
case OP_CACHE_UPDATE:
return true;
diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
index c0dc34a..b9fba9d 100644
--- a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
@@ -17,6 +17,8 @@
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -31,19 +33,24 @@
*/
public class PackageIncrementalDownloadUpdatedTask extends BaseModelUpdateTask {
+ @NonNull
private final UserHandle mUser;
+
private final int mProgress;
+
+ @NonNull
private final String mPackageName;
- public PackageIncrementalDownloadUpdatedTask(
- String packageName, UserHandle user, float progress) {
+ public PackageIncrementalDownloadUpdatedTask(@NonNull final String packageName,
+ @NonNull final UserHandle user, final float progress) {
mUser = user;
mProgress = 1 - progress > 0.001 ? (int) (100 * progress) : 100;
mPackageName = packageName;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ public void execute(@NonNull LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList appsList) {
PackageInstallInfo downloadInfo = new PackageInstallInfo(
mPackageName,
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING,
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index b74d0fc..76a87ed 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,6 +18,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -33,14 +35,16 @@
*/
public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
+ @NonNull
private final PackageInstallInfo mInstallInfo;
- public PackageInstallStateChangedTask(PackageInstallInfo installInfo) {
+ public PackageInstallStateChangedTask(@NonNull final PackageInstallInfo installInfo) {
mInstallInfo = installInfo;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
if (mInstallInfo.state == PackageInstallInfo.STATUS_INSTALLED) {
try {
// For instant apps we do not get package-add. Use setting events to update
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index a9d272e..3d9d81f 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -29,6 +29,8 @@
import android.os.UserManager;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -80,17 +82,23 @@
public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
private final int mOp;
+
+ @NonNull
private final UserHandle mUser;
+
+ @NonNull
private final String[] mPackages;
- public PackageUpdatedTask(int op, UserHandle user, String... packages) {
+ public PackageUpdatedTask(final int op, @NonNull final UserHandle user,
+ @NonNull final String... packages) {
mOp = op;
mUser = user;
mPackages = packages;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList appsList) {
final Context context = app.getContext();
final IconCache iconCache = app.getIconCache();
diff --git a/src/com/android/launcher3/model/ReloadStringCacheTask.java b/src/com/android/launcher3/model/ReloadStringCacheTask.java
index f4d4298..34f7057 100644
--- a/src/com/android/launcher3/model/ReloadStringCacheTask.java
+++ b/src/com/android/launcher3/model/ReloadStringCacheTask.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
/**
@@ -22,14 +24,17 @@
* {@link android.app.admin.DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED}.
*/
public class ReloadStringCacheTask extends BaseModelUpdateTask {
+
+ @NonNull
private ModelDelegate mModelDelegate;
- public ReloadStringCacheTask(ModelDelegate modelDelegate) {
+ public ReloadStringCacheTask(@NonNull final ModelDelegate modelDelegate) {
mModelDelegate = modelDelegate;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList appsList) {
synchronized (dataModel) {
mModelDelegate.loadStringCache(dataModel.stringCache);
StringCache cloneSC = dataModel.stringCache.clone();
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1026e0b..a6a04a7 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -19,6 +19,8 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -38,13 +40,20 @@
*/
public class ShortcutsChangedTask extends BaseModelUpdateTask {
+ @NonNull
private final String mPackageName;
+
+ @NonNull
private final List<ShortcutInfo> mShortcuts;
+
+ @NonNull
private final UserHandle mUser;
+
private final boolean mUpdateIdMap;
- public ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user, boolean updateIdMap) {
+ public ShortcutsChangedTask(@NonNull final String packageName,
+ @NonNull final List<ShortcutInfo> shortcuts, @NonNull final UserHandle user,
+ final boolean updateIdMap) {
mPackageName = packageName;
mShortcuts = shortcuts;
mUser = user;
@@ -52,7 +61,8 @@
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
final Context context = app.getContext();
// Find WorkspaceItemInfo's that have changed on the workspace.
ArrayList<WorkspaceItemInfo> matchingWorkspaceItems = new ArrayList<>();
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 1565b19..63ca35b 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,6 +21,8 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -40,16 +42,18 @@
*/
public class UserLockStateChangedTask extends BaseModelUpdateTask {
+ @NonNull
private final UserHandle mUser;
private boolean mIsUserUnlocked;
- public UserLockStateChangedTask(UserHandle user, boolean isUserUnlocked) {
+ public UserLockStateChangedTask(@NonNull final UserHandle user, final boolean isUserUnlocked) {
mUser = user;
mIsUserUnlocked = isUserUnlocked;
}
@Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList apps) {
Context context = app.getContext();
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 76a0c4d..e5fb015 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,7 +16,6 @@
package com.android.launcher3.model.data;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -220,10 +219,10 @@
/** Creates an intent to that launches the app store at this app's page. */
@Nullable
public Intent getMarketIntent(Context context) {
- ComponentName componentName = getTargetComponent();
+ String targetPackage = getTargetPackage();
- return componentName != null
- ? new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName())
+ return targetPackage != null
+ ? new PackageManagerHelper(context).getMarketIntent(targetPackage)
: null;
}
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 1f16474..59ef320 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -24,7 +24,6 @@
import android.text.TextUtils;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -76,6 +75,7 @@
/**
* The intent used to start the application.
*/
+ @NonNull
public Intent intent;
/**
@@ -148,7 +148,7 @@
}
@Override
- @Nullable
+ @NonNull
public Intent getIntent() {
return intent;
}
@@ -166,7 +166,8 @@
return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
}
- public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
+ public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo,
+ @NonNull final Context context) {
// {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
intent = ShortcutKey.makeIntent(shortcutInfo);
title = shortcutInfo.getShortLabel();
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 29eefe2..b4cb0ee 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -16,6 +16,8 @@
package com.android.launcher3.pageindicators;
+import static com.android.launcher3.config.FeatureFlags.SHOW_DELIGHTFUL_PAGINATION_FOLDER;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -28,6 +30,7 @@
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Property;
import android.view.View;
@@ -37,6 +40,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.Themes;
/**
@@ -53,12 +57,16 @@
private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
private static final int ENTER_ANIMATION_DURATION = 400;
- private static final int DOT_ACTIVE_ALPHA = 255;
- private static final int DOT_INACTIVE_ALPHA = 128;
+ private static final int PAGE_INDICATOR_ALPHA = 255;
+ private static final int DOT_ALPHA = 128;
+ private static final int DOT_GAP_FACTOR = 3;
+ private static final float DOT_GAP_FACTOR_FLOAT = 3.8f;
// This value approximately overshoots to 1.5 times the original size.
private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
+ private static final float INDICATOR_ROTATION = 180f;
+
private static final RectF sTempRect = new RectF();
private static final Property<PageIndicatorDots, Float> CURRENT_POSITION
@@ -76,21 +84,27 @@
}
};
- private final Paint mCirclePaint;
+ private final Paint mPaginationPaint;
+ private final Drawable mPageIndicatorDrawable;
private final float mDotRadius;
+ private final float mCircleGap;
+ private final float mPageIndicatorSize;
+ private final float mPageIndicatorRadius;
private final boolean mIsRtl;
private int mNumPages;
private int mActivePage;
+ private int mCurrentScroll;
+ private int mTotalScroll;
/**
* The current position of the active dot including the animation progress.
* For ex:
- * 0.0 => Active dot is at position 0
- * 0.33 => Active dot is at position 0 and is moving towards 1
- * 0.50 => Active dot is at position [0, 1]
- * 0.77 => Active dot has left position 0 and is collapsing towards position 1
- * 1.0 => Active dot is at position 1
+ * 0.0 => Active dot is at position 0
+ * 0.33 => Active dot is at position 0 and is moving towards 1
+ * 0.50 => Active dot is at position [0, 1]
+ * 0.77 => Active dot has left position 0 and is collapsing towards position 1
+ * 1.0 => Active dot is at position 1
*/
private float mCurrentPosition;
private float mFinalPosition;
@@ -109,37 +123,66 @@
public PageIndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mCirclePaint.setStyle(Style.FILL);
- mCirclePaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
+ mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaginationPaint.setStyle(Style.FILL);
+ mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
- setOutlineProvider(new MyOutlineProver());
+
+ if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ mPageIndicatorSize = getResources().getDimension(
+ R.dimen.page_indicator_size);
+ mPageIndicatorRadius = mPageIndicatorSize / 2;
+ mPageIndicatorDrawable = context.getDrawable(R.drawable.page_indicator);
+ mPageIndicatorDrawable.setBounds(0, 0, (int) mPageIndicatorSize,
+ (int) mPageIndicatorSize);
+ mCircleGap = DOT_GAP_FACTOR_FLOAT * mDotRadius;
+
+ } else {
+ mPageIndicatorSize = 0;
+ mPageIndicatorRadius = 0;
+ mPageIndicatorDrawable = null;
+ mCircleGap = DOT_GAP_FACTOR * mDotRadius;
+ }
+ if (!SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ setOutlineProvider(new MyOutlineProver());
+ }
mIsRtl = Utilities.isRtl(getResources());
}
@Override
public void setScroll(int currentScroll, int totalScroll) {
- if (mNumPages > 1) {
- if (mIsRtl) {
- currentScroll = totalScroll - currentScroll;
- }
- int scrollPerPage = totalScroll / (mNumPages - 1);
- int pageToLeft = currentScroll / scrollPerPage;
- int pageToLeftScroll = pageToLeft * scrollPerPage;
- int pageToRightScroll = pageToLeftScroll + scrollPerPage;
+ if (mNumPages <= 1) {
+ mCurrentScroll = 0;
+ return;
+ }
- float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
- if (currentScroll < pageToLeftScroll + scrollThreshold) {
- // scroll is within the left page's threshold
- animateToPosition(pageToLeft);
- } else if (currentScroll > pageToRightScroll - scrollThreshold) {
- // scroll is far enough from left page to go to the right page
- animateToPosition(pageToLeft + 1);
- } else {
- // scroll is between left and right page
- animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
- }
+ if (mIsRtl) {
+ currentScroll = totalScroll - currentScroll;
+ }
+
+ if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ mCurrentScroll = currentScroll;
+ mTotalScroll = totalScroll;
+ invalidate();
+ return;
+ }
+
+ int scrollPerPage = totalScroll / (mNumPages - 1);
+ int pageToLeft = currentScroll / scrollPerPage;
+ int pageToLeftScroll = pageToLeft * scrollPerPage;
+ int pageToRightScroll = pageToLeftScroll + scrollPerPage;
+
+ float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
+ if (currentScroll < pageToLeftScroll + scrollThreshold) {
+ // scroll is within the left page's threshold
+ animateToPosition(pageToLeft);
+ } else if (currentScroll > pageToRightScroll - scrollThreshold) {
+ // scroll is far enough from left page to go to the right page
+ animateToPosition(pageToLeft + 1);
+ } else {
+ // scroll is between left and right page
+ animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
}
}
@@ -177,7 +220,7 @@
}
public void playEntryAnimation() {
- int count = mEntryAnimationRadiusFactors.length;
+ int count = mEntryAnimationRadiusFactors.length;
if (count == 0) {
mEntryAnimationRadiusFactors = null;
invalidate();
@@ -231,16 +274,16 @@
// Add extra spacing of mDotRadius on all sides so than entry animation could be run.
int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
- int height= MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ?
- MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
+ int height = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
+ ? MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
// Draw all page indicators;
- float circleGap = 3 * mDotRadius;
- float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+ float circleGap = mCircleGap;
+ float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2;
float x = startX + mDotRadius;
float y = getHeight() / 2;
@@ -252,43 +295,123 @@
circleGap = -circleGap;
}
for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
- mCirclePaint.setAlpha(i == mActivePage ? DOT_ACTIVE_ALPHA : DOT_INACTIVE_ALPHA);
- canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], mCirclePaint);
+ mPaginationPaint.setAlpha(i == mActivePage ? PAGE_INDICATOR_ALPHA : DOT_ALPHA);
+ if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ if (i != mActivePage) {
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
+ } else {
+ drawPageIndicator(canvas, mEntryAnimationRadiusFactors[i]);
+ }
+ } else {
+ canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i],
+ mPaginationPaint);
+ }
x += circleGap;
}
} else {
- mCirclePaint.setAlpha(DOT_INACTIVE_ALPHA);
+ // Here we draw the dots
+ mPaginationPaint.setAlpha(DOT_ALPHA);
for (int i = 0; i < mNumPages; i++) {
- canvas.drawCircle(x, y, mDotRadius, mCirclePaint);
+ if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ canvas.drawCircle(x, y, getRadius(x), mPaginationPaint);
+ } else {
+ canvas.drawCircle(x, y, mDotRadius, mPaginationPaint);
+ }
x += circleGap;
}
- mCirclePaint.setAlpha(DOT_ACTIVE_ALPHA);
- canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mCirclePaint);
+ // Here we draw the current page indicator
+ mPaginationPaint.setAlpha(PAGE_INDICATOR_ALPHA);
+ if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ drawPageIndicator(canvas, 1);
+ } else {
+ canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mPaginationPaint);
+ }
}
}
+ /**
+ * Draws the page indicator, denoting the currently selected page
+ *
+ * @param canvas is used to draw the page indicator and to rotate it as we scroll
+ * @param scale is used to set the scale of our canvas
+ */
+ private void drawPageIndicator(Canvas canvas, float scale) {
+ RectF currRect = getActiveRect();
+
+ // saves the canvas so we can later restore it to its original scale
+ canvas.save();
+
+ // Moves the canvas to start at the top left corner of the page indicator
+ canvas.translate(currRect.left, currRect.top);
+
+ // Scales the canvas in place to animate the indicator on entry
+ canvas.scale(scale, scale, mPageIndicatorRadius, mPageIndicatorRadius);
+
+ int scrollPerPage = getScrollPerPage();
+ // This IF is to avoid division by 0
+ if (scrollPerPage != 0) {
+ int delta = mCurrentScroll % scrollPerPage;
+ canvas.rotate((INDICATOR_ROTATION * delta) / scrollPerPage,
+ mPageIndicatorRadius, mPageIndicatorRadius);
+ }
+
+ mPageIndicatorDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ /**
+ * Returns the radius of the circle based on how close the page indicator is to it
+ *
+ * @param dotPositionX is the position the dot is located at in the x-axis
+ */
+ private float getRadius(float dotPositionX) {
+
+ float startXIndicator =
+ ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
+ float indicatorPosition = startXIndicator + getIndicatorScrollDistance()
+ + mPageIndicatorRadius;
+
+ // If the indicator gets close enough to a dot then we change the radius
+ // of the dot based on how close the indicator is to it.
+ float dotDistance = Math.abs(indicatorPosition - dotPositionX);
+ if (dotDistance <= mCircleGap) {
+ return Utilities.mapToRange(dotDistance, 0, mCircleGap, 0f, mDotRadius,
+ Interpolators.LINEAR);
+ }
+ return mDotRadius;
+ }
+
private RectF getActiveRect() {
float startCircle = (int) mCurrentPosition;
float delta = mCurrentPosition - startCircle;
float diameter = 2 * mDotRadius;
- float circleGap = 3 * mDotRadius;
- float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+ float startX;
- sTempRect.top = getHeight() * 0.5f - mDotRadius;
- sTempRect.bottom = getHeight() * 0.5f + mDotRadius;
- sTempRect.left = startX + startCircle * circleGap;
- sTempRect.right = sTempRect.left + diameter;
-
- if (delta < SHIFT_PER_ANIMATION) {
- // dot is capturing the right circle.
- sTempRect.right += delta * circleGap * 2;
+ if (SHOW_DELIGHTFUL_PAGINATION_FOLDER.get()) {
+ startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2) - getOffset();
+ sTempRect.top = (getHeight() - mPageIndicatorSize) * 0.5f;
+ sTempRect.bottom = (getHeight() + mPageIndicatorSize) * 0.5f;
+ sTempRect.left = startX + getIndicatorScrollDistance();
+ sTempRect.right = sTempRect.left + mPageIndicatorSize;
} else {
- // Dot is leaving the left circle.
- sTempRect.right += circleGap;
+ startX = ((getWidth() - (mNumPages * mCircleGap) + mDotRadius) / 2);
+ sTempRect.top = (getHeight() * 0.5f) - mDotRadius;
+ sTempRect.bottom = (getHeight() * 0.5f) + mDotRadius;
+ sTempRect.left = startX + (startCircle * mCircleGap);
+ sTempRect.right = sTempRect.left + diameter;
- delta -= SHIFT_PER_ANIMATION;
- sTempRect.left += delta * circleGap * 2;
+ if (delta < SHIFT_PER_ANIMATION) {
+ // dot is capturing the right circle.
+ sTempRect.right += delta * mCircleGap * 2;
+ } else {
+ // Dot is leaving the left circle.
+ sTempRect.right += mCircleGap;
+
+ delta -= SHIFT_PER_ANIMATION;
+ sTempRect.left += delta * mCircleGap * 2;
+ }
}
if (mIsRtl) {
@@ -296,9 +419,33 @@
sTempRect.right = getWidth() - sTempRect.left;
sTempRect.left = sTempRect.right - rectWidth;
}
+
return sTempRect;
}
+ /**
+ * The offset between the radius of the dot and the midpoint of the indicator so that
+ * the indicator is centered in with the indicator circles
+ */
+ private float getOffset() {
+ return mPageIndicatorRadius - mDotRadius;
+ }
+
+ /**
+ * Returns an int that is the amount we need to scroll per page
+ */
+ private int getScrollPerPage() {
+ return mNumPages > 1 ? mTotalScroll / (mNumPages - 1) : 0;
+ }
+
+ /**
+ * The current scroll adjusted for the distance the indicator needs to travel on the screen
+ */
+ private float getIndicatorScrollDistance() {
+ int scrollPerPage = getScrollPerPage();
+ return scrollPerPage != 0 ? ((float) mCurrentScroll / scrollPerPage) * mCircleGap : 0;
+ }
+
private class MyOutlineProver extends ViewOutlineProvider {
@Override
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index b695194..16bb868 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -30,6 +30,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.WorkerThread;
@@ -51,38 +52,50 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/**
* Utility class to tracking install sessions
*/
public class InstallSessionHelper {
+ @NonNull
private static final String LOG = "InstallSessionHelper";
// Set<String> of session ids of promise icons that have been added to the home screen
// as FLAG_PROMISE_NEW_INSTALLS.
+ @NonNull
protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
private static final boolean DEBUG = false;
+ @NonNull
public static final MainThreadInitializedObject<InstallSessionHelper> INSTANCE =
new MainThreadInitializedObject<>(InstallSessionHelper::new);
+ @Nullable
private final LauncherApps mLauncherApps;
+
+ @NonNull
private final Context mAppContext;
+ @NonNull
private final PackageInstaller mInstaller;
+
+ @NonNull
private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
+ @Nullable
private IntSet mPromiseIconIds;
- public InstallSessionHelper(Context context) {
+ public InstallSessionHelper(@NonNull final Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
}
@WorkerThread
+ @NonNull
private IntSet getPromiseIconIds() {
Preconditions.assertWorkerThread();
if (mPromiseIconIds != null) {
@@ -108,6 +121,7 @@
return mPromiseIconIds;
}
+ @NonNull
public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
HashMap<PackageUserKey, SessionInfo> activePackages = new HashMap<>();
for (SessionInfo info : getAllVerifiedSessions()) {
@@ -117,6 +131,7 @@
return activePackages;
}
+ @Nullable
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
for (SessionInfo info : getAllVerifiedSessions()) {
boolean match = pkg.equals(info.getAppPackageName());
@@ -136,11 +151,13 @@
.apply();
}
- SessionInfo getVerifiedSessionInfo(int sessionId) {
+ @Nullable
+ SessionInfo getVerifiedSessionInfo(final int sessionId) {
return verify(mInstaller.getSessionInfo(sessionId));
}
- private SessionInfo verify(SessionInfo sessionInfo) {
+ @Nullable
+ private SessionInfo verify(@Nullable final SessionInfo sessionInfo) {
if (sessionInfo == null
|| sessionInfo.getInstallerPackageName() == null
|| TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
@@ -167,9 +184,10 @@
return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
}
+ @NonNull
public List<SessionInfo> getAllVerifiedSessions() {
List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
- ? mLauncherApps.getAllPackageInstallerSessions()
+ ? Objects.requireNonNull(mLauncherApps).getAllPackageInstallerSessions()
: mInstaller.getAllSessions());
Iterator<SessionInfo> it = list.iterator();
while (it.hasNext()) {
@@ -201,12 +219,12 @@
}
@WorkerThread
- public boolean promiseIconAddedForId(int sessionId) {
+ public boolean promiseIconAddedForId(final int sessionId) {
return getPromiseIconIds().contains(sessionId);
}
@WorkerThread
- public void removePromiseIconId(int sessionId) {
+ public void removePromiseIconId(final int sessionId) {
if (promiseIconAddedForId(sessionId)) {
getPromiseIconIds().getArray().removeValue(sessionId);
updatePromiseIconPrefs();
@@ -222,7 +240,7 @@
* - A promise icon for the session has not already been created
*/
@WorkerThread
- void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
+ void tryQueuePromiseAppIcon(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.MISSING_PROMISE_ICON, LOG + " tryQueuePromiseAppIcon"
+ ", SessionCommitReceiveEnabled" + SessionCommitReceiver.isEnabled(mAppContext)
@@ -244,7 +262,7 @@
}
}
- public boolean verifySessionInfo(PackageInstaller.SessionInfo sessionInfo) {
+ public boolean verifySessionInfo(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
if (TestProtocol.sDebugTracing) {
boolean appNotInstalled = sessionInfo == null
|| !new PackageManagerHelper(mAppContext)
@@ -267,14 +285,15 @@
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
}
- public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
+ public InstallSessionTracker registerInstallTracker(
+ @Nullable final InstallSessionTracker.Callback callback) {
InstallSessionTracker tracker = new InstallSessionTracker(
this, callback, mInstaller, mLauncherApps);
tracker.register();
return tracker;
}
- public static UserHandle getUserHandle(SessionInfo info) {
+ public static UserHandle getUserHandle(@NonNull final SessionInfo info) {
return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
}
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index b16aaa2..aeaa320 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -28,12 +28,15 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.PackageUserKey;
import java.lang.ref.WeakReference;
+import java.util.Objects;
@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
@@ -41,14 +44,22 @@
// Lazily initialized
private SparseArray<PackageUserKey> mActiveSessions = null;
+ @NonNull
private final WeakReference<InstallSessionHelper> mWeakHelper;
+
+ @NonNull
private final WeakReference<Callback> mWeakCallback;
+
+ @NonNull
private final PackageInstaller mInstaller;
+
+ @Nullable
private final LauncherApps mLauncherApps;
- InstallSessionTracker(InstallSessionHelper installerCompat, Callback callback,
- PackageInstaller installer, LauncherApps launcherApps) {
+ InstallSessionTracker(@Nullable final InstallSessionHelper installerCompat,
+ @Nullable final Callback callback, @NonNull final PackageInstaller installer,
+ @Nullable LauncherApps launcherApps) {
mWeakHelper = new WeakReference<>(installerCompat);
mWeakCallback = new WeakReference<>(callback);
mInstaller = installer;
@@ -56,7 +67,7 @@
}
@Override
- public void onCreated(int sessionId) {
+ public void onCreated(final int sessionId) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (TestProtocol.sDebugTracing) {
@@ -80,7 +91,7 @@
}
@Override
- public void onFinished(int sessionId, boolean success) {
+ public void onFinished(final int sessionId, final boolean success) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
@@ -108,7 +119,7 @@
}
@Override
- public void onProgressChanged(int sessionId, float progress) {
+ public void onProgressChanged(final int sessionId, final float progress) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
@@ -121,10 +132,10 @@
}
@Override
- public void onActiveChanged(int sessionId, boolean active) { }
+ public void onActiveChanged(final int sessionId, final boolean active) { }
@Override
- public void onBadgingChanged(int sessionId) {
+ public void onBadgingChanged(final int sessionId) {
InstallSessionHelper helper = mWeakHelper.get();
Callback callback = mWeakCallback.get();
if (callback == null || helper == null) {
@@ -136,8 +147,9 @@
}
}
- private SessionInfo pushSessionDisplayToLauncher(
- int sessionId, InstallSessionHelper helper, Callback callback) {
+ @Nullable
+ private SessionInfo pushSessionDisplayToLauncher(final int sessionId,
+ @NonNull final InstallSessionHelper helper, @NonNull final Callback callback) {
SessionInfo session = helper.getVerifiedSessionInfo(sessionId);
if (session != null && session.getAppPackageName() != null) {
PackageUserKey key =
@@ -149,7 +161,9 @@
return null;
}
- private SparseArray<PackageUserKey> getActiveSessionMap(InstallSessionHelper helper) {
+ @NonNull
+ private SparseArray<PackageUserKey> getActiveSessionMap(
+ @NonNull final InstallSessionHelper helper) {
if (mActiveSessions == null) {
mActiveSessions = new SparseArray<>();
helper.getActiveSessions().forEach(
@@ -162,7 +176,8 @@
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
mInstaller.registerSessionCallback(this, MODEL_EXECUTOR.getHandler());
} else {
- mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, this);
+ Objects.requireNonNull(mLauncherApps).registerPackageInstallerSessionCallback(
+ MODEL_EXECUTOR, this);
}
}
@@ -170,18 +185,18 @@
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
mInstaller.unregisterSessionCallback(this);
} else {
- mLauncherApps.unregisterPackageInstallerSessionCallback(this);
+ Objects.requireNonNull(mLauncherApps).unregisterPackageInstallerSessionCallback(this);
}
}
public interface Callback {
- void onSessionFailure(String packageName, UserHandle user);
+ void onSessionFailure(@NonNull String packageName, @NonNull UserHandle user);
- void onUpdateSessionDisplay(PackageUserKey key, SessionInfo info);
+ void onUpdateSessionDisplay(@NonNull PackageUserKey key, @NonNull SessionInfo info);
- void onPackageStateChanged(PackageInstallInfo info);
+ void onPackageStateChanged(@NonNull PackageInstallInfo info);
- void onInstallSessionCreated(PackageInstallInfo info);
+ void onInstallSessionCreated(@NonNull PackageInstallInfo info);
}
}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 54e8e5b..86277a7 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -343,11 +343,6 @@
onStateTransitionEnd(state);
}
- @Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- onStateTransitionFailed(state);
- }
};
}
@@ -360,12 +355,6 @@
}
}
- private void onStateTransitionFailed(STATE_TYPE state) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onStateTransitionFailed(state);
- }
- }
-
private void onStateTransitionEnd(STATE_TYPE state) {
// Only change the stable states after the transitions have finished
if (state != mCurrentStableState) {
@@ -600,11 +589,6 @@
default void onStateTransitionStart(STATE_TYPE toState) { }
- /**
- * If the state transition animation fails (e.g. is canceled by the user), this fires.
- */
- default void onStateTransitionFailed(STATE_TYPE toState) { }
-
default void onStateTransitionComplete(STATE_TYPE finalState) { }
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index c408904..b94ea07 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -24,12 +24,16 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
+import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Handler;
import android.os.Message;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
@@ -58,6 +62,7 @@
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
+ @Nullable
private BaseActivity mActivity;
private SharedPreferences mSharedPrefs = null;
private final Handler mRequestOrientationHandler;
@@ -209,8 +214,12 @@
}
}
+ @WorkerThread
private boolean setOrientationAsync(Message msg) {
- mActivity.setRequestedOrientation(msg.what);
+ Activity activity = mActivity;
+ if (activity != null) {
+ activity.setRequestedOrientation(msg.what);
+ }
return true;
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d3c9bc9..269baf0 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().getCurrentScrollY());
+ l -> l.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset());
}
case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
return getLauncherUIProperty(Bundle::putInt,
- l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+ l -> WidgetsFullSheet.getWidgetsView(l).computeVerticalScrollOffset());
}
case TestProtocol.REQUEST_TARGET_INSETS: {
diff --git a/src/com/android/launcher3/testing/TestInformationProvider.java b/src/com/android/launcher3/testing/TestInformationProvider.java
index bcc7c2d..5444d92 100644
--- a/src/com/android/launcher3/testing/TestInformationProvider.java
+++ b/src/com/android/launcher3/testing/TestInformationProvider.java
@@ -21,10 +21,14 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import com.android.launcher3.Utilities;
public class TestInformationProvider extends ContentProvider {
+
+ private static final String TAG = "TestInformationProvider";
+
@Override
public boolean onCreate() {
return true;
@@ -60,7 +64,13 @@
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
TestInformationHandler handler = TestInformationHandler.newInstance(getContext());
handler.init(getContext());
- return handler.call(method, arg, extras);
+
+ Bundle response = handler.call(method, arg, extras);
+ if (response == null) {
+ Log.e(TAG, "Couldn't handle method: " + method + "; current handler="
+ + handler.getClass().getSimpleName());
+ }
+ return response;
}
return null;
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 5fa30bc..b4be061 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -248,13 +248,19 @@
final Launcher launcher = Launcher.getLauncher(context);
if (shortcut.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& shortcut.isDisabledVersionLower()) {
+ final Intent marketIntent = shortcut.getMarketIntent(context);
+ // No market intent means no target package for the shortcut, which should be an
+ // issue. Falling back to showing toast messages.
+ if (marketIntent == null) {
+ return false;
+ }
new AlertDialog.Builder(context)
.setTitle(R.string.dialog_update_title)
.setMessage(R.string.dialog_update_message)
.setPositiveButton(R.string.dialog_update, (d, i) -> {
// Direct the user to the play store to update the app
- context.startActivity(shortcut.getMarketIntent(context));
+ context.startActivity(marketIntent);
})
.setNeutralButton(R.string.dialog_remove, (d, i) -> {
// Remove the icon if launcher is successfully initialized
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 89c300d..9afca4f 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -453,7 +453,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
@@ -461,8 +461,8 @@
int insetCorrectionX = dp.getInsets().left;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX);
- out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ out.setTranslationX(distanceToEdge - insetCorrectionX);
+ out.setTranslationY(((-splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to LEFT instead of the lint-recommended START because we always want this
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 450205d..bc1b634 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -520,7 +520,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge;
if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 387e980..55bb5e8 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -190,7 +190,7 @@
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
- out.setPivotY(0);
+ out.setPivotY(splitInstructionsHeight);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
@@ -198,9 +198,8 @@
int insetCorrectionX = dp.getInsets().right;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge
- + insetCorrectionX);
- out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ out.setTranslationX(splitInstructionsWidth - distanceToEdge + insetCorrectionX);
+ out.setTranslationY(((-splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to RIGHT instead of the lint-recommended END because we always want this
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index f42d304..12e8b54 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -43,6 +43,9 @@
import android.util.Pair;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -54,6 +57,7 @@
import java.net.URISyntaxException;
import java.util.List;
+import java.util.Objects;
/**
* Utility methods using package manager
@@ -62,22 +66,28 @@
private static final String TAG = "PackageManagerHelper";
+ @NonNull
private final Context mContext;
+
+ @NonNull
private final PackageManager mPm;
+
+ @NonNull
private final LauncherApps mLauncherApps;
- public PackageManagerHelper(Context context) {
+ public PackageManagerHelper(@NonNull final Context context) {
mContext = context;
mPm = context.getPackageManager();
- mLauncherApps = context.getSystemService(LauncherApps.class);
+ mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
}
/**
* Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
* guarantee that the app is on SD card.
*/
- public boolean isAppOnSdcard(String packageName, UserHandle user) {
- ApplicationInfo info = getApplicationInfo(
+ public boolean isAppOnSdcard(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
+ final ApplicationInfo info = getApplicationInfo(
packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
@@ -86,23 +96,27 @@
* Returns whether the target app is suspended for a given user as per
* {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
*/
- public boolean isAppSuspended(String packageName, UserHandle user) {
- ApplicationInfo info = getApplicationInfo(packageName, user, 0);
+ public boolean isAppSuspended(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
+ final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
return info != null && isAppSuspended(info);
}
/**
* Returns whether the target app is installed for a given user
*/
- public boolean isAppInstalled(String packageName, UserHandle user) {
- ApplicationInfo info = getApplicationInfo(packageName, user, 0);
+ public boolean isAppInstalled(@NonNull final String packageName,
+ @NonNull final UserHandle user) {
+ final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
return info != null;
}
/**
* Returns the application info for the provided package or null
*/
- public ApplicationInfo getApplicationInfo(String packageName, UserHandle user, int flags) {
+ @Nullable
+ public ApplicationInfo getApplicationInfo(@NonNull final String packageName,
+ @NonNull final UserHandle user, final int flags) {
try {
ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
@@ -116,7 +130,8 @@
return mPm.isSafeMode();
}
- public Intent getAppLaunchIntent(String pkg, UserHandle user) {
+ @Nullable
+ public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) {
List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
return activities.isEmpty() ? null :
AppInfo.makeLaunchIntent(activities.get(0));
@@ -251,7 +266,8 @@
return packageFilter;
}
- public static boolean isSystemApp(Context context, Intent intent) {
+ public static boolean isSystemApp(@NonNull final Context context,
+ @NonNull final Intent intent) {
PackageManager pm = context.getPackageManager();
ComponentName cn = intent.getComponent();
String packageName = null;
diff --git a/src/com/android/launcher3/util/PendingSplitSelectInfo.java b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
index ed02465..58c3be5 100644
--- a/src/com/android/launcher3/util/PendingSplitSelectInfo.java
+++ b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
@@ -16,6 +16,7 @@
package com.android.launcher3.util;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
/**
@@ -28,10 +29,13 @@
private final int mStagedTaskId;
private final int mStagePosition;
+ private final StatsLogManager.EventEnum mSource;
- public PendingSplitSelectInfo(int stagedTaskId, int stagePosition) {
+ public PendingSplitSelectInfo(int stagedTaskId, int stagePosition,
+ StatsLogManager.EventEnum source) {
this.mStagedTaskId = stagedTaskId;
this.mStagePosition = stagePosition;
+ this.mSource = source;
}
public int getStagedTaskId() {
@@ -41,4 +45,8 @@
public @StagePosition int getStagePosition() {
return mStagePosition;
}
+
+ public StatsLogManager.EventEnum getSource() {
+ return mSource;
+ }
}
diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
new file mode 100644
index 0000000..17eaefd
--- /dev/null
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.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.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 f14d985..88e1b22 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -16,12 +16,17 @@
package com.android.launcher3.util;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.graphics.Rect;
import androidx.annotation.IntDef;
+import com.android.launcher3.logging.StatsLogManager;
+
import java.lang.annotation.Retention;
public final class SplitConfigurationOptions {
@@ -170,4 +175,10 @@
@StageType
public int stageType = STAGE_TYPE_UNDEFINED;
}
+
+ public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
+ return position == STAGE_POSITION_TOP_OR_LEFT
+ ? LAUNCHER_APP_ICON_MENU_SPLIT_LEFT_TOP
+ : LAUNCHER_APP_ICON_MENU_SPLIT_RIGHT_BOTTOM;
+ }
}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 47503b1..f73347a 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -33,6 +33,8 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -41,6 +43,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Extension of {@link AbstractFloatingView} with common methods for sliding in from bottom.
@@ -79,6 +82,7 @@
protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
protected boolean mNoIntercept;
+ protected @Nullable OnCloseListener mOnCloseBeginListener;
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -204,6 +208,11 @@
}
}
+ /** Callback invoked when the view is beginning to close (e.g. close animation is started). */
+ public void setOnCloseBeginListener(@Nullable OnCloseListener onCloseBeginListener) {
+ mOnCloseBeginListener = onCloseBeginListener;
+ }
+
/** Registers an {@link OnCloseListener}. */
public void addOnCloseListener(OnCloseListener listener) {
mOnCloseListeners.add(listener);
@@ -213,6 +222,8 @@
if (!mIsOpen) {
return;
}
+ Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
+
if (!animate) {
mOpenCloseAnimator.cancel();
setTranslationShift(TRANSLATION_SHIFT_CLOSED);
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
index b1e69c7..ab8e5db 100644
--- a/src/com/android/launcher3/views/AllAppsButton.java
+++ b/src/com/android/launcher3/views/AllAppsButton.java
@@ -45,5 +45,6 @@
Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
.createScaledBitmapWithShadow(theme.getDrawable(R.drawable.ic_all_apps_button));
setIcon(new FastBitmapDrawable(bitmap));
+ setContentDescription(context.getString(R.string.all_apps_button_label));
}
}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index efc83eb..55af622 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
+import static android.view.Gravity.LEFT;
+
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -181,8 +183,10 @@
updatePosition(positionOut, lp);
setLayoutParams(lp);
- mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
- mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+ // For code simplicity, we always layout the child views using Gravity.LEFT
+ // and manually handle RTL for FloatingIconView when positioning it on the screen.
+ mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height, LEFT));
+ mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height, LEFT));
}
private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 40e4ce1..3af2e3c 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -119,7 +119,6 @@
// 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;
@@ -207,16 +206,11 @@
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/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 5ce8fcf..3e80699 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -323,6 +323,7 @@
if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
// First, we clear any previously cached content from existing widgets
mCachedRemoteViews.clear();
+ mDeferredViews.clear();
// Then we proceed to cache the content from the widgets
for (int i = 0; i < mViews.size(); i++) {
final int appWidgetId = mViews.keyAt(i);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 35fa7a4..5969e3e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -19,7 +19,6 @@
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;
@@ -28,6 +27,7 @@
import com.android.launcher3.FastScrollRecyclerView;
import com.android.launcher3.R;
+import com.android.launcher3.util.ScrollableLayoutManager;
/**
* The widgets recycler view.
@@ -42,14 +42,6 @@
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);
}
@@ -68,9 +60,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- // create a layout manager with Launcher's context so that scroll position
- // can be preserved during screen rotation.
- setLayoutManager(new LinearLayoutManager(getContext()));
+ setLayoutManager(new ScrollableLayoutManager(getContext()));
}
@Override
@@ -114,7 +104,7 @@
}
// Skip early if, there no child laid out in the container.
- int scrollY = getCurrentScrollY();
+ int scrollY = computeVerticalScrollOffset();
if (scrollY < 0) {
mScrollbar.setThumbOffsetY(-1);
return;
@@ -164,39 +154,6 @@
}
/**
- * 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 a66b09a..2d519d9 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -151,6 +151,8 @@
device.executeShellCommand(
"am dumpheap " + device.getLauncherPackageName() + " " + fileName);
}
+ Log.d(TAG, "Saved leak dump, the leak is still present: "
+ + !launcher.noLeakedActivities());
sDumpWasGenerated = true;
result = "saved memory dump as an artifact";
} catch (Throwable e) {
@@ -525,7 +527,7 @@
}
protected int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset();
}
private void checkLauncherIntegrity(
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index be49974..07bfe4c 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -303,7 +303,7 @@
}
private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
+ return getWidgetsView(launcher).computeVerticalScrollOffset();
}
private boolean isOptionsPopupVisible(Launcher launcher) {
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 3324959..e7e551f 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -47,6 +47,7 @@
import android.test.mock.MockContentResolver;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.uiautomator.UiDevice;
@@ -194,8 +195,9 @@
Executor mockExecutor = mock(Executor.class);
model.enqueueModelUpdateTask(new ModelUpdateTask() {
@Override
- public void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
- AllAppsList allAppsList, Executor uiExecutor) {
+ public void init(@NonNull final LauncherAppState app,
+ @NonNull final LauncherModel model, @NonNull final BgDataModel dataModel,
+ @NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor) {
task.init(app, model, dataModel, allAppsList, mockExecutor);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 1fb8cc7..3986df6 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -68,7 +68,6 @@
import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.testing.shared.TestInformationRequest;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.systemui.shared.system.ContextUtils;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Assert;
@@ -111,8 +110,11 @@
static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
static final Pattern EVENT_TOUCH_CANCEL_TIS = getTouchEventPatternTIS("ACTION_CANCEL");
- static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
- static final Pattern EVENT_KEY_BACK_UP = getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+ private static final Pattern EVENT_KEY_BACK_DOWN =
+ getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
+ private static final Pattern EVENT_KEY_BACK_UP =
+ getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+ private static final Pattern EVENT_ON_BACK_INVOKED = Pattern.compile("onBackInvoked");
private final String mLauncherPackage;
private Boolean mIsLauncher3;
@@ -237,7 +239,7 @@
// Launcher package. As during inproc tests the tested launcher may not be selected as the
// current launcher, choosing target package for inproc. For out-of-proc, use the installed
// launcher package.
- mLauncherPackage = testPackage.equals(targetPackage)
+ mLauncherPackage = testPackage.equals(targetPackage) || isGradleInstrumentation()
? getLauncherPackageName()
: targetPackage;
@@ -263,7 +265,7 @@
SystemClock.sleep(5000);
} else {
try {
- final int userId = ContextUtils.getUserId(getContext());
+ final int userId = getContext().getUserId();
final String launcherPidCommand = "pidof " + pi.packageName;
final String initialPid = mDevice.executeShellCommand(launcherPidCommand)
.replaceAll("\\s", "");
@@ -284,6 +286,20 @@
}
}
+ /**
+ * Gradle only supports out of process instrumentation. The test package is automatically
+ * generated by appending `.test` to the target package.
+ */
+ private boolean isGradleInstrumentation() {
+ final String testPackage = getContext().getPackageName();
+ final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
+ final String testSuffix = ".test";
+
+ return testPackage.endsWith(testSuffix) && testPackage.length() > testSuffix.length()
+ && testPackage.substring(0, testPackage.length() - testSuffix.length())
+ .equals(targetPackage);
+ }
+
public void enableCheckEventsForSuccessfulGestures() {
mCheckEventsForSuccessfulGestures = true;
}
@@ -891,7 +907,14 @@
}
/**
- * Presses nav bar home button.
+ * Goes to home by swiping up in zero-button mode or pressing Home button.
+ * Calling it after another TAPL call is safe because all TAPL methods wait for the animations
+ * to finish.
+ * When calling it after a non-TAPL method, make sure that all animations have already
+ * completed, otherwise it may detect the current state (for example "Application" or "Home")
+ * incorrectly.
+ * The method expects either app or Launcher to be active when it's called. Other states, such
+ * as visible notification shade are not supported.
*
* @return the Workspace object.
*/
@@ -984,8 +1007,12 @@
}
}
if (launcherVisible) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+ if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
+ } else {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+ }
}
}
}
@@ -1888,8 +1915,9 @@
/**
* Taps outside container to dismiss.
+ *
* @param container container to be dismissed
- * @param tapRight tap on the right of the container if true, or left otherwise
+ * @param tapRight tap on the right of the container if true, or left otherwise
*/
void touchOutsideContainer(UiObject2 container, boolean tapRight) {
try (LauncherInstrumentation.Closable c = addContextLayer(
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 5fab7eb..2c9fdb3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -475,7 +475,9 @@
// Since the destination can be on another page, we need to drag to the edge first
// until we reach the target page
while (targetDest.x > displayX || targetDest.x < 0) {
- int edgeX = targetDest.x > 0 ? displayX : 0;
+ // Don't drag all the way to the edge to prevent touch events from getting out of
+ //screen bounds.
+ int edgeX = targetDest.x > 0 ? displayX - 1 : 1;
Point screenEdge = new Point(edgeX, targetDest.y);
Point finalDragStart = dragStart;
executeAndWaitForPageScroll(launcher,