From a421143dd7c10d4cc7ac2b98e765c4f3e7b0fa3f Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Tue, 25 Feb 2020 18:47:54 -0800 Subject: [PATCH 01/39] Enable support for same page hotseat migration Bug: 142753423 Test: Manual Change-Id: Ib53a64629a595c412e30ca5ff46077efc73a3f3e --- .../res/layout/predicted_hotseat_edu.xml | 10 +- .../hybridhotseat/HotseatEduController.java | 27 ++++-- .../hybridhotseat/HotseatEduDialog.java | 97 +++++++++++++++---- quickstep/res/values/strings.xml | 33 ++++--- src/com/android/launcher3/CellLayout.java | 20 ++++ .../launcher3/config/FeatureFlags.java | 4 + 6 files changed, 145 insertions(+), 46 deletions(-) diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml index fe99037ccf..c93cad65bf 100644 --- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml +++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml @@ -35,25 +35,27 @@ @@ -83,7 +85,7 @@ android:layout_height="wrap_content" android:layout_gravity="end" android:background="?android:attr/selectableItemBackground" - android:text="@string/hotseat_migrate_accept" + android:text="@string/hotseat_edu_accept" android:textAlignment="textEnd" android:textColor="@android:color/white" /> @@ -91,7 +93,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/no_thanks" - android:text="@string/hotseat_migrate_dismiss" + android:text="@string/hotseat_edu_dismiss" android:layout_gravity="start" android:background="?android:attr/selectableItemBackground" android:textColor="@android:color/white" /> diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index e7290a4482..a07cd1d82c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -27,11 +27,15 @@ import android.view.ViewGroup; import androidx.core.app.NotificationCompat; +import com.android.launcher3.CellLayout; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; +import com.android.launcher3.Workspace; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.WorkspaceLayoutManager; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.ActivityTracker; import com.android.launcher3.util.Themes; @@ -61,16 +65,25 @@ public class HotseatEduController { mNotification = createNotification(); } - void migrate() { + boolean migrate() { + Workspace workspace = mLauncher.getWorkspace(); + CellLayout firstScreen = workspace.getScreenWithId(WorkspaceLayoutManager.FIRST_SCREEN_ID); + int toPage = Workspace.FIRST_SCREEN_ID; + int toRow = mLauncher.getDeviceProfile().inv.numRows - 1; + if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) { + toPage = workspace.getScreenIdForPageIndex(workspace.getPageCount()); + toRow = 0; + } else if (!firstScreen.makeSpaceForHotseatMigration(true)) { + return false; + } ViewGroup hotseatVG = mLauncher.getHotseat().getShortcutsAndWidgets(); - int workspacePageCount = mLauncher.getWorkspace().getPageCount(); for (int i = 0; i < hotseatVG.getChildCount(); i++) { View child = hotseatVG.getChildAt(i); ItemInfo tag = (ItemInfo) child.getTag(); mLauncher.getModelWriter().moveItemInDatabase(tag, - LauncherSettings.Favorites.CONTAINER_DESKTOP, workspacePageCount, tag.screenId, - 0); + LauncherSettings.Favorites.CONTAINER_DESKTOP, toPage, tag.screenId, toRow); } + return true; } void removeNotification() { @@ -93,7 +106,7 @@ public class HotseatEduController { private void createNotificationChannel() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; - CharSequence name = mLauncher.getString(R.string.hotseat_migrate_title); + CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title); int importance = NotificationManager.IMPORTANCE_LOW; NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance); @@ -104,8 +117,8 @@ public class HotseatEduController { Intent intent = new Intent(mLauncher.getApplicationContext(), mLauncher.getClass()); intent = new NotificationHandler().addToIntent(intent); - CharSequence name = mLauncher.getString(R.string.hotseat_migrate_prompt_title); - String description = mLauncher.getString(R.string.hotseat_migrate_prompt_content); + CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title); + String description = mLauncher.getString(R.string.hotseat_edu_prompt_content); NotificationCompat.Builder builder = new NotificationCompat.Builder(mLauncher, NOTIFICATION_CHANNEL_ID) .setContentTitle(name) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 8926246ad2..538b7f3952 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -26,6 +26,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; +import android.widget.TextView; import android.widget.Toast; import com.android.launcher3.CellLayout; @@ -34,7 +35,9 @@ import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.WorkspaceLayoutManager; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -49,12 +52,24 @@ import java.util.List; public class HotseatEduDialog extends AbstractSlideInView implements Insettable { private static final int DEFAULT_CLOSE_DURATION = 200; + protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000; + + // We don't migrate if user has more than SAME_PAGE_MAX_ROWS rows of item in their screen + private static final int SAME_PAGE_MAX_ROWS = 2; + + private static final int MIGRATE_SAME_PAGE = 0; + private static final int MIGRATE_NEW_PAGE = 1; + private static final int MIGRATE_NO_MIGRATE = 2; - public static boolean shown = false; private final Rect mInsets = new Rect(); private View mHotseatWrapper; private CellLayout mSampleHotseat; + private TextView mEduHeading; + private TextView mEduContent; + private Button mDismissBtn; + + private int mMigrationMode = MIGRATE_SAME_PAGE; public void setHotseatEduController(HotseatEduController hotseatEduController) { mHotseatEduController = hotseatEduController; @@ -78,6 +93,8 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable super.onFinishInflate(); mHotseatWrapper = findViewById(R.id.hotseat_wrapper); mSampleHotseat = findViewById(R.id.sample_prediction); + mEduHeading = findViewById(R.id.hotseat_edu_heading); + mEduContent = findViewById(R.id.hotseat_edu_content); DeviceProfile grid = mLauncher.getDeviceProfile(); Rect padding = grid.getHotseatLayoutPadding(); @@ -87,24 +104,27 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable mSampleHotseat.setPadding(padding.left, 0, padding.right, 0); Button turnOnBtn = findViewById(R.id.turn_predictions_on); - turnOnBtn.setOnClickListener(this::onMigrate); + turnOnBtn.setOnClickListener(this::onAccept); - Button learnMoreBtn = findViewById(R.id.no_thanks); - learnMoreBtn.setOnClickListener(this::onKeepDefault); + mDismissBtn = findViewById(R.id.no_thanks); + mDismissBtn.setOnClickListener(this::onDismiss); } - private void onMigrate(View v) { - if (mHotseatEduController == null) return; + private void onAccept(View v) { + if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) { + onDismiss(v); + return; + } handleClose(true); - mHotseatEduController.migrate(); mHotseatEduController.finishOnboarding(); logUserAction(true); - Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show(); + int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE + ? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt; + Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show(); } - private void onKeepDefault(View v) { - if (mHotseatEduController == null) return; + private void onDismiss(View v) { Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show(); mHotseatEduController.finishOnboarding(); logUserAction(false); @@ -148,6 +168,8 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT; target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED : HYBRID_HOTSEAT_CANCELED; + // encoding migration type on pageIndex + target.pageIndex = mMigrationMode; LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null); } @@ -161,6 +183,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null); } + private void animateOpen() { if (mIsOpen || mOpenCloseAnimator.isRunning()) { return; @@ -183,17 +206,12 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable handleClose(false); } - /** - * Opens User education dialog with a list of suggested apps - */ - public void show(List predictions) { - if (getParent() != null - || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) { - return; - } - attachToContainer(); - logOnBoardingSeen(); - animateOpen(); + @Override + protected int getScrimColor(Context context) { + return FINAL_SCRIM_BG_COLOR; + } + + private void populatePreview(List predictions) { for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) { WorkspaceItemInfo info = predictions.get(i); PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info); @@ -204,6 +222,43 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable } } + @Override + protected void attachToContainer() { + super.attachToContainer(); + if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) { + mEduContent.setText(R.string.hotseat_edu_message_migrate_alt); + mMigrationMode = MIGRATE_NEW_PAGE; + return; + } + CellLayout page = mLauncher.getWorkspace().getScreenWithId( + WorkspaceLayoutManager.FIRST_SCREEN_ID); + + int maxItemsOnPage = SAME_PAGE_MAX_ROWS * mLauncher.getDeviceProfile().inv.numColumns + + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0); + if (page.getShortcutsAndWidgets().getChildCount() > maxItemsOnPage + || !page.makeSpaceForHotseatMigration(false)) { + mMigrationMode = MIGRATE_NO_MIGRATE; + mEduContent.setText(R.string.hotseat_edu_message_no_migrate); + mEduHeading.setText(R.string.hotseat_edu_title_no_migrate); + mDismissBtn.setVisibility(GONE); + } + } + + /** + * Opens User education dialog with a list of suggested apps + */ + public void show(List predictions) { + if (getParent() != null + || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons + || mHotseatEduController == null) { + return; + } + attachToContainer(); + logOnBoardingSeen(); + animateOpen(); + populatePreview(predictions); + } + /** * Factory method for HotseatPredictionUserEdu dialog */ diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 45a62ab151..90d42456d2 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -66,27 +66,32 @@ Close + - Easily access your most-used apps + Get app suggestions based on your routines - Pixel suggests your favorite apps based on your routines. Tap to learn more. - - Suggested apps replace the bottom row of apps - - Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row. + Tap to set up + + + + Suggested apps replace the bottom row of apps + Your hotseat items will be moved up on the homescreen + Your hotseat items will be moved to the last page of your workspace + + + + Suggested apps will be found at the bottom row of your home screen + Drag one or many apps off the bottom row of home screen to see app suggestions + - Bottom row of apps moved to last screen + Bottom row of apps moved up. + Bottom row of apps moved to last page. Bottom row won\'t be replaced. Manually drag apps for predictions. - Turn On + Got it - No thanks - - Your hotseat just got smarter - - Tap here to set it up - + No thanks diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 8718820bd2..e3eb38792a 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -2783,6 +2783,26 @@ public class CellLayout extends ViewGroup implements Transposable { return false; } + /** + * Finds solution to accept hotseat migration to cell layout. commits solution if commitConfig + */ + public boolean makeSpaceForHotseatMigration(boolean commitConfig) { + if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) return false; + int[] cellPoint = new int[2]; + int[] directionVector = new int[]{0, -1}; + cellToPoint(0, mCountY, cellPoint); + ItemConfiguration configuration = new ItemConfiguration(); + if (findReorderSolution(cellPoint[0], cellPoint[1], mCountX, 1, mCountX, 1, + directionVector, null, false, configuration).isSolution) { + if (commitConfig) { + copySolutionToTempState(configuration, null); + commitTempPlacement(); + } + return true; + } + return false; + } + public boolean isRegionVacant(int x, int y, int spanX, int spanY) { return mOccupied.isRegionVacant(x, y, spanX, spanY); } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 3d8a9d7bbf..96903e416d 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -113,6 +113,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag( "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps"); + public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = new DeviceFlag( + "HOTSEAT_MIGRATE_NEW_PAGE", true, + "Migrates hotseat to a new workspace page instead of same page"); + public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag( "ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache"); From b08f2d722238a919e46286d21d35d1bb72384cd8 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 10 Feb 2020 14:35:35 -0800 Subject: [PATCH 02/39] Indicate PiP animation type from Launcher to SysUI Also, need callback from SysUI to Launcher once the pip animation is started to keep back compatibility. Bug: 146594635 Test: manually Merged-In: I617a17fd5f6049d9f1d49f9f834623a2bb49f6ca Change-Id: I617a17fd5f6049d9f1d49f9f834623a2bb49f6ca (cherry picked from commit a1a91b0c5044b73433705584512794c408476d64) --- .../quickstep/FallbackSwipeHandler.java | 2 ++ .../quickstep/LauncherSwipeHandler.java | 2 ++ .../android/quickstep/views/RecentsView.java | 8 +++++ .../com/android/quickstep/SystemUiProxy.java | 33 +++++++++++++++++-- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java index 700feef790..cd001a128b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java @@ -332,6 +332,8 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler { if (!mTouchedHomeDuringTransition) { // If the user hasn't interacted with the screen during the transition, diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java index 345a147ca5..646d01fb8f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java @@ -699,6 +699,8 @@ public class LauncherSwipeHandler switch (mGestureState.getEndTarget()) { case HOME: mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); + // Notify swipe-to-home (recents animation) is finished + SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished(); break; case RECENTS: mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 007493d0de..3046564a70 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -108,11 +108,13 @@ import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskVisualsChangeListener; +import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; import com.android.quickstep.ViewUtils; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.LayoutUtils; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -264,7 +266,10 @@ public abstract class RecentsView extends PagedView impl } }); } + }; + private final IPinnedStackAnimationListener mIPinnedStackAnimationListener = + new IPinnedStackAnimationListener.Stub() { @Override public void onPinnedStackAnimationStarted() { // Needed for activities that auto-enter PiP, which will not trigger a remote @@ -446,6 +451,8 @@ public abstract class RecentsView extends PagedView impl mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this); RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this); mIdp.addOnChangeListener(this); + SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener( + mIPinnedStackAnimationListener); } @Override @@ -458,6 +465,7 @@ public abstract class RecentsView extends PagedView impl mSyncTransactionApplier = null; RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this); mIdp.removeOnChangeListener(this); + SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null); } @Override diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 458d6a9553..eb60601c44 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; /** @@ -268,9 +269,7 @@ public class SystemUiProxy implements ISystemUiProxy { } } - /** - * See SharedApiCompat#setShelfHeight() - */ + @Override public void setShelfHeight(boolean visible, int shelfHeight) { boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight; if (mSystemUiProxy != null && changed) { @@ -306,4 +305,32 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + /** + * Notifies that swipe-to-home action is finished. + */ + @Override + public void notifySwipeToHomeFinished() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.notifySwipeToHomeFinished(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setPinnedStackAnimationType", e); + } + } + } + + /** + * Sets listener to get pinned stack animation callbacks. + */ + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.setPinnedStackAnimationListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setPinnedStackAnimationListener", e); + } + } + } } From 18b9f396d6f48b35a0070aef8cb93bb92f903ca8 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 28 Feb 2020 12:08:19 -0800 Subject: [PATCH 03/39] Not failing when getSystemAnomaly fails It's a supplemental method, and mysterious glitches that happen during its work shouldn't cause the whole test to crash. Example: java.lang.NullPointerException: Attempt to read from field 'com.android.server.appwidget.AppWidgetServiceImpl$ProviderId com.android.server.appwidget.AppWidgetServiceImpl$Provider.id' on a null object reference at android.os.Parcel.createExceptionOrNull(Parcel.java:2291) at android.os.Parcel.createException(Parcel.java:2269) at android.os.Parcel.readException(Parcel.java:2252) at android.os.Parcel.readException(Parcel.java:2194) at android.accessibilityservice.IAccessibilityServiceConnection$Stub$Proxy.findAccessibilityNodeInfoByAccessibilityId(IAccessibilityServiceConnection.java:875) at android.view.accessibility.AccessibilityInteractionClient.findAccessibilityNodeInfoByAccessibilityId(AccessibilityInteractionClient.java:430) at android.view.accessibility.AccessibilityInteractionClient.getRootInActiveWindow(AccessibilityInteractionClient.java:214) at android.app.UiAutomation.getRootInActiveWindow(UiAutomation.java:607) at androidx.test.uiautomator.UiDevice.getWindowRoots(UiDevice.java:1102) at androidx.test.uiautomator.UiDevice.findObject(UiDevice.java:150) at com.android.launcher3.tapl.LauncherInstrumentation.getSystemAnomalyMessage(LauncherInstrumentation.java:334) at com.android.launcher3.tapl.LauncherInstrumentation.getAnomalyMessage(LauncherInstrumentation.java:359) at com.android.launcher3.tapl.LauncherInstrumentation.checkForAnomaly(LauncherInstrumentation.java:369) at com.android.launcher3.tapl.LauncherInstrumentation.lambda$eventsCheck$11(LauncherInstrumentation.java:1269) at com.android.launcher3.tapl.LauncherInstrumentation.lambda$eventsCheck$11$LauncherInstrumentation(Unknown Source:0) at com.android.launcher3.tapl.-$$Lambda$LauncherInstrumentation$3iFY1gd72Tm3mPf31PMij-eBaBk.close(Unknown Source:2) at com.android.launcher3.tapl.AddToHomeScreenPrompt.addAutomatically(AddToHomeScreenPrompt.java:53) at com.android.launcher3.ui.widget.RequestPinItemTest.runTest(RequestPinItemTest.java:163) at com.android.launcher3.ui.widget.RequestPinItemTest.testPinWidgetNoConfig_customPreview(RequestPinItemTest.java:95) at java.lang.reflect.Method.invoke(Native Method) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:52) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:148) at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:142) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.lang.Thread.run(Thread.java:923) Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.appwidget.AppWidgetServiceImpl$AppWidgetManagerLocal.getHostedWidgetPackages(AppWidgetServiceImpl.java:4887) at com.android.server.accessibility.AccessibilitySecurityPolicy.computeValidReportedPackages(AccessibilitySecurityPolicy.java:229) at com.android.server.accessibility.AbstractAccessibilityServiceConnection.findAccessibilityNodeInfoByAccessibilityId(AbstractAccessibilityServiceConnection.java:621) at android.accessibilityservice.IAccessibilityServiceConnection$Stub.onTransact(IAccessibilityServiceConnection.java:372) at android.os.Binder.execTransactInternal(Binder.java:1138) Change-Id: If7ae23d07b69d524c3f300607f0866cce6405416 --- .../tapl/LauncherInstrumentation.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 47a60cfcbe..f80fee0859 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -333,21 +333,25 @@ public final class LauncherInstrumentation { } private String getSystemAnomalyMessage() { - UiObject2 object = mDevice.findObject(By.res("android", "alertTitle")); - if (object != null) { - return "System alert popup is visible: " + object.getText(); + try { + UiObject2 object = mDevice.findObject(By.res("android", "alertTitle")); + if (object != null) { + return "System alert popup is visible: " + object.getText(); + } + + object = mDevice.findObject(By.res("android", "message")); + if (object != null) { + return "Message popup by " + object.getApplicationPackage() + " is visible: " + + object.getText(); + } + + if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked"; + + if (!mDevice.hasObject(By.textStartsWith(""))) return "Screen is empty"; + } catch (Throwable e) { + Log.w(TAG, "getSystemAnomalyMessage failed", e); } - object = mDevice.findObject(By.res("android", "message")); - if (object != null) { - return "Message popup by " + object.getApplicationPackage() + " is visible: " - + object.getText(); - } - - if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked"; - - if (!mDevice.hasObject(By.textStartsWith(""))) return "Screen is empty"; - return null; } From 5667f6fbd3bb9c81f762c3bd288404d8d8ba52a8 Mon Sep 17 00:00:00 2001 From: Becky Qiu Date: Fri, 28 Feb 2020 21:59:58 +0000 Subject: [PATCH 04/39] Revert "Revert "[Overview Actions] Disable swiping for TaskView if it's in select mode."" This reverts commit 7ff297a81faee4815a1feba6d64e6dd464bb7f0d. Reason for revert: Verified this was not the cl broke post submit. Change-Id: Id992c6937a4230b945436ddb868204366ffd045e --- .../touchcontrollers/TaskViewTouchController.java | 9 +++++++++ .../src/com/android/quickstep/TaskOverlayFactory.java | 7 +++++++ .../src/com/android/quickstep/views/RecentsView.java | 7 +++++++ .../src/com/android/quickstep/views/TaskView.java | 4 ++++ 4 files changed, 27 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 947a8614d2..f79ad25571 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -105,6 +105,10 @@ public abstract class TaskViewTouchController @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if ((ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) + && mCurrentAnimation == null) { + clearState(); + } if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = !canInterceptTouch(); if (mNoIntercept) { @@ -125,6 +129,11 @@ public abstract class TaskViewTouchController TaskView view = mRecentsView.getTaskViewAt(i); if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer() .isEventOverView(view, ev)) { + // Disable swiping up and down if the task overlay is modal. + if (view.isTaskOverlayModal()) { + mTaskBeingDragged = null; + break; + } mTaskBeingDragged = view; if (!SysUINavigationMode.getMode(mActivity).hasGestures) { // Don't allow swipe down to open if we don't support swipe up diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java index ec7cddfe07..eb3b190110 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java @@ -87,5 +87,12 @@ public class TaskOverlayFactory implements ResourceBasedOverride { * Called when the overlay is no longer used. */ public void reset() { } + + /** + * Whether the overlay is modal, which means only tapping is enabled, but no swiping. + */ + public boolean isOverlayModal() { + return false; + } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index e34e74c7d7..e668ca0801 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -565,6 +565,13 @@ public abstract class RecentsView extends PagedView impl return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev); } + @Override + protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { + // Enables swiping to the left or right only if the task overlay is not modal. + if (getCurrentPageTaskView() == null || !getCurrentPageTaskView().isTaskOverlayModal()) { + super.determineScrollingStart(ev, touchSlopScale); + } + } protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) { return true; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index dce92ff390..79b9a9d833 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -251,6 +251,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } + public boolean isTaskOverlayModal() { + return mSnapshotView.getTaskOverlay().isOverlayModal(); + } + public TaskMenuView getMenuView() { return mMenuView; } From 0ac58e375d6be89e9d6b91a6576e452b50f53ecd Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 28 Feb 2020 14:39:09 -0800 Subject: [PATCH 05/39] Updating owners to include immediate Launcher team Change-Id: I17db2a06fa7bf34216538de0f8ca3771840e7e6f --- OWNERS | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/OWNERS b/OWNERS index 7340e84883..bf4cd0bb6b 100644 --- a/OWNERS +++ b/OWNERS @@ -8,9 +8,26 @@ adamcohen@google.com hyunyoungs@google.com mrcasey@google.com sunnygoyal@google.com +awickham@google.com twickham@google.com winsonc@google.com zakcohen@google.com +santie@google.com +vadimt@google.com +mett@google.com +jonmiranda@google.com +pinyaoting@google.com +sfufa@google.com +gwasserman@google.com +jamesoleary@google.com +joshtrask@google.com +mrenouf@google.com +mkephart@google.com +hwwang@google.com +tracyzhou@google.com +peanutbutter@google.com +xuqiu@google.com +sreyasr@google.com per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com From 908e9bb1cca50c76285676004a4c2f2cd6fae38f Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 28 Feb 2020 18:02:30 -0800 Subject: [PATCH 06/39] Fix remaining cause of a leak in SecondaryDropTarget Bug: 139137636 Change-Id: Ib94c5804616910ee562d63c022579042bb1727f5 --- src/com/android/launcher3/SecondaryDropTarget.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java index 114c49118f..184113413b 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -67,7 +67,6 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList super(context, attrs, defStyle); mCacheExpireAlarm = new Alarm(); - mCacheExpireAlarm.setOnAlarmListener(this); } @Override @@ -75,6 +74,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList super.onAttachedToWindow(); if (mHadPendingAlarm) { mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT); + mCacheExpireAlarm.setOnAlarmListener(this); mHadPendingAlarm = false; } } @@ -84,6 +84,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList super.onDetachedFromWindow(); if (mCacheExpireAlarm.alarmPending()) { mCacheExpireAlarm.cancelAlarm(); + mCacheExpireAlarm.setOnAlarmListener(null); mHadPendingAlarm = true; } } @@ -168,6 +169,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList } // Cancel any pending alarm and set cache expiry after some time mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT); + mCacheExpireAlarm.setOnAlarmListener(this); if (uninstallDisabled) { return false; } From bd2254668341fff85ab871a43f469c5eab36060a Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Mon, 24 Feb 2020 13:50:29 -0800 Subject: [PATCH 07/39] Render user's actual workspace in ThemePicker preview (Part 6) Didn't test out widgets in Part 5. So in this change, - Added some widget classes to the PreviewContext WHITELIST - Manually update widget model after loading workspace since we do not attach widget listeners to the LauncherAppModel for preview. Change-Id: I0a555b2319b2e91432dbd58289ddb66aca1384df --- .../graphics/LauncherPreviewRenderer.java | 24 +++++++++++++++++-- .../android/launcher3/model/LoaderTask.java | 2 +- .../widget/custom/CustomWidgetManager.java | 6 +++++ .../android/launcher3/model/WidgetsModel.java | 4 ++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 0927b26505..02e98e80df 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -87,9 +87,11 @@ import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.UserCache; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; +import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; import java.util.Arrays; @@ -128,7 +130,8 @@ public class LauncherPreviewRenderer implements Callable { private static final Set WHITELIST = new HashSet<>( Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE, - LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE)); + LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE, + CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE)); private final InvariantDeviceProfile mIdp; private final Map mObjectMap = new HashMap<>(); @@ -145,6 +148,14 @@ public class LauncherPreviewRenderer implements Callable { return this; } + public void onDestroy() { + CustomWidgetManager customWidgetManager = (CustomWidgetManager) mObjectMap.get( + CustomWidgetManager.INSTANCE); + if (customWidgetManager != null) { + customWidgetManager.onDestroy(); + } + } + /** * Find a cached object from mObjectMap if we have already created one. If not, generate * an object using the provider. @@ -349,6 +360,9 @@ public class LauncherPreviewRenderer implements Callable { private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) { WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName( info.providerName); + if (widgetItem == null) { + return; + } AppWidgetHostView view = new AppWidgetHostView(mContext); view.setAppWidget(-1, widgetItem.widgetInfo); view.updateAppWidget(null); @@ -385,9 +399,11 @@ public class LauncherPreviewRenderer implements Callable { } WorkspaceFetcher fetcher; + PreviewContext previewContext = null; if (needsToMigrate && success) { + previewContext = new PreviewContext(mContext, mIdp); LauncherAppState appForPreview = new LauncherAppState( - new PreviewContext(mContext, mIdp), null /* iconCacheFileName */); + previewContext, null /* iconCacheFileName */); fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview); MODEL_EXECUTOR.execute(fetcher); } else { @@ -396,6 +412,9 @@ public class LauncherPreviewRenderer implements Callable { (LauncherModel.ModelUpdateTask) fetcher); } WorkspaceResult workspaceResult = fetcher.get(); + if (previewContext != null) { + previewContext.onDestroy(); + } if (workspaceResult == null) { return; @@ -557,6 +576,7 @@ public class LauncherPreviewRenderer implements Callable { public WorkspaceResult call() throws Exception { List allShortcuts = new ArrayList<>(); loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI); + mBgDataModel.widgetsModel.update(mApp, null); return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets, mBgDataModel.widgetsModel); } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index fbf01fc777..62904ae4a4 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -104,7 +104,7 @@ import java.util.concurrent.CancellationException; public class LoaderTask implements Runnable { private static final String TAG = "LoaderTask"; - private final LauncherAppState mApp; + protected final LauncherAppState mApp; private final AllAppsList mBgAllAppsList; protected final BgDataModel mBgDataModel; diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java index 2aed9369e3..0ea7d854ee 100644 --- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java +++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java @@ -51,6 +51,7 @@ public class CustomWidgetManager implements PluginListener { public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(CustomWidgetManager::new); + private final Context mContext; /** * auto provider Id is an ever-increasing number that serves as the providerId whenever a new * custom widget has been connected. @@ -62,6 +63,7 @@ public class CustomWidgetManager implements PluginListener { private Consumer mWidgetRefreshCallback; private CustomWidgetManager(Context context) { + mContext = context; mPlugins = new SparseArray<>(); mCustomWidgets = new ArrayList<>(); mWidgetsIdMap = new SparseArray<>(); @@ -69,6 +71,10 @@ public class CustomWidgetManager implements PluginListener { .addPluginListener(this, CustomWidgetPlugin.class, true); } + public void onDestroy() { + PluginManagerWrapper.INSTANCE.get(mContext).removePluginListener(this); + } + @Override public void onPluginConnected(CustomWidgetPlugin plugin, Context context) { mPlugins.put(mAutoProviderId, plugin); diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java index ae32692809..0b99e7a898 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java @@ -249,6 +249,10 @@ public class WidgetsModel { ComponentName providerName) { ArrayList widgetsList = mWidgetsList.get( new PackageItemInfo(providerName.getPackageName())); + if (widgetsList == null) { + return null; + } + for (WidgetItem item : widgetsList) { if (item.componentName.equals(providerName)) { return item; From 424e7fdf6b8681b365b87053f36ee3ce64221ff7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 2 Mar 2020 13:32:25 -0800 Subject: [PATCH 08/39] Fixing unregistering mOtherHomeAppUpdateReceiver twice mUpdateRegisteredPackage was not cleared when unregistering mOtherHomeAppUpdateReceiver Bug: 150543327 Change-Id: I1058c0262588cbeb1d6c61299cb4d4c38d622d98 --- .../quickstep/OverviewComponentObserver.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index 85ef4c6a91..866836ee72 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -33,6 +33,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.util.SparseIntArray; +import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.systemui.shared.system.PackageManagerWrapper; import java.io.PrintWriter; @@ -44,18 +45,11 @@ import java.util.Objects; * and provide callers the relevant classes. */ public final class OverviewComponentObserver { - private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - updateOverviewTargets(); - } - }; - private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - updateOverviewTargets(); - } - }; + private final BroadcastReceiver mUserPreferenceChangeReceiver = + new SimpleBroadcastReceiver(this::updateOverviewTargets); + private final BroadcastReceiver mOtherHomeAppUpdateReceiver = + new SimpleBroadcastReceiver(this::updateOverviewTargets); + private final Context mContext; private final RecentsAnimationDeviceState mDeviceState; private final Intent mCurrentHomeIntent; @@ -106,6 +100,10 @@ public final class OverviewComponentObserver { } } + private void updateOverviewTargets(Intent unused) { + updateOverviewTargets(); + } + /** * Update overview intent and {@link BaseActivityInterface} based off the current launcher home * component. @@ -131,11 +129,8 @@ public final class OverviewComponentObserver { mOverviewIntent = mMyHomeIntent; mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent()); - if (mUpdateRegisteredPackage != null) { - // Remove any update listener as we don't care about other packages. - mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); - mUpdateRegisteredPackage = null; - } + // Remove any update listener as we don't care about other packages. + unregisterOtherHomeAppUpdateReceiver(); } else { // The default home app is a different launcher. Use the fallback Overview instead. @@ -149,13 +144,9 @@ public final class OverviewComponentObserver { // Listen for package updates of this app (and remove any previously attached // package listener). if (defaultHome == null) { - if (mUpdateRegisteredPackage != null) { - mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); - } + unregisterOtherHomeAppUpdateReceiver(); } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) { - if (mUpdateRegisteredPackage != null) { - mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); - } + unregisterOtherHomeAppUpdateReceiver(); mUpdateRegisteredPackage = defaultHome.getPackageName(); mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter( @@ -170,7 +161,10 @@ public final class OverviewComponentObserver { */ public void onDestroy() { mContext.unregisterReceiver(mUserPreferenceChangeReceiver); + unregisterOtherHomeAppUpdateReceiver(); + } + private void unregisterOtherHomeAppUpdateReceiver() { if (mUpdateRegisteredPackage != null) { mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); mUpdateRegisteredPackage = null; From c8bae0ab714f0732543a075569579cf3ec027372 Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 2 Mar 2020 13:35:55 -0800 Subject: [PATCH 09/39] Disabling failing test Change-Id: I5989ce406c66834b77ff4bd073cd3ad92df9fda2 --- .../src/com/android/quickstep/DigitalWellBeingToastTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java index 3e84a76695..9a053f2f62 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java @@ -3,6 +3,8 @@ package com.android.quickstep; import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; +import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -16,6 +18,7 @@ import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.Launcher; +import com.android.launcher3.util.rule.TestStabilityRule; import com.android.quickstep.views.DigitalWellBeingToast; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -32,6 +35,8 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest { resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); @Test + // b/150303529 + @TestStabilityRule.Stability(flavors = UNBUNDLED_POSTSUBMIT | PLATFORM_POSTSUBMIT) public void testToast() throws Exception { startAppFast(CALCULATOR_PACKAGE); From 20d2683d92cd0f6943b700608adc678293d94c91 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Mon, 2 Mar 2020 15:21:53 -0800 Subject: [PATCH 10/39] Enable Grid Preview flag Bug: 144052839 Test: grid preview with v1 algorithm Change-Id: Ic788995b899bf25c5420120a95e71e4a565ba22a --- src/com/android/launcher3/config/FeatureFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index d7498a5c11..f6487619d0 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -120,7 +120,7 @@ public final class FeatureFlags { "MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm"); public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag( - "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false, + "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker"); public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag( From 1901939be7355b0265f09389f92689077367769f Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Fri, 18 Oct 2019 14:46:11 -0700 Subject: [PATCH 11/39] Modify input points to register in correct swipe touch regions While the recents task list is frozen, launcher will continue to listen for touch regions in all orientations of apps it has seen while frozen. Ex. Start w/ portrait app, then quick switch to 90-deg landscape app, touches in the navbars of both those orientations will be treated as valid. Once the task list unfreezes, only the orientation that is currently displayed will have a valid navbar touch region fixes: 140116135 Test: tested manually in all 4 orientations with quickswitch, swipe to recents and swipe to home Attempted to write unit tests, but only basic ones since there are some limitations on the MotionEvent API for mocking Change-Id: I8d3751571a939497b44e0dd249a0248299ba5ceb --- .../quickstep/LauncherSwipeHandler.java | 10 + .../quickstep/TouchInteractionService.java | 4 + .../OtherActivityInputConsumer.java | 1 - .../OrientationTouchTransformerTest.java | 194 +++++++++++ .../OrientationTouchTransformer.java | 329 ++++++++++++++++++ .../RecentsAnimationDeviceState.java | 100 +++--- .../com/android/quickstep/SystemUiProxy.java | 22 ++ src/com/android/launcher3/ResourceUtils.java | 4 +- .../launcher3/util/DefaultDisplay.java | 14 + 9 files changed, 629 insertions(+), 49 deletions(-) create mode 100644 quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java create mode 100644 quickstep/src/com/android/quickstep/OrientationTouchTransformer.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java index 646d01fb8f..e0b8a3743c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java @@ -770,6 +770,16 @@ public class LauncherSwipeHandler } } + if (endTarget == NEW_TASK) { + SystemUiProxy.INSTANCE.get(mContext).onQuickSwitchToNewTask(); + } + + if (endTarget == RECENTS || endTarget == HOME) { + // Since we're now done quickStepping, we want to only listen for touch events + // for the main orientation's nav bar, instead of multiple + mDeviceState.enableMultipleRegions(false); + } + if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) { return LAST_TASK; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 28e8fb6604..e1b5df0bca 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -449,6 +449,8 @@ public class TouchInteractionService extends Service implements PluginListener 0); + } + + @Test + public void disabledMultipeRegions_shouldOverrideFirstRegion() { + mTouchTransformer.createOrAddTouchRegion(mInfo); + DefaultDisplay.Info info2 = createDisplayInfo(Surface.ROTATION_90); + mTouchTransformer.createOrAddTouchRegion(info2); + + float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1; + MotionEvent inOldRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y); + mTouchTransformer.transform(inOldRegion); + assertFalse(mTouchTransformer.touchInValidSwipeRegions(inOldRegion.getX(), inOldRegion.getY())); + + // Override region + mTouchTransformer.createOrAddTouchRegion(mInfo); + inOldRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y); + mTouchTransformer.transform(inOldRegion); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inOldRegion.getX(), inOldRegion.getY())); + } + + @Test + public void allowMultipeRegions_shouldOverrideFirstRegion() { + DefaultDisplay.Info info2 = createDisplayInfo(Surface.ROTATION_90); + mTouchTransformer.createOrAddTouchRegion(info2); + // We have to add 0 rotation second so that gets set as the current rotation, otherwise + // matrix transform will fail (tests only work in Portrait at the moment) + mTouchTransformer.enableMultipleRegions(true, mInfo); + mTouchTransformer.createOrAddTouchRegion(mInfo); + + float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1; + MotionEvent inNewRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y); + mTouchTransformer.transform(inNewRegion); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inNewRegion.getX(), inNewRegion.getY())); + } + + @Test + public void applyTransform_taskNotFrozen_notInRegion() { + mTouchTransformer.createOrAddTouchRegion(mInfo); + MotionEvent outOfRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, 100); + mTouchTransformer.transform(outOfRegion); + assertFalse(mTouchTransformer.touchInValidSwipeRegions(outOfRegion.getX(), outOfRegion.getY())); + } + + @Test + public void applyTransform_taskFrozen_noRotate_outOfRegion() { + mTouchTransformer.createOrAddTouchRegion(mInfo); + mTouchTransformer.enableMultipleRegions(true, mInfo); + MotionEvent outOfRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, 100); + mTouchTransformer.transform(outOfRegion); + assertFalse(mTouchTransformer.touchInValidSwipeRegions(outOfRegion.getX(), outOfRegion.getY())); + } + + @Test + public void applyTransform_taskFrozen_noRotate_inRegion() { + mTouchTransformer.createOrAddTouchRegion(mInfo); + mTouchTransformer.enableMultipleRegions(true, mInfo); + float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1; + MotionEvent inRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y); + mTouchTransformer.transform(inRegion); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion.getX(), inRegion.getY())); + } + + @Test + public void applyTransform_taskNotFrozen_noRotate_inDefaultRegion() { + mTouchTransformer.createOrAddTouchRegion(mInfo); + float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1; + MotionEvent inRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y); + mTouchTransformer.transform(inRegion); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion.getX(), inRegion.getY())); + } + + @Test + public void applyTransform_taskNotFrozen_90Rotate_inRegion() { + mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90)); + float y = generateTouchRegionHeight(Surface.ROTATION_90) + 1; + MotionEvent inRegion = generateMotionEvent(MotionEvent.ACTION_DOWN, 100, y); + mTouchTransformer.transform(inRegion); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion.getX(), inRegion.getY())); + } + + @Test + @Ignore("There's too much that goes into needing to mock a real motion event so the " + + "transforms in native code get applied correctly. Once that happens then maybe we can" + + " write slightly more complex unit tests") + public void applyTransform_taskNotFrozen_90Rotate_inTwoRegions() { + mTouchTransformer.createOrAddTouchRegion(mInfo); + mTouchTransformer.enableMultipleRegions(true, mInfo); + mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90)); + // Landscape point + float y1 = generateTouchRegionHeight(Surface.ROTATION_90) + 1; + MotionEvent inRegion1_down = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, y1); + MotionEvent inRegion1_up = generateMotionEvent(MotionEvent.ACTION_UP, 10, y1); + // Portrait point in landscape orientation axis + MotionEvent inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 10); + mTouchTransformer.transform(inRegion1_down); + mTouchTransformer.transform(inRegion2); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion1_down.getX(), inRegion1_down.getY())); + // We only process one gesture region until we see a MotionEvent.ACTION_UP + assertFalse(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY())); + + mTouchTransformer.transform(inRegion1_up); + + // Set the new region with this MotionEvent.ACTION_DOWN + inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 370); + mTouchTransformer.transform(inRegion2); + assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY())); + } + + private DefaultDisplay.Info createDisplayInfo(int rotation) { + Point p = new Point(SIZE_WIDTH, SIZE_HEIGHT); + if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { + p = new Point(SIZE_HEIGHT, SIZE_WIDTH); + } + return new DefaultDisplay.Info(0, rotation, 0, p, p, p, null); + } + + private float generateTouchRegionHeight(int rotation) { + float height = SIZE_HEIGHT; + if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { + height = SIZE_WIDTH; + } + return height - ResourceUtils.DEFAULT_NAVBAR_VALUE * DENSITY_DISPLAY_METRICS; + } + + private MotionEvent generateMotionEvent(int motionAction, float x, float y) { + return MotionEvent.obtain(0, 0, motionAction, x, y, 0); + } +} diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java new file mode 100644 index 0000000000..3dae510d4a --- /dev/null +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep; + +import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_POINTER_DOWN; +import static android.view.MotionEvent.ACTION_UP; + +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.RectF; +import android.util.Log; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.Surface; + +import com.android.launcher3.R; +import com.android.launcher3.ResourceUtils; +import com.android.launcher3.util.DefaultDisplay; + +/** + * Maintains state for supporting nav bars and tracking their gestures in multiple orientations. + * See {@link OrientationRectF#applyTransform(MotionEvent, boolean)} for transformation of + * MotionEvents from one orientation's coordinate space to another's. + * + * This class only supports single touch/pointer gesture tracking for touches started in a supported + * nav bar region. + */ +class OrientationTouchTransformer { + + private static final String TAG = "OrientationTouchTransformer"; + private static final boolean DEBUG = true; + private static final int MAX_ORIENTATIONS = 4; + + private SparseArray mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS); + private final RectF mAssistantLeftRegion = new RectF(); + private final RectF mAssistantRightRegion = new RectF(); + private int mCurrentRotation; + private boolean mEnableMultipleRegions; + private Resources mResources; + private OrientationRectF mLastRectTouched; + private SysUINavigationMode.Mode mMode; + private QuickStepContractInfo mContractInfo; + private int mQuickStepStartingRotation = -1; + + /** For testability */ + interface QuickStepContractInfo { + float getWindowCornerRadius(); + } + + OrientationTouchTransformer(Resources resources, SysUINavigationMode.Mode mode, + QuickStepContractInfo contractInfo) { + mResources = resources; + mMode = mode; + mContractInfo = contractInfo; + } + + void setNavigationMode(SysUINavigationMode.Mode newMode) { + this.mMode = newMode; + } + + /** + * Sets the current nav bar region to listen to events for as determined by + * {@param info}. If multiple nav bar regions are enabled, then this region will be added + * alongside other regions. + * Ok to call multiple times + * + * @see #enableMultipleRegions(boolean, DefaultDisplay.Info) + */ + void createOrAddTouchRegion(DefaultDisplay.Info info) { + mCurrentRotation = info.rotation; + if (mQuickStepStartingRotation > -1 && mCurrentRotation == mQuickStepStartingRotation) { + // Ignore nav bars in other rotations except for the one we started out in + resetSwipeRegions(info); + return; + } + + OrientationRectF region = mSwipeTouchRegions.get(mCurrentRotation); + if (region != null) { + return; + } + + if (mEnableMultipleRegions) { + mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(info)); + } else { + resetSwipeRegions(info); + } + } + + /** + * Call when we want to start tracking nav bar touch regions in multiple orientations. + * ALSO, you BETTER call this with {@param enableMultipleRegions} set to false once you're done. + * + * @param enableMultipleRegions Set to true to start tracking multiple nav bar regions + * @param info The current displayInfo + */ + void enableMultipleRegions(boolean enableMultipleRegions, DefaultDisplay.Info info) { + mEnableMultipleRegions = enableMultipleRegions; + if (!enableMultipleRegions) { + mQuickStepStartingRotation = -1; + resetSwipeRegions(info); + } else { + if (mQuickStepStartingRotation < 0) { + mQuickStepStartingRotation = mLastRectTouched.mRotation; + } + } + } + + /** + * Only saves the swipe region represented by {@param region}, clears the + * rest from {@link #mSwipeTouchRegions} + * To be called whenever we want to stop tracking more than one swipe region. + * Ok to call multiple times. + */ + private void resetSwipeRegions(DefaultDisplay.Info region) { + if (DEBUG) { + Log.d(TAG, "clearing all regions except rotation: " + mCurrentRotation); + } + + mCurrentRotation = region.rotation; + OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentRotation); + mSwipeTouchRegions.clear(); + mSwipeTouchRegions.put(mCurrentRotation, + regionToKeep != null ? regionToKeep : createRegionForDisplay(region)); + } + + private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) { + if (DEBUG) { + Log.d(TAG, "creating rotation region for: " + mCurrentRotation); + } + + Point size = display.realSize; + int rotation = display.rotation; + OrientationRectF orientationRectF = + new OrientationRectF(0, 0, size.x, size.y, rotation, + size.y, size.x); + if (mMode == SysUINavigationMode.Mode.NO_BUTTON) { + int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + orientationRectF.top = orientationRectF.bottom - touchHeight; + + final int assistantWidth = mResources + .getDimensionPixelSize(R.dimen.gestures_assistant_width); + final float assistantHeight = Math.max(touchHeight, + mContractInfo.getWindowCornerRadius()); + mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = orientationRectF.bottom; + mAssistantLeftRegion.top = mAssistantRightRegion.top = + orientationRectF.bottom - assistantHeight; + + mAssistantLeftRegion.left = 0; + mAssistantLeftRegion.right = assistantWidth; + + mAssistantRightRegion.right = orientationRectF.right; + mAssistantRightRegion.left = orientationRectF.right - assistantWidth; + } else { + mAssistantLeftRegion.setEmpty(); + mAssistantRightRegion.setEmpty(); + switch (rotation) { + case Surface.ROTATION_90: + orientationRectF.left = orientationRectF.right + - getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE); + break; + case Surface.ROTATION_270: + orientationRectF.right = orientationRectF.left + + getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE); + break; + default: + orientationRectF.top = orientationRectF.bottom + - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + } + } + + return orientationRectF; + } + + boolean touchInAssistantRegion(MotionEvent ev) { + return mAssistantLeftRegion.contains(ev.getX(), ev.getY()) + || mAssistantRightRegion.contains(ev.getX(), ev.getY()); + + } + + private int getNavbarSize(String resName) { + return ResourceUtils.getNavbarSize(resName, mResources); + } + + boolean touchInValidSwipeRegions(float x, float y) { + if (mLastRectTouched != null) { + return mLastRectTouched.contains(x, y); + } + return false; + } + + public void transform(MotionEvent event) { + int eventAction = event.getActionMasked(); + switch (eventAction) { + case ACTION_MOVE: { + if (mLastRectTouched == null) { + return; + } + mLastRectTouched.applyTransform(event, true); + break; + } + case ACTION_CANCEL: + case ACTION_UP: { + if (mLastRectTouched == null) { + return; + } + mLastRectTouched.applyTransform(event, true); + mLastRectTouched = null; + break; + } + case ACTION_POINTER_DOWN: + case ACTION_DOWN: { + if (mLastRectTouched != null) { + return; + } + + for (int i = 0; i < MAX_ORIENTATIONS; i++) { + OrientationRectF rect = mSwipeTouchRegions.get(i); + if (rect == null) { + continue; + } + if (rect.applyTransform(event, false)) { + mLastRectTouched = rect; + if (DEBUG) { + Log.d(TAG, "set active region: " + rect); + } + return; + } + } + break; + } + } + } + + private class OrientationRectF extends RectF { + + /** + * Delta to subtract width and height by because if we report the translated touch + * bounds as the width and height, calling {@link RectF#contains(float, float)} will + * be false + */ + private float maxDelta = 0.001f; + + private int mRotation; + private float mHeight; + private float mWidth; + + OrientationRectF(float left, float top, float right, float bottom, int rotation, + float height, float width) { + super(left, top, right, bottom); + this.mRotation = rotation; + mHeight = height - maxDelta; + mWidth = width - maxDelta; + } + + @Override + public String toString() { + String s = super.toString(); + s += " rotation: " + mRotation; + return s; + } + + boolean applyTransform(MotionEvent event, boolean forceTransform) { + MotionEvent tmp = MotionEvent.obtain(event); + Matrix outMatrix = new Matrix(); + int delta = deltaRotation(mCurrentRotation, mRotation); + switch (delta) { + case Surface.ROTATION_0: + outMatrix.reset(); + break; + case Surface.ROTATION_90: + outMatrix.setRotate(270); + outMatrix.postTranslate(0, mHeight); + break; + case Surface.ROTATION_180: + outMatrix.setRotate(180); + outMatrix.postTranslate(mHeight, mWidth); + break; + case Surface.ROTATION_270: + outMatrix.setRotate(90); + outMatrix.postTranslate(mWidth, 0); + break; + } + + tmp.transform(outMatrix); + if (DEBUG) { + Log.d(TAG, "original: " + event.getX() + ", " + event.getY() + + " new: " + tmp.getX() + ", " + tmp.getY() + + " rect: " + this + " forceTransform: " + forceTransform + + " contains: " + contains(tmp.getX(), tmp.getY())); + } + + if (forceTransform || contains(tmp.getX(), tmp.getY())) { + event.transform(outMatrix); + tmp.recycle(); + return true; + } + return false; + } + + /** + * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. + * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... + * A value of 0 means no rotation has been applied + */ + private int deltaRotation(int oldRotation, int newRotation) { + int delta = newRotation - oldRotation; + if (delta < 0) delta += 4; + return delta; + } + } +} diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index abe15927d4..259d1ddddb 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -45,15 +45,16 @@ import android.graphics.RectF; import android.graphics.Region; import android.os.Process; import android.os.UserManager; +import android.graphics.Region; +import android.os.Process; import android.text.TextUtils; import android.util.Log; import android.view.MotionEvent; -import android.view.Surface; import androidx.annotation.BinderThread; +import com.android.launcher3.PagedView; import com.android.launcher3.R; -import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.DefaultDisplay; @@ -63,6 +64,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat; +import com.android.systemui.shared.system.TaskStackChangeListener; import java.io.PrintWriter; import java.util.ArrayList; @@ -87,10 +89,7 @@ public class RecentsAnimationDeviceState implements private SysUINavigationMode.Mode mMode = THREE_BUTTONS; private NavBarPosition mNavBarPosition; - private final RectF mSwipeUpTouchRegion = new RectF(); private final Region mDeferredGestureRegion = new Region(); - private final RectF mAssistantLeftRegion = new RectF(); - private final RectF mAssistantRightRegion = new RectF(); private boolean mAssistantAvailable; private float mAssistantVisibility; @@ -106,10 +105,13 @@ public class RecentsAnimationDeviceState implements } }; + private OrientationTouchTransformer mOrientationTouchTransformer; + private Region mExclusionRegion; private SystemGestureExclusionListenerCompat mExclusionListener; private final List mGestureBlockedActivities; + private TaskStackChangeListener mFrozenTaskListener; public RecentsAnimationDeviceState(Context context) { final ContentResolver resolver = context.getContentResolver(); @@ -139,6 +141,8 @@ public class RecentsAnimationDeviceState implements }; runOnDestroy(mExclusionListener::unregister); + setupOrientationSwipeHandler(context); + // Register for navigation mode changes onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this)); runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this)); @@ -160,6 +164,26 @@ public class RecentsAnimationDeviceState implements } } + private void setupOrientationSwipeHandler(Context context) { + final Resources resources = context.getResources(); + mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode, + () -> QuickStepContract.getWindowCornerRadius(resources)); + + if (!PagedView.sFlagForcedRotation) { + return; + } + + mFrozenTaskListener = new TaskStackChangeListener() { + @Override + public void onRecentTaskListFrozenChanged(boolean frozen) { + mOrientationTouchTransformer.enableMultipleRegions(frozen, mDefaultDisplay.getInfo()); + } + }; + ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener); + runOnDestroy(() -> ActivityManagerWrapper.getInstance() + .unregisterTaskStackListener(mFrozenTaskListener)); + } + private void runOnDestroy(Runnable action) { mOnDestroyActions.add(action); } @@ -198,7 +222,10 @@ public class RecentsAnimationDeviceState implements mExclusionListener.unregister(); } mMode = newMode; + mNavBarPosition = new NavBarPosition(mMode, mDefaultDisplay.getInfo()); + + mOrientationTouchTransformer.setNavigationMode(mMode); } @Override @@ -209,6 +236,7 @@ public class RecentsAnimationDeviceState implements mNavBarPosition = new NavBarPosition(mMode, info); updateGestureTouchRegions(); + mOrientationTouchTransformer.createOrAddTouchRegion(info); } /** @@ -380,50 +408,14 @@ public class RecentsAnimationDeviceState implements return; } - Resources res = mContext.getResources(); - DefaultDisplay.Info displayInfo = mDefaultDisplay.getInfo(); - Point realSize = new Point(displayInfo.realSize); - mSwipeUpTouchRegion.set(0, 0, realSize.x, realSize.y); - if (mMode == NO_BUTTON) { - int touchHeight = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, res); - mSwipeUpTouchRegion.top = mSwipeUpTouchRegion.bottom - touchHeight; - - final int assistantWidth = res.getDimensionPixelSize(R.dimen.gestures_assistant_width); - final float assistantHeight = Math.max(touchHeight, - QuickStepContract.getWindowCornerRadius(res)); - mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeUpTouchRegion.bottom; - mAssistantLeftRegion.top = mAssistantRightRegion.top = - mSwipeUpTouchRegion.bottom - assistantHeight; - - mAssistantLeftRegion.left = 0; - mAssistantLeftRegion.right = assistantWidth; - - mAssistantRightRegion.right = mSwipeUpTouchRegion.right; - mAssistantRightRegion.left = mSwipeUpTouchRegion.right - assistantWidth; - } else { - mAssistantLeftRegion.setEmpty(); - mAssistantRightRegion.setEmpty(); - switch (displayInfo.rotation) { - case Surface.ROTATION_90: - mSwipeUpTouchRegion.left = mSwipeUpTouchRegion.right - - ResourceUtils.getNavbarSize(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, res); - break; - case Surface.ROTATION_270: - mSwipeUpTouchRegion.right = mSwipeUpTouchRegion.left - + ResourceUtils.getNavbarSize(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, res); - break; - default: - mSwipeUpTouchRegion.top = mSwipeUpTouchRegion.bottom - - ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, res); - } - } + mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo()); } /** * @return whether the coordinates of the {@param event} is in the swipe up gesture region. */ public boolean isInSwipeUpTouchRegion(MotionEvent event) { - return mSwipeUpTouchRegion.contains(event.getX(), event.getY()); + return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()); } /** @@ -431,7 +423,8 @@ public class RecentsAnimationDeviceState implements * is in the swipe up gesture region. */ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) { - return mSwipeUpTouchRegion.contains(event.getX(pointerIndex), event.getY(pointerIndex)); + return mOrientationTouchTransformer.touchInValidSwipeRegions(event.getX(pointerIndex), + event.getY(pointerIndex)); } /** @@ -490,11 +483,22 @@ public class RecentsAnimationDeviceState implements public boolean canTriggerAssistantAction(MotionEvent ev) { return mAssistantAvailable && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) - && (mAssistantLeftRegion.contains(ev.getX(), ev.getY()) - || mAssistantRightRegion.contains(ev.getX(), ev.getY())) + && mOrientationTouchTransformer.touchInAssistantRegion(ev) && !isLockToAppActive(); } + /** + * *May* apply a transform on the motion event if it lies in the nav bar region for another + * orientation that is currently being tracked as a part of quickstep + */ + public void setOrientationTransformIfNeeded(MotionEvent event) { + // negative coordinates bug b/143901881 + if (event.getX() < 0 || event.getY() < 0) { + event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY())); + } + mOrientationTouchTransformer.transform(event); + } + public void dump(PrintWriter pw) { pw.println("DeviceState:"); pw.println(" canStartSystemGesture=" + canStartSystemGesture()); @@ -505,4 +509,8 @@ public class RecentsAnimationDeviceState implements pw.println(" assistantDisabled=" + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); } + + public void enableMultipleRegions(boolean enable) { + mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + } } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index eb60601c44..908747c6b5 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -333,4 +333,26 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + @Override + public void setSplitScreenMinimized(boolean minimized) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.setSplitScreenMinimized(minimized); + } catch (RemoteException e) { + Log.w(TAG, "Failed call stopScreenPinning", e); + } + } + } + + @Override + public void onQuickSwitchToNewTask() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.onQuickSwitchToNewTask(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call onQuickstepStarted", e); + } + } + } } diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/ResourceUtils.java index 7f327a5e5d..403d77913c 100644 --- a/src/com/android/launcher3/ResourceUtils.java +++ b/src/com/android/launcher3/ResourceUtils.java @@ -21,12 +21,12 @@ import android.util.DisplayMetrics; import android.util.TypedValue; public class ResourceUtils { + public static final int DEFAULT_NAVBAR_VALUE = 48; public static final String NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE = "navigation_bar_width"; public static final String NAVBAR_BOTTOM_GESTURE_SIZE = "navigation_bar_gesture_height"; - public static int getNavbarSize(String resName, Resources res) { - return getDimenByName(resName, res, 48); + return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE); } public static int getDimenByName(String resName, Resources res, int defaultValue) { diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java index d3dac04f98..f18e411d36 100644 --- a/src/com/android/launcher3/util/DefaultDisplay.java +++ b/src/com/android/launcher3/util/DefaultDisplay.java @@ -28,6 +28,8 @@ import android.util.Log; import android.view.Display; import android.view.WindowManager; +import androidx.annotation.VisibleForTesting; + import java.util.ArrayList; /** @@ -127,6 +129,18 @@ public class DefaultDisplay implements DisplayListener { public final DisplayMetrics metrics; + @VisibleForTesting + public Info(int id, int rotation, int singleFrameMs, Point realSize, Point smallestSize, + Point largestSize, DisplayMetrics metrics) { + this.id = id; + this.rotation = rotation; + this.singleFrameMs = singleFrameMs; + this.realSize = realSize; + this.smallestSize = smallestSize; + this.largestSize = largestSize; + this.metrics = metrics; + } + private Info(Context context) { this(context.getSystemService(WindowManager.class).getDefaultDisplay()); } From a406f727ad966d73dd616eb8747cee8606c738e6 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 19 Dec 2019 11:50:55 -0800 Subject: [PATCH 12/39] Add vertical layout support for Overview in portrait WM is making changes which allows apps to maintain their orientation independent of the orientation of the foreground app. This allows recents to always start in portrait even when the app currently running is in landscape. This means we have to give the illusion of a landscape oriented overview when user swipes up in gesterual nav when launcher is started in portrait configuration. PagedOrientationHandler abstracts all coordinate specific logic from Paged/RecentsView primarily, but also all other dynamic calculations throughout launcher. PagedViewOrientationState is the single point of exposure to other classes that depend on those changes. The goal is to also minimize holding state to allow for default implementations of PagedOrientationHandler for all the 3p/Fallback classes. PagedViewOrientationState also holds other data around rotation that isn't specifically tied to view logic. The fake landscape overview can be toggled with: adb shell settings put global forced_rotation [0/1] Fixes: 146176182 Change-Id: I65d8d4e9f92b93931cbe0053ccaf0cda8d2ffd6c --- .../LauncherAppTransitionManagerImpl.java | 12 +- .../FlingAndHoldTouchController.java | 4 +- .../NavBarToHomeTouchController.java | 2 +- ...ButtonNavbarToOverviewTouchController.java | 4 +- .../NoButtonQuickSwitchTouchController.java | 5 +- .../QuickSwitchTouchController.java | 4 +- .../TaskViewTouchController.java | 28 +- .../AppToOverviewAnimationProvider.java | 3 +- .../android/quickstep/BaseSwipeUpHandler.java | 60 ++- .../quickstep/FallbackActivityInterface.java | 3 +- .../quickstep/FallbackSwipeHandler.java | 3 +- .../quickstep/LauncherActivityInterface.java | 19 +- .../quickstep/LauncherSwipeHandler.java | 2 + .../android/quickstep/RecentsActivity.java | 3 +- .../com/android/quickstep/TaskViewUtils.java | 1 + .../quickstep/TouchInteractionService.java | 19 + .../fallback/FallbackRecentsView.java | 5 + .../OtherActivityInputConsumer.java | 3 +- .../util/AppWindowAnimationHelper.java | 60 ++- .../quickstep/views/ClearAllButton.java | 42 +- .../quickstep/views/LauncherRecentsView.java | 28 +- .../android/quickstep/views/RecentsView.java | 179 +++++--- .../quickstep/views/TaskThumbnailView.java | 1 + .../com/android/quickstep/views/TaskView.java | 86 +++- quickstep/res/layout/task.xml | 1 - .../launcher3/BaseQuickstepLauncher.java | 9 +- .../quickstep/BaseActivityInterface.java | 1 + .../OrientationTouchTransformer.java | 47 ++- .../quickstep/RecentsAnimationController.java | 2 +- .../RecentsAnimationDeviceState.java | 27 +- .../com/android/quickstep/SystemUiProxy.java | 27 +- .../android/quickstep/util/LayoutUtils.java | 3 + src/com/android/launcher3/PagedView.java | 388 ++++++++++-------- src/com/android/launcher3/Utilities.java | 6 + src/com/android/launcher3/Workspace.java | 6 +- .../launcher3/allapps/AllAppsPagedView.java | 2 +- .../launcher3/folder/FolderPagedView.java | 2 +- .../model/PagedViewOrientedState.java | 100 +++++ .../notification/NotificationMainView.java | 2 +- .../launcher3/states/RotationHelper.java | 137 +++++++ .../AbstractStateChangeTouchController.java | 2 +- .../launcher3/touch/BaseSwipeDetector.java | 2 +- .../touch/LandscapePagedViewHandler.java | 263 ++++++++++++ .../touch/PagedOrientationHandler.java | 120 ++++++ .../touch/PortraitPagedViewHandler.java | 257 ++++++++++++ .../touch/SeascapePagedViewHandler.java | 71 ++++ .../touch/SingleAxisSwipeDetector.java | 12 +- .../launcher3/views/AbstractSlideInView.java | 3 +- .../touch/SingleAxisSwipeDetectorTest.java | 14 +- 49 files changed, 1682 insertions(+), 398 deletions(-) create mode 100644 src/com/android/launcher3/model/PagedViewOrientedState.java create mode 100644 src/com/android/launcher3/touch/LandscapePagedViewHandler.java create mode 100644 src/com/android/launcher3/touch/PagedOrientationHandler.java create mode 100644 src/com/android/launcher3/touch/PortraitPagedViewHandler.java create mode 100644 src/com/android/launcher3/touch/SeascapePagedViewHandler.java diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 51ee216139..772820719b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; @@ -39,6 +38,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; +import android.util.FloatProperty; import android.view.View; import androidx.annotation.NonNull; @@ -50,6 +50,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.SpringAnimationBuilder; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -88,7 +89,8 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets); - AppWindowAnimationHelper helper = new AppWindowAnimationHelper(mLauncher); + AppWindowAnimationHelper helper = + new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher); anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION)); @@ -197,7 +199,11 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(), RecentsView.CONTENT_ALPHA, values); case INDEX_RECENTS_TRANSLATE_X_ANIM: - return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), VIEW_TRANSLATE_X) + PagedOrientationHandler orientationHandler = + ((RecentsView)mLauncher.getOverviewPanel()).getPagedViewOrientedState() + .getOrientationHandler(); + FloatProperty translate = orientationHandler.getPrimaryViewTranslate(); + return new SpringAnimationBuilder<>(mLauncher.getOverviewPanel(), translate) .setDampingRatio(0.8f) .setStiffness(250) .setValues(values) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index 519939e8d0..9cbe11acc3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -80,10 +80,10 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { mMotionPauseDetector.clear(); - super.onDragStart(start); + super.onDragStart(start, startDisplacement); if (handlingOverviewAnim()) { mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index ad4a343cb4..19a2bae467 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -126,7 +126,7 @@ public class NavBarToHomeTouchController implements TouchController, } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { initCurrentAnimation(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 34fc3e492c..ab634a4b12 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -94,8 +94,8 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo } @Override - public void onDragStart(boolean start) { - super.onDragStart(start); + public void onDragStart(boolean start, float startDisplacement) { + super.onDragStart(start, startDisplacement); mReachedOverview = false; } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 799f1ad182..715529e355 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -116,12 +116,13 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mLauncher = launcher; mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this); mShelfPeekAnim = mLauncher.getShelfPeekAnim(); + mRecentsView = mLauncher.getOverviewPanel(); mXRange = mLauncher.getDeviceProfile().widthPx / 2f; - mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile()); + mYRange = LayoutUtils.getShelfTrackingDistance( + mLauncher, mLauncher.getDeviceProfile()); mMotionPauseDetector = new MotionPauseDetector(mLauncher); mMotionPauseMinDisplacement = mLauncher.getResources().getDimension( R.dimen.motion_pause_detector_min_displacement_from_app); - mRecentsView = mLauncher.getOverviewPanel(); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index 912be983f7..d5b221db06 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -92,8 +92,8 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll } @Override - public void onDragStart(boolean start) { - super.onDragStart(start); + public void onDragStart(boolean start, float startDisplacement) { + super.onDragStart(start, startDisplacement); mStartContainerType = LauncherLogProto.ContainerType.NAVBAR; mTaskToLaunch = mLauncher.getOverviewPanel().getTaskViewAt(0); ActivityManagerWrapper.getInstance() diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index f79ad25571..e0532ac4cf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -36,6 +36,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.touch.BaseSwipeDetector; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.FlingBlockCheck; @@ -77,7 +78,9 @@ public abstract class TaskViewTouchController public TaskViewTouchController(T activity) { mActivity = activity; mRecentsView = activity.getOverviewPanel(); - mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL); + SingleAxisSwipeDetector.Direction dir = + mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection(); + mDetector = new SingleAxisSwipeDetector(activity, this, dir); } private boolean canInterceptTouch() { @@ -190,15 +193,18 @@ public abstract class TaskViewTouchController mPendingAnimation = null; } + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); mCurrentAnimationIsGoingUp = goingUp; BaseDragLayer dl = mActivity.getDragLayer(); - long maxDuration = (long) (2 * dl.getHeight()); - + final int secondaryLayerDimension = orientationHandler.getSecondaryDimension(dl); + long maxDuration = (long) (2 * secondaryLayerDimension); + int verticalFactor = -orientationHandler.getTaskDismissDirectionFactor(); + int secondaryTaskDimension = orientationHandler.getSecondaryDimension(mTaskBeingDragged); if (goingUp) { mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged, true /* animateTaskView */, true /* removeTask */, maxDuration); - mEndDisplacement = -mTaskBeingDragged.getHeight(); + mEndDisplacement = -secondaryTaskDimension; } else { mPendingAnimation = mRecentsView.createTaskLaunchAnimation( mTaskBeingDragged, maxDuration, Interpolators.ZOOM_IN); @@ -207,6 +213,7 @@ public abstract class TaskViewTouchController dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords); mEndDisplacement = dl.getHeight() - mTempCords[1]; } + mEndDisplacement *= verticalFactor; if (mCurrentAnimation != null) { mCurrentAnimation.setOnCancelRunnable(null); @@ -220,9 +227,10 @@ public abstract class TaskViewTouchController } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); if (mCurrentAnimation == null) { - reInitAnimationController(mDetector.wasInitialTouchPositive()); + reInitAnimationController(orientationHandler.isGoingUp(startDisplacement)); mDisplacementShift = 0; } else { mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier; @@ -233,9 +241,10 @@ public abstract class TaskViewTouchController @Override public boolean onDrag(float displacement) { + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); float totalDisplacement = displacement + mDisplacementShift; - boolean isGoingUp = - totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0; + boolean isGoingUp = totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : + orientationHandler.isGoingUp(totalDisplacement); if (isGoingUp != mCurrentAnimationIsGoingUp) { reInitAnimationController(isGoingUp); mFlingBlockCheck.blockFling(); @@ -262,11 +271,12 @@ public abstract class TaskViewTouchController if (blockedFling) { fling = false; } + PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler(); float progress = mCurrentAnimation.getProgressFraction(); float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress(); if (fling) { logAction = Touch.FLING; - boolean goingUp = velocity < 0; + boolean goingUp = orientationHandler.isGoingUp(velocity); goingToEnd = goingUp == mCurrentAnimationIsGoingUp; } else { logAction = Touch.SWIPE; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 59b117f2e8..375f16013f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -125,7 +125,8 @@ final class AppToOverviewAnimationProvider imple return anim; } - final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(mActivity); + final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper( + mRecentsView.getPagedViewOrientedState(), mActivity); // At this point, the activity is already started and laid-out. Get the home-bounds // relative to the screen using the rootView of the activity. diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 3601af2f32..8957b0d3e9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -45,7 +45,9 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.model.PagedViewOrientedState; +import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory; @@ -94,7 +96,7 @@ public abstract class BaseSwipeUpHandler mActivityInterface; protected final InputConsumerController mInputConsumer; - protected final AppWindowAnimationHelper mAppWindowAnimationHelper; + protected AppWindowAnimationHelper mAppWindowAnimationHelper; protected final TransformParams mTransformParams = new TransformParams(); // Shift in the range of [0, 1]. @@ -123,6 +125,8 @@ public abstract class BaseSwipeUpHandler getRecentsViewDispatcher(RotationMode rotationMode) { - return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null; + public Consumer getRecentsViewDispatcher() { + return mRecentsView != null ? mRecentsView.getEventDispatcher() : null; } @UiThread @@ -326,10 +328,19 @@ public abstract class BaseSwipeUpHandler @Override protected boolean onActivityInit(Boolean alreadyOnHome) { + super.onActivityInit(alreadyOnHome); final T activity = mActivityInterface.getCreatedActivity(); if (mActivity == activity) { return true; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java index 3f5179f76c..94b0051a51 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java @@ -183,7 +183,8 @@ public final class RecentsActivity extends BaseRecentsActivity { RemoteAnimationTargetCompat[] wallpaperTargets) { AnimatorSet target = new AnimatorSet(); boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING); - AppWindowAnimationHelper helper = new AppWindowAnimationHelper(this); + AppWindowAnimationHelper helper = new AppWindowAnimationHelper( + mFallbackRecentsView.getPagedViewOrientedState(), this); target.play(getRecentsWindowAnimator(taskView, !activityClosing, appTargets, wallpaperTargets, helper).setDuration(RECENTS_LAUNCH_DURATION)); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index 8d735915d9..aedb756101 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -130,6 +130,7 @@ public final class TaskViewUtils { .setLauncherOnTop(true); final RecentsView recentsView = v.getRecentsView(); + params.setPagedOrientedState(recentsView.getPagedViewOrientedState()); final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); appAnimator.addUpdateListener(new MultiValueUpdateListener() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index e1b5df0bca..7617ffe5bc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -56,6 +56,8 @@ import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.config.FeatureFlags; @@ -80,6 +82,7 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.ProtoTracer; +import com.android.quickstep.views.RecentsView; import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.recents.IOverviewProxy; @@ -537,6 +540,22 @@ public class TouchInteractionService extends Service implements PluginListener { } super.applyLoadPlan(tasks); } + + @Override + protected boolean supportsVerticalLandscape() { + return false; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 2f8682f9a0..bd9f330082 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -176,8 +176,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Proxy events to recents view if (mPassedWindowMoveSlop && mInteractionHandler != null && !mRecentsViewDispatcher.hasConsumer()) { - mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher( - mNavBarPosition.getRotationMode())); + mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher()); } int edgeFlags = ev.getEdgeFlags(); ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java index 5a9c2fe594..6923ca25ff 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java @@ -15,12 +15,6 @@ */ package com.android.quickstep.util; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; -import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; - import android.annotation.TargetApi; import android.content.Context; import android.graphics.Matrix; @@ -37,6 +31,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.SystemUiProxy; @@ -50,6 +45,12 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. import com.android.systemui.shared.system.TransactionCompat; import com.android.systemui.shared.system.WindowManagerWrapper; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius; +import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; + /** * Utility class to handle window clip animation */ @@ -82,6 +83,7 @@ public class AppWindowAnimationHelper { private final Rect mTmpRect = new Rect(); private final RectF mTmpRectF = new RectF(); private final RectF mCurrentRectWithInsets = new RectF(); + private PagedViewOrientedState mOrientedState; // Corner radius of windows, in pixels private final float mWindowCornerRadius; // Corner radius of windows when they're in overview mode. @@ -100,20 +102,24 @@ public class AppWindowAnimationHelper { private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a; private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1; - public AppWindowAnimationHelper(Context context) { + public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) { + mOrientedState = orientedState; mWindowCornerRadius = getWindowCornerRadius(context.getResources()); mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources()); mTaskCornerRadius = TaskCornerRadius.get(context); mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows; } + public AppWindowAnimationHelper(Context context) { + this(null, context); + } + private void updateSourceStack(RemoteAnimationTargetCompat target) { mSourceInsets.set(target.contentInsets); mSourceStackBounds.set(target.sourceContainerBounds); // TODO: Should sourceContainerBounds already have this offset? mSourceStackBounds.offsetTo(target.position.x, target.position.y); - } public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) { @@ -138,8 +144,9 @@ public class AppWindowAnimationHelper { // from the source rect. The difference between the target rect (scaled to the // source rect) is the amount to clip on each edge. RectF scaledTargetRect = new RectF(mTargetRect); - Utilities.scaleRectFAboutCenter(scaledTargetRect, - mSourceRect.width() / mTargetRect.width()); + float scale = getSrcToTargetScale(); + Utilities.scaleRectFAboutCenter(scaledTargetRect, scale); + scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top); mSourceWindowClipInsets.set( Math.max(scaledTargetRect.left, 0), @@ -149,6 +156,15 @@ public class AppWindowAnimationHelper { mSourceRect.set(scaledTargetRect); } + private float getSrcToTargetScale() { + if (mOrientedState == null) { + return mSourceRect.width() / mTargetRect.width(); + } else { + return mOrientedState.getOrientationHandler() + .getCurrentAppAnimationScale(mSourceRect, mTargetRect); + } + } + public void prepareAnimation(DeviceProfile dp, boolean isOpening) { mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING; mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode; @@ -221,7 +237,6 @@ public class AppWindowAnimationHelper { layer = Integer.MAX_VALUE; } } - // Since radius is in Surface space, but we draw the rounded corners in screen space, we // have to undo the scale. surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer, @@ -237,11 +252,16 @@ public class AppWindowAnimationHelper { mTmpRectF.set(mTargetRect); Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale); mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF)); - mCurrentRect.offset(params.mOffsetX, 0); + if (mOrientedState == null || mOrientedState.areMultipleLayoutOrientationsDisabled()) { + mCurrentRect.offset(params.mOffset, 0); + } else { + int displayRotation = mOrientedState.getDisplayRotation(); + mOrientedState.getOrientationHandler().offsetTaskRect(mCurrentRect, + params.mOffset, displayRotation); + } } updateClipRect(params); - return mCurrentRect; } @@ -340,7 +360,7 @@ public class AppWindowAnimationHelper { * @return The source rect's scale and translation relative to the target rect. */ public LauncherState.ScaleAndTranslation getScaleAndTranslation() { - float scale = mSourceRect.width() / mTargetRect.width(); + float scale = getSrcToTargetScale(); float translationY = mSourceRect.centerY() - mSourceRect.top - mTargetRect.centerY(); return new LauncherState.ScaleAndTranslation(scale, 0, translationY); } @@ -390,7 +410,7 @@ public class AppWindowAnimationHelper { public static class TransformParams { private float mProgress; - private float mOffsetX; + private float mOffset; private float mOffsetScale; private @Nullable RectF mCurrentRect; private float mTargetAlpha; @@ -401,7 +421,7 @@ public class AppWindowAnimationHelper { public TransformParams() { mProgress = 0; - mOffsetX = 0; + mOffset = 0; mOffsetScale = 1; mCurrentRect = null; mTargetAlpha = 1; @@ -453,8 +473,8 @@ public class AppWindowAnimationHelper { * the default), then offset the current rect by this amount after computing the rect based * on {@link #mProgress}. */ - public TransformParams setOffsetX(float offsetX) { - mOffsetX = offsetX; + public TransformParams setOffset(float offset) { + mOffset = offset; return this; } @@ -504,8 +524,8 @@ public class AppWindowAnimationHelper { return mProgress; } - public float getOffsetX() { - return mOffsetX; + public float getOffset() { + return mOffset; } public float getOffsetScale() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java index 9db0c09ae2..d0819c187b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java @@ -21,7 +21,7 @@ import android.util.AttributeSet; import android.util.Property; import android.widget.Button; -import com.android.launcher3.Utilities; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.views.RecentsView.PageCallbacks; import com.android.quickstep.views.RecentsView.ScrollState; @@ -44,21 +44,26 @@ public class ClearAllButton extends Button implements PageCallbacks { private float mContentAlpha = 1; private float mVisibilityAlpha = 1; - private final boolean mIsRtl; + private boolean mIsRtl; private int mScrollOffset; + private RecentsView mParent; public ClearAllButton(Context context, AttributeSet attrs) { super(context, attrs); - mIsRtl = Utilities.isRtl(context.getResources()); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); + mScrollOffset = mIsRtl ? mParent.getPaddingRight() / 2 : - mParent.getPaddingLeft() / 2; + } - RecentsView parent = (RecentsView) getParent(); - mScrollOffset = mIsRtl ? parent.getPaddingRight() / 2 : - parent.getPaddingLeft() / 2; + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mParent = (RecentsView) getParent(); + mIsRtl = !mParent.getPagedOrientationHandler().getRecentsRtlSetting(getResources()); } @Override @@ -73,6 +78,21 @@ public class ClearAllButton extends Button implements PageCallbacks { } } + public void onLayoutChanged() { + if (mParent == null) { + return; + } + setRotation(mParent.getPagedOrientationHandler().getDegreesRotated()); + } + + public void setRtl(boolean rtl) { + if (mIsRtl == rtl) { + return; + } + mIsRtl = rtl; + invalidate(); + } + public void setVisibilityAlpha(float alpha) { if (mVisibilityAlpha != alpha) { mVisibilityAlpha = alpha; @@ -82,14 +102,16 @@ public class ClearAllButton extends Button implements PageCallbacks { @Override public void onPageScroll(ScrollState scrollState) { - float width = getWidth(); - if (width == 0) { + PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler(); + float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight()); + if (orientationSize == 0) { return; } - float shift = Math.min(scrollState.scrollFromEdge, width); - setTranslationX(mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift)); - mScrollAlpha = 1 - shift / width; + float shift = Math.min(scrollState.scrollFromEdge, orientationSize); + float translation = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift); + orientationHandler.setPrimaryAndResetSecondaryTranslate(this, translation); + mScrollAlpha = 1 - shift / orientationSize; updateAlpha(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 3e106aa463..b2d182b331 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -42,6 +42,7 @@ import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; +import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.appprediction.PredictionUiStateManager; @@ -180,19 +181,21 @@ public class LauncherRecentsView extends RecentsView implements StateL * @return The translationX to apply to this view so that the first task is just offscreen. */ public float getOffscreenTranslationX(float recentsScale) { - float offscreenX = NORMAL.getOverviewScaleAndTranslation(mActivity).translationX; + LauncherState.ScaleAndTranslation overviewScaleAndTranslation = + NORMAL.getOverviewScaleAndTranslation(mActivity); + float offscreen = mOrientationHandler.getTranslationValue(overviewScaleAndTranslation); // Offset since scale pushes tasks outwards. getTaskSize(sTempRect); - int taskWidth = sTempRect.width(); - offscreenX += taskWidth * (recentsScale - 1) / 2; + int taskSize = mOrientationHandler.getPrimarySize(sTempRect); + offscreen += taskSize * (recentsScale - 1) / 2; if (mRunningTaskTileHidden) { // The first task is hidden, so offset by its width. - offscreenX -= (taskWidth + getPageSpacing()) * recentsScale; + offscreen -= (taskSize + getPageSpacing()) * recentsScale; } if (isRtl()) { - offscreenX = -offscreenX; + offscreen = -offscreen; } - return offscreenX; + return offscreen; } @Override @@ -276,6 +279,11 @@ public class LauncherRecentsView extends RecentsView implements StateL return mTransformParams; } + @Override + protected boolean supportsVerticalLandscape() { + return PagedView.sFlagForcedRotation; + } + @Override public void reset() { super.reset(); @@ -339,19 +347,19 @@ public class LauncherRecentsView extends RecentsView implements StateL } @Override - protected int computeMinScrollX() { + protected int computeMinScroll() { if (canComputeScrollX() && !mIsRtl) { return computeScrollX(); } - return super.computeMinScrollX(); + return super.computeMinScroll(); } @Override - protected int computeMaxScrollX() { + protected int computeMaxScroll() { if (canComputeScrollX() && mIsRtl) { return computeScrollX(); } - return super.computeMaxScrollX(); + return super.computeMaxScroll(); } private boolean canComputeScrollX() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 27ef93c614..50a66299b4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -22,7 +22,6 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; @@ -52,7 +51,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; @@ -66,11 +64,13 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Property; import android.util.SparseBooleanArray; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.OrientationEventListener; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -90,12 +90,15 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; @@ -162,6 +165,8 @@ public abstract class RecentsView extends PagedView impl } }; + private final OrientationEventListener mOrientationListener; + private int mPreviousRotation; protected RecentsAnimationController mRecentsAnimationController; protected RecentsAnimationTargets mRecentsAnimationTargets; protected AppWindowAnimationHelper mAppWindowAnimationHelper; @@ -340,7 +345,8 @@ public abstract class RecentsView extends PagedView impl mActivity = (T) BaseActivity.fromContext(context); mModel = RecentsModel.INSTANCE.get(context); mIdp = InvariantDeviceProfile.INSTANCE.get(context); - mTempAppWindowAnimationHelper = new AppWindowAnimationHelper(context); + mTempAppWindowAnimationHelper = + new AppWindowAnimationHelper(getPagedViewOrientedState(), context); mClearAllButton = (ClearAllButton) LayoutInflater.from(context) .inflate(R.layout.overview_clear_all_button, this, false); @@ -348,7 +354,7 @@ public abstract class RecentsView extends PagedView impl mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */, 10 /* initial size */); - mIsRtl = !Utilities.isRtl(getResources()); + mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources()); setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); mTaskTopMargin = getResources() .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); @@ -368,9 +374,21 @@ public abstract class RecentsView extends PagedView impl .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding); setWillNotDraw(false); updateEmptyMessage(); + disableMultipleLayoutRotations(!supportsVerticalLandscape()); // Initialize quickstep specific cache params here, as this is constructed only once mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5); + mOrientationListener = new OrientationEventListener(getContext()) { + @Override + public void onOrientationChanged(int i) { + int rotation = RotationHelper.getRotationFromDegrees(i, mPreviousRotation); + if (mPreviousRotation != rotation) { + animateRecentsRotationInPlace(rotation); + mPreviousRotation = rotation; + } + } + }; + } public OverScroller getScroller() { @@ -496,6 +514,13 @@ public abstract class RecentsView extends PagedView impl } public void setOverviewStateEnabled(boolean enabled) { + if (supportsVerticalLandscape() && mOrientationListener.canDetectOrientation()) { + if (enabled) { + mOrientationListener.enable(); + } else { + mOrientationListener.disable(); + } + } mOverviewStateEnabled = enabled; updateTaskStackListenerState(); if (!enabled) { @@ -623,7 +648,7 @@ public abstract class RecentsView extends PagedView impl final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex; final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); - taskView.bind(task); + taskView.bind(task, mLayoutRotation); } if (mNextPage == INVALID_PAGE) { @@ -755,19 +780,21 @@ public abstract class RecentsView extends PagedView impl if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) { return; } - int scrollX = getScrollX(); - final int halfPageWidth = getNormalChildWidth() / 2; - final int screenCenter = mInsets.left + getPaddingLeft() + scrollX + halfPageWidth; - final int halfScreenWidth = getMeasuredWidth() / 2; + CurveProperties curveProperties = mOrientationHandler + .getCurveProperties(this, mInsets); + int scroll = curveProperties.scroll; + final int halfPageSize = curveProperties.halfPageSize; + final int screenCenter = curveProperties.screenCenter; + final int halfScreenSize = curveProperties.halfScreenSize; final int pageSpacing = mPageSpacing; - mScrollState.scrollFromEdge = mIsRtl ? scrollX : (mMaxScrollX - scrollX); + mScrollState.scrollFromEdge = mIsRtl ? scroll : (mMaxScroll - scroll); final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth; + float pageCenter = mOrientationHandler.getViewCenterPosition(page) + halfPageSize; float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing; + float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; mScrollState.linearInterpolation = Math.min(1, Math.abs(distanceFromScreenCenter) / distanceToReachEdge); ((PageCallbacks) page).onPageScroll(mScrollState); @@ -915,6 +942,47 @@ public abstract class RecentsView extends PagedView impl setSwipeDownShouldLaunchApp(true); } + private void animateRecentsRotationInPlace(int newRotation) { + if (!supportsVerticalLandscape()) { + return; + } + + AnimatorSet pa = setRecentsChangedOrientation(true); + pa.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + updateLayoutRotation(newRotation); + ((DragLayer)mActivity.getDragLayer()).recreateControllers(); + rotateAllChildTasks(); + setRecentsChangedOrientation(false).start(); + } + }); + pa.start(); + } + + public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) { + getRunningTaskIndex(); + int runningIndex = getCurrentPage(); + AnimatorSet as = new AnimatorSet(); + for (int i = 0; i < getTaskViewCount(); i++) { + if (runningIndex == i) { + continue; + } + View taskView = getTaskViewAt(i); + as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1)); + } + return as; + } + + abstract protected boolean supportsVerticalLandscape(); + + private void rotateAllChildTasks() { + for (int i = 0; i < getTaskViewCount(); i++) { + TaskView taskView = getTaskViewAt(i); + taskView.setOverviewRotation(mLayoutRotation); + } + } + /** * Called when a gesture from an app has finished. */ @@ -950,7 +1018,7 @@ public abstract class RecentsView extends PagedView impl new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0, false, true, false, false, new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false); - taskView.bind(mTmpRunningTask); + taskView.bind(mTmpRunningTask, mLayoutRotation); } boolean runningTaskTileHidden = mRunningTaskTileHidden; @@ -1126,15 +1194,18 @@ public abstract class RecentsView extends PagedView impl private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) { addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); + FloatProperty secondaryViewTranslate = + mOrientationHandler.getSecondaryViewTranslate(); + int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView); + int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor(); if (UNSTABLE_SPRINGS.get() && taskView instanceof TaskView) { - addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y, - MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, - SpringForce.STIFFNESS_MEDIUM, - 0, -taskView.getHeight()), - duration, LINEAR, anim); + addAnim(new SpringObjectAnimator<>(taskView, secondaryViewTranslate, + MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, + SpringForce.STIFFNESS_MEDIUM, 0, verticalFactor * secondaryTaskDimension), + duration, LINEAR, anim); } else { - addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()), - duration, LINEAR, anim); + addAnim(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate, + verticalFactor * secondaryTaskDimension), duration, LINEAR, anim); } } @@ -1165,11 +1236,9 @@ public abstract class RecentsView extends PagedView impl } int[] oldScroll = new int[count]; - getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); - int[] newScroll = new int[count]; + getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC); getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView); - int taskCount = getTaskViewCount(); int scrollDiffPerPage = 0; if (count > 1) { @@ -1212,8 +1281,9 @@ public abstract class RecentsView extends PagedView impl SpringForce.STIFFNESS_MEDIUM, 0, scrollDiff), duration, ACCEL, anim); } else { - addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration, - ACCEL, anim); + Property translationProperty = mOrientationHandler.getPrimaryViewTranslate(); + addAnim(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff), + duration, ACCEL, anim); } needsCurveUpdates = true; @@ -1429,6 +1499,18 @@ public abstract class RecentsView extends PagedView impl } } + @Override + public void setLayoutRotation(int touchRotation, int displayRotation) { + if (!sFlagForcedRotation) { + return; + } + + super.setLayoutRotation(touchRotation, displayRotation); + mClearAllButton.onLayoutChanged(); + mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources()); + setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); + } + @Override public void onViewAdded(View child) { super.onViewAdded(child); @@ -1649,7 +1731,8 @@ public abstract class RecentsView extends PagedView impl } }); - AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper(mActivity); + AppWindowAnimationHelper appWindowAnimationHelper = new AppWindowAnimationHelper( + getPagedViewOrientedState(), mActivity); appWindowAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this); appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */); AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper); @@ -1806,7 +1889,7 @@ public abstract class RecentsView extends PagedView impl } @Override - protected int computeMinScrollX() { + protected int computeMinScroll() { if (getTaskViewCount() > 0) { if (mDisallowScrollToClearAll) { // We aren't showing the clear all button, @@ -1821,11 +1904,11 @@ public abstract class RecentsView extends PagedView impl } return getScrollForPage(mTaskViewStartIndex); } - return super.computeMinScrollX(); + return super.computeMinScroll(); } @Override - protected int computeMaxScrollX() { + protected int computeMaxScroll() { if (getTaskViewCount() > 0) { if (mDisallowScrollToClearAll) { // We aren't showing the clear all button, @@ -1840,7 +1923,7 @@ public abstract class RecentsView extends PagedView impl } return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1); } - return super.computeMaxScrollX(); + return super.computeMaxScroll(); } public ClearAllButton getClearAllButton() { @@ -1855,31 +1938,25 @@ public abstract class RecentsView extends PagedView impl return 0; } int startScroll = getScrollForPage(getRunningTaskIndex()); - int offsetX = startScroll - getScrollX(); - offsetX *= getScaleX(); + int offsetX = startScroll - mOrientationHandler.getPrimaryScroll(this); + offsetX *= mOrientationHandler.getPrimaryScale(this); return offsetX; } - public Consumer getEventDispatcher(RotationMode rotationMode) { - if (rotationMode.isTransposed) { - Matrix transform = new Matrix(); - transform.setRotate(-rotationMode.surfaceRotation); - - if (getWidth() > 0 && getHeight() > 0) { - float scale = ((float) getWidth()) / getHeight(); - transform.postScale(scale, 1 / scale); - } - - Matrix inverse = new Matrix(); - transform.invert(inverse); - return e -> { - e.transform(transform); - super.onTouchEvent(e); - e.transform(inverse); - }; - } else { + public Consumer getEventDispatcher() { + int degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation); + if (degreesRotated == 0) { return super::onTouchEvent; } + + // At this point the event coordinates have already been transformed, so we need to + // undo that transformation since PagedView also accommodates for the transformation via + // PagedOrientationHandler + return e -> { + RotationHelper.transformEvent(-degreesRotated, e, true); + super.onTouchEvent(e); + RotationHelper.transformEvent(-degreesRotated, e, false); + }; } public AppWindowAnimationHelper getClipAnimationHelper() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 8ed139269a..178ff32b4a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -36,6 +36,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Log; import android.util.Property; import android.view.Surface; import android.view.View; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 79b9a9d833..9150cc76d0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -16,15 +16,6 @@ package com.android.quickstep.views; -import static android.widget.Toast.LENGTH_SHORT; - -import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -41,7 +32,7 @@ import android.os.Handler; import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; -import android.view.Gravity; +import android.view.Surface; import android.view.View; import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityNodeInfo; @@ -58,8 +49,10 @@ import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; @@ -83,6 +76,20 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; +import static android.view.Gravity.BOTTOM; +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Gravity.CENTER_VERTICAL; +import static android.view.Gravity.END; +import static android.view.Gravity.START; +import static android.view.Gravity.TOP; +import static android.widget.Toast.LENGTH_SHORT; +import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; + /** * A task in the Recents view. */ @@ -186,6 +193,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mFooterVerticalOffset = 0; private float mFooterAlpha = 1; private int mStackHeight; + private boolean mHideActionsView; + private PagedOrientationHandler mOrientationHandler; public TaskView(Context context) { this(context, null); @@ -244,7 +253,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { if (mActionsView != null) { TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize(R.dimen.overview_actions_height), - Gravity.BOTTOM); + BOTTOM); addView(mActionsView, params); mActionsView.setAlpha(0); } @@ -266,10 +275,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { /** * Updates this task view to the given {@param task}. */ - public void bind(Task task) { + public void bind(Task task, int recentsRotation) { cancelPendingLoadTasks(); mTask = task; mSnapshotView.bind(task); + setOverviewRotation(recentsRotation); } public Task getTask() { @@ -439,6 +449,45 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } + void setOverviewRotation(int iconRotation) { + PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler(); + boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources()); + LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams(); + snapshotParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getContext()); + int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); + LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); + int rotation = RotationHelper.getDegreesFromRotation(iconRotation); + mHideActionsView = true; + switch (iconRotation) { + case Surface.ROTATION_90: + iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; + iconParams.rightMargin = -thumbnailPadding; + iconParams.leftMargin = iconParams.topMargin = iconParams.bottomMargin = 0; + break; + case Surface.ROTATION_180: + iconParams.gravity = BOTTOM | CENTER_HORIZONTAL; + iconParams.bottomMargin = -thumbnailPadding; + iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0; + break; + case Surface.ROTATION_270: + iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; + iconParams.leftMargin = -thumbnailPadding; + iconParams.rightMargin = iconParams.topMargin = iconParams.bottomMargin = 0; + break; + case Surface.ROTATION_0: + default: + iconParams.gravity = TOP | CENTER_HORIZONTAL; + iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = + iconParams.bottomMargin = 0; + mHideActionsView = false; + break; + } + mSnapshotView.setLayoutParams(snapshotParams); + mIconView.setLayoutParams(iconParams); + mIconView.setRotation(rotation); + updateActionsViewVisibility(!mHideActionsView); + } + private void setIconAndDimTransitionProgress(float progress, boolean invert) { if (invert) { progress = 1 - progress; @@ -601,8 +650,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { addView(view, indexToAdd); LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.gravity = - Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL; layoutParams.bottomMargin = ((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin; view.setAlpha(mFooterAlpha); @@ -855,9 +903,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mFullscreenProgress = progress; boolean isFullscreen = mFullscreenProgress > 0; mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); - if (mActionsView != null) { - mActionsView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); - } + updateActionsViewVisibility(progress < 1 && !mHideActionsView); setClipChildren(!isFullscreen); setClipToPadding(!isFullscreen); @@ -891,6 +937,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { invalidateOutline(); } + private void updateActionsViewVisibility(boolean isVisible) { + if (mActionsView != null) { + mActionsView.setVisibility(isVisible ? VISIBLE : GONE); + } + } + public boolean isRunningTask() { if (getRecentsView() == null) { return false; diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 60cfa0cbaa..f9bb2f2b14 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -31,7 +31,6 @@ android:id="@+id/icon" android:layout_width="@dimen/task_thumbnail_icon_size" android:layout_height="@dimen/task_thumbnail_icon_size" - android:layout_gravity="top|center_horizontal" android:focusable="false" android:importantForAccessibility="no"/> \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 07d2381aba..fa0e840b20 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -41,6 +41,7 @@ import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.proxy.ProxyActivityStarter; import com.android.launcher3.proxy.StartActivityParams; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.uioverrides.BackButtonAlphaHandler; import com.android.launcher3.uioverrides.RecentsViewStateController; import com.android.launcher3.util.UiThreadHelper; @@ -51,6 +52,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.ShelfPeekAnim; +import com.android.quickstep.views.RecentsView; import java.util.stream.Stream; @@ -210,9 +212,10 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() { if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) { - float offscreenTranslationX = getDeviceProfile().widthPx - - getOverviewPanel().getPaddingStart(); - return new ScaleAndTranslation(1f, offscreenTranslationX, 0f); + PagedOrientationHandler layoutVertical = + ((RecentsView)getOverviewPanel()).getPagedViewOrientedState().getOrientationHandler(); + return layoutVertical.getScaleAndTranslation(getDeviceProfile(), + getOverviewPanel()); } return super.getOverviewScaleAndTranslationForNormalState(); } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index fd55e077fd..58d6ee7998 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -31,6 +31,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.ShelfPeekAnim; import com.android.systemui.shared.recents.model.ThumbnailData; diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 3dae510d4a..f78ac57cd7 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -33,8 +33,11 @@ import android.view.Surface; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.DefaultDisplay; +import java.io.PrintWriter; + /** * Maintains state for supporting nav bars and tracking their gestures in multiple orientations. * See {@link OrientationRectF#applyTransform(MotionEvent, boolean)} for transformation of @@ -149,8 +152,7 @@ class OrientationTouchTransformer { Point size = display.realSize; int rotation = display.rotation; OrientationRectF orientationRectF = - new OrientationRectF(0, 0, size.x, size.y, rotation, - size.y, size.x); + new OrientationRectF(0, 0, size.x, size.y, rotation); if (mMode == SysUINavigationMode.Mode.NO_BUTTON) { int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); orientationRectF.top = orientationRectF.bottom - touchHeight; @@ -206,6 +208,14 @@ class OrientationTouchTransformer { return false; } + int getCurrentActiveRotation() { + if (mLastRectTouched == null) { + return 0; + } else { + return mLastRectTouched.mRotation; + } + } + public void transform(MotionEvent event) { int eventAction = event.getActionMasked(); switch (eventAction) { @@ -249,6 +259,19 @@ class OrientationTouchTransformer { } } + public void dump(PrintWriter pw) { + pw.println("OrientationTouchTransformerState: "); + pw.println(" currentActiveRotation=" + getCurrentActiveRotation()); + pw.println(" lastTouchedRegion=" + mLastRectTouched); + pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions); + StringBuilder regions = new StringBuilder(" currentTouchableRotations="); + for(int i = 0; i < mSwipeTouchRegions.size(); i++) { + OrientationRectF rectF = mSwipeTouchRegions.get(mSwipeTouchRegions.keyAt(i)); + regions.append(rectF.mRotation).append(" "); + } + pw.println(regions.toString()); + } + private class OrientationRectF extends RectF { /** @@ -262,12 +285,11 @@ class OrientationTouchTransformer { private float mHeight; private float mWidth; - OrientationRectF(float left, float top, float right, float bottom, int rotation, - float height, float width) { + OrientationRectF(float left, float top, float right, float bottom, int rotation) { super(left, top, right, bottom); this.mRotation = rotation; - mHeight = height - maxDelta; - mWidth = width - maxDelta; + mHeight = bottom - maxDelta; + mWidth = right - maxDelta; } @Override @@ -280,7 +302,7 @@ class OrientationTouchTransformer { boolean applyTransform(MotionEvent event, boolean forceTransform) { MotionEvent tmp = MotionEvent.obtain(event); Matrix outMatrix = new Matrix(); - int delta = deltaRotation(mCurrentRotation, mRotation); + int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation); switch (delta) { case Surface.ROTATION_0: outMatrix.reset(); @@ -314,16 +336,5 @@ class OrientationTouchTransformer { } return false; } - - /** - * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. - * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... - * A value of 0 means no rotation has been applied - */ - private int deltaRotation(int oldRotation, int newRotation) { - int delta = newRotation - oldRotation; - if (delta < 0) delta += 4; - return delta; - } } } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 21a4918b1b..8dd4aa4cab 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -39,7 +39,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; /** - * Wrapper around RecentsAnimationController to help with some synchronization + * Wrapper around RecentsAnimationControllerCompat to help with some synchronization */ public class RecentsAnimationController { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 259d1ddddb..d845650637 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,8 +17,6 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; -import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE; -import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; @@ -40,13 +38,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.RectF; import android.graphics.Region; import android.os.Process; import android.os.UserManager; -import android.graphics.Region; -import android.os.Process; import android.text.TextUtils; import android.util.Log; import android.view.MotionEvent; @@ -82,6 +76,7 @@ public class RecentsAnimationDeviceState implements private final SysUINavigationMode mSysUiNavMode; private final DefaultDisplay mDefaultDisplay; private final int mDisplayId; + private int mDisplayRotation; private final ArrayList mOnDestroyActions = new ArrayList<>(); @@ -234,6 +229,7 @@ public class RecentsAnimationDeviceState implements return; } + mDisplayRotation = info.rotation; mNavBarPosition = new NavBarPosition(mMode, info); updateGestureTouchRegions(); mOrientationTouchTransformer.createOrAddTouchRegion(info); @@ -499,6 +495,18 @@ public class RecentsAnimationDeviceState implements mOrientationTouchTransformer.transform(event); } + public void enableMultipleRegions(boolean enable) { + mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + } + + public int getCurrentActiveRotation() { + return mOrientationTouchTransformer.getCurrentActiveRotation(); + } + + public int getDisplayRotation() { + return mDisplayRotation; + } + public void dump(PrintWriter pw) { pw.println("DeviceState:"); pw.println(" canStartSystemGesture=" + canStartSystemGesture()); @@ -508,9 +516,8 @@ public class RecentsAnimationDeviceState implements pw.println(" assistantAvailable=" + mAssistantAvailable); pw.println(" assistantDisabled=" + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)); - } - - public void enableMultipleRegions(boolean enable) { - mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + pw.println(" currentActiveRotation=" + getCurrentActiveRotation()); + pw.println(" displayRotation=" + getDisplayRotation()); + mOrientationTouchTransformer.dump(pw); } } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 908747c6b5..0210a814cf 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -334,25 +334,14 @@ public class SystemUiProxy implements ISystemUiProxy { } } - @Override - public void setSplitScreenMinimized(boolean minimized) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.setSplitScreenMinimized(minimized); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stopScreenPinning", e); - } - } - } - - @Override public void onQuickSwitchToNewTask() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onQuickSwitchToNewTask(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onQuickstepStarted", e); - } - } + //TODO(b/150250451) add back in after big CL goes through +// if (mSystemUiProxy != null) { +// try { +// mSystemUiProxy.onQuickSwitchToNewTask(); +// } catch (RemoteException e) { +// Log.w(TAG, "Failed call onQuickstepStarted", e); +// } +// } } } diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index 1d8a79f683..ba99016561 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -33,6 +33,9 @@ import com.android.quickstep.SysUINavigationMode; import java.lang.annotation.Retention; +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; +import static java.lang.annotation.RetentionPolicy.SOURCE; + public class LayoutUtils { private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1; diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 01893e92b1..ef6bd3d019 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -16,11 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; -import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; -import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; -import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; - import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; @@ -35,6 +30,7 @@ import android.util.Log; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -49,13 +45,25 @@ import android.widget.ScrollView; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.pageindicators.PageIndicator; +import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.touch.OverScroll; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds; import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.Thunk; import java.util.ArrayList; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; +import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; +import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR; +import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE; +import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY; +import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO; + /** * An abstraction of the original Workspace which supports browsing through a * sequential list of "pages" @@ -64,6 +72,8 @@ public abstract class PagedView extends ViewGrou private static final String TAG = "PagedView"; private static final boolean DEBUG = false; + public static boolean sFlagForcedRotation = false; + public static final int INVALID_PAGE = -1; protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE; @@ -97,8 +107,8 @@ public abstract class PagedView extends ViewGrou @ViewDebug.ExportedProperty(category = "launcher") protected int mNextPage = INVALID_PAGE; - protected int mMinScrollX; - protected int mMaxScrollX; + protected int mMaxScroll; + protected int mMinScroll; protected OverScroller mScroller; private Interpolator mDefaultInterpolator; private VelocityTracker mVelocityTracker; @@ -106,9 +116,12 @@ public abstract class PagedView extends ViewGrou private float mDownMotionX; private float mDownMotionY; - private float mLastMotionX; - private float mLastMotionXRemainder; - private float mTotalMotionX; + private float mDownMotionPrimary; + private float mLastMotion; + private float mLastMotionRemainder; + private float mTotalMotion; + protected PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler(); + protected final PagedViewOrientedState mOrientationState = new PagedViewOrientedState(); protected int[] mPageScrolls; private boolean mIsBeingDragged; @@ -123,11 +136,14 @@ public abstract class PagedView extends ViewGrou protected boolean mIsPageInTransition = false; - protected float mSpringOverScrollX; + protected float mSpringOverScroll; protected boolean mWasInOverscroll = false; - protected int mUnboundedScrollX; + protected int mUnboundedScroll; + + protected int mLayoutRotation = Surface.ROTATION_0; + protected int mDisplayRotation = Surface.ROTATION_0; // Page Indicator @Thunk int mPageIndicatorViewId; @@ -166,11 +182,12 @@ public abstract class PagedView extends ViewGrou * Initializes various states for this workspace. */ protected void init() { - mScroller = new OverScroller(getContext()); + Context context = getContext(); + mScroller = new OverScroller(context); setDefaultInterpolator(Interpolators.SCROLL); mCurrentPage = 0; - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); @@ -182,6 +199,8 @@ public abstract class PagedView extends ViewGrou if (Utilities.ATLEAST_OREO) { setDefaultFocusHighlightEnabled(false); } + + sFlagForcedRotation = Utilities.isForcedRotation(context); } protected void setDefaultInterpolator(Interpolator interpolator) { @@ -236,12 +255,12 @@ public abstract class PagedView extends ViewGrou */ protected void updateCurrentPageScroll() { // If the current page is invalid, just reset the scroll position to zero - int newX = 0; + int newPosition = 0; if (0 <= mCurrentPage && mCurrentPage < getPageCount()) { - newX = getScrollForPage(mCurrentPage); + newPosition = getScrollForPage(mCurrentPage); } - scrollTo(newX, 0); - mScroller.startScroll(mScroller.getCurrPos(), newX - mScroller.getCurrPos()); + mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition); + mOrientationHandler.scrollerStartScroll(mScroller, newPosition); forceFinishScroller(true); } @@ -285,7 +304,7 @@ public abstract class PagedView extends ViewGrou int dir = !mIsRtl ? 1 : - 1; int currScroll = getScrollForPage(page); int prevScroll; - while (currScroll < mMinScrollX) { + while (currScroll < mMinScroll) { page += dir; prevScroll = currScroll; currScroll = getScrollForPage(page); @@ -294,7 +313,7 @@ public abstract class PagedView extends ViewGrou break; } } - while (currScroll > mMaxScrollX) { + while (currScroll > mMaxScroll) { page -= dir; prevScroll = currScroll; currScroll = getScrollForPage(page); @@ -378,45 +397,73 @@ public abstract class PagedView extends ViewGrou AccessibilityEvent.TYPE_VIEW_FOCUSED, null); } - protected int getUnboundedScrollX() { - return mUnboundedScrollX; + protected int getUnboundedScroll() { + return mUnboundedScroll; + } + + protected void updateLayoutRotation(int touchRotation) { + setLayoutRotation(touchRotation, mDisplayRotation); + } + + /** @param touchRotation Must be one of {@link android.view.Surface.ROTATION_0/90/180/270} */ + public void setLayoutRotation(int touchRotation, int displayRotation) { + if (mLayoutRotation == touchRotation && mDisplayRotation == displayRotation) { + return; + } + + mOrientationState.update(touchRotation, displayRotation); + mOrientationHandler = mOrientationState.getOrientationHandler(); + mLayoutRotation = touchRotation; + mDisplayRotation = displayRotation; + requestLayout(); + } + + public PagedViewOrientedState getPagedViewOrientedState() { + return mOrientationState; + } + + public PagedOrientationHandler getPagedOrientationHandler() { + return getPagedViewOrientedState().getOrientationHandler(); + } + + public void disableMultipleLayoutRotations(boolean disable) { + mOrientationState.disableMultipleOrientations(disable); + mOrientationHandler = mOrientationState.getOrientationHandler(); + requestLayout(); } @Override public void scrollBy(int x, int y) { - scrollTo(getUnboundedScrollX() + x, getScrollY() + y); + mOrientationHandler.delegateScrollBy(this, getUnboundedScroll(), x, y); } @Override public void scrollTo(int x, int y) { - mUnboundedScrollX = x; + int primaryScroll = mOrientationHandler.getPrimaryValue(x, y); + int secondaryScroll = mOrientationHandler.getSecondaryValue(x, y); + mUnboundedScroll = primaryScroll; - boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < mMinScrollX); - boolean isXAfterLastPage = mIsRtl ? (x < mMinScrollX) : (x > mMaxScrollX); - - if (!isXBeforeFirstPage && !isXAfterLastPage) { - mSpringOverScrollX = 0; + boolean isBeforeFirstPage = mIsRtl ? + (primaryScroll > mMaxScroll) : (primaryScroll < mMinScroll); + boolean isAfterLastPage = mIsRtl ? + (primaryScroll < mMinScroll) : (primaryScroll > mMaxScroll); + if (!isBeforeFirstPage && !isAfterLastPage) { + mSpringOverScroll = 0; } - if (isXBeforeFirstPage) { - super.scrollTo(mIsRtl ? mMaxScrollX : mMinScrollX, y); + if (isBeforeFirstPage) { + mOrientationHandler.delegateScrollTo(this, + secondaryScroll, mIsRtl ? mMaxScroll : mMinScroll); if (mAllowOverScroll) { mWasInOverscroll = true; - if (mIsRtl) { - overScroll(x - mMaxScrollX); - } else { - overScroll(x - mMinScrollX); - } + overScroll(primaryScroll - (mIsRtl ? mMaxScroll : mMinScroll)); } - } else if (isXAfterLastPage) { - super.scrollTo(mIsRtl ? mMinScrollX : mMaxScrollX, y); + } else if (isAfterLastPage) { + mOrientationHandler.delegateScrollTo(this, + secondaryScroll, mIsRtl ? mMinScroll : mMaxScroll); if (mAllowOverScroll) { mWasInOverscroll = true; - if (mIsRtl) { - overScroll(x - mMinScrollX); - } else { - overScroll(x - mMaxScrollX); - } + overScroll(primaryScroll - (mIsRtl ? mMinScroll : mMaxScroll)); } } else { if (mWasInOverscroll) { @@ -425,7 +472,13 @@ public abstract class PagedView extends ViewGrou } super.scrollTo(x, y); } + } + /** + * Helper for {@link PagedOrientationHandler} to be able to call parent's scrollTo method + */ + public void superScrollTo(int x, int y) { + super.scrollTo(x, y); } private void sendScrollAccessibilityEvent() { @@ -436,9 +489,7 @@ public abstract class PagedView extends ViewGrou ev.setScrollable(true); ev.setScrollX(getScrollX()); ev.setScrollY(getScrollY()); - ev.setMaxScrollX(mMaxScrollX); - ev.setMaxScrollY(0); - + mOrientationHandler.setMaxScroll(ev, mMaxScroll); sendAccessibilityEventUnchecked(ev); } } @@ -459,9 +510,10 @@ public abstract class PagedView extends ViewGrou protected boolean computeScrollHelper(boolean shouldInvalidate) { if (mScroller.computeScrollOffset()) { // Don't bother scrolling if the page does not need to be moved - if (getUnboundedScrollX() != mScroller.getCurrPos() - || getScrollX() != mScroller.getCurrPos()) { - scrollTo(mScroller.getCurrPos(), 0); + int currentScroll = mOrientationHandler.getPrimaryScroll(this); + if (mUnboundedScroll != mScroller.getCurrPos() + || currentScroll != mScroller.getCurrPos()) { + mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrPos()); } if (shouldInvalidate) { invalidate(); @@ -580,7 +632,8 @@ public abstract class PagedView extends ViewGrou if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); - if (getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC)) { + boolean isScrollChanged = getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC); + if (isScrollChanged) { pageScrollChanged = true; } @@ -621,7 +674,6 @@ public abstract class PagedView extends ViewGrou /** * Initializes {@code outPageScrolls} with scroll positions for view at that index. The length * of {@code outPageScrolls} should be same as the the childCount - * */ protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren, ComputePageScrollsLogic scrollLogic) { @@ -631,36 +683,30 @@ public abstract class PagedView extends ViewGrou final int endIndex = mIsRtl ? -1 : childCount; final int delta = mIsRtl ? -1 : 1; - final int verticalCenter = (getPaddingTop() + getMeasuredHeight() + mInsets.top - - mInsets.bottom - getPaddingBottom()) / 2; + final int pageCenter = mOrientationHandler.getCenterForPage(this, mInsets); - final int scrollOffsetLeft = mInsets.left + getPaddingLeft(); - final int scrollOffsetRight = getWidth() - getPaddingRight() - mInsets.right; + final int scrollOffsetStart = mOrientationHandler.getScrollOffsetStart(this, mInsets); + final int scrollOffsetEnd = mOrientationHandler.getScrollOffsetEnd(this, mInsets); boolean pageScrollChanged = false; - for (int i = startIndex, childLeft = scrollOffsetLeft; i != endIndex; i += delta) { + for (int i = startIndex, childStart = scrollOffsetStart; i != endIndex; i += delta) { final View child = getPageAt(i); if (scrollLogic.shouldIncludeView(child)) { - final int childWidth = child.getMeasuredWidth(); - final int childRight = childLeft + childWidth; - - if (layoutChildren) { - final int childHeight = child.getMeasuredHeight(); - final int childTop = verticalCenter - childHeight / 2; - child.layout(childLeft, childTop, childRight, childTop + childHeight); - } + ChildBounds bounds = mOrientationHandler.getChildBounds(child, childStart, + pageCenter, layoutChildren); + final int primaryDimension = bounds.primaryDimension; + final int childPrimaryEnd = bounds.childPrimaryEnd; // In case the pages are of different width, align the page to left or right edge // based on the orientation. final int pageScroll = mIsRtl - ? (childLeft - scrollOffsetLeft) - : Math.max(0, childRight - scrollOffsetRight); + ? (childStart - scrollOffsetStart) + : Math.max(0, childPrimaryEnd - scrollOffsetEnd); if (outPageScrolls[i] != pageScroll) { pageScrollChanged = true; outPageScrolls[i] = pageScroll; } - - childLeft += childWidth + mPageSpacing + getChildGap(); + childStart += primaryDimension + mPageSpacing + getChildGap(); } } return pageScrollChanged; @@ -671,15 +717,15 @@ public abstract class PagedView extends ViewGrou } protected void updateMinAndMaxScrollX() { - mMinScrollX = computeMinScrollX(); - mMaxScrollX = computeMaxScrollX(); + mMinScroll = computeMinScroll(); + mMaxScroll = computeMaxScroll(); } - protected int computeMinScrollX() { + protected int computeMinScroll() { return 0; } - protected int computeMaxScrollX() { + protected int computeMaxScroll() { int childCount = getChildCount(); if (childCount > 0) { final int index = mIsRtl ? 0 : childCount - 1; @@ -722,7 +768,8 @@ public abstract class PagedView extends ViewGrou protected int getChildOffset(int index) { if (index < 0 || index > getChildCount() - 1) return 0; - return getPageAt(index).getLeft(); + View pageAtIndex = getPageAt(index); + return mOrientationHandler.getChildStart(pageAtIndex); } @Override @@ -873,13 +920,13 @@ public abstract class PagedView extends ViewGrou case MotionEvent.ACTION_MOVE: { /* * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. + * whether the user has moved far enough from their original down touch. */ if (mActivePointerId != INVALID_POINTER) { determineScrollingStart(ev); } // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN - // event. in that case, treat the first occurence of a move event as a ACTION_DOWN + // event. in that case, treat the first occurrence of a move event as a ACTION_DOWN // i.e. fall through to the next case (don't break) // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events // while it's small- this was causing a crash before we checked for INVALID_POINTER) @@ -892,9 +939,9 @@ public abstract class PagedView extends ViewGrou // Remember location of down touch mDownMotionX = x; mDownMotionY = y; - mLastMotionX = x; - mLastMotionXRemainder = 0; - mTotalMotionX = 0; + mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0); + mLastMotionRemainder = 0; + mTotalMotion = 0; mActivePointerId = ev.getPointerId(0); updateIsBeingDraggedOnTouchDown(); @@ -956,17 +1003,17 @@ public abstract class PagedView extends ViewGrou final int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == -1) return; - final float x = ev.getX(pointerIndex); - final int xDiff = (int) Math.abs(x - mLastMotionX); + final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, pointerIndex); + final int diff = (int) Math.abs(primaryDirection - mLastMotion); final int touchSlop = Math.round(touchSlopScale * mTouchSlop); - boolean xMoved = xDiff > touchSlop; + boolean moved = diff > touchSlop; - if (xMoved) { + if (moved) { // Scroll if the user moved far enough along the X axis mIsBeingDragged = true; - mTotalMotionX += Math.abs(mLastMotionX - x); - mLastMotionX = x; - mLastMotionXRemainder = 0; + mTotalMotion += Math.abs(mLastMotion - primaryDirection); + mLastMotion = primaryDirection; + mLastMotionRemainder = 0; onScrollInteractionBegin(); pageBeginTransition(); // Stop listening for things like pinches. @@ -1033,10 +1080,9 @@ public abstract class PagedView extends ViewGrou @Override protected void dispatchDraw(Canvas canvas) { - if (mScroller.isSpringing() && mSpringOverScrollX != 0) { + if (mScroller.isSpringing() && mSpringOverScroll != 0) { int saveCount = canvas.save(); - - canvas.translate(-mSpringOverScrollX, 0); + mOrientationHandler.set(canvas, CANVAS_TRANSLATE, -mSpringOverScroll); super.dispatchDraw(canvas); canvas.restoreToCount(saveCount); @@ -1046,25 +1092,27 @@ public abstract class PagedView extends ViewGrou } protected void dampedOverScroll(int amount) { - mSpringOverScrollX = amount; + mSpringOverScroll = amount; if (amount == 0) { return; } - int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth()); - mSpringOverScrollX = overScrollAmount; + int size = mOrientationHandler.getMeasuredSize(this); + int overScrollAmount = OverScroll.dampedScroll(amount, size); + mSpringOverScroll = overScrollAmount; if (mScroller.isSpringing()) { invalidate(); return; } - int x = Utilities.boundToRange(getScrollX(), mMinScrollX, mMaxScrollX); - super.scrollTo(x + overScrollAmount, getScrollY()); + int primaryScroll = mOrientationHandler.getPrimaryScroll(this); + int boundedScroll = Utilities.boundToRange(primaryScroll, mMinScroll, mMaxScroll); + mOrientationHandler.delegateScrollTo(this, boundedScroll + overScrollAmount); invalidate(); } protected void overScroll(int amount) { - mSpringOverScrollX = amount; + mSpringOverScroll = amount; if (mScroller.isSpringing()) { invalidate(); return; @@ -1073,11 +1121,8 @@ public abstract class PagedView extends ViewGrou if (amount == 0) return; if (mFreeScroll && !mScroller.isFinished()) { - if (amount < 0) { - super.scrollTo(mMinScrollX + amount, getScrollY()); - } else { - super.scrollTo(mMaxScrollX + amount, getScrollY()); - } + int scrollAmount = amount < 0 ? mMinScroll + amount : mMaxScroll + amount; + mOrientationHandler.delegateScrollTo(this, scrollAmount); } else { dampedOverScroll(amount); } @@ -1127,37 +1172,37 @@ public abstract class PagedView extends ViewGrou } // Remember where the motion event started - mDownMotionX = mLastMotionX = ev.getX(); + mDownMotionX = ev.getX(); mDownMotionY = ev.getY(); - mLastMotionXRemainder = 0; - mTotalMotionX = 0; + mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0); + mLastMotionRemainder = 0; + mTotalMotion = 0; mActivePointerId = ev.getPointerId(0); - if (mIsBeingDragged) { onScrollInteractionBegin(); pageBeginTransition(); } break; - case MotionEvent.ACTION_MOVE: - if (mIsBeingDragged) { + case MotionEvent.ACTION_MOVE: + if (mIsBeingDragged) { // Scroll to follow the motion event final int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == -1) return true; - final float x = ev.getX(pointerIndex); - final float deltaX = mLastMotionX + mLastMotionXRemainder - x; - - mTotalMotionX += Math.abs(deltaX); + float direction = mOrientationHandler.getPrimaryDirection(ev, pointerIndex); + float delta = mLastMotion + mLastMotionRemainder - direction; + mTotalMotion += Math.abs(delta); // Only scroll and update mLastMotionX if we have moved some discrete amount. We // keep the remainder because we are actually testing if we've moved from the last // scrolled position (which is discrete). - if (Math.abs(deltaX) >= 1.0f) { - scrollBy((int) deltaX, 0); - mLastMotionX = x; - mLastMotionXRemainder = deltaX - (int) deltaX; + if (Math.abs(delta) >= 1.0f) { + mLastMotion = direction; + mLastMotionRemainder = delta - (int) delta; + + mOrientationHandler.set(this, VIEW_SCROLL_BY, (int) delta); } else { awakenScrollBars(); } @@ -1170,27 +1215,31 @@ public abstract class PagedView extends ViewGrou if (mIsBeingDragged) { final int activePointerId = mActivePointerId; final int pointerIndex = ev.findPointerIndex(activePointerId); - final float x = ev.getX(pointerIndex); + final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, + pointerIndex); final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); - final int deltaX = (int) (x - mDownMotionX); - final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth(); - boolean isSignificantMove = Math.abs(deltaX) > pageWidth * - SIGNIFICANT_MOVE_THRESHOLD; - mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); - boolean isFling = mTotalMotionX > mTouchSlop && shouldFlingForVelocity(velocityX); - boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0; - boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0; + int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker, + mActivePointerId); + int delta = (int) (primaryDirection - mDownMotionPrimary); + int pageOrientedSize = mOrientationHandler.getMeasuredSize(getPageAt(mCurrentPage)); + + boolean isSignificantMove = Math.abs(delta) > pageOrientedSize * + SIGNIFICANT_MOVE_THRESHOLD; + + mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection); + boolean isFling = mTotalMotion > mTouchSlop && shouldFlingForVelocity(velocity); + boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0; + boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0; if (!mFreeScroll) { // In the case that the page is moved far to one direction and then is flung // in the opposite direction, we use a threshold to determine whether we should // just return to the starting page, or if we should skip one further. boolean returnToOriginalPage = false; - if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && - Math.signum(velocityX) != Math.signum(deltaX) && isFling) { + if (Math.abs(delta) > pageOrientedSize * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && + Math.signum(velocity) != Math.signum(delta) && isFling) { returnToOriginalPage = true; } @@ -1199,15 +1248,15 @@ public abstract class PagedView extends ViewGrou // test for a large move if a fling has been registered. That is, a large // move to the left and fling to the right will register as a fling to the right. - if (((isSignificantMove && !isDeltaXLeft && !isFling) || - (isFling && !isVelocityXLeft)) && mCurrentPage > 0) { + if (((isSignificantMove && !isDeltaLeft && !isFling) || + (isFling && !isVelocityLeft)) && mCurrentPage > 0) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; - snapToPageWithVelocity(finalPage, velocityX); - } else if (((isSignificantMove && isDeltaXLeft && !isFling) || - (isFling && isVelocityXLeft)) && + snapToPageWithVelocity(finalPage, velocity); + } else if (((isSignificantMove && isDeltaLeft && !isFling) || + (isFling && isVelocityLeft)) && mCurrentPage < getChildCount() - 1) { finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; - snapToPageWithVelocity(finalPage, velocityX); + snapToPageWithVelocity(finalPage, velocity); } else { snapToDestination(); } @@ -1216,38 +1265,40 @@ public abstract class PagedView extends ViewGrou abortScrollerAnimation(true); } - int initialScrollX = getScrollX(); + int initialScroll = mOrientationHandler.getPrimaryScroll(this); + int maxScroll = mMaxScroll; + int minScroll = mMinScroll; - if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) || - ((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) { - mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX); + if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) || + ((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) { + mScroller.springBack(initialScroll, minScroll, maxScroll); mNextPage = getPageNearestToCenterOfScreen(); } else { mScroller.setInterpolator(mDefaultInterpolator); - mScroller.fling(initialScrollX, -velocityX, - mMinScrollX, mMaxScrollX, - Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR)); + mScroller.fling(initialScroll, -velocity, + minScroll, maxScroll, + Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR)); - int finalX = mScroller.getFinalPos(); - mNextPage = getPageNearestToCenterOfScreen(finalX); + int finalPos = mScroller.getFinalPos(); + mNextPage = getPageNearestToCenterOfScreen(finalPos); int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1); int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0); - if (finalX > mMinScrollX && finalX < mMaxScrollX) { + if (finalPos > minScroll && finalPos < maxScroll) { // If scrolling ends in the half of the added space that is closer to // the end, settle to the end. Otherwise snap to the nearest page. // If flinging past one of the ends, don't change the velocity as it // will get stopped at the end anyway. - int pageSnappedX = finalX < (firstPageScroll + mMinScrollX) / 2 - ? mMinScrollX - : finalX > (lastPageScroll + mMaxScrollX) / 2 - ? mMaxScrollX - : getScrollForPage(mNextPage); + int pageSnapped = finalPos < (firstPageScroll + minScroll) / 2 + ? minScroll + : finalPos > (lastPageScroll + maxScroll) / 2 + ? maxScroll + : getScrollForPage(mNextPage); - mScroller.setFinalPos(pageSnappedX); + mScroller.setFinalPos(pageSnapped); // Ensure the scroll/snap doesn't happen too fast; int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION - - mScroller.getDuration(); + - mScroller.getDuration(); if (extraScrollDuration > 0) { mScroller.extendDuration(extraScrollDuration); } @@ -1279,8 +1330,8 @@ public abstract class PagedView extends ViewGrou return true; } - protected boolean shouldFlingForVelocity(int velocityX) { - return Math.abs(velocityX) > mFlingThresholdVelocity; + protected boolean shouldFlingForVelocity(int velocity) { + return Math.abs(velocity) > mFlingThresholdVelocity; } private void resetTouchState() { @@ -1364,8 +1415,9 @@ public abstract class PagedView extends ViewGrou // active pointer and adjust accordingly. // TODO: Make this decision more intelligent. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); - mLastMotionXRemainder = 0; + mLastMotion = mDownMotionPrimary = mOrientationHandler.getPrimaryDirection(ev, + newPointerIndex); + mLastMotionRemainder = 0; mActivePointerId = ev.getPointerId(newPointerIndex); if (mVelocityTracker != null) { mVelocityTracker.clear(); @@ -1383,19 +1435,20 @@ public abstract class PagedView extends ViewGrou } public int getPageNearestToCenterOfScreen() { - return getPageNearestToCenterOfScreen(getScrollX()); + return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this)); } - private int getPageNearestToCenterOfScreen(int scaledScrollX) { - int screenCenter = scaledScrollX + (getMeasuredWidth() / 2); + private int getPageNearestToCenterOfScreen(int scaledScroll) { + int pageOrientationSize = mOrientationHandler.getMeasuredSize(this); + int screenCenter = scaledScroll + (pageOrientationSize / 2); int minDistanceFromScreenCenter = Integer.MAX_VALUE; int minDistanceFromScreenCenterIndex = -1; final int childCount = getChildCount(); for (int i = 0; i < childCount; ++i) { View layout = getPageAt(i); - int childWidth = layout.getMeasuredWidth(); - int halfChildWidth = (childWidth / 2); - int childCenter = getChildOffset(i) + halfChildWidth; + int childSize = mOrientationHandler.getMeasuredSize(layout); + int halfChildSize = (childSize / 2); + int childCenter = getChildOffset(i) + halfChildSize; int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); if (distanceFromScreenCenter < minDistanceFromScreenCenter) { minDistanceFromScreenCenter = distanceFromScreenCenter; @@ -1410,7 +1463,8 @@ public abstract class PagedView extends ViewGrou } protected boolean isInOverScroll() { - return (getScrollX() > mMaxScrollX || getScrollX() < mMinScrollX); + int scroll = mOrientationHandler.getPrimaryScroll(this); + return scroll > mMaxScroll || scroll < mMinScroll; } protected int getPageSnapDuration() { @@ -1432,10 +1486,10 @@ public abstract class PagedView extends ViewGrou protected boolean snapToPageWithVelocity(int whichPage, int velocity) { whichPage = validateNewPage(whichPage); - int halfScreenSize = getMeasuredWidth() / 2; + int halfScreenSize = mOrientationHandler.getMeasuredSize(this) / 2; - final int newX = getScrollForPage(whichPage); - int delta = newX - getUnboundedScrollX(); + final int newLoc = getScrollForPage(whichPage); + int delta = newLoc - getUnboundedScroll(); int duration = 0; if (Math.abs(velocity) < mMinFlingVelocity) { @@ -1462,7 +1516,7 @@ public abstract class PagedView extends ViewGrou if (QUICKSTEP_SPRINGS.get()) { return snapToPage(whichPage, delta, duration, false, null, - velocity * Math.signum(newX - getUnboundedScrollX()), true); + velocity * Math.signum(delta), true); } else { return snapToPage(whichPage, delta, duration); } @@ -1488,8 +1542,8 @@ public abstract class PagedView extends ViewGrou TimeInterpolator interpolator) { whichPage = validateNewPage(whichPage); - int newX = getScrollForPage(whichPage); - final int delta = newX - getUnboundedScrollX(); + int newLoc = getScrollForPage(whichPage); + final int delta = newLoc - getUnboundedScroll(); return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false); } @@ -1535,9 +1589,9 @@ public abstract class PagedView extends ViewGrou } if (spring && QUICKSTEP_SPRINGS.get()) { - mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity); + mScroller.startScrollSpring(getUnboundedScroll(), delta, duration, velocity); } else { - mScroller.startScroll(getUnboundedScrollX(), delta, duration); + mScroller.startScroll(getUnboundedScroll(), delta, duration); } updatePageIndicator(); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index e0e4cc0ce4..9780630b78 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -17,6 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED; +import static com.android.launcher3.states.RotationHelper.FIXED_ROTATION_TRANSFORM_SETTING_NAME; import android.animation.ValueAnimator; import android.annotation.TargetApi; @@ -127,6 +128,11 @@ public final class Utilities { Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; } + public static boolean isForcedRotation(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0; + } + // An intent extra to indicate the horizontal scroll of the wallpaper. public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET"; public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR"; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a8f492f623..590c620020 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -966,8 +966,8 @@ public class Workspace extends PagedView private boolean isScrollingOverlay() { return mLauncherOverlay != null && - ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) - || (!mIsRtl && getUnboundedScrollX() < mMinScrollX)); + ((mIsRtl && getUnboundedScroll() > mMaxScroll) + || (!mIsRtl && getUnboundedScroll() < mMinScroll)); } @Override @@ -1003,7 +1003,7 @@ public class Workspace extends PagedView public void showPageIndicatorAtCurrentScroll() { if (mPageIndicator != null) { - mPageIndicator.setScroll(getScrollX(), computeMaxScrollX()); + mPageIndicator.setScroll(getScrollX(), computeMaxScroll()); } } diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java index 5b73940488..ab4cb6b8db 100644 --- a/src/com/android/launcher3/allapps/AllAppsPagedView.java +++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java @@ -48,7 +48,7 @@ public class AllAppsPagedView extends PagedView { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - mPageIndicator.setScroll(l, mMaxScrollX); + mPageIndicator.setScroll(l, mMaxScroll); } @Override diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 3b5fd5902c..c6d62f8bc7 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -258,7 +258,7 @@ public class FolderPagedView extends PagedView { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - mPageIndicator.setScroll(l, mMaxScrollX); + mPageIndicator.setScroll(l, mMaxScroll); } /** diff --git a/src/com/android/launcher3/model/PagedViewOrientedState.java b/src/com/android/launcher3/model/PagedViewOrientedState.java new file mode 100644 index 0000000000..fd1154c9fa --- /dev/null +++ b/src/com/android/launcher3/model/PagedViewOrientedState.java @@ -0,0 +1,100 @@ +/* + * + * * Copyright (C) 2020 The Android Open Source Project + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package com.android.launcher3.model; + +import android.view.Surface; + +import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.touch.PortraitPagedViewHandler; +import com.android.launcher3.touch.LandscapePagedViewHandler; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.touch.SeascapePagedViewHandler; + +/** + * Container to hold orientation/rotation related information for Launcher. + * This is not meant to be an abstraction layer for applying different functionality between + * the different orientation/rotations. For that see {@link PagedOrientationHandler} + * + * This class has initial default state assuming the device and foreground app have + * no ({@link Surface.ROTATION_0} rotation. + * + * Currently this class resides in {@link com.android.launcher3.PagedView}, but there's a ticket + * to disassociate it from Launcher since it's needed before Launcher is instantiated + * See TODO(b/150300347) + */ +public final class PagedViewOrientedState { + + private PagedOrientationHandler mOrientationHandler = new PortraitPagedViewHandler(); + + private int mTouchRotation = Surface.ROTATION_0; + private int mDisplayRotation = Surface.ROTATION_0; + /** + * If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake + * launcher orientations. + */ + private boolean mDisableMultipleOrientations; + + public void update(int touchRotation, int displayRotation) { + mDisplayRotation = displayRotation; + mTouchRotation = touchRotation; + if (mTouchRotation == Surface.ROTATION_90) { + mOrientationHandler = new LandscapePagedViewHandler(); + } else if (mTouchRotation == Surface.ROTATION_270) { + mOrientationHandler = new SeascapePagedViewHandler(); + } else { + mOrientationHandler = new PortraitPagedViewHandler(); + } + } + + /** + * @return {@code true} if the area where the user touched the nav bar is the expected + * location for the given display rotation. Ex. bottom of phone in portrait, or left side of + * phone in landscape, right side in seascape, etc. + * False otherwise + */ + public boolean isTouchRegionNaturalForDisplay() { + return mTouchRotation == mDisplayRotation; + } + + public boolean areMultipleLayoutOrientationsDisabled() { + return mDisableMultipleOrientations; + } + + public void disableMultipleOrientations(boolean disable) { + mDisableMultipleOrientations = disable; + if (disable) { + mOrientationHandler = new PortraitPagedViewHandler(); + } + } + + /** + * Gets the difference between the rotation of the device/display and which region the + * user is currently interacting with in factors of 90 degree clockwise rotations. + * Ex. Display is in portrait -> 0, user touches landscape region -> 1, this + * method would return 3 because it takes 3 clockwise 90 degree rotations from normal to + * landscape (portrait -> seascape -> reverse portrait -> landscape) + */ + public int getTouchDisplayDelta() { + return RotationHelper.deltaRotation(mTouchRotation, mDisplayRotation); + } + + public PagedOrientationHandler getOrientationHandler() { + return mOrientationHandler; + } +} diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index 812268a0f4..b193ffd8cb 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -176,7 +176,7 @@ public class NotificationMainView extends FrameLayout implements SingleAxisSwipe // SingleAxisSwipeDetector.Listener's @Override - public void onDragStart(boolean start) { } + public void onDragStart(boolean start, float startDisplacement) { } @Override diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 852928b416..95b13b448a 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -17,15 +17,26 @@ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import android.content.ContentResolver; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.provider.Settings; +import android.view.MotionEvent; +import android.view.Surface; import android.view.WindowManager; import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; @@ -38,6 +49,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; + public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; + public static boolean getAllowRotationDefaultValue() { // If the device was scaled, used the original dimensions to determine if rotation // is allowed of not. @@ -92,6 +105,18 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } else { mPrefs = null; } + + // TODO(b/150260456) Add this in home settings as well + final ContentResolver resolver = launcher.getContentResolver(); + final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) { + @Override + public void onChange(boolean selfChange) { + PagedView.sFlagForcedRotation = Utilities.isForcedRotation(mLauncher); + } + }; + resolver.registerContentObserver(Settings.Global.getUriFor( + FIXED_ROTATION_TRANSFORM_SETTING_NAME), + false, observer); } public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) { @@ -204,6 +229,118 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } } + public static int getDegreesFromRotation(int rotation) { + int degrees; + switch (rotation) { + case Surface.ROTATION_90: + degrees = 90; + break; + case Surface.ROTATION_180: + degrees = 180; + break; + case Surface.ROTATION_270: + degrees = 270; + break; + case Surface.ROTATION_0: + default: + degrees = 0; + break; + } + return degrees; + } + + public static int getRotationFromDegrees(int degrees, int currentRotation) { + int threshold = 70; + if (degrees >= (360 - threshold) || degrees < (threshold)) { + return Surface.ROTATION_0; + } else if (degrees < (90 + threshold)) { + return Surface.ROTATION_270; + } else if (degrees < 180 + threshold) { + return Surface.ROTATION_180; + } else { + return Surface.ROTATION_90; + } + } + + /** + * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. + * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... + * A value of 0 means no rotation has been applied + */ + public static int deltaRotation(int oldRotation, int newRotation) { + int delta = newRotation - oldRotation; + if (delta < 0) delta += 4; + return delta; + } + + /** + * Creates a matrix to transform the given motion event specified by degrees. + * If {@param inverse} is {@code true}, the inverse of that matrix will be applied + */ + public static void transformEvent(int degrees, MotionEvent ev, boolean inverse) { + Matrix transform = new Matrix(); + transform.setRotate(degrees); + if (inverse) { + Matrix inv = new Matrix(); + transform.invert(inv); + ev.transform(inv); + } else { + ev.transform(transform); + } + // TODO: Add scaling back in based on degrees +// if (getWidth() > 0 && getHeight() > 0) { +// float scale = ((float) getWidth()) / getHeight(); +// transform.postScale(scale, 1 / scale); +// } + } + + /** + * TODO(b/149658423): Have {@link com.android.quickstep.OrientationTouchTransformer + * also use this} + */ + public static Matrix getRotationMatrix(int screenWidth, int screenHeight, int displayRotation) { + Matrix m = new Matrix(); + switch (displayRotation) { + case Surface.ROTATION_0: + return m; + case Surface.ROTATION_90: + m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation)); + m.postTranslate(0, screenWidth); + break; + case Surface.ROTATION_270: + m.setRotate(360 - RotationHelper.getDegreesFromRotation(displayRotation)); + m.postTranslate(screenHeight, 0); + break; + } + return m; + } + + public static void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight, + int displayRotation) { + Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation); + m.mapRect(src); + } + + public static void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, + int screenHeight, int displayRotation) { + Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation); + Matrix inverse = new Matrix(); + m.invert(inverse); + inverse.mapRect(src); + } + + public static void getTargetRectForRotation(Rect srcOut, int screenWidth, int screenHeight, + int displayRotation) { + RectF wrapped = new RectF(srcOut); + Matrix m = RotationHelper.getRotationMatrix(screenWidth, screenHeight, displayRotation); + m.mapRect(wrapped); + wrapped.round(srcOut); + } + + public static boolean isRotationLandscape(int rotation) { + return rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90; + } + @Override public String toString() { return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d," diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index 9df6241a5b..34d69e9eec 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -230,7 +230,7 @@ public abstract class AbstractStateChangeTouchController } @Override - public void onDragStart(boolean start) { + public void onDragStart(boolean start, float startDisplacement) { mStartState = mLauncher.getStateManager().getState(); mIsLogContainerSet = false; if (mCurrentAnimation == null) { diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java index 30283dab8d..1276ece7e2 100644 --- a/src/com/android/launcher3/touch/BaseSwipeDetector.java +++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java @@ -236,7 +236,7 @@ public abstract class BaseSwipeDetector { } else { mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop; mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop; - } + } } protected abstract boolean shouldScrollStart(PointF displacement); diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java new file mode 100644 index 0000000000..3090d97fe0 --- /dev/null +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.touch; + +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState.ScaleAndTranslation; +import com.android.launcher3.PagedView; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.OverScroller; + +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; + +public class LandscapePagedViewHandler implements PagedOrientationHandler { + @Override + public float getCurrentAppAnimationScale(RectF src, RectF target) { + return src.height() / target.height(); + } + + @Override + public int getPrimaryValue(int x, int y) { + return y; + } + + @Override + public int getSecondaryValue(int x, int y) { + return x; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int minMaxScroll) { + pagedView.superScrollTo(secondaryScroll, minMaxScroll); + } + + @Override + public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) { + pagedView.scrollTo(pagedView.getScrollX() + x, unboundedScroll + y); + } + + @Override + public void scrollerStartScroll(OverScroller scroller, int newPosition) { + scroller.startScroll(scroller.getCurrPos(), newPosition - scroller.getCurrPos()); + } + + @Override + public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { + int scroll = pagedView.getScrollY(); + final int halfPageSize = pagedView.getNormalChildHeight() / 2; + final int screenCenter = mInsets.top + pagedView.getPaddingTop() + scroll + halfPageSize; + final int halfScreenSize = pagedView.getMeasuredHeight() / 2; + return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + } + + @Override + public float getDragLengthFactor(int dimension, int transitionDragLength) { + return Math.min(1.0f, (float) dimension / transitionDragLength); + } + + @Override + public boolean isGoingUp(float displacement) { + return displacement > 0; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int primaryScroll) { + pagedView.superScrollTo(pagedView.getScrollX(), primaryScroll); + } + + @Override + public void set(T target, Int2DAction action, int param) { + action.call(target, 0, param); + } + + @Override + public void set(T target, Float2DAction action, float param) { + action.call(target, 0, param); + } + + @Override + public float getPrimaryDirection(MotionEvent event, int pointerIndex) { + return event.getY(pointerIndex); + } + + @Override + public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) { + return velocityTracker.getYVelocity(pointerId); + } + + @Override + public int getMeasuredSize(View view) { + return view.getMeasuredHeight(); + } + + @Override + public int getPrimarySize(Rect rect) { + return rect.height(); + } + + @Override + public float getPrimarySize(RectF rect) { + return rect.height(); + } + + @Override + public int getSecondaryDimension(View view) { + return view.getWidth(); + } + + @Override + public ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view) { + float offscreenTranslationY = dp.heightPx - view.getPaddingTop(); + return new ScaleAndTranslation(1f, 0f, offscreenTranslationY); + } + + @Override + public float getTranslationValue(ScaleAndTranslation scaleAndTranslation) { + return scaleAndTranslation.translationY; + } + + @Override + public FloatProperty getPrimaryViewTranslate() { + return VIEW_TRANSLATE_Y; + } + + @Override + public FloatProperty getSecondaryViewTranslate() { + return VIEW_TRANSLATE_X; + } + + @Override + public void setPrimaryAndResetSecondaryTranslate(View view, float translation) { + view.setTranslationX(0); + view.setTranslationY(translation); + } + + @Override + public float getViewCenterPosition(View view) { + return view.getTop() + view.getTranslationY(); + } + + @Override + public int getPrimaryScroll(View view) { + return view.getScrollY(); + } + + @Override + public float getPrimaryScale(View view) { + return view.getScaleY(); + } + + @Override + public void setMaxScroll(AccessibilityEvent event, int maxScroll) { + event.setMaxScrollY(maxScroll); + } + + @Override + public boolean getRecentsRtlSetting(Resources resources) { + return !Utilities.isRtl(resources); + } + + @Override + public float getDegreesRotated() { + return 90; + } + + @Override + public void offsetTaskRect(RectF rect, float value, int delta) { + if (delta == 0) { + rect.offset(value, 0); + } else if (delta == 1) { + rect.offset(0, -value); + } else if (delta == 2) { + rect.offset(-value, 0); + } else { + rect.offset(0, value); + } + } + + @Override + public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { + Matrix m = new Matrix(); + m.setRotate(270); + m.postTranslate(0, screenWidth); + RectF newTarget = new RectF(); + RectF oldTarget = new RectF(src); + m.mapRect(newTarget, oldTarget); + src.set((int)newTarget.left, (int)newTarget.top, (int)newTarget.right, (int)newTarget.bottom); + } + + @Override + public int getChildStart(View view) { + return view.getTop(); + } + + @Override + public int getCenterForPage(View view, Rect insets) { + return (view.getPaddingLeft() + view.getMeasuredWidth() + insets.left + - insets.right - view.getPaddingRight()) / 2; + } + + @Override + public int getScrollOffsetStart(View view, Rect insets) { + return insets.top + view.getPaddingTop(); + } + + @Override + public int getScrollOffsetEnd(View view, Rect insets) { + return view.getHeight() - view.getPaddingBottom() - insets.bottom; + } + + @Override + public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() { + return HORIZONTAL; + } + + @Override + public int getShortEdgeLength(DeviceProfile dp) { + return dp.heightPx; + } + + @Override + public int getTaskDismissDirectionFactor() { + return 1; + } + + @Override + public ChildBounds getChildBounds(View child, int childStart, int pageCenter, + boolean layoutChild) { + final int childHeight = child.getMeasuredHeight(); + final int childBottom = childStart + childHeight; + final int childWidth = child.getMeasuredWidth(); + final int childLeft = pageCenter - childWidth/ 2; + if (layoutChild) { + child.layout(childLeft, childStart, childLeft + childWidth, childBottom); + } + return new ChildBounds(childHeight, childWidth, childBottom, childLeft); + } +} diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java new file mode 100644 index 0000000000..2f02076f8e --- /dev/null +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.touch; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState; +import com.android.launcher3.PagedView; +import com.android.launcher3.util.OverScroller; + +/** + * Abstraction layer to separate horizontal and vertical specific implementations + * for {@link com.android.launcher3.PagedView}. Majority of these implementations are (should be) as + * simple as choosing the correct X and Y analogous methods. + */ +public interface PagedOrientationHandler { + + interface Int2DAction { + void call(T target, int x, int y); + } + interface Float2DAction { + void call(T target, float x, float y); + } + Int2DAction VIEW_SCROLL_BY = View::scrollBy; + Int2DAction VIEW_SCROLL_TO = View::scrollTo; + Float2DAction CANVAS_TRANSLATE = Canvas::translate; + void set(T target, Int2DAction action, int param); + void set(T target, Float2DAction action, float param); + float getPrimaryDirection(MotionEvent event, int pointerIndex); + float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId); + int getMeasuredSize(View view); + int getPrimarySize(Rect rect); + float getPrimarySize(RectF rect); + int getSecondaryDimension(View view); + LauncherState.ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view); + float getTranslationValue(LauncherState.ScaleAndTranslation scaleAndTranslation); + FloatProperty getPrimaryViewTranslate(); + FloatProperty getSecondaryViewTranslate(); + void setPrimaryAndResetSecondaryTranslate(View view, float translation); + float getViewCenterPosition(View view); + int getPrimaryScroll(View view); + float getPrimaryScale(View view); + int getChildStart(View view); + int getCenterForPage(View view, Rect insets); + int getScrollOffsetStart(View view, Rect insets); + int getScrollOffsetEnd(View view, Rect insets); + SingleAxisSwipeDetector.Direction getOppositeSwipeDirection(); + int getShortEdgeLength(DeviceProfile dp); + int getTaskDismissDirectionFactor(); + ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild); + void setMaxScroll(AccessibilityEvent event, int maxScroll); + boolean getRecentsRtlSetting(Resources resources); + float getDegreesRotated(); + void offsetTaskRect(RectF rect, float value, int delta); + void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight); + float getCurrentAppAnimationScale(RectF src, RectF target); + int getPrimaryValue(int x, int y); + int getSecondaryValue(int x, int y); + void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll); + /** Uses {@params pagedView}.getScroll[X|Y]() method for the secondary amount*/ + void delegateScrollTo(PagedView pagedView, int primaryScroll); + void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y); + void scrollerStartScroll(OverScroller scroller, int newPosition); + CurveProperties getCurveProperties(PagedView pagedView, Rect insets); + float getDragLengthFactor(int dimension, int transitionDragLength); + boolean isGoingUp(float displacement); + + class CurveProperties { + public final int scroll; + public final int halfPageSize; + public final int screenCenter; + public final int halfScreenSize; + + public CurveProperties(int scroll, int halfPageSize, int screenCenter, int halfScreenSize) { + this.scroll = scroll; + this.halfPageSize = halfPageSize; + this.screenCenter = screenCenter; + this.halfScreenSize = halfScreenSize; + } + } + + class ChildBounds { + + public final int primaryDimension; + public final int secondaryDimension; + public final int childPrimaryEnd; + public final int childSecondaryEnd; + + ChildBounds(int primaryDimension, int secondaryDimension, int childPrimaryEnd, + int childSecondaryEnd) { + this.primaryDimension = primaryDimension; + this.secondaryDimension = secondaryDimension; + this.childPrimaryEnd = childPrimaryEnd; + this.childSecondaryEnd = childSecondaryEnd; + } + } +} diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java new file mode 100644 index 0000000000..fbd80bbfd1 --- /dev/null +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.touch; + +import android.content.res.Resources; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.FloatProperty; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState.ScaleAndTranslation; +import com.android.launcher3.PagedView; +import com.android.launcher3.Utilities; +import com.android.launcher3.util.OverScroller; + +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; + +public class PortraitPagedViewHandler implements PagedOrientationHandler { + + @Override + public float getCurrentAppAnimationScale(RectF src, RectF target) { + return src.width() / target.width(); + } + + @Override + public int getPrimaryValue(int x, int y) { + return x; + } + + @Override + public int getSecondaryValue(int x, int y) { + return y; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll) { + pagedView.superScrollTo(primaryScroll, secondaryScroll); + } + + @Override + public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) { + pagedView.scrollTo(unboundedScroll + x, pagedView.getScrollY() + y); + } + + @Override + public void scrollerStartScroll(OverScroller scroller, int newPosition) { + scroller.startScroll(newPosition - scroller.getCurrPos(), scroller.getCurrPos()); + } + + @Override + public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { + int scroll = pagedView.getScrollX(); + final int halfPageSize = pagedView.getNormalChildWidth() / 2; + final int screenCenter = mInsets.left + pagedView.getPaddingLeft() + scroll + halfPageSize; + final int halfScreenSize = pagedView.getMeasuredWidth() / 2; + return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + } + + @Override + public float getDragLengthFactor(int dimension, int transitionDragLength) { + return (float) dimension / transitionDragLength; + } + + @Override + public boolean isGoingUp(float displacement) { + return displacement < 0; + } + + @Override + public void delegateScrollTo(PagedView pagedView, int primaryScroll) { + pagedView.superScrollTo(primaryScroll, pagedView.getScrollY()); + } + + @Override + public void set(T target, Int2DAction action, int param) { + action.call(target, param, 0); + } + + @Override + public void set(T target, Float2DAction action, float param) { + action.call(target, param, 0); + } + + @Override + public float getPrimaryDirection(MotionEvent event, int pointerIndex) { + return event.getX(pointerIndex); + } + + @Override + public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) { + return velocityTracker.getXVelocity(pointerId); + } + + @Override + public int getMeasuredSize(View view) { + return view.getMeasuredWidth(); + } + + @Override + public int getPrimarySize(Rect rect) { + return rect.width(); + } + + @Override + public float getPrimarySize(RectF rect) { + return rect.width(); + } + + @Override + public int getSecondaryDimension(View view) { + return view.getHeight(); + } + + @Override + public ScaleAndTranslation getScaleAndTranslation(DeviceProfile dp, View view) { + float offscreenTranslationX = dp.widthPx - view.getPaddingStart(); + return new ScaleAndTranslation(1f, offscreenTranslationX, 0f); + } + + @Override + public float getTranslationValue(ScaleAndTranslation scaleAndTranslation) { + return scaleAndTranslation.translationX; + } + + @Override + public FloatProperty getPrimaryViewTranslate() { + return VIEW_TRANSLATE_X; + } + + @Override + public FloatProperty getSecondaryViewTranslate() { + return VIEW_TRANSLATE_Y; + } + + @Override + public void setPrimaryAndResetSecondaryTranslate(View view, float translation) { + view.setTranslationX(translation); + view.setTranslationY(0); + } + + @Override + public float getViewCenterPosition(View view) { + return view.getLeft() + view.getTranslationX(); + } + + @Override + public int getPrimaryScroll(View view) { + return view.getScrollX(); + } + + @Override + public float getPrimaryScale(View view) { + return view.getScaleX(); + } + + @Override + public void setMaxScroll(AccessibilityEvent event, int maxScroll) { + event.setMaxScrollX(maxScroll); + } + + @Override + public boolean getRecentsRtlSetting(Resources resources) { + return !Utilities.isRtl(resources); + } + + @Override + public float getDegreesRotated() { + return 0; + } + + @Override + public void offsetTaskRect(RectF rect, float value, int delta) { + if (delta == 0) { + rect.offset(value, 0); + } else if (delta == 1) { + rect.offset(0, -value); + } else if (delta == 2) { + rect.offset(-value, 0); + } else { + rect.offset(0, value); + } + } + + @Override + public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { + //no-op + } + + @Override + public int getChildStart(View view) { + return view.getLeft(); + } + + @Override + public int getCenterForPage(View view, Rect insets) { + return (view.getPaddingTop() + view.getMeasuredHeight() + insets.top + - insets.bottom - view.getPaddingBottom()) / 2; + } + + @Override + public int getScrollOffsetStart(View view, Rect insets) { + return insets.left + view.getPaddingLeft(); + } + + @Override + public int getScrollOffsetEnd(View view, Rect insets) { + return view.getWidth() - view.getPaddingRight() - insets.right; + } + + @Override + public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() { + return VERTICAL; + } + + @Override + public int getShortEdgeLength(DeviceProfile dp) { + return dp.widthPx; + } + + @Override + public int getTaskDismissDirectionFactor() { + return -1; + } + + @Override + public ChildBounds getChildBounds(View child, int childStart, int pageCenter, + boolean layoutChild) { + final int childWidth = child.getMeasuredWidth(); + final int childRight = childStart + childWidth; + final int childHeight = child.getMeasuredHeight(); + final int childTop = pageCenter - childHeight / 2; + if (layoutChild) { + child.layout(childStart, childTop, childRight, childTop + childHeight); + } + return new ChildBounds(childWidth, childHeight, childRight, childTop); + } +} diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java new file mode 100644 index 0000000000..f1875cc32f --- /dev/null +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.touch; + +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; + +import com.android.launcher3.Utilities; + +public class SeascapePagedViewHandler extends LandscapePagedViewHandler { + + @Override + public int getTaskDismissDirectionFactor() { + return -1; + } + + @Override + public boolean getRecentsRtlSetting(Resources resources) { + return Utilities.isRtl(resources); + } + + @Override + public void offsetTaskRect(RectF rect, float value, int delta) { + if (delta == 0) { + rect.offset(-value, 0); + } else if (delta == 1) { + rect.offset(0, value); + } else if (delta == 2) { + rect.offset(-value, 0); + } else { + rect.offset(0, -value); + } + } + + @Override + public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { + Matrix m = new Matrix(); + m.setRotate(90); + m.postTranslate(screenHeight, 0); + RectF newTarget = new RectF(); + RectF oldTarget = new RectF(src); + m.mapRect(newTarget, oldTarget); + src.set((int)newTarget.left, (int)newTarget.top, (int)newTarget.right, (int)newTarget.bottom); + } + + @Override + public float getDegreesRotated() { + return 270; + } + + @Override + public boolean isGoingUp(float displacement) { + return displacement < 0; + } +} diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java index 9d406f3bef..d72548619f 100644 --- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java +++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java @@ -148,7 +148,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { @Override protected void reportDragStartInternal(boolean recatch) { - mListener.onDragStart(!recatch); + float startDisplacement = mDir.extractDirection(mSubtractDisplacement); + mListener.onDragStart(!recatch, startDisplacement); } @Override @@ -165,8 +166,13 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { /** Listener to receive updates on the swipe. */ public interface Listener { - /** @param start whether this was the original drag start, as opposed to a recatch. */ - void onDragStart(boolean start); + /** + * TODO(b/150256055) consolidate all the different onDrag() methods into one + * @param start whether this was the original drag start, as opposed to a recatch. + * @param startDisplacement the initial touch displacement for the primary direction as + * given by by {@link Direction#extractDirection(PointF)} + */ + void onDragStart(boolean start, float startDisplacement); boolean onDrag(float displacement); diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index bdba39c371..11c10293e4 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -149,8 +149,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView /* SingleAxisSwipeDetector.Listener */ @Override - public void onDragStart(boolean start) { - } + public void onDragStart(boolean start, float startDisplacement) { } @Override public boolean onDrag(float displacement) { diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java index b0ece77c9b..472e1a1c91 100644 --- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java +++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java @@ -89,7 +89,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100, 100 - mTouchSlop); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -99,7 +99,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100, 100 + mTouchSlop); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -107,7 +107,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 + mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener, never()).onDragStart(anyBoolean()); + verify(mMockListener, never()).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -118,7 +118,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 + mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -129,7 +129,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 - mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -140,7 +140,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 - mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test @@ -151,7 +151,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100 + mTouchSlop, 100); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDragStart(anyBoolean()); + verify(mMockListener).onDragStart(anyBoolean(), anyFloat()); } @Test From acaf0749b3a58679793deb21e0cf655d3700bd8a Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Fri, 14 Feb 2020 10:45:55 -0800 Subject: [PATCH 13/39] Adjust target and src rects for swipe to home animation Floating icon animation runs entirely in portrait since that's what orienation launcher starts in. Current app window target rects are in landscape to be able animate to Overview correctly (which is not in portrait since the leash from WM is in the same orientation as that of foreground app). Invert that rect as the animation from app window to floating icon progresses. Fixes: 148528795 Change-Id: Ie1149a1a8904afc80bd1986f8d67b6f2d88c49f2 --- .../android/quickstep/BaseSwipeUpHandler.java | 40 +++++++++++------ .../quickstep/FallbackSwipeHandler.java | 3 +- .../quickstep/LauncherActivityInterface.java | 3 +- .../quickstep/LauncherSwipeHandler.java | 1 + .../com/android/quickstep/TaskViewUtils.java | 1 - .../util/AppWindowAnimationHelper.java | 8 ++-- .../android/quickstep/views/RecentsView.java | 2 +- .../quickstep/BaseActivityInterface.java | 15 ++++--- .../OrientationTouchTransformer.java | 1 + .../model/PagedViewOrientedState.java | 4 ++ .../launcher3/states/RotationHelper.java | 2 +- .../touch/LandscapePagedViewHandler.java | 44 +++++++------------ .../touch/PagedOrientationHandler.java | 10 +++-- .../touch/PortraitPagedViewHandler.java | 30 +++++-------- .../touch/SeascapePagedViewHandler.java | 36 +++++++-------- 15 files changed, 105 insertions(+), 95 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 8957b0d3e9..f50bb3e59a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -125,7 +125,7 @@ public abstract class BaseSwipeUpHandler mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); } }); + getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); windowAnim.start(velocityPxPerMs); homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index aedb756101..8d735915d9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -130,7 +130,6 @@ public final class TaskViewUtils { .setLauncherOnTop(true); final RecentsView recentsView = v.getRecentsView(); - params.setPagedOrientedState(recentsView.getPagedViewOrientedState()); final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); appAnimator.addUpdateListener(new MultiValueUpdateListener() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java index 6923ca25ff..91af1563d2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java @@ -22,6 +22,7 @@ import android.graphics.Matrix.ScaleToFit; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; +import android.view.Surface; import androidx.annotation.Nullable; @@ -157,11 +158,12 @@ public class AppWindowAnimationHelper { } private float getSrcToTargetScale() { - if (mOrientedState == null) { + if (mOrientedState == null || + (mOrientedState.getDisplayRotation() == Surface.ROTATION_0 + || mOrientedState.getDisplayRotation() == Surface.ROTATION_180)) { return mSourceRect.width() / mTargetRect.width(); } else { - return mOrientedState.getOrientationHandler() - .getCurrentAppAnimationScale(mSourceRect, mTargetRect); + return mSourceRect.height() / mTargetRect.height(); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 50a66299b4..4bd1215710 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -381,7 +381,7 @@ public abstract class RecentsView extends PagedView impl mOrientationListener = new OrientationEventListener(getContext()) { @Override public void onOrientationChanged(int i) { - int rotation = RotationHelper.getRotationFromDegrees(i, mPreviousRotation); + int rotation = RotationHelper.getRotationFromDegrees(i); if (mPreviousRotation != rotation) { animateRecentsRotationInPlace(rotation); mPreviousRotation = rotation; diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 58d6ee7998..64e053f318 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -149,13 +149,18 @@ public interface BaseActivityInterface { // No-op } - static RectF getDefaultWindowTargetRect(DeviceProfile dp) { + static RectF getDefaultWindowTargetRect(PagedOrientationHandler orientationHandler, + DeviceProfile dp) { final int halfIconSize = dp.iconSizePx / 2; - final float targetCenterX = dp.availableWidthPx / 2f; - final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx; + float primaryDimension = orientationHandler + .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); + float secondaryDimension = orientationHandler + .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); + final float targetX = primaryDimension / 2f; + final float targetY = secondaryDimension - dp.hotseatBarSizePx; // Fallback to animate to center of screen. - return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize, - targetCenterX + halfIconSize, targetCenterY + halfIconSize); + return new RectF(targetX - halfIconSize, targetY - halfIconSize, + targetX + halfIconSize, targetY + halfIconSize); } } diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index f78ac57cd7..832baf5eb8 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -300,6 +300,7 @@ class OrientationTouchTransformer { } boolean applyTransform(MotionEvent event, boolean forceTransform) { + // TODO(b/149658423): See if we can use RotationHelper.getRotationMatrix here MotionEvent tmp = MotionEvent.obtain(event); Matrix outMatrix = new Matrix(); int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation); diff --git a/src/com/android/launcher3/model/PagedViewOrientedState.java b/src/com/android/launcher3/model/PagedViewOrientedState.java index fd1154c9fa..1349effe72 100644 --- a/src/com/android/launcher3/model/PagedViewOrientedState.java +++ b/src/com/android/launcher3/model/PagedViewOrientedState.java @@ -83,6 +83,10 @@ public final class PagedViewOrientedState { } } + public int getDisplayRotation() { + return mDisplayRotation; + } + /** * Gets the difference between the rotation of the device/display and which region the * user is currently interacting with in factors of 90 degree clockwise rotations. diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 95b13b448a..3b134b0c03 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -249,7 +249,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { return degrees; } - public static int getRotationFromDegrees(int degrees, int currentRotation) { + public static int getRotationFromDegrees(int degrees) { int threshold = 70; if (degrees >= (360 - threshold) || degrees < (threshold)) { return Surface.ROTATION_0; diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 3090d97fe0..1db65b9705 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -18,10 +18,12 @@ package com.android.launcher3.touch; import android.content.res.Resources; import android.graphics.Matrix; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.util.FloatProperty; import android.view.MotionEvent; +import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.accessibility.AccessibilityEvent; @@ -30,6 +32,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.OverScroller; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; @@ -37,10 +40,6 @@ import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; public class LandscapePagedViewHandler implements PagedOrientationHandler { - @Override - public float getCurrentAppAnimationScale(RectF src, RectF target) { - return src.height() / target.height(); - } @Override public int getPrimaryValue(int x, int y) { @@ -77,13 +76,15 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @Override - public float getDragLengthFactor(int dimension, int transitionDragLength) { - return Math.min(1.0f, (float) dimension / transitionDragLength); + public boolean isGoingUp(float displacement) { + return displacement > 0; } @Override - public boolean isGoingUp(float displacement) { - return displacement > 0; + public void adjustFloatingIconStartVelocity(PointF velocity) { + float oldX = velocity.x; + float oldY = velocity.y; + velocity.set(-oldY, oldX); } @Override @@ -189,29 +190,18 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @Override - public void offsetTaskRect(RectF rect, float value, int delta) { - if (delta == 0) { - rect.offset(value, 0); - } else if (delta == 1) { - rect.offset(0, -value); - } else if (delta == 2) { - rect.offset(-value, 0); - } else { + public void offsetTaskRect(RectF rect, float value, int displayRotation) { + if (displayRotation == Surface.ROTATION_0) { rect.offset(0, value); + } else if (displayRotation == Surface.ROTATION_90) { + rect.offset(value, 0); + } else if (displayRotation == Surface.ROTATION_180) { + rect.offset(0, -value); + } else { + rect.offset(-value, 0); } } - @Override - public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { - Matrix m = new Matrix(); - m.setRotate(270); - m.postTranslate(0, screenWidth); - RectF newTarget = new RectF(); - RectF oldTarget = new RectF(src); - m.mapRect(newTarget, oldTarget); - src.set((int)newTarget.left, (int)newTarget.top, (int)newTarget.right, (int)newTarget.bottom); - } - @Override public int getChildStart(View view) { return view.getTop(); diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index 2f02076f8e..b4802cda90 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -18,6 +18,7 @@ package com.android.launcher3.touch; import android.content.res.Resources; import android.graphics.Canvas; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.util.FloatProperty; @@ -75,8 +76,6 @@ public interface PagedOrientationHandler { boolean getRecentsRtlSetting(Resources resources); float getDegreesRotated(); void offsetTaskRect(RectF rect, float value, int delta); - void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight); - float getCurrentAppAnimationScale(RectF src, RectF target); int getPrimaryValue(int x, int y); int getSecondaryValue(int x, int y); void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll); @@ -85,9 +84,14 @@ public interface PagedOrientationHandler { void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y); void scrollerStartScroll(OverScroller scroller, int newPosition); CurveProperties getCurveProperties(PagedView pagedView, Rect insets); - float getDragLengthFactor(int dimension, int transitionDragLength); boolean isGoingUp(float displacement); + /** + * Maps the velocity from the coordinate plane of the foreground app to that + * of Launcher's (which now will always be portrait) + */ + void adjustFloatingIconStartVelocity(PointF velocity); + class CurveProperties { public final int scroll; public final int halfPageSize; diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index fbd80bbfd1..22eee49df6 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -17,10 +17,13 @@ package com.android.launcher3.touch; import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.util.FloatProperty; import android.view.MotionEvent; +import android.view.Surface; import android.view.VelocityTracker; import android.view.View; import android.view.accessibility.AccessibilityEvent; @@ -29,6 +32,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.OverScroller; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; @@ -37,11 +41,6 @@ import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; public class PortraitPagedViewHandler implements PagedOrientationHandler { - @Override - public float getCurrentAppAnimationScale(RectF src, RectF target) { - return src.width() / target.width(); - } - @Override public int getPrimaryValue(int x, int y) { return x; @@ -77,13 +76,13 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @Override - public float getDragLengthFactor(int dimension, int transitionDragLength) { - return (float) dimension / transitionDragLength; + public boolean isGoingUp(float displacement) { + return displacement < 0; } @Override - public boolean isGoingUp(float displacement) { - return displacement < 0; + public void adjustFloatingIconStartVelocity(PointF velocity) { + //no-op } @Override @@ -189,23 +188,18 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @Override - public void offsetTaskRect(RectF rect, float value, int delta) { - if (delta == 0) { + public void offsetTaskRect(RectF rect, float value, int displayRotation) { + if (displayRotation == Surface.ROTATION_0) { rect.offset(value, 0); - } else if (delta == 1) { + } else if (displayRotation == Surface.ROTATION_90) { rect.offset(0, -value); - } else if (delta == 2) { + } else if (displayRotation == Surface.ROTATION_180) { rect.offset(-value, 0); } else { rect.offset(0, value); } } - @Override - public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { - //no-op - } - @Override public int getChildStart(View view) { return view.getLeft(); diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java index f1875cc32f..eebd87fc86 100644 --- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -17,9 +17,9 @@ package com.android.launcher3.touch; import android.content.res.Resources; -import android.graphics.Matrix; -import android.graphics.Rect; +import android.graphics.PointF; import android.graphics.RectF; +import android.view.Surface; import com.android.launcher3.Utilities; @@ -36,29 +36,18 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { } @Override - public void offsetTaskRect(RectF rect, float value, int delta) { - if (delta == 0) { - rect.offset(-value, 0); - } else if (delta == 1) { + public void offsetTaskRect(RectF rect, float value, int displayRotation) { + if (displayRotation == Surface.ROTATION_0) { rect.offset(0, value); - } else if (delta == 2) { - rect.offset(-value, 0); - } else { + } else if (displayRotation == Surface.ROTATION_90) { + rect.offset(value, 0); + } else if (displayRotation == Surface.ROTATION_180) { rect.offset(0, -value); + } else { + rect.offset(-value, 0); } } - @Override - public void mapRectFromNormalOrientation(Rect src, int screenWidth, int screenHeight) { - Matrix m = new Matrix(); - m.setRotate(90); - m.postTranslate(screenHeight, 0); - RectF newTarget = new RectF(); - RectF oldTarget = new RectF(src); - m.mapRect(newTarget, oldTarget); - src.set((int)newTarget.left, (int)newTarget.top, (int)newTarget.right, (int)newTarget.bottom); - } - @Override public float getDegreesRotated() { return 270; @@ -68,4 +57,11 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { public boolean isGoingUp(float displacement) { return displacement < 0; } + + @Override + public void adjustFloatingIconStartVelocity(PointF velocity) { + float oldX = velocity.x; + float oldY = velocity.y; + velocity.set(oldY, -oldX); + } } From b1eaa1752eeef669b3fb0cbbddc99c04b3ff0e55 Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 2 Mar 2020 18:34:34 -0800 Subject: [PATCH 14/39] Fixing activity being referred after destroying. This is perhaps not a leak, but it conflicts with how StrictMode activity leak detection works. Bug: 139137636 Change-Id: I1608e348d8b238142c42ed81ae125fd07ef6b673 --- .../src/com/android/launcher3/util/rule/SimpleActivityRule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java index 33a6cf9766..1dbba6a1f6 100644 --- a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java +++ b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java @@ -64,6 +64,7 @@ public class SimpleActivityRule implements TestRule { mBase.evaluate(); } finally { app.unregisterActivityLifecycleCallbacks(this); + mActivity = null; } } From c4c16e7aebea980a1acc1dd96be32392f99a55fc Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 2 Mar 2020 18:44:24 -0800 Subject: [PATCH 15/39] Fixing activity referred after destroying by PinnedStackAnimationListener This is perhaps not a leak, but it conflicts with how StrictMode activity leak detection works. Bug: 139137636 Change-Id: If7fd0361b8e5ff81a2c0adb2308793ff87349951 --- .../android/quickstep/views/RecentsView.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 27ef93c614..114071a93e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -268,15 +268,8 @@ public abstract class RecentsView extends PagedView impl } }; - private final IPinnedStackAnimationListener mIPinnedStackAnimationListener = - new IPinnedStackAnimationListener.Stub() { - @Override - public void onPinnedStackAnimationStarted() { - // Needed for activities that auto-enter PiP, which will not trigger a remote - // animation to be created - mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); - } - }; + private final PinnedStackAnimationListener mIPinnedStackAnimationListener = + new PinnedStackAnimationListener(); // Used to keep track of the last requested task list id, so that we do not request to load the // tasks again if we have already requested it and the task list has not changed @@ -451,6 +444,7 @@ public abstract class RecentsView extends PagedView impl mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this); RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this); mIdp.addOnChangeListener(this); + mIPinnedStackAnimationListener.setActivity(mActivity); SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener( mIPinnedStackAnimationListener); } @@ -466,6 +460,7 @@ public abstract class RecentsView extends PagedView impl RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this); mIdp.removeOnChangeListener(this); SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null); + mIPinnedStackAnimationListener.setActivity(null); } @Override @@ -1962,4 +1957,20 @@ public abstract class RecentsView extends PagedView impl /** @param isEmpty Whether RecentsView is empty (i.e. has no children) */ void onEmptyMessageUpdated(boolean isEmpty); } + + private static class PinnedStackAnimationListener extends + IPinnedStackAnimationListener.Stub { + private T mActivity; + + public void setActivity(T activity) { + mActivity = activity; + } + + @Override + public void onPinnedStackAnimationStarted() { + // Needed for activities that auto-enter PiP, which will not trigger a remote + // animation to be created + mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); + } + } } From 7edcd7803fc3ce452ff1c4cb06fcc56fe9176f41 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 2 Mar 2020 13:18:55 -0800 Subject: [PATCH 16/39] Makes spring variable dynamic resources so that they can be changed via plugin Bug: 147302669 Change-Id: Ia4e74ff46a4d8338a56ea75ed826b69102c42d93 --- .../quickstep/FallbackSwipeHandler.java | 2 +- .../quickstep/LauncherSwipeHandler.java | 3 +- .../quickstep/util/RectFSpringAnim.java | 18 +++++++-- .../util/StaggeredWorkspaceAnim.java | 12 +++--- .../android/quickstep/views/RecentsView.java | 22 +++++++---- res/values/config.xml | 38 ++++++++++++++++++- .../allapps/AllAppsTransitionController.java | 11 ++++-- .../launcher3/util/DynamicResource.java | 5 +++ .../android/launcher3/util/OverScroller.java | 13 +++++-- .../systemui/plugins/ResourceProvider.java | 5 +++ 10 files changed, 103 insertions(+), 26 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java index 41790803ef..65fba08831 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java @@ -420,7 +420,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler } }); getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); - windowAnim.start(velocityPxPerMs); + windowAnim.start(mContext, velocityPxPerMs); homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); mLauncherTransitionController = null; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java index a59c99cf2a..682c92c26d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java @@ -16,6 +16,7 @@ package com.android.quickstep.util; import android.animation.Animator; +import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; import android.graphics.RectF; @@ -28,6 +29,8 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.FlingSpringAnim; +import com.android.launcher3.util.DynamicResource; +import com.android.systemui.plugins.ResourceProvider; import java.util.ArrayList; import java.util.List; @@ -137,7 +140,12 @@ public class RectFSpringAnim { mAnimatorListeners.add(animatorListener); } - public void start(PointF velocityPxPerMs) { + /** + * Starts the fling/spring animation. + * @param context The activity context. + * @param velocityPxPerMs Velocity of swipe in px/ms. + */ + public void start(Context context, PointF velocityPxPerMs) { // Only tell caller that we ended if both x and y animations have ended. OnAnimationEndListener onXEndListener = ((animation, canceled, centerX, velocityX) -> { mRectXAnimEnded = true; @@ -166,10 +174,14 @@ public class RectFSpringAnim { mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener); float minVisibleChange = Math.abs(1f / mStartRect.height()); + ResourceProvider rp = DynamicResource.provider(context); + float damping = rp.getFloat(R.dimen.swipe_up_rect_damping_ratio); + float stiffness = rp.getFloat(R.dimen.swipe_up_rect_stiffness); + mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS) .setSpring(new SpringForce(1f) - .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) - .setStiffness(SpringForce.STIFFNESS_LOW)) + .setDampingRatio(damping) + .setStiffness(stiffness)) .setStartVelocity(velocityPxPerMs.y * minVisibleChange) .setMaxValue(1f) .setMinimumVisibleChange(minVisibleChange) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index babf13e88d..d2805ed431 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -41,7 +41,9 @@ import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.graphics.OverviewScrim; +import com.android.launcher3.util.DynamicResource; import com.android.quickstep.views.RecentsView; +import com.android.systemui.plugins.ResourceProvider; /** * Creates an animation where all the workspace items are moved into their final location, @@ -55,9 +57,6 @@ public class StaggeredWorkspaceAnim { private static final float MAX_VELOCITY_PX_PER_S = 22f; - private static final float DAMPING_RATIO = 0.7f; - private static final float STIFFNESS = 150f; - private final float mVelocity; private final float mSpringTransY; @@ -177,9 +176,12 @@ public class StaggeredWorkspaceAnim { v.setTranslationY(mSpringTransY); + ResourceProvider rp = DynamicResource.provider(v.getContext()); + float stiffness = rp.getFloat(R.dimen.staggered_stiffness); + float damping = rp.getFloat(R.dimen.staggered_damping_ratio); ObjectAnimator springTransY = new SpringAnimationBuilder<>(v, VIEW_TRANSLATE_Y) - .setStiffness(STIFFNESS) - .setDampingRatio(DAMPING_RATIO) + .setStiffness(stiffness) + .setDampingRatio(damping) .setMinimumVisibleChange(1f) .setEndValue(0) .setStartVelocity(mVelocity) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 4bd1215710..55b095f770 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -80,7 +80,6 @@ import android.view.animation.Interpolator; import android.widget.ListView; import androidx.annotation.Nullable; -import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; @@ -103,6 +102,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.PendingAnimation; import com.android.launcher3.util.Themes; @@ -117,6 +117,7 @@ import com.android.quickstep.TaskUtils; import com.android.quickstep.ViewUtils; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.LayoutUtils; +import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -1199,10 +1200,14 @@ public abstract class RecentsView extends PagedView impl int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView); int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor(); if (UNSTABLE_SPRINGS.get() && taskView instanceof TaskView) { + ResourceProvider rp = DynamicResource.provider(mActivity); + float dampingRatio = rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio); + float stiffness = rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness); + addAnim(new SpringObjectAnimator<>(taskView, secondaryViewTranslate, - MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, - SpringForce.STIFFNESS_MEDIUM, 0, verticalFactor * secondaryTaskDimension), - duration, LINEAR, anim); + MIN_VISIBLE_CHANGE_PIXELS, dampingRatio, + stiffness, 0, verticalFactor * secondaryTaskDimension), + duration, LINEAR, anim); } else { addAnim(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate, verticalFactor * secondaryTaskDimension), duration, LINEAR, anim); @@ -1276,10 +1281,13 @@ public abstract class RecentsView extends PagedView impl int scrollDiff = newScroll[i] - oldScroll[i] + offset; if (scrollDiff != 0) { if (UNSTABLE_SPRINGS.get() && child instanceof TaskView) { + ResourceProvider rp = DynamicResource.provider(mActivity); + float damping = rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio); + float stiffness = rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness); + addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X, - MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, - SpringForce.STIFFNESS_MEDIUM, - 0, scrollDiff), duration, ACCEL, anim); + MIN_VISIBLE_CHANGE_PIXELS, damping, + stiffness, 0, scrollDiff), duration, ACCEL, anim); } else { Property translationProperty = mOrientationHandler.getPrimaryViewTranslate(); addAnim(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff), diff --git a/res/values/config.xml b/res/values/config.xml index 8aff6da7dc..35e5e6dc63 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -121,5 +121,41 @@ true - + 0.75 + 600 + + 0.5 + 1500 + + 0.5 + 1500 + + 0.75 + 200 + + 0.75 + 200 + + 0.7 + 150 + + + @dimen/all_apps_spring_damping_ratio + @dimen/all_apps_spring_stiffness + + @dimen/dismiss_task_trans_y_damping_ratio + @dimen/dismiss_task_trans_y_stiffness + + @dimen/dismiss_task_trans_x_damping_ratio + @dimen/dismiss_task_trans_x_stiffness + + @dimen/horizontal_spring_damping_ratio + @dimen/horizontal_spring_stiffness + + @dimen/swipe_up_rect_damping_ratio + @dimen/swipe_up_rect_stiffness + + @dimen/staggered_damping_ratio + @dimen/staggered_stiffness + diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index e49ff30f20..217916205d 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -32,8 +32,10 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.anim.SpringObjectAnimator; +import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; +import com.android.systemui.plugins.ResourceProvider; /** * Handles AllApps view transition. @@ -47,9 +49,6 @@ import com.android.launcher3.views.ScrimView; */ public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { - private static final float SPRING_DAMPING_RATIO = 0.75f; - private static final float SPRING_STIFFNESS = 600f; - public static final FloatProperty ALL_APPS_PROGRESS = new FloatProperty("allAppsProgress") { @@ -187,8 +186,12 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil public Animator createSpringAnimation(float... progressValues) { if (UNSTABLE_SPRINGS.get()) { + ResourceProvider rp = DynamicResource.provider(mLauncher); + float damping = rp.getFloat(R.dimen.all_apps_spring_damping_ratio); + float stiffness = rp.getFloat(R.dimen.all_apps_spring_stiffness); + return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange, - SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues); + damping, stiffness, progressValues); } return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues); } diff --git a/src/com/android/launcher3/util/DynamicResource.java b/src/com/android/launcher3/util/DynamicResource.java index 8a75767f42..e6ee1864dc 100644 --- a/src/com/android/launcher3/util/DynamicResource.java +++ b/src/com/android/launcher3/util/DynamicResource.java @@ -68,6 +68,11 @@ public class DynamicResource implements ResourceProvider, PluginListener { diff --git a/src_plugins/com/android/systemui/plugins/ResourceProvider.java b/src_plugins/com/android/systemui/plugins/ResourceProvider.java index eaed9e7f1c..d1767a0931 100644 --- a/src_plugins/com/android/systemui/plugins/ResourceProvider.java +++ b/src_plugins/com/android/systemui/plugins/ResourceProvider.java @@ -44,4 +44,9 @@ public interface ResourceProvider extends Plugin { * @see android.content.res.Resources#getColor(int) */ int getColor(int resId); + + /** + * @see android.content.res.Resources#getFloat(int) + */ + float getFloat(int resId); } From 294238a2fa0553cbeec038df79cafed68beb8dd2 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 2 Mar 2020 22:03:06 -0800 Subject: [PATCH 17/39] Dump GestureState and created overview activity Bug: 150525635 Change-Id: I6f5bf0f9c917845b6e880323dbba99552a9c43ba --- .../quickstep/TouchInteractionService.java | 16 +++++++++------- .../src/com/android/quickstep/GestureState.java | 13 ++++++++++++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 7617ffe5bc..ffa17ce119 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -17,13 +17,7 @@ package com.android.quickstep; import static android.view.MotionEvent.ACTION_DOWN; -import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; -import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME; -import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI; -import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; -import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; @@ -755,10 +749,18 @@ public class TouchInteractionService extends Service implements PluginListener Date: Tue, 3 Mar 2020 15:03:04 -0800 Subject: [PATCH 19/39] Rotate touch events for land/seascape navbars Previous change removed checking for navbar location to transpose touch event. fixes: 150700927 Test: Quickstep TAPL test passes. Change-Id: I47683f8a75f24c8bfc46437e0eef9172c23cc452 --- .../src/com/android/quickstep/BaseSwipeUpHandler.java | 5 +++-- .../inputconsumers/OtherActivityInputConsumer.java | 3 ++- .../src/com/android/quickstep/views/RecentsView.java | 10 ++++++++-- src/com/android/launcher3/states/RotationHelper.java | 3 +-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index f50bb3e59a..1d1c7bb4e2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -45,6 +45,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.touch.PagedOrientationHandler; @@ -146,8 +147,8 @@ public abstract class BaseSwipeUpHandler getRecentsViewDispatcher() { - return mRecentsView != null ? mRecentsView.getEventDispatcher() : null; + public Consumer getRecentsViewDispatcher(RotationMode navBarRotationMode) { + return mRecentsView != null ? mRecentsView.getEventDispatcher(navBarRotationMode) : null; } @UiThread diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index bd9f330082..893868bdf8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -176,7 +176,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Proxy events to recents view if (mPassedWindowMoveSlop && mInteractionHandler != null && !mRecentsViewDispatcher.hasConsumer()) { - mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher()); + mRecentsViewDispatcher.setConsumer(mInteractionHandler + .getRecentsViewDispatcher(mNavBarPosition.getRotationMode())); } int edgeFlags = ev.getEdgeFlags(); ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index c201455cd2..72e6391707 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -96,6 +96,7 @@ import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -1946,8 +1947,13 @@ public abstract class RecentsView extends PagedView impl return offsetX; } - public Consumer getEventDispatcher() { - int degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation); + public Consumer getEventDispatcher(RotationMode navBarRotationMode) { + float degreesRotated; + if (navBarRotationMode == RotationMode.NORMAL) { + degreesRotated = RotationHelper.getDegreesFromRotation(mLayoutRotation); + } else { + degreesRotated = -navBarRotationMode.surfaceRotation; + } if (degreesRotated == 0) { return super::onTouchEvent; } diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 3b134b0c03..b47ef72424 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -17,7 +17,6 @@ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -277,7 +276,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { * Creates a matrix to transform the given motion event specified by degrees. * If {@param inverse} is {@code true}, the inverse of that matrix will be applied */ - public static void transformEvent(int degrees, MotionEvent ev, boolean inverse) { + public static void transformEvent(float degrees, MotionEvent ev, boolean inverse) { Matrix transform = new Matrix(); transform.setRotate(degrees); if (inverse) { From 9151c011b97950038de799bd99c68ffa7c4effa8 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Mon, 24 Feb 2020 17:06:28 -0800 Subject: [PATCH 20/39] Allow Launcher to automatically restore widgets This replaces PendingAppWidgetHostView with configured widget view when OPTION_APPWIDGET_RESTORE_COMPLETED is set to True by a widget provider. Bug: 63667276 Test: Manual Change-Id: Ide21f5e9a7dac7e3d6a745660a38ad0b951b47d3 --- src/com/android/launcher3/Launcher.java | 12 +++++++ .../launcher3/LauncherAppWidgetHost.java | 19 ++++++++++-- .../widget/PendingAppWidgetHostView.java | 10 ++++++ .../launcher3/widget/WidgetManagerHelper.java | 11 +++++++ .../launcher3/ui/widget/BindWidgetTest.java | 31 +++++++++++++++++-- 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 33f5a9591b..d6e8710120 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2305,6 +2305,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; getModelWriter().updateItemInDatabase(item); } + else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY) + && appWidgetInfo.configure != null) { + if (mAppWidgetManager.isAppWidgetRestored(item.appWidgetId)) { + item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; + getModelWriter().updateItemInDatabase(item); + } + } } if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { @@ -2318,6 +2325,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, item.minSpanX = appWidgetInfo.minSpanX; item.minSpanY = appWidgetInfo.minSpanY; view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo); + } else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) + && appWidgetInfo != null) { + mAppWidgetHost.addPendingView(item.appWidgetId, + new PendingAppWidgetHostView(this, item, mIconCache, false)); + view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo); } else { view = new PendingAppWidgetHostView(this, item, mIconCache, false); } diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 4e29a95fb2..9921f76f0c 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -32,6 +32,7 @@ import android.widget.Toast; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.widget.DeferredAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; @@ -53,12 +54,14 @@ public class LauncherAppWidgetHost extends AppWidgetHost { private final ArrayList mProviderChangeListeners = new ArrayList<>(); private final SparseArray mViews = new SparseArray<>(); + private final SparseArray mPendingViews = new SparseArray<>(); private final Context mContext; private int mFlags = FLAG_RESUMED; private IntConsumer mAppWidgetRemovedCallback = null; + public LauncherAppWidgetHost(Context context) { this(context, null); } @@ -73,7 +76,13 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @Override protected LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { - LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context); + final LauncherAppWidgetHostView view; + if (mPendingViews.get(appWidgetId) != null) { + view = mPendingViews.get(appWidgetId); + mPendingViews.remove(appWidgetId); + } else { + view = new LauncherAppWidgetHostView(context); + } mViews.put(appWidgetId, view); return view; } @@ -189,6 +198,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } } + void addPendingView(int appWidgetId, PendingAppWidgetHostView view) { + mPendingViews.put(appWidgetId, view); + } + public AppWidgetHostView createView(Context context, int appWidgetId, LauncherAppWidgetProviderInfo appWidget) { if (appWidget.isCustomWidget()) { @@ -238,8 +251,8 @@ public class LauncherAppWidgetHost extends AppWidgetHost { /** * Called on an appWidget is removed for a widgetId - * @param appWidgetId - * TODO: make this override when SDK is updated + * + * @param appWidgetId TODO: make this override when SDK is updated */ public void onAppWidgetRemoved(int appWidgetId) { if (mAppWidgetRemovedCallback == null) { diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java index 6038873c42..895f8de5a9 100644 --- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java @@ -33,6 +33,7 @@ import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.View; import android.view.View.OnClickListener; +import android.widget.RemoteViews; import com.android.launcher3.DeviceProfile; import com.android.launcher3.FastBitmapDrawable; @@ -93,6 +94,15 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView } } + @Override + public void updateAppWidget(RemoteViews remoteViews) { + super.updateAppWidget(remoteViews); + WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext()); + if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) { + reInflate(); + } + } + @Override public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth, int maxHeight) { diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java index 8b08d7757c..f3c7822d0b 100644 --- a/src/com/android/launcher3/widget/WidgetManagerHelper.java +++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java @@ -49,6 +49,9 @@ import java.util.stream.Stream; */ public class WidgetManagerHelper { + //TODO: replace this with OPTION_APPWIDGET_RESTORE_COMPLETED b/63667276 + public static final String WIDGET_OPTION_RESTORE_COMPLETED = "appWidgetRestoreCompleted"; + final AppWidgetManager mAppWidgetManager; final Context mContext; @@ -127,6 +130,14 @@ public class WidgetManagerHelper { return null; } + /** + * Returns if a AppWidgetProvider has marked a widget restored + */ + public boolean isAppWidgetRestored(int appWidgetId) { + return !WidgetsModel.GO_DISABLE_WIDGETS && mAppWidgetManager.getAppWidgetOptions( + appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED); + } + public static Map getAllProvidersMap(Context context) { if (WidgetsModel.GO_DISABLE_WIDGETS) { return Collections.emptyMap(); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 21a654f43b..793af48e29 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -21,6 +21,7 @@ import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOption import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.appwidget.AppWidgetHost; @@ -33,6 +34,7 @@ import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; +import android.widget.RemoteViews; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -41,6 +43,7 @@ import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.R; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; @@ -86,7 +89,8 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // Clear all existing data LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); - LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); + LauncherSettings.Settings.call(mResolver, + LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); } @After @@ -172,6 +176,26 @@ public class BindWidgetTest extends AbstractLauncherUiTest { assertNotNull(AppWidgetManager.getInstance(mTargetContext) .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( LauncherSettings.Favorites.APPWIDGET_ID)))); + + // send OPTION_APPWIDGET_RESTORE_COMPLETED + int appWidgetId = mCursor.getInt( + mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID)); + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext); + + Bundle b = new Bundle(); + b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true); + RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready); + appWidgetManager.updateAppWidgetOptions(appWidgetId, b); + appWidgetManager.updateAppWidget(appWidgetId, remoteViews); + + + // verify changes are reflected + waitForLauncherCondition("App widget options did not update", + l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean( + WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED)); + executeOnLauncher(l -> l.getAppWidgetHost().startListening()); + verifyWidgetPresent(info); + assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT)); } @Test @@ -254,6 +278,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { /** * Creates a LauncherAppWidgetInfo corresponding to {@param info} + * * @param bindWidget if true the info is bound and a valid widgetId is assigned to * the LauncherAppWidgetInfo */ @@ -306,7 +331,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName)); return packages; }); - while(true) { + while (true) { try { mTargetContext.getPackageManager().getPackageInfo( pkg, PackageManager.GET_UNINSTALLED_PACKAGES); @@ -316,7 +341,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } } pkg = invalidPackage + count; - count ++; + count++; } LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10, new ComponentName(pkg, "com.test.widgetprovider")); From a9673f4b5cba8176583dec8100cc8be203c6e382 Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Wed, 19 Feb 2020 23:48:21 +0000 Subject: [PATCH 21/39] Adds option to launch Gesture Sandbox from dev settings. This is set to have the same task affinity as Launcher so that it inherits the ability to ignore system gestures. Bug: 148542211 Change-Id: I29bc5599dea61c44be721dcc0bec7bc494c415c4 --- quickstep/AndroidManifest-launcher.xml | 2 +- quickstep/AndroidManifest.xml | 1 + .../settings/DeveloperOptionsFragment.java | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml index 60afddb0a8..527bfc3854 100644 --- a/quickstep/AndroidManifest-launcher.xml +++ b/quickstep/AndroidManifest-launcher.xml @@ -52,7 +52,7 @@ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize" android:resizeableActivity="true" android:resumeWhilePausing="true" - android:taskAffinity="" + android:taskAffinity="${packageName}.launcher" android:enabled="true"> diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index a06a2dd208..d3606132e5 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -93,6 +93,7 @@ android:name="com.android.quickstep.interaction.GestureSandboxActivity" android:autoRemoveFromRecents="true" android:excludeFromRecents="true" + android:taskAffinity="${packageName}.launcher" android:screenOrientation="portrait"> diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java index 049fda9372..8dc2e611e9 100644 --- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java +++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java @@ -96,6 +96,7 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { initFlags(); loadPluginPrefs(); + maybeAddSandboxCategory(); } @Override @@ -203,6 +204,31 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { }); } + private void maybeAddSandboxCategory() { + Context context = getContext(); + if (context == null) { + return; + } + Intent launchSandboxIntent = + new Intent("com.android.quickstep.action.GESTURE_SANDBOX") + .setPackage(context.getPackageName()) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (launchSandboxIntent.resolveActivity(context.getPackageManager()) == null) { + return; + } + PreferenceCategory sandboxCategory = newCategory("Sandbox"); + Preference launchSandboxPreference = new Preference(context); + launchSandboxPreference.setKey("launchSandbox"); + launchSandboxPreference.setTitle("Launch Gesture Navigation Sandbox"); + launchSandboxPreference.setSummary( + "This provides tutorials and a place to practice navigation gestures."); + launchSandboxPreference.setOnPreferenceClickListener(preference -> { + startActivity(launchSandboxIntent); + return true; + }); + sandboxCategory.addPreference(launchSandboxPreference); + } + private String toName(String action) { String str = action.replace("com.android.systemui.action.PLUGIN_", "") .replace("com.android.launcher3.action.PLUGIN_", ""); From 5fcf061cb9854bef17800f8f0df292d82867d3ca Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 3 Mar 2020 12:22:25 -0800 Subject: [PATCH 22/39] Set default launcher orientation to portrait Also stop OrientationTouchTransformer from spamming logs b/150214193 should track properly handling this in the future Fixes: 146176182 Test: New vertical layout showed up when swiping to Overview w/ landscape app. Works for both autorotate on and off. Change-Id: I3707cf7b5693be0fcf4d63491901f4ec00793f4f --- .../android/quickstep/views/RecentsView.java | 8 +++ .../OrientationTouchTransformer.java | 2 +- src/com/android/launcher3/PagedView.java | 1 + .../launcher3/states/RotationHelper.java | 71 +++++++++++++++---- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 72e6391707..2942bef939 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -85,6 +85,7 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; @@ -275,6 +276,9 @@ public abstract class RecentsView extends PagedView impl } }; + private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener = + isForcedRotation -> RecentsView.this.disableMultipleLayoutRotations(!isForcedRotation); + private final PinnedStackAnimationListener mIPinnedStackAnimationListener = new PinnedStackAnimationListener(); @@ -467,6 +471,8 @@ public abstract class RecentsView extends PagedView impl mIPinnedStackAnimationListener.setActivity(mActivity); SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener( mIPinnedStackAnimationListener); + Launcher launcher = Launcher.getLauncher(getContext()); + launcher.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener); } @Override @@ -481,6 +487,8 @@ public abstract class RecentsView extends PagedView impl mIdp.removeOnChangeListener(this); SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null); mIPinnedStackAnimationListener.setActivity(null); + Launcher launcher = Launcher.getLauncher(getContext()); + launcher.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener); } @Override diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 832baf5eb8..92eb036fbc 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -49,7 +49,7 @@ import java.io.PrintWriter; class OrientationTouchTransformer { private static final String TAG = "OrientationTouchTransformer"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final int MAX_ORIENTATIONS = 4; private SparseArray mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index ef6bd3d019..a6180a6360 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -47,6 +47,7 @@ import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.pageindicators.PageIndicator; +import com.android.launcher3.states.RotationHelper; import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.touch.OverScroll; import com.android.launcher3.touch.PagedOrientationHandler; diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index b47ef72424..43d54eb28b 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -17,8 +17,10 @@ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; + import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.content.ContentResolver; @@ -41,6 +43,9 @@ import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.UiThreadHelper; +import java.util.ArrayList; +import java.util.List; + /** * Utility class to manage launcher rotation */ @@ -49,16 +54,38 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; + private final ContentResolver mContentResolver; + + /** + * Listener to receive changes when {@link #FIXED_ROTATION_TRANSFORM_SETTING_NAME} flag changes. + */ + public interface ForcedRotationChangedListener { + void onForcedRotationChanged(boolean isForcedRotation); + } public static boolean getAllowRotationDefaultValue() { - // If the device was scaled, used the original dimensions to determine if rotation - // is allowed of not. + // If the device's pixel density was scaled (usually via settings for A11y), use the + // original dimensions to determine if rotation is allowed of not. Resources res = Resources.getSystem(); int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE; return originalSmallestWidth >= 600; } + + private final ContentObserver mContentObserver = + new ContentObserver(MAIN_EXECUTOR.getHandler()) { + @Override + public void onChange(boolean selfChange) { + boolean forcedRotation = Utilities.isForcedRotation(mLauncher); + PagedView.sFlagForcedRotation = forcedRotation; + updateForcedRotation(); + for (ForcedRotationChangedListener listener : mForcedRotationChangedListeners) { + listener.onForcedRotationChanged(forcedRotation); + } + } + }; + public static final int REQUEST_NONE = 0; public static final int REQUEST_ROTATE = 1; public static final int REQUEST_LOCK = 2; @@ -68,6 +95,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { private boolean mIgnoreAutoRotateSettings; private boolean mAutoRotateEnabled; + private boolean mForcedRotation; + private List mForcedRotationChangedListeners = new ArrayList<>(); /** * Rotation request made by @@ -96,6 +125,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { // On large devices we do not handle auto-rotate differently. mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation); + updateForcedRotation(); if (!mIgnoreAutoRotateSettings) { mPrefs = Utilities.getPrefs(mLauncher); mPrefs.registerOnSharedPreferenceChangeListener(this); @@ -106,16 +136,24 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } // TODO(b/150260456) Add this in home settings as well - final ContentResolver resolver = launcher.getContentResolver(); - final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) { - @Override - public void onChange(boolean selfChange) { - PagedView.sFlagForcedRotation = Utilities.isForcedRotation(mLauncher); - } - }; - resolver.registerContentObserver(Settings.Global.getUriFor( - FIXED_ROTATION_TRANSFORM_SETTING_NAME), - false, observer); + mContentResolver = launcher.getContentResolver(); + mContentResolver.registerContentObserver(Settings.Global.getUriFor( + FIXED_ROTATION_TRANSFORM_SETTING_NAME), false, mContentObserver); + } + + private void updateForcedRotation() { + mForcedRotation = !getAllowRotationDefaultValue() && Utilities.isForcedRotation(mLauncher); + } + + /** + * will not be called when first registering the listener. + */ + public void addForcedRotationCallback(ForcedRotationChangedListener listener) { + mForcedRotationChangedListeners.add(listener); + } + + public void removeForcedRotationCallback(ForcedRotationChangedListener listener) { + mForcedRotationChangedListeners.remove(listener); } public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) { @@ -197,6 +235,10 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { if (mPrefs != null) { mPrefs.unregisterOnSharedPreferenceChangeListener(this); } + if (mContentResolver != null) { + mContentResolver.unregisterContentObserver(mContentObserver); + } + mForcedRotationChangedListeners.clear(); } } @@ -206,7 +248,10 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } final int activityFlags; - if (mStateHandlerRequest != REQUEST_NONE) { + if (mForcedRotation) { + // TODO(b/150214193) Properly address this + activityFlags = SCREEN_ORIENTATION_PORTRAIT; + } else if (mStateHandlerRequest != REQUEST_NONE) { activityFlags = mStateHandlerRequest == REQUEST_LOCK ? SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; } else if (mCurrentTransitionRequest != REQUEST_NONE) { From 99f8a99463aee09ad48aa704611a974a0a96fd8f Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 4 Mar 2020 10:52:22 -0800 Subject: [PATCH 23/39] Enabling testQuickSwitchFromApp after causes of flakiness were fixed Bug: 143285809 Change-Id: Ie0c20bfdb04276adf349e90bf6826c89e9fbb5e3 --- .../tests/src/com/android/quickstep/TaplTestsQuickstep.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 03d7c3e31a..75fcfe2f19 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -17,8 +17,6 @@ package com.android.quickstep; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; -import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL; -import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -41,7 +39,6 @@ import com.android.launcher3.tapl.Overview; import com.android.launcher3.tapl.OverviewTask; import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.ui.TaplTestsLauncher3; -import com.android.launcher3.util.rule.TestStabilityRule; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import com.android.quickstep.views.RecentsView; @@ -254,8 +251,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch @PortraitLandscape - // b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake. - @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT) public void testQuickSwitchFromApp() throws Exception { startTestActivity(2); startTestActivity(3); From 321bfae905a90583e666b0150cf2edf8a83a5cda Mon Sep 17 00:00:00 2001 From: Sreyas Date: Fri, 21 Feb 2020 17:53:02 -0800 Subject: [PATCH 24/39] OverviewActions: Creating single view instance. Makes use of there being a single instance of OverviewActionsView rather than each Task having it's own. Change-Id: I881121f84de99cade3cd8f07fa8510a557b28f57 --- .../res/layout/fallback_recents_activity.xml | 5 ++ .../res/layout/overview_panel.xml | 24 ++---- .../android/quickstep/views/RecentsView.java | 63 +++++++++++++- .../com/android/quickstep/views/TaskView.java | 86 +++---------------- .../launcher3/BaseQuickstepLauncher.java | 5 +- .../BaseRecentsViewStateController.java | 11 +++ res/layout/launcher.xml | 9 +- res/layout/overview_actions_holder.xml | 20 +++++ res/layout/overview_panel.xml | 10 +-- src/com/android/launcher3/Launcher.java | 11 ++- .../tapl/LauncherInstrumentation.java | 2 +- 11 files changed, 140 insertions(+), 106 deletions(-) create mode 100644 res/layout/overview_actions_holder.xml diff --git a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml index ef272ed806..686189ea81 100644 --- a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml +++ b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml @@ -28,4 +28,9 @@ android:clipToPadding="false" android:outlineProvider="none" android:theme="@style/HomeScreenElementTheme" /> + + + diff --git a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml b/quickstep/recents_ui_overrides/res/layout/overview_panel.xml index a572cad273..eac0bfa8eb 100644 --- a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml +++ b/quickstep/recents_ui_overrides/res/layout/overview_panel.xml @@ -1,5 +1,4 @@ - - - - - + android:layout_width="match_parent" + android:layout_height="match_parent" + android:accessibilityPaneTitle="@string/accessibility_recent_apps" + android:clipChildren="false" + android:clipToPadding="false" + android:theme="@style/HomeScreenElementTheme" + android:visibility="invisible" /> diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 2942bef939..9bc95d7fd6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -29,6 +29,7 @@ import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS; @@ -66,6 +67,7 @@ import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Property; import android.util.SparseBooleanArray; +import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -84,6 +86,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; +import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; @@ -113,6 +116,7 @@ import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskVisualsChangeListener; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; @@ -206,7 +210,7 @@ public abstract class RecentsView extends PagedView impl private boolean mDwbToastShown; protected boolean mDisallowScrollToClearAll; private boolean mOverlayEnabled; - private boolean mFreezeViewVisibility; + protected boolean mFreezeViewVisibility; /** * TODO: Call reloadIdNeeded in onTaskStackChanged. @@ -325,6 +329,8 @@ public abstract class RecentsView extends PagedView impl // Keeps track of the index where the first TaskView should be private int mTaskViewStartIndex = 0; + private View mActionsView; + private boolean mGestureRunning = false; private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = (inMultiWindowMode) -> { @@ -383,6 +389,11 @@ public abstract class RecentsView extends PagedView impl int rotation = RotationHelper.getRotationFromDegrees(i); if (mPreviousRotation != rotation) { animateRecentsRotationInPlace(rotation); + if (rotation == 0) { + showActionsView(); + } else { + hideActionsView(); + } mPreviousRotation = rotation; } } @@ -473,6 +484,7 @@ public abstract class RecentsView extends PagedView impl mIPinnedStackAnimationListener); Launcher launcher = Launcher.getLauncher(getContext()); launcher.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener); + addActionsView(); } @Override @@ -636,6 +648,7 @@ public abstract class RecentsView extends PagedView impl if (getTaskViewCount() != requiredTaskCount) { if (indexOfChild(mClearAllButton) != -1) { removeView(mClearAllButton); + hideActionsView(); } for (int i = getTaskViewCount(); i < requiredTaskCount; i++) { addView(mTaskViewPool.getView()); @@ -645,6 +658,7 @@ public abstract class RecentsView extends PagedView impl } if (requiredTaskCount > 0) { addView(mClearAllButton); + showActionsView(); } } @@ -684,6 +698,7 @@ public abstract class RecentsView extends PagedView impl if (indexOfChild(mClearAllButton) != -1) { removeView(mClearAllButton); } + hideActionsView(); } public int getTaskViewCount() { @@ -931,6 +946,7 @@ public abstract class RecentsView extends PagedView impl setEnableDrawingLiveTile(false); setRunningTaskHidden(true); setRunningTaskIconScaledDown(true); + mGestureRunning = true; } /** @@ -1000,6 +1016,7 @@ public abstract class RecentsView extends PagedView impl } setRunningTaskHidden(false); animateUpRunningTaskIconScale(); + mGestureRunning = false; } /** @@ -1016,6 +1033,7 @@ public abstract class RecentsView extends PagedView impl addView(taskView, mTaskViewStartIndex); if (wasEmpty) { addView(mClearAllButton); + showActionsView(); } // The temporary running task is only used for the duration between the start of the // gesture and the task list is loaded and applied @@ -1341,6 +1359,7 @@ public abstract class RecentsView extends PagedView impl if (getTaskViewCount() == 0) { removeView(mClearAllButton); + hideActionsView(); startHome(); } else { snapToPageImmediately(pageToSnapTo); @@ -1485,15 +1504,17 @@ public abstract class RecentsView extends PagedView impl } } mClearAllButton.setContentAlpha(mContentAlpha); - int alphaInt = Math.round(alpha * 255); mEmptyMessagePaint.setAlpha(alphaInt); mEmptyIcon.setAlpha(alphaInt); - if (alpha > 0) { setVisibility(VISIBLE); + if (!mGestureRunning) { + showActionsView(); + } } else if (!mFreezeViewVisibility) { setVisibility(GONE); + hideActionsView(); } } @@ -1507,6 +1528,11 @@ public abstract class RecentsView extends PagedView impl if (!mFreezeViewVisibility) { setVisibility(mContentAlpha > 0 ? VISIBLE : GONE); + if (mContentAlpha > 0) { + showActionsView(); + } else { + hideActionsView(); + } } } } @@ -2057,6 +2083,7 @@ public abstract class RecentsView extends PagedView impl void onEmptyMessageUpdated(boolean isEmpty); } + private static class PinnedStackAnimationListener extends IPinnedStackAnimationListener.Stub { private T mActivity; @@ -2072,4 +2099,34 @@ public abstract class RecentsView extends PagedView impl mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); } } + + private void showActionsView() { + if (mActionsView != null && getTaskViewCount() > 0) { + mActionsView.setVisibility(VISIBLE); + } + } + + private void hideActionsView() { + if (mActionsView != null) { + mActionsView.setVisibility(GONE); + } + } + + private void addActionsView() { + if (mActionsView == null && ENABLE_OVERVIEW_ACTIONS.get() + && SysUINavigationMode.removeShelfFromOverview(mActivity)) { + mActionsView = ((ViewGroup) getParent()).findViewById(R.id.overview_actions_view); + if (mActionsView != null) { + Rect rect = new Rect(); + getTaskSize(rect); + InsettableFrameLayout.LayoutParams layoutParams = + new InsettableFrameLayout.LayoutParams(rect.width(), + getResources().getDimensionPixelSize( + R.dimen.overview_actions_height)); + layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + mActionsView.setLayoutParams(layoutParams); + showActionsView(); + } + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 9150cc76d0..e09e01ff9f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -16,6 +16,20 @@ package com.android.quickstep.views; +import static android.view.Gravity.BOTTOM; +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Gravity.CENTER_VERTICAL; +import static android.view.Gravity.END; +import static android.view.Gravity.START; +import static android.view.Gravity.TOP; +import static android.widget.Toast.LENGTH_SHORT; + +import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -39,14 +53,11 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.Toast; -import androidx.annotation.Nullable; - import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.states.RotationHelper; @@ -76,20 +87,6 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import static android.view.Gravity.BOTTOM; -import static android.view.Gravity.CENTER_HORIZONTAL; -import static android.view.Gravity.CENTER_VERTICAL; -import static android.view.Gravity.END; -import static android.view.Gravity.START; -import static android.view.Gravity.TOP; -import static android.widget.Toast.LENGTH_SHORT; -import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; - /** * A task in the Recents view. */ @@ -173,8 +170,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private final float mWindowCornerRadius; private final BaseDraggingActivity mActivity; - @Nullable private View mActionsView; - private ObjectAnimator mIconAndDimAnimator; private float mIconScaleAnimStartProgress = 0; private float mFocusTransitionProgress = 1; @@ -193,8 +188,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mFooterVerticalOffset = 0; private float mFooterAlpha = 1; private int mStackHeight; - private boolean mHideActionsView; - private PagedOrientationHandler mOrientationHandler; public TaskView(Context context) { this(context, null); @@ -246,18 +239,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams(); thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(context); mSnapshotView.setLayoutParams(thumbnailParams); - - - if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) { - mActionsView = mSnapshotView.getTaskOverlay().getActionsView(); - if (mActionsView != null) { - TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT, - getResources().getDimensionPixelSize(R.dimen.overview_actions_height), - BOTTOM); - addView(mActionsView, params); - mActionsView.setAlpha(0); - } - } } public boolean isTaskOverlayModal() { @@ -457,7 +438,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); int rotation = RotationHelper.getDegreesFromRotation(iconRotation); - mHideActionsView = true; switch (iconRotation) { case Surface.ROTATION_90: iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; @@ -479,13 +459,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { iconParams.gravity = TOP | CENTER_HORIZONTAL; iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = iconParams.bottomMargin = 0; - mHideActionsView = false; break; } mSnapshotView.setLayoutParams(snapshotParams); mIconView.setLayoutParams(iconParams); mIconView.setRotation(rotation); - updateActionsViewVisibility(!mHideActionsView); } private void setIconAndDimTransitionProgress(float progress, boolean invert) { @@ -502,11 +480,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mIconView.setScaleX(scale); mIconView.setScaleY(scale); - - if (mActionsView != null && isRunningTask()) { - mActionsView.setAlpha(scale); - } - mFooterVerticalOffset = 1.0f - scale; for (FooterWrapper footer : mFooters) { if (footer != null) { @@ -597,31 +570,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mMenuView.setScaleX(getScaleX()); mMenuView.setScaleY(getScaleY()); } - - // This is not the proper implementation and will be replaced with a proper layout. - if (mActionsView != null) { - if (mFocusTransitionProgress == 1f) { - mActionsView.setAlpha(1 - curveInterpolation / MAX_PAGE_SCRIM_ALPHA); - } - maintainActionViewPosition(curveScaleForCurveInterpolation); - } - } - private void maintainActionViewPosition(float curveScaleForCurveInterpolation) { - float inverseCurveScaleFactor = curveScaleForCurveInterpolation == 0 ? 0 : - (1f / curveScaleForCurveInterpolation); - mActionsView.setScaleX(inverseCurveScaleFactor); - mActionsView.setScaleY(inverseCurveScaleFactor); - mActionsView.setTranslationX(inverseCurveScaleFactor * (-getX() - + getRecentsView().getScrollX() + getRecentsView().scrollOffsetLeft())); - mActionsView.setTranslationY( - (1f - curveScaleForCurveInterpolation) * (mSnapshotView.getHeight() - + mActionsView.getHeight()) / 2f - + inverseCurveScaleFactor * (-getTranslationY())); - } - - /** * Sets the footer at the specific index and returns the previously set footer. */ @@ -903,7 +853,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mFullscreenProgress = progress; boolean isFullscreen = mFullscreenProgress > 0; mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); - updateActionsViewVisibility(progress < 1 && !mHideActionsView); setClipChildren(!isFullscreen); setClipToPadding(!isFullscreen); @@ -937,12 +886,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { invalidateOutline(); } - private void updateActionsViewVisibility(boolean isVisible) { - if (mActionsView != null) { - mActionsView.setVisibility(isVisible ? VISIBLE : GONE); - } - } - public boolean isRunningTask() { if (getRecentsView() == null) { return false; @@ -990,5 +933,4 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mScale = scale; } } - } diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index fa0e840b20..c5d7c95e0b 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -196,7 +196,10 @@ public abstract class BaseQuickstepLauncher extends Launcher if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) { // Overview is above all other launcher elements, including qsb, so move it to the top. - getOverviewPanelContainer().bringToFront(); + getOverviewPanel().bringToFront(); + if (getActionsView() != null) { + getActionsView().bringToFront(); + } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 174e49b8d2..7481445600 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -52,10 +52,12 @@ public abstract class BaseRecentsViewStateController implements StateHandler { protected final T mRecentsView; protected final Launcher mLauncher; + protected final View mActionsView; public BaseRecentsViewStateController(@NonNull Launcher launcher) { mLauncher = launcher; mRecentsView = launcher.getOverviewPanel(); + mActionsView = launcher.getActionsView(); } @Override @@ -72,6 +74,10 @@ public abstract class BaseRecentsViewStateController getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher)); + if (mActionsView != null) { + mActionsView.setTranslationX(translationX); + mActionsView.setAlpha(state.overviewUi ? 1f : 0); + } } @Override @@ -118,6 +124,11 @@ public abstract class BaseRecentsViewStateController OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim(); setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher), builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR)); + if (mActionsView != null) { + setter.setFloat(mActionsView, View.TRANSLATION_X, translationX, translateXInterpolator); + setter.setFloat(mActionsView, View.ALPHA, toState.overviewUi ? 1 : 0, + builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT)); + } } /** diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index 6c66897a45..196eb0f1cf 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -44,8 +44,13 @@ layout="@layout/hotseat" /> + android:id="@+id/overview_panel" + layout="@layout/overview_panel" + android:visibility="gone" /> + + diff --git a/res/layout/overview_actions_holder.xml b/res/layout/overview_actions_holder.xml new file mode 100644 index 0000000000..5946bf67ec --- /dev/null +++ b/res/layout/overview_actions_holder.xml @@ -0,0 +1,20 @@ + + + diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml index 7fff711190..2637f038c5 100644 --- a/res/layout/overview_panel.xml +++ b/res/layout/overview_panel.xml @@ -14,9 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + \ No newline at end of file diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d6e8710120..7c85bb7396 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -80,7 +80,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.OvershootInterpolator; -import android.widget.FrameLayout; import android.widget.Toast; import androidx.annotation.Nullable; @@ -274,7 +273,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // UI and state for the overview panel private View mOverviewPanel; - private FrameLayout mOverviewPanelContainer; + private View mActionsView; @Thunk boolean mWorkspaceLoading = true; @@ -1145,8 +1144,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mFocusHandler = mDragLayer.getFocusIndicatorHelper(); mWorkspace = mDragLayer.findViewById(R.id.workspace); mWorkspace.initParentViews(mDragLayer); - mOverviewPanel = findViewById(R.id.overview_panel_recents); - mOverviewPanelContainer = findViewById(R.id.overview_panel_container); + mOverviewPanel = findViewById(R.id.overview_panel); + mActionsView = findViewById(R.id.overview_actions_view); mHotseat = findViewById(R.id.hotseat); mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN @@ -1389,8 +1388,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return (T) mOverviewPanel; } - public FrameLayout getOverviewPanelContainer() { - return mOverviewPanelContainer; + public View getActionsView() { + return mActionsView; } public DropTargetBar getDropTargetBar() { diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 17858a0290..9b12a62bde 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -145,7 +145,7 @@ public final class LauncherInstrumentation { private static final String WORKSPACE_RES_ID = "workspace"; private static final String APPS_RES_ID = "apps_view"; - private static final String OVERVIEW_RES_ID = "overview_panel_recents"; + private static final String OVERVIEW_RES_ID = "overview_panel"; private static final String WIDGETS_RES_ID = "widgets_list_view"; private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container"; public static final int WAIT_TIME_MS = 10000; From 3bba168e62be29e71433525631385fc8c79e39fc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 19 Aug 2016 13:13:03 -0700 Subject: [PATCH 25/39] Allowing Launcher to be backed up while in foreground Full backup requies the process to be killed, and hence avoids backing up apps which are in foreground. Since the launcher is more likely to be the foreground app when device is locked, it never gets backuped up. Note that full backup only runs when the screen is off, and the launcher activity is restarted as soon as the full backup is complete (without waiting for the screen to turn on). There would not be much visible impact to the user. Bug: 30970354 Change-Id: Icc16b35682c5b8d59316bd9d68774d38cbee47dd --- AndroidManifest-common.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index d747bb8855..6d105ac4fc 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -71,6 +71,7 @@ Date: Wed, 4 Mar 2020 10:21:17 -0800 Subject: [PATCH 26/39] Add more swipe up to home/spring variables as dynamic resources. Bug: 147302669 Change-Id: I7a13f1eac8324d963302f5f42793478896f10097 --- .../res/values/dimens.xml | 6 ---- .../android/quickstep/BaseSwipeUpHandler.java | 2 +- .../quickstep/util/RectFSpringAnim.java | 16 +++++------ res/values/config.xml | 24 +++++++++++++--- .../launcher3/anim/FlingSpringAnim.java | 28 ++++++++++++------- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml index de97d0845e..61c576e82b 100644 --- a/quickstep/recents_ui_overrides/res/values/dimens.xml +++ b/quickstep/recents_ui_overrides/res/values/dimens.xml @@ -23,10 +23,4 @@ 80dp - - - 18dp - 10dp - -60dp - \ No newline at end of file diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 1d1c7bb4e2..a17476eb18 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -448,7 +448,7 @@ public abstract class BaseSwipeUpHandler0.75 200 - 0.75 - 200 + 0.75 + 200 + + 1.5 + 0.8 + 200 0.7 150 + + 18dp + 10dp + -60dp + @dimen/all_apps_spring_damping_ratio @dimen/all_apps_spring_stiffness @@ -152,10 +161,17 @@ @dimen/horizontal_spring_damping_ratio @dimen/horizontal_spring_stiffness - @dimen/swipe_up_rect_damping_ratio - @dimen/swipe_up_rect_stiffness + @dimen/swipe_up_rect_scale_damping_ratio + @dimen/swipe_up_rect_scale_stiffness + + @dimen/swipe_up_rect_xy_fling_friction + @dimen/swipe_up_rect_xy_damping_ratio + @dimen/swipe_up_rect_xy_stiffness @dimen/staggered_damping_ratio @dimen/staggered_stiffness + + @dimen/swipe_up_fling_min_visible_change + @dimen/swipe_up_y_overshoot diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java index eaf3b1cb24..06d0f1c463 100644 --- a/src/com/android/launcher3/anim/FlingSpringAnim.java +++ b/src/com/android/launcher3/anim/FlingSpringAnim.java @@ -15,32 +15,40 @@ */ package com.android.launcher3.anim; +import android.content.Context; + import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener; import androidx.dynamicanimation.animation.FlingAnimation; import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.android.launcher3.R; +import com.android.launcher3.util.DynamicResource; +import com.android.systemui.plugins.ResourceProvider; + /** * Given a property to animate and a target value and starting velocity, first apply friction to * the fling until we pass the target, then apply a spring force to pull towards the target. */ public class FlingSpringAnim { - private static final float FLING_FRICTION = 1.5f; - private static final float SPRING_STIFFNESS = 200; - private static final float SPRING_DAMPING = 0.8f; - private final FlingAnimation mFlingAnim; private SpringAnimation mSpringAnim; private float mTargetPosition; - public FlingSpringAnim(K object, FloatPropertyCompat property, float startPosition, - float targetPosition, float startVelocity, float minVisChange, float minValue, - float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) { + public FlingSpringAnim(K object, Context context, FloatPropertyCompat property, + float startPosition, float targetPosition, float startVelocity, float minVisChange, + float minValue, float maxValue, float springVelocityFactor, + OnAnimationEndListener onEndListener) { + ResourceProvider rp = DynamicResource.provider(context); + float damping = rp.getFloat(R.dimen.swipe_up_rect_xy_damping_ratio); + float stiffness = rp.getFloat(R.dimen.swipe_up_rect_xy_stiffness); + float friction = rp.getFloat(R.dimen.swipe_up_rect_xy_fling_friction); + mFlingAnim = new FlingAnimation(object, property) - .setFriction(FLING_FRICTION) + .setFriction(friction) // Have the spring pull towards the target if we've slowed down too much before // reaching it. .setMinimumVisibleChange(minVisChange) @@ -54,8 +62,8 @@ public class FlingSpringAnim { .setStartValue(value) .setStartVelocity(velocity * springVelocityFactor) .setSpring(new SpringForce(mTargetPosition) - .setStiffness(SPRING_STIFFNESS) - .setDampingRatio(SPRING_DAMPING)); + .setStiffness(stiffness) + .setDampingRatio(damping)); mSpringAnim.addEndListener(onEndListener); mSpringAnim.animateToFinalPosition(mTargetPosition); })); From 73cb009470d8d0a5547de7431a915a26d24aab4f Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 4 Mar 2020 15:43:15 -0800 Subject: [PATCH 27/39] Default ENABLE_PREDICTION_DISMISS to true Bug: 142753423 Change-Id: I73bfb35fdd952a64233950254870afa393ed79ae --- src/com/android/launcher3/config/FeatureFlags.java | 2 +- src/com/android/launcher3/logging/FileLog.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index f6487619d0..6762435423 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -101,7 +101,7 @@ public final class FeatureFlags { "Adds localized title and keyword search and ranking"); public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag( - "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list"); + "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list"); public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag( "ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture"); diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java index a3fdf8da0b..bfeb1dc3ef 100644 --- a/src/com/android/launcher3/logging/FileLog.java +++ b/src/com/android/launcher3/logging/FileLog.java @@ -38,7 +38,7 @@ public final class FileLog { private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); - private static final long MAX_LOG_FILE_SIZE = 4 << 20; // 4 mb + private static final long MAX_LOG_FILE_SIZE = 8 << 20; // 4 mb private static Handler sHandler = null; private static File sLogsDirectory = null; From d91cb6e75fa7819d9c7ecf5bf8201dbe96755eec Mon Sep 17 00:00:00 2001 From: Hongming Jin Date: Mon, 27 Jan 2020 16:23:03 -0800 Subject: [PATCH 28/39] Register/unregister all apps system action with accessibility Bug: 136286274 Change-Id: Ia43d30b7cf078f56cc9887c8b3fb31924ad2b1b0 --- .../launcher3/BaseQuickstepLauncher.java | 19 ++++ .../accessibility/SystemActions.java | 90 +++++++++++++++++++ src/com/android/launcher3/Launcher.java | 1 + 3 files changed, 110 insertions(+) create mode 100644 quickstep/src/com/android/launcher3/accessibility/SystemActions.java diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index fa0e840b20..b3b0572f5a 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -36,6 +36,7 @@ import android.os.CancellationSignal; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherStateManager.StateHandler; +import com.android.launcher3.accessibility.SystemActions; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; @@ -62,6 +63,8 @@ import java.util.stream.Stream; public abstract class BaseQuickstepLauncher extends Launcher implements NavigationModeChangeListener { + protected SystemActions mSystemActions; + /** * Reusable command for applying the back button alpha on the background thread. */ @@ -74,6 +77,7 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mSystemActions = new SystemActions(this); SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this) .addModeChangeListener(this); @@ -131,6 +135,12 @@ public abstract class BaseQuickstepLauncher extends Launcher getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON); } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + mSystemActions.onActivityResult(requestCode); + } + @Override public void onEnterAnimationComplete() { super.onEnterAnimationComplete(); @@ -188,6 +198,15 @@ public abstract class BaseQuickstepLauncher extends Launcher // removes the task itself. startActivity(ProxyActivityStarter.getLaunchIntent(this, null)); } + + // Register all system actions once they are available + mSystemActions.register(); + } + + @Override + protected void onPause() { + super.onPause(); + mSystemActions.unregister(); } @Override diff --git a/quickstep/src/com/android/launcher3/accessibility/SystemActions.java b/quickstep/src/com/android/launcher3/accessibility/SystemActions.java new file mode 100644 index 0000000000..669877f5b3 --- /dev/null +++ b/quickstep/src/com/android/launcher3/accessibility/SystemActions.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.accessibility; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; + +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.drawable.Icon; +import android.view.accessibility.AccessibilityManager; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.R; + +/** + * Manages the launcher system actions presented to accessibility services. + */ +public class SystemActions { + + /** + * System Action ID to show all apps. This ID should follow the ones in + * com.android.systemui.accessibility.SystemActions. + */ + private static final int SYSTEM_ACTION_ID_ALL_APPS = 100; + + private Launcher mLauncher; + private AccessibilityManager mAccessibilityManager; + private RemoteAction mAllAppsAction; + private boolean mRegistered; + + public SystemActions(Launcher launcher) { + mLauncher = launcher; + mAccessibilityManager = (AccessibilityManager) launcher.getSystemService( + Context.ACCESSIBILITY_SERVICE); + mAllAppsAction = new RemoteAction( + Icon.createWithResource(launcher, R.drawable.ic_apps), + launcher.getString(R.string.all_apps_label), + launcher.getString(R.string.all_apps_label), + launcher.createPendingResult(SYSTEM_ACTION_ID_ALL_APPS, new Intent(), + 0 /* flags */)); + } + + public void register() { + if (mRegistered) { + return; + } + mAccessibilityManager.registerSystemAction(mAllAppsAction, SYSTEM_ACTION_ID_ALL_APPS); + mRegistered = true; + } + + public void unregister() { + if (!mRegistered) { + return; + } + mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); + mRegistered = false; + } + + public void onActivityResult(int requestCode) { + if (requestCode == SYSTEM_ACTION_ID_ALL_APPS) { + showAllApps(); + } + } + + private void showAllApps() { + LauncherStateManager stateManager = mLauncher.getStateManager(); + stateManager.goToState(NORMAL); + stateManager.goToState(ALL_APPS); + } +} diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d6e8710120..8e87d1699c 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -834,6 +834,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } } + mDragLayer.clearAnimatedView(); } From 9879c3845283bcae6b4333586d5fd16a4b6b4aa8 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Wed, 4 Mar 2020 16:02:05 -0800 Subject: [PATCH 29/39] FeatureFlags OWNERS rule should not inherit parent Bug: 150812984 Change-Id: I01c6d4509183915ed187c8d36c4cf00fe6eaa5c6 --- OWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OWNERS b/OWNERS index bf4cd0bb6b..3069afaedc 100644 --- a/OWNERS +++ b/OWNERS @@ -29,5 +29,5 @@ peanutbutter@google.com xuqiu@google.com sreyasr@google.com -per-file FeatureFlags.java = sunnygoyal@google.com, adamcohen@google.com -per-file BaseFlags.java = sunnygoyal@google.com, adamcohen@google.com +per-file FeatureFlags.java, globs = set noparent +per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com From 7a7468fd39fa2cad227cf320bdca87d7502469ad Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Thu, 21 Nov 2019 01:32:58 +0800 Subject: [PATCH 30/39] Invalidate snapshot of home if its UI is changed Snapshot may be enabled for home task when turning off screen. If home UI has changed after screen off, the snapshot should not be used to avoid ugly visual effect. Bug: 140811348 Test: Lock/unlock device while home is on top with 4 cases: 1. All apps is shown. 2. Overview is shown. 3. Options menu is shown. 4. Quick search bar is active. The expected result should be no starting window when unlocking because the snapshot is dropped. Change-Id: I4bd9926b6f9332ac16b1b3a25ffdd44706015a33 (cherry picked from commit 0ae61c8d2979569c5ca74593b8e4e48e825f86e2) --- .../launcher3/BaseQuickstepLauncher.java | 7 ++++++ src/com/android/launcher3/Launcher.java | 22 +++++++++++++++++++ .../launcher3/LauncherStateManager.java | 9 ++++++++ 3 files changed, 38 insertions(+) diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index d39dfda930..fb0cd17d27 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -54,6 +54,7 @@ import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.ShelfPeekAnim; import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.system.ActivityManagerWrapper; import java.util.stream.Stream; @@ -157,6 +158,12 @@ public abstract class BaseQuickstepLauncher extends Launcher RecentsModel.INSTANCE.get(this).onTrimMemory(level); } + @Override + protected void onUiChangedWhileSleeping() { + // Remove the snapshot because the content view may have obvious changes. + ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this); + } + @Override public void startIntentSenderForResult(IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3e1032319a..20ebc7a27d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -924,6 +924,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void onStop() { + final boolean wasActive = isUserActive(); + final LauncherState origState = getStateManager().getState(); + final int origDragLayerChildCount = mDragLayer.getChildCount(); super.onStop(); if (mDeferOverlayCallbacks) { @@ -941,6 +944,20 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Workaround for b/78520668, explicitly trim memory once UI is hidden onTrimMemory(TRIM_MEMORY_UI_HIDDEN); + + if (wasActive) { + // The expected condition is that this activity is stopped because the device goes to + // sleep and the UI may have noticeable changes. + mDragLayer.post(() -> { + if ((!getStateManager().isInStableState(origState) + // The drag layer may be animating (e.g. dismissing QSB). + || mDragLayer.getAlpha() < 1 + // Maybe an ArrowPopup is closed. + || mDragLayer.getChildCount() != origDragLayerChildCount)) { + onUiChangedWhileSleeping(); + } + }); + } } @Override @@ -1336,11 +1353,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Reset AllApps to its initial state only if we are not in the middle of // processing a multi-step drop if (mPendingRequestArgs == null) { + if (!isInState(NORMAL)) { + onUiChangedWhileSleeping(); + } mStateManager.goToState(NORMAL); } } }; + protected void onUiChangedWhileSleeping() { } + private void updateNotificationDots(Predicate updatedDots) { mWorkspace.updateNotificationDots(updatedDots); mAppsView.getAppsStore().updateNotificationDots(updatedDots); diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 195e69bf67..9f25729602 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -163,6 +163,15 @@ public class LauncherStateManager { return !mLauncher.isForceInvisible() && mLauncher.isStarted(); } + /** + * @return {@code true} if the state matches the current state and there is no active + * transition to different state. + */ + public boolean isInStableState(LauncherState state) { + return mState == state && mCurrentStableState == state + && (mConfig.mTargetState == null || mConfig.mTargetState == state); + } + /** * @see #goToState(LauncherState, boolean, Runnable) */ From 34e88d0999d4e74300c9e542663cf8b7c8547f96 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 26 Feb 2020 11:58:28 -0800 Subject: [PATCH 31/39] Update pullback start and max progress based on RecentsView scale Instead of having defined constants that are hard to tune, now we calculate the factors at runtime based on the launcher animation scale. This ensures the pullback always has reasonable values regardless of orientation, feature flags, etc. Bug: 149934536 Change-Id: If07e3b87475f8f9613e94df00c394430aad7d507 --- .../android/quickstep/BaseSwipeUpHandler.java | 24 ++++++++++--------- .../quickstep/LauncherActivityInterface.java | 17 ++++++++++++- .../quickstep/BaseActivityInterface.java | 10 ++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index a17476eb18..e9ce6922fa 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; @@ -32,6 +31,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; +import android.util.Pair; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; @@ -78,18 +78,16 @@ public abstract class BaseSwipeUpHandler DRAG_LENGTH_FACTOR_START_PULLBACK) { + if (shift > mDragLengthFactorStartPullback) { float pullbackProgress = Utilities.getProgress(shift, - DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor); + mDragLengthFactorStartPullback, mDragLengthFactor); pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); - shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress - * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK); + shift = mDragLengthFactorStartPullback + pullbackProgress + * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); } } @@ -344,6 +342,10 @@ public abstract class BaseSwipeUpHandler dragFactorStartAndMaxProgress = + mActivityInterface.getSwipeUpPullbackStartAndMaxProgress(); + mDragLengthFactorStartPullback = dragFactorStartAndMaxProgress.first; + mDragLengthFactorMaxPullback = dragFactorStartAndMaxProgress.second; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index f19ec69aa9..ccc2150ce0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -38,6 +38,7 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.RectF; import android.os.UserHandle; +import android.util.Pair; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; @@ -54,7 +55,6 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.appprediction.PredictionUiStateManager; -import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.FloatingIconView; @@ -80,6 +80,8 @@ import java.util.function.Predicate; public final class LauncherActivityInterface implements BaseActivityInterface { private Runnable mAdjustInterpolatorsRunnable; + private Pair mSwipeUpPullbackStartAndMaxProgress = + BaseActivityInterface.super.getSwipeUpPullbackStartAndMaxProgress(); @Override public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { @@ -93,6 +95,11 @@ public final class LauncherActivityInterface implements BaseActivityInterface getSwipeUpPullbackStartAndMaxProgress() { + return mSwipeUpPullbackStartAndMaxProgress; + } + @Override public void onTransitionCancelled(boolean activityVisible) { Launcher launcher = getCreatedActivity(); @@ -385,6 +392,14 @@ public final class LauncherActivityInterface implements BaseActivityInterface( + pullbackStartProgress, pullbackMaxProgress); } @Override diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 64e053f318..1d71fe226b 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; +import android.util.Pair; import android.view.MotionEvent; import android.view.View; import android.view.animation.Interpolator; @@ -50,6 +51,15 @@ public interface BaseActivityInterface { int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect); + /** + * @return The progress of the swipe where we start resisting the user, where 0 is fullscreen + * and 1 is recents. These values should probably be greater than 1 to let the user swipe past + * recents before we start resisting them. + */ + default Pair getSwipeUpPullbackStartAndMaxProgress() { + return new Pair<>(1.4f, 1.8f); + } + void onSwipeUpToRecentsComplete(); default void onSwipeUpToHomeComplete() { } From 5a07aadf86dc7e96e4b3a9bc2c762f754ddcf522 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 12 Feb 2020 14:40:05 -0800 Subject: [PATCH 32/39] Feature flag for using hardware optimization for folder animations Bug: 144338174 Change-Id: I92cc399da077c4c4da8264c20136302ede25ed01 --- src/com/android/launcher3/config/FeatureFlags.java | 5 +++++ src/com/android/launcher3/folder/Folder.java | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b689a0a4a7..aab6671196 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -143,6 +143,11 @@ public final class FeatureFlags { "ENABLE_LSQ_VELOCITY_PROVIDER", false, "Use Least Square algorithm for motion pause detection."); + public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS = + getDebugFlag( + "ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false, + "Always use hardware optimization for folder animations."); + public static void initialize(Context context) { synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index e33d89fe4f..69f93de55e 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -26,6 +26,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; +import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS; import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM; import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY; import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED; @@ -546,6 +547,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) { + if (ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS.get()) return true; + int folderCount = 0; final ShortcutAndWidgetContainer container = currentCellLayout.getShortcutsAndWidgets(); for (int i = container.getChildCount() - 1; i >= 0; --i) { From f6385576d51a4bba48f5e9760a54d9fc33aa8b7f Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 4 Mar 2020 18:49:52 -0800 Subject: [PATCH 33/39] Disabling testToast on virtual devices Bug: 150303529 Change-Id: Ie523b79468f37e9edbd50f77db4d358e6713a463 --- .../com/android/quickstep/DigitalWellBeingToastTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java index 9a053f2f62..ccfa3fc0fb 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java @@ -3,8 +3,6 @@ package com.android.quickstep; import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT; -import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -13,12 +11,12 @@ import static org.junit.Assert.assertTrue; import android.app.PendingIntent; import android.app.usage.UsageStatsManager; import android.content.Intent; +import android.os.Build; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.Launcher; -import com.android.launcher3.util.rule.TestStabilityRule; import com.android.quickstep.views.DigitalWellBeingToast; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -35,9 +33,10 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest { resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR); @Test - // b/150303529 - @TestStabilityRule.Stability(flavors = UNBUNDLED_POSTSUBMIT | PLATFORM_POSTSUBMIT) public void testToast() throws Exception { + // b/150303529 + if (Build.MODEL.contains("Cuttlefish")) return; + startAppFast(CALCULATOR_PACKAGE); final UsageStatsManager usageStatsManager = From bfa3be34ce9b592186ac6315a17bccf14da808b4 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 4 Mar 2020 14:46:47 -0800 Subject: [PATCH 34/39] [DO NOT MERGE] Prevent hotseat touch hijack Currently in AllAppsContinerView#onInterceptTouchEvent we check if a touch event is in bounds of RecyclerViewFastScroller and set mTouchHandler so the scroll bar handles onTouchEvent. However, if a user performs a back gesture from the right side (which overlaps with the scroller) and returns home, touches to the hotseat get intercepted. Bug: 148639821 Test: Manual Change-Id: Ie44f3a16b628b12ad00a7ae6c6bc5703171fbb56 --- .../allapps/AllAppsContainerView.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 37ee24850e..2d5b04058c 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -26,14 +26,18 @@ import android.os.Process; import android.text.Selection; import android.text.SpannableStringBuilder; import android.util.AttributeSet; -import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.AppInfo; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; @@ -45,11 +49,11 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.ItemInfoMatcher; @@ -60,13 +64,6 @@ import com.android.launcher3.views.BottomUserEducationView; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.SpringRelativeLayout; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - /** * The all apps view container. */ @@ -200,7 +197,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo // The AllAppsContainerView houses the QSB and is hence visible from the Workspace // Overview states. We shouldn't intercept for the scrubber in these cases. - if (!mLauncher.isInState(LauncherState.ALL_APPS)) return false; + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + mTouchHandler = null; + return false; + } if (ev.getAction() == MotionEvent.ACTION_DOWN) { AllAppsRecyclerView rv = getActiveRecyclerView(); @@ -219,6 +219,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo @Override public boolean onTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + AllAppsRecyclerView rv = getActiveRecyclerView(); + if (rv != null && rv.getScrollbar() + .isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) { + mTouchHandler = rv.getScrollbar(); + } else { + mTouchHandler = null; + } + } + if (mTouchHandler != null) { mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset); return true; From 0318a652c12325bfd9feeba4a34f2d0bb17804c4 Mon Sep 17 00:00:00 2001 From: Matt Casey Date: Tue, 3 Mar 2020 17:20:42 -0500 Subject: [PATCH 35/39] Let launcher to provide its own OverscrollPlugin If the local OverscrollPlugin provided by the activity is non-null and FORCE_LOCAL_OVERSCROLL_PLUGIN is true, the local plugin object overrides the loaded actual plugin. Test: Manual testing flipping the flag and observing which plugin is activated. Bug: 150245271 Change-Id: I0cd5184628c2cb572b5e1c56d6a008dcaa8807fb --- .../android/quickstep/TaskOverlayFactory.java | 8 +++++ .../quickstep/TouchInteractionService.java | 31 ++++++++++++------- .../launcher3/config/FeatureFlags.java | 4 +++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java index eb3b190110..33d9d9aa55 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java @@ -31,6 +31,7 @@ import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.ResourceBasedOverride; import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; +import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -67,6 +68,13 @@ public class TaskOverlayFactory implements ResourceBasedOverride { public static final MainThreadInitializedObject INSTANCE = forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class); + /** + * @return a launcher-provided OverscrollPlugin if available, otherwise null + */ + public OverscrollPlugin getLocalOverscrollPlugin() { + return null; + } + public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) { return new TaskOverlay(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 28e8fb6604..7be0f4e4ed 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -17,13 +17,7 @@ package com.android.quickstep; import static android.view.MotionEvent.ACTION_DOWN; -import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; -import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME; -import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI; -import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; -import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; @@ -507,12 +501,25 @@ public class TouchInteractionService extends Service implements PluginListener Date: Thu, 5 Mar 2020 11:32:54 -0800 Subject: [PATCH 36/39] Prevent hotseat touch hijack Bug: 148639821 Test: Manual Change-Id: Ia08af6b243a3492a898371b7bc66158df49d2427 --- .../launcher3/allapps/AllAppsContainerView.java | 14 ++++++++++++-- .../allapps/LauncherAllAppsContainerView.java | 5 ++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index fcccc598ec..afb72172b8 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -98,8 +98,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo protected boolean mUsingTabs; private boolean mSearchModeWhileUsingTabs = false; - private RecyclerViewFastScroller mTouchHandler; - private final Point mFastScrollerOffset = new Point(); + protected RecyclerViewFastScroller mTouchHandler; + protected final Point mFastScrollerOffset = new Point(); private final MultiValueAlpha mMultiValueAlpha; @@ -238,6 +238,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo @Override public boolean onTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + AllAppsRecyclerView rv = getActiveRecyclerView(); + if (rv != null && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), + mFastScrollerOffset)) { + mTouchHandler = rv.getScrollbar(); + } else { + mTouchHandler = null; + + } + } if (mTouchHandler != null) { mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset); return true; diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java index 25db0e7f1d..f6766c4023 100644 --- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java @@ -51,7 +51,10 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView { public boolean onInterceptTouchEvent(MotionEvent ev) { // The AllAppsContainerView houses the QSB and is hence visible from the Workspace // Overview states. We shouldn't intercept for the scrubber in these cases. - if (!mLauncher.isInState(LauncherState.ALL_APPS)) return false; + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + mTouchHandler = null; + return false; + } return super.onInterceptTouchEvent(ev); } From 6d605da821537d52442809b2d2273bb6d9c10675 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 5 Mar 2020 13:36:12 -0800 Subject: [PATCH 37/39] Verifying that tests run against a matching Launcher version Change-Id: I3a6c9e1a2487cfd571abdf4bdd18ab17c7ac9406 --- .../launcher3/ui/AbstractLauncherUiTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index c6192bc7b7..6a4b81f171 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -34,6 +34,7 @@ import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Debug; import android.os.Process; @@ -70,6 +71,7 @@ import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.util.rule.TestStabilityRule; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.rules.RuleChain; @@ -213,6 +215,21 @@ public abstract class AbstractLauncherUiTest { @Before public void setUp() throws Exception { + final String launcherPackageName = mDevice.getLauncherPackageName(); + try { + final Context context = InstrumentationRegistry.getContext(); + final PackageManager pm = context.getPackageManager(); + final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0); + + if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) { + Assert.assertEquals("Launcher version doesn't match tests version", + pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(), + launcherPackage.getLongVersionCode()); + } + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + mLauncherPid = 0; // Disable app tracker AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker()); From 8f1e00b662cc56e8a246c365f369e6f3e520cb72 Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Thu, 5 Mar 2020 15:41:22 -0800 Subject: [PATCH 38/39] Log proto - add select mode item interaction type. Test: build locally Change-Id: I3d296bc17bed91669223182d2ed5cff7af70940f --- protos/launcher_log.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index d08d851e15..0fe53100b7 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -155,6 +155,7 @@ enum ControlType { OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25; OVERVIEW_ACTIONS_SELECT_BUTTON = 26; SELECT_MODE_CLOSE_BUTTON = 27; + SELECT_MODE_ITEM = 28; } enum TipType { From a6fb77f1b5c0a776396f1429ca32c6eb26c24e85 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 5 Mar 2020 16:19:26 -0800 Subject: [PATCH 39/39] Move getRotationHelper() call to LauncherRecentsView 3P launchers were causing a ClassCastException so moved it down to Launcher specific code. 3P as a whole will have to be handled better with the new rotation paradigm so this will be addressed then. Fixes: 146176182 Test: Set Nova Launcher as default home app Swiped up to recents, nothing breaks. Change-Id: I91a267f3a29bd5b9638bc9b69d864ac2de3f3d72 --- .../src/com/android/quickstep/BaseSwipeUpHandler.java | 4 ++++ .../com/android/quickstep/views/LauncherRecentsView.java | 7 +++++++ .../src/com/android/quickstep/views/RecentsView.java | 7 ------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index e9ce6922fa..9f19bb301e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -49,6 +49,7 @@ import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory; @@ -428,6 +429,9 @@ public abstract class BaseSwipeUpHandler implements StateL } }; + private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener = + isForcedRotation -> LauncherRecentsView.this + .disableMultipleLayoutRotations(!isForcedRotation); + public LauncherRecentsView(Context context) { this(context, null); } @@ -337,6 +342,7 @@ public class LauncherRecentsView extends RecentsView implements StateL super.onAttachedToWindow(); PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener( mRecentsExtraCardPluginListener, RecentsExtraCard.class); + mActivity.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener); } @Override @@ -344,6 +350,7 @@ public class LauncherRecentsView extends RecentsView implements StateL super.onDetachedFromWindow(); PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener( mRecentsExtraCardPluginListener); + mActivity.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 9bc95d7fd6..872e690acc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -280,9 +280,6 @@ public abstract class RecentsView extends PagedView impl } }; - private RotationHelper.ForcedRotationChangedListener mForcedRotationChangedListener = - isForcedRotation -> RecentsView.this.disableMultipleLayoutRotations(!isForcedRotation); - private final PinnedStackAnimationListener mIPinnedStackAnimationListener = new PinnedStackAnimationListener(); @@ -482,8 +479,6 @@ public abstract class RecentsView extends PagedView impl mIPinnedStackAnimationListener.setActivity(mActivity); SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener( mIPinnedStackAnimationListener); - Launcher launcher = Launcher.getLauncher(getContext()); - launcher.getRotationHelper().addForcedRotationCallback(mForcedRotationChangedListener); addActionsView(); } @@ -499,8 +494,6 @@ public abstract class RecentsView extends PagedView impl mIdp.removeOnChangeListener(this); SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null); mIPinnedStackAnimationListener.setActivity(null); - Launcher launcher = Launcher.getLauncher(getContext()); - launcher.getRotationHelper().removeForcedRotationCallback(mForcedRotationChangedListener); } @Override