From 768fe5585d564d5bc294559a6c5cb2aec3f89da9 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 6 May 2022 20:17:57 +0100 Subject: [PATCH 01/40] Add DisplayController in PreviewContext's allowed objects Fix: 231526132 Test: switch grids Change-Id: Ie43c54c5fbbea51ee3b795be6baa9c2eaf5e15e4 (cherry picked from commit 114572268581eead50b6f536959c50ef9da9d276) Merged-In: Ie43c54c5fbbea51ee3b795be6baa9c2eaf5e15e4 --- .../android/launcher3/graphics/LauncherPreviewRenderer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index a11bd4fdad..d5bcb0cbcb 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -83,6 +83,7 @@ import com.android.launcher3.pm.UserCache; import com.android.launcher3.uioverrides.PredictedAppIconInflater; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext; @@ -129,7 +130,7 @@ public class LauncherPreviewRenderer extends ContextWrapper super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE, CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE, - WindowManagerProxy.INSTANCE); + WindowManagerProxy.INSTANCE, DisplayController.INSTANCE); mIdp = idp; mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp); mObjectMap.put(LauncherAppState.INSTANCE, From f6ec317d8f8e634fe499a84a4ab6668261a87d50 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 23 Jun 2022 12:00:57 -0700 Subject: [PATCH 02/40] Moving widget padding to drawable instead of using itemDecorator ItemDecorator uses item position which is not stable during animations. Moving it to the background allows the padding to be stable Bug: 236961658 Test: Verified that the app doesn't crash. Change-Id: Ied12077de4097e827c5c4157f5196346a301f185 (cherry picked from commit ed681548fcc3519ed4c28d758a2a2cbbc22fd2db) Merged-In: Ied12077de4097e827c5c4157f5196346a301f185 --- res/layout/widgets_list_row_header.xml | 1 - .../picker/WidgetsListDrawableFactory.java | 11 ++++- .../widget/picker/WidgetsRecyclerView.java | 41 +------------------ 3 files changed, 12 insertions(+), 41 deletions(-) diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml index 3cdc2e844b..35bea279c0 100644 --- a/res/layout/widgets_list_row_header.xml +++ b/res/layout/widgets_list_row_header.xml @@ -19,7 +19,6 @@ android:id="@+id/widgets_list_header" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingVertical="@dimen/widget_list_header_view_vertical_padding" android:orientation="horizontal" android:importantForAccessibility="yes" android:focusable="true" diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java index c61e3a4349..984a2741e7 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java @@ -27,6 +27,7 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.StateListDrawable; @@ -40,6 +41,8 @@ final class WidgetsListDrawableFactory { private final float mMiddleCornerRadius; private final ColorStateList mSurfaceColor; private final ColorStateList mRippleColor; + private final int mVerticalPadding; + private final int mHeaderMargin; WidgetsListDrawableFactory(Context context) { Resources res = context.getResources(); @@ -48,6 +51,9 @@ final class WidgetsListDrawableFactory { mSurfaceColor = context.getColorStateList(R.color.surface); mRippleColor = ColorStateList.valueOf( Themes.getAttrColor(context, android.R.attr.colorControlHighlight)); + mVerticalPadding = + res.getDimensionPixelSize(R.dimen.widget_list_header_view_vertical_padding); + mHeaderMargin = res.getDimensionPixelSize(R.dimen.widget_list_entry_spacing); } /** @@ -74,7 +80,10 @@ final class WidgetsListDrawableFactory { stateList.addState( LAST.mStateSet, createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius)); - return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList); + RippleDrawable ripple = + new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList); + ripple.setPadding(0, mVerticalPadding, 0, mVerticalPadding); + return new InsetDrawable(ripple, 0, mHeaderMargin, 0, 0); } /** diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java index daa67a973c..4c0e0d595f 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java @@ -16,18 +16,12 @@ package com.android.launcher3.widget.picker; -import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_HEADER; -import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_SEARCH_HEADER; - import android.content.Context; import android.graphics.Point; -import android.graphics.Rect; import android.util.AttributeSet; import android.util.SparseIntArray; import android.view.MotionEvent; -import android.view.View; -import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; @@ -55,7 +49,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte * VIEW_TYPE_WIDGETS_LIST is not visible on the screen. */ private final SparseIntArray mCachedSizes = new SparseIntArray(); - private final SpacingDecoration mSpacingDecoration; public WidgetsRecyclerView(Context context) { this(context, null); @@ -70,9 +63,6 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte super(context, attrs, defStyleAttr); mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); addOnItemTouchListener(this); - - mSpacingDecoration = new SpacingDecoration(context); - addItemDecoration(mSpacingDecoration); } @Override @@ -183,7 +173,7 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte @Override protected int getItemsHeight(int untilIndex) { // Initialize cache - int childCount = getChildCount(); + int childCount = Math.min(getChildCount(), getAdapter().getItemCount()); int startPosition; if (childCount > 0 && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) { @@ -200,7 +190,7 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte int totalItemsHeight = 0; for (int i = 0; i < untilIndex; i++) { int type = mAdapter.getItemViewType(i); - totalItemsHeight += mCachedSizes.get(type) + mSpacingDecoration.getSpacing(i, type); + totalItemsHeight += mCachedSizes.get(type); } return totalItemsHeight; } @@ -216,31 +206,4 @@ public class WidgetsRecyclerView extends FastScrollRecyclerView implements OnIte */ int getHeaderViewHeight(); } - - private static class SpacingDecoration extends RecyclerView.ItemDecoration { - - private final int mSpacingBetweenEntries; - - SpacingDecoration(@NonNull Context context) { - mSpacingBetweenEntries = - context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing); - } - - @Override - public void getItemOffsets( - @NonNull Rect outRect, - @NonNull View view, - @NonNull RecyclerView parent, - @NonNull RecyclerView.State state) { - super.getItemOffsets(outRect, view, parent, state); - int position = parent.getChildAdapterPosition(view); - outRect.top += getSpacing(position, parent.getAdapter().getItemViewType(position)); - } - - public int getSpacing(int position, int type) { - boolean isHeader = type == VIEW_TYPE_WIDGETS_SEARCH_HEADER - || type == VIEW_TYPE_WIDGETS_HEADER; - return position > 0 && isHeader ? mSpacingBetweenEntries : 0; - } - } } From 2294c05c451cae78718071893d585758c1b948ad Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 21 Feb 2023 18:10:08 +0000 Subject: [PATCH 03/40] Revert "Migrate IDP_GRID_NAME usage to LauncherPrefs" This reverts commit 6f9a57186c53fe3352a12a27e566ffff3c5c64e0. Reason for revert: A Bug where only device preferences were being used, not the main shared preference file. Bug: 269569568 Test: Verified this on device. Change-Id: I8422b2d7073537bd46c3a91033bd2281bbd306b7 (cherry picked from commit df11959779ca08a48308d9e390eb5b3fdb4bbf35) Merged-In: I8422b2d7073537bd46c3a91033bd2281bbd306b7 --- .../launcher3/InvariantDeviceProfile.java | 13 +++-- src/com/android/launcher3/LauncherPrefs.kt | 52 +++++++++---------- .../android/launcher3/LauncherPrefsTest.kt | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 2fb0fa6207..604c1b87e6 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherPrefs.GRID_NAME; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.config.FeatureFlags.ENABLE_DEVICE_PROFILE_LOGGING; import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; @@ -94,6 +93,8 @@ public class InvariantDeviceProfile { public static final int TYPE_MULTI_DISPLAY = 1; public static final int TYPE_TABLET = 2; + private static final String KEY_IDP_GRID_NAME = "idp_grid_name"; + private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; // Constants that affects the interpolation curve between statically defined device profile @@ -206,7 +207,8 @@ public class InvariantDeviceProfile { String gridName = getCurrentGridName(context); String newGridName = initGrid(context, gridName); if (!newGridName.equals(gridName)) { - LauncherPrefs.get(context).put(GRID_NAME, newGridName); + LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName) + .apply(); } new DeviceGridState(this).writeToPrefs(context); @@ -314,7 +316,7 @@ public class InvariantDeviceProfile { } public static String getCurrentGridName(Context context) { - return LauncherPrefs.get(context).get(GRID_NAME); + return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null); } private String initGrid(Context context, String gridName) { @@ -456,8 +458,9 @@ public class InvariantDeviceProfile { public void setCurrentGrid(Context context, String gridName) { - LauncherPrefs.get(context).put(GRID_NAME, gridName); - MAIN_EXECUTOR.execute(() -> onConfigChanged(context.getApplicationContext())); + Context appContext = context.getApplicationContext(); + LauncherPrefs.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply(); + MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext)); } private Object[] toModelState() { diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index befaa64d50..2e07e3022a 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -4,8 +4,6 @@ import android.content.Context import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener import androidx.annotation.VisibleForTesting -import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY -import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY import com.android.launcher3.allapps.WorkProfileManager import com.android.launcher3.model.DeviceGridState import com.android.launcher3.pm.InstallSessionHelper @@ -22,10 +20,11 @@ import com.android.launcher3.util.Themes class LauncherPrefs(private val context: Context) { /** Wrapper around `getInner` for a `ContextualItem` */ - fun get(item: ContextualItem): T = getInner(item, item.defaultValueFromContext(context)) + fun get(item: ContextualItem): T = + getInner(item, item.defaultValueFromContext(context)) /** Wrapper around `getInner` for an `Item` */ - fun get(item: ConstantItem): T = getInner(item, item.defaultValue) + fun get(item: ConstantItem): T = getInner(item, item.defaultValue) /** * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the @@ -33,11 +32,11 @@ class LauncherPrefs(private val context: Context) { * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set`. */ @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") - private fun getInner(item: Item, default: T): T { + private fun getInner(item: Item, default: T): T { val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) - return when (item.type) { - String::class.java -> sp.getString(item.sharedPrefKey, default as? String) + return when (default::class.java) { + String::class.java -> sp.getString(item.sharedPrefKey, default as String) Boolean::class.java, java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean) Int::class.java, @@ -46,10 +45,11 @@ class LauncherPrefs(private val context: Context) { java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float) Long::class.java, java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long) - Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set) + Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as Set) else -> throw IllegalArgumentException( - "item type: ${item.type}" + " is not compatible with sharedPref methods" + "item type: ${default::class.java}" + + " is not compatible with sharedPref methods" ) } as T @@ -224,36 +224,39 @@ class LauncherPrefs(private val context: Context) { backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE) @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "") @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "") - @JvmField val GRID_NAME = ConstantItem("idp_grid_name", true, null, String::class.java) @JvmField val ALLOW_ROTATION = - backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) { + backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY) { RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info) } @VisibleForTesting @JvmStatic fun backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem = - ConstantItem(sharedPrefKey, true, defaultValue) + ConstantItem(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue) @JvmStatic fun backedUpItem( sharedPrefKey: String, - type: Class, defaultValueFromContext: (c: Context) -> T - ): ContextualItem = ContextualItem(sharedPrefKey, true, defaultValueFromContext, type) + ): ContextualItem = + ContextualItem( + sharedPrefKey, + LauncherFiles.SHARED_PREFERENCES_KEY, + defaultValueFromContext + ) @VisibleForTesting @JvmStatic fun nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem = - ConstantItem(sharedPrefKey, false, defaultValue) + ConstantItem(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue) @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.") @JvmStatic fun getPrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so we use single cached instance return context.applicationContext.getSharedPreferences( - SHARED_PREFERENCES_KEY, + LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE ) } @@ -263,7 +266,7 @@ class LauncherPrefs(private val context: Context) { fun getDevicePrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so we use a single cached instance return context.applicationContext.getSharedPreferences( - DEVICE_PREFERENCES_KEY, + LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE ) } @@ -272,26 +275,21 @@ class LauncherPrefs(private val context: Context) { abstract class Item { abstract val sharedPrefKey: String - abstract val isBackedUp: Boolean - abstract val type: Class<*> - val sharedPrefFile: String = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY + abstract val sharedPrefFile: String fun to(value: T): Pair = Pair(this, value) } data class ConstantItem( override val sharedPrefKey: String, - override val isBackedUp: Boolean, - val defaultValue: T, - // The default value can be null. If so, the type needs to be explicitly stated, or else NPE - override val type: Class = defaultValue!!::class.java + override val sharedPrefFile: String, + val defaultValue: T ) : Item() data class ContextualItem( override val sharedPrefKey: String, - override val isBackedUp: Boolean, - private val defaultSupplier: (c: Context) -> T, - override val type: Class + override val sharedPrefFile: String, + private val defaultSupplier: (c: Context) -> T ) : Item() { private var default: T? = null diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt index d40a7bcf8c..31e8d3099b 100644 --- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt +++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt @@ -13,7 +13,7 @@ import org.junit.runner.RunWith private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false) private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)") private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1) -private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, Boolean::class.java) +private val TEST_CONTEXTUAL_ITEM = LauncherPrefs.backedUpItem("4") { true } @SmallTest @RunWith(AndroidJUnit4::class) From cce551df7b9bb20dae70a5e61ac8e9c0e6d46b9a Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 21 Feb 2023 18:10:08 +0000 Subject: [PATCH 04/40] Revert "Migrate IDP_GRID_NAME usage to LauncherPrefs" This reverts commit 6f9a57186c53fe3352a12a27e566ffff3c5c64e0. Reason for revert: A Bug where only device preferences were being used, not the main shared preference file. Bug: 269569568 Test: Verified this on device. Change-Id: I8422b2d7073537bd46c3a91033bd2281bbd306b7 (cherry picked from commit df11959779ca08a48308d9e390eb5b3fdb4bbf35) Merged-In: I8422b2d7073537bd46c3a91033bd2281bbd306b7 --- .../launcher3/InvariantDeviceProfile.java | 13 +++-- src/com/android/launcher3/LauncherPrefs.kt | 52 +++++++++---------- .../android/launcher3/LauncherPrefsTest.kt | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 2fb0fa6207..604c1b87e6 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherPrefs.GRID_NAME; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.config.FeatureFlags.ENABLE_DEVICE_PROFILE_LOGGING; import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; @@ -94,6 +93,8 @@ public class InvariantDeviceProfile { public static final int TYPE_MULTI_DISPLAY = 1; public static final int TYPE_TABLET = 2; + private static final String KEY_IDP_GRID_NAME = "idp_grid_name"; + private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; // Constants that affects the interpolation curve between statically defined device profile @@ -206,7 +207,8 @@ public class InvariantDeviceProfile { String gridName = getCurrentGridName(context); String newGridName = initGrid(context, gridName); if (!newGridName.equals(gridName)) { - LauncherPrefs.get(context).put(GRID_NAME, newGridName); + LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName) + .apply(); } new DeviceGridState(this).writeToPrefs(context); @@ -314,7 +316,7 @@ public class InvariantDeviceProfile { } public static String getCurrentGridName(Context context) { - return LauncherPrefs.get(context).get(GRID_NAME); + return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null); } private String initGrid(Context context, String gridName) { @@ -456,8 +458,9 @@ public class InvariantDeviceProfile { public void setCurrentGrid(Context context, String gridName) { - LauncherPrefs.get(context).put(GRID_NAME, gridName); - MAIN_EXECUTOR.execute(() -> onConfigChanged(context.getApplicationContext())); + Context appContext = context.getApplicationContext(); + LauncherPrefs.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply(); + MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext)); } private Object[] toModelState() { diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index befaa64d50..2e07e3022a 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -4,8 +4,6 @@ import android.content.Context import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener import androidx.annotation.VisibleForTesting -import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY -import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY import com.android.launcher3.allapps.WorkProfileManager import com.android.launcher3.model.DeviceGridState import com.android.launcher3.pm.InstallSessionHelper @@ -22,10 +20,11 @@ import com.android.launcher3.util.Themes class LauncherPrefs(private val context: Context) { /** Wrapper around `getInner` for a `ContextualItem` */ - fun get(item: ContextualItem): T = getInner(item, item.defaultValueFromContext(context)) + fun get(item: ContextualItem): T = + getInner(item, item.defaultValueFromContext(context)) /** Wrapper around `getInner` for an `Item` */ - fun get(item: ConstantItem): T = getInner(item, item.defaultValue) + fun get(item: ConstantItem): T = getInner(item, item.defaultValue) /** * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the @@ -33,11 +32,11 @@ class LauncherPrefs(private val context: Context) { * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set`. */ @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") - private fun getInner(item: Item, default: T): T { + private fun getInner(item: Item, default: T): T { val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) - return when (item.type) { - String::class.java -> sp.getString(item.sharedPrefKey, default as? String) + return when (default::class.java) { + String::class.java -> sp.getString(item.sharedPrefKey, default as String) Boolean::class.java, java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean) Int::class.java, @@ -46,10 +45,11 @@ class LauncherPrefs(private val context: Context) { java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float) Long::class.java, java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long) - Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set) + Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as Set) else -> throw IllegalArgumentException( - "item type: ${item.type}" + " is not compatible with sharedPref methods" + "item type: ${default::class.java}" + + " is not compatible with sharedPref methods" ) } as T @@ -224,36 +224,39 @@ class LauncherPrefs(private val context: Context) { backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE) @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "") @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "") - @JvmField val GRID_NAME = ConstantItem("idp_grid_name", true, null, String::class.java) @JvmField val ALLOW_ROTATION = - backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) { + backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY) { RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info) } @VisibleForTesting @JvmStatic fun backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem = - ConstantItem(sharedPrefKey, true, defaultValue) + ConstantItem(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue) @JvmStatic fun backedUpItem( sharedPrefKey: String, - type: Class, defaultValueFromContext: (c: Context) -> T - ): ContextualItem = ContextualItem(sharedPrefKey, true, defaultValueFromContext, type) + ): ContextualItem = + ContextualItem( + sharedPrefKey, + LauncherFiles.SHARED_PREFERENCES_KEY, + defaultValueFromContext + ) @VisibleForTesting @JvmStatic fun nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem = - ConstantItem(sharedPrefKey, false, defaultValue) + ConstantItem(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue) @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.") @JvmStatic fun getPrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so we use single cached instance return context.applicationContext.getSharedPreferences( - SHARED_PREFERENCES_KEY, + LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE ) } @@ -263,7 +266,7 @@ class LauncherPrefs(private val context: Context) { fun getDevicePrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so we use a single cached instance return context.applicationContext.getSharedPreferences( - DEVICE_PREFERENCES_KEY, + LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE ) } @@ -272,26 +275,21 @@ class LauncherPrefs(private val context: Context) { abstract class Item { abstract val sharedPrefKey: String - abstract val isBackedUp: Boolean - abstract val type: Class<*> - val sharedPrefFile: String = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY + abstract val sharedPrefFile: String fun to(value: T): Pair = Pair(this, value) } data class ConstantItem( override val sharedPrefKey: String, - override val isBackedUp: Boolean, - val defaultValue: T, - // The default value can be null. If so, the type needs to be explicitly stated, or else NPE - override val type: Class = defaultValue!!::class.java + override val sharedPrefFile: String, + val defaultValue: T ) : Item() data class ContextualItem( override val sharedPrefKey: String, - override val isBackedUp: Boolean, - private val defaultSupplier: (c: Context) -> T, - override val type: Class + override val sharedPrefFile: String, + private val defaultSupplier: (c: Context) -> T ) : Item() { private var default: T? = null diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt index d40a7bcf8c..31e8d3099b 100644 --- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt +++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt @@ -13,7 +13,7 @@ import org.junit.runner.RunWith private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false) private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)") private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1) -private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, Boolean::class.java) +private val TEST_CONTEXTUAL_ITEM = LauncherPrefs.backedUpItem("4") { true } @SmallTest @RunWith(AndroidJUnit4::class) From 21cec7e9ffbeedddbd761aeee1772d50d38aa3d9 Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 21 Feb 2023 18:10:08 +0000 Subject: [PATCH 05/40] Revert "Migrate IDP_GRID_NAME usage to LauncherPrefs" This reverts commit 6f9a57186c53fe3352a12a27e566ffff3c5c64e0. Reason for revert: A Bug where only device preferences were being used, not the main shared preference file. Bug: 269569568 Test: Verified this on device. Change-Id: I8422b2d7073537bd46c3a91033bd2281bbd306b7 (cherry picked from commit df11959779ca08a48308d9e390eb5b3fdb4bbf35) Merged-In: I8422b2d7073537bd46c3a91033bd2281bbd306b7 --- .../launcher3/InvariantDeviceProfile.java | 13 +++-- src/com/android/launcher3/LauncherPrefs.kt | 52 +++++++++---------- .../android/launcher3/LauncherPrefsTest.kt | 2 +- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 2fb0fa6207..604c1b87e6 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherPrefs.GRID_NAME; import static com.android.launcher3.Utilities.dpiFromPx; import static com.android.launcher3.config.FeatureFlags.ENABLE_DEVICE_PROFILE_LOGGING; import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; @@ -94,6 +93,8 @@ public class InvariantDeviceProfile { public static final int TYPE_MULTI_DISPLAY = 1; public static final int TYPE_TABLET = 2; + private static final String KEY_IDP_GRID_NAME = "idp_grid_name"; + private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; // Constants that affects the interpolation curve between statically defined device profile @@ -206,7 +207,8 @@ public class InvariantDeviceProfile { String gridName = getCurrentGridName(context); String newGridName = initGrid(context, gridName); if (!newGridName.equals(gridName)) { - LauncherPrefs.get(context).put(GRID_NAME, newGridName); + LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName) + .apply(); } new DeviceGridState(this).writeToPrefs(context); @@ -314,7 +316,7 @@ public class InvariantDeviceProfile { } public static String getCurrentGridName(Context context) { - return LauncherPrefs.get(context).get(GRID_NAME); + return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null); } private String initGrid(Context context, String gridName) { @@ -456,8 +458,9 @@ public class InvariantDeviceProfile { public void setCurrentGrid(Context context, String gridName) { - LauncherPrefs.get(context).put(GRID_NAME, gridName); - MAIN_EXECUTOR.execute(() -> onConfigChanged(context.getApplicationContext())); + Context appContext = context.getApplicationContext(); + LauncherPrefs.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply(); + MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext)); } private Object[] toModelState() { diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt index befaa64d50..2e07e3022a 100644 --- a/src/com/android/launcher3/LauncherPrefs.kt +++ b/src/com/android/launcher3/LauncherPrefs.kt @@ -4,8 +4,6 @@ import android.content.Context import android.content.SharedPreferences import android.content.SharedPreferences.OnSharedPreferenceChangeListener import androidx.annotation.VisibleForTesting -import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY -import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY import com.android.launcher3.allapps.WorkProfileManager import com.android.launcher3.model.DeviceGridState import com.android.launcher3.pm.InstallSessionHelper @@ -22,10 +20,11 @@ import com.android.launcher3.util.Themes class LauncherPrefs(private val context: Context) { /** Wrapper around `getInner` for a `ContextualItem` */ - fun get(item: ContextualItem): T = getInner(item, item.defaultValueFromContext(context)) + fun get(item: ContextualItem): T = + getInner(item, item.defaultValueFromContext(context)) /** Wrapper around `getInner` for an `Item` */ - fun get(item: ConstantItem): T = getInner(item, item.defaultValue) + fun get(item: ConstantItem): T = getInner(item, item.defaultValue) /** * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the @@ -33,11 +32,11 @@ class LauncherPrefs(private val context: Context) { * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set`. */ @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") - private fun getInner(item: Item, default: T): T { + private fun getInner(item: Item, default: T): T { val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE) - return when (item.type) { - String::class.java -> sp.getString(item.sharedPrefKey, default as? String) + return when (default::class.java) { + String::class.java -> sp.getString(item.sharedPrefKey, default as String) Boolean::class.java, java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean) Int::class.java, @@ -46,10 +45,11 @@ class LauncherPrefs(private val context: Context) { java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float) Long::class.java, java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long) - Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set) + Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as Set) else -> throw IllegalArgumentException( - "item type: ${item.type}" + " is not compatible with sharedPref methods" + "item type: ${default::class.java}" + + " is not compatible with sharedPref methods" ) } as T @@ -224,36 +224,39 @@ class LauncherPrefs(private val context: Context) { backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE) @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "") @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "") - @JvmField val GRID_NAME = ConstantItem("idp_grid_name", true, null, String::class.java) @JvmField val ALLOW_ROTATION = - backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) { + backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY) { RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info) } @VisibleForTesting @JvmStatic fun backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem = - ConstantItem(sharedPrefKey, true, defaultValue) + ConstantItem(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue) @JvmStatic fun backedUpItem( sharedPrefKey: String, - type: Class, defaultValueFromContext: (c: Context) -> T - ): ContextualItem = ContextualItem(sharedPrefKey, true, defaultValueFromContext, type) + ): ContextualItem = + ContextualItem( + sharedPrefKey, + LauncherFiles.SHARED_PREFERENCES_KEY, + defaultValueFromContext + ) @VisibleForTesting @JvmStatic fun nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem = - ConstantItem(sharedPrefKey, false, defaultValue) + ConstantItem(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue) @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.") @JvmStatic fun getPrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so we use single cached instance return context.applicationContext.getSharedPreferences( - SHARED_PREFERENCES_KEY, + LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE ) } @@ -263,7 +266,7 @@ class LauncherPrefs(private val context: Context) { fun getDevicePrefs(context: Context): SharedPreferences { // Use application context for shared preferences, so we use a single cached instance return context.applicationContext.getSharedPreferences( - DEVICE_PREFERENCES_KEY, + LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE ) } @@ -272,26 +275,21 @@ class LauncherPrefs(private val context: Context) { abstract class Item { abstract val sharedPrefKey: String - abstract val isBackedUp: Boolean - abstract val type: Class<*> - val sharedPrefFile: String = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY + abstract val sharedPrefFile: String fun to(value: T): Pair = Pair(this, value) } data class ConstantItem( override val sharedPrefKey: String, - override val isBackedUp: Boolean, - val defaultValue: T, - // The default value can be null. If so, the type needs to be explicitly stated, or else NPE - override val type: Class = defaultValue!!::class.java + override val sharedPrefFile: String, + val defaultValue: T ) : Item() data class ContextualItem( override val sharedPrefKey: String, - override val isBackedUp: Boolean, - private val defaultSupplier: (c: Context) -> T, - override val type: Class + override val sharedPrefFile: String, + private val defaultSupplier: (c: Context) -> T ) : Item() { private var default: T? = null diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt index d40a7bcf8c..31e8d3099b 100644 --- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt +++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt @@ -13,7 +13,7 @@ import org.junit.runner.RunWith private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false) private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)") private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1) -private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, Boolean::class.java) +private val TEST_CONTEXTUAL_ITEM = LauncherPrefs.backedUpItem("4") { true } @SmallTest @RunWith(AndroidJUnit4::class) From 41d57e9fcf637820ee59387192c80090bcbada61 Mon Sep 17 00:00:00 2001 From: Sam Dubey Date: Mon, 20 Mar 2023 16:10:58 +0000 Subject: [PATCH 06/40] Revert "Move recents animation controller to shell" Revert submission 21912191-recents_to_shell Reason for revert: Testing b/274454574 Reverted changes: /q/submissionid:21912191-recents_to_shell Change-Id: If1732c1f539a973a2ef0dc0f05302fcaaf366300 (cherry picked from commit on googleplex-android-review.googlesource.com host: 1c52543678526aaae4a14bea7bdab12a92302aab) Merged-In: If1732c1f539a973a2ef0dc0f05302fcaaf366300 --- .../com/android/quickstep/SystemUiProxy.java | 55 ------------------- .../quickstep/TaskAnimationManager.java | 8 ++- 2 files changed, 6 insertions(+), 57 deletions(-) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index d8f49fffdd..52b32d7785 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -21,7 +21,6 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.app.ActivityManager; -import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.content.ComponentName; @@ -39,8 +38,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; -import android.view.IRecentsAnimationController; -import android.view.IRecentsAnimationRunner; import android.view.IRemoteAnimationRunner; import android.view.MotionEvent; import android.view.RemoteAnimationAdapter; @@ -48,7 +45,6 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.IOnBackInvokedCallback; import android.window.RemoteTransition; -import android.window.TaskSnapshot; import android.window.TransitionFilter; import androidx.annotation.Nullable; @@ -59,9 +55,6 @@ import com.android.internal.util.ScreenshotRequest; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.systemui.shared.recents.ISystemUiProxy; -import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.RecentsAnimationControllerCompat; -import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController; import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; import com.android.systemui.shared.system.smartspace.SmartspaceState; @@ -142,20 +135,9 @@ public class SystemUiProxy implements ISystemUiProxy { // TODO(141886704): Find a way to remove this private int mLastSystemUiStateFlags; - /** - * This is a singleton pending intent that is used to start recents via Shell (which is a - * different process). It is bare-bones, so it's expected that the component and options will - * be provided via fill-in intent. - */ - private final PendingIntent mRecentsPendingIntent; - public SystemUiProxy(Context context) { mContext = context; mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync); - final Intent baseIntent = new Intent().setPackage(mContext.getPackageName()); - mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent, - PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT - | Intent.FILL_IN_COMPONENT); } @Override @@ -1097,41 +1079,4 @@ public class SystemUiProxy implements ISystemUiProxy { Log.e(TAG, "Failed call setUnfoldAnimationListener", e); } } - - - /** - * Starts the recents activity. The caller should manage the thread on which this is called. - */ - public boolean startRecentsActivity(Intent intent, ActivityOptions options, - RecentsAnimationListener listener) { - final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() { - @Override - public void onAnimationStart(IRecentsAnimationController controller, - RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, - Rect homeContentInsets, Rect minimizedHomeBounds) { - listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps, - wallpapers, homeContentInsets, minimizedHomeBounds); - } - - @Override - public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) { - listener.onAnimationCanceled( - ThumbnailData.wrap(taskIds, taskSnapshots)); - } - - @Override - public void onTasksAppeared(RemoteAnimationTarget[] apps) { - listener.onTasksAppeared(apps); - } - }; - final Bundle optsBundle = options.toBundle(); - try { - mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle, - mContext.getIApplicationThread(), runner); - } catch (RemoteException e) { - Log.e(TAG, "Error starting recents via shell", e); - return false; - } - return true; - } } diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 5010ea564e..2c95516b68 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -22,6 +22,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION; +import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -30,6 +31,7 @@ import android.content.Intent; import android.os.SystemProperties; import android.util.Log; import android.view.RemoteAnimationTarget; +import android.window.RemoteTransition; import androidx.annotation.Nullable; import androidx.annotation.UiThread; @@ -228,7 +230,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn mCallbacks.addListener(listener); if (ENABLE_SHELL_TRANSITIONS) { - final ActivityOptions options = ActivityOptions.makeBasic(); + RemoteTransition transition = newRemoteTransition(mCallbacks, + mCtx.getIApplicationThread()); + final ActivityOptions options = ActivityOptions.makeRemoteTransition(transition); // Allowing to pause Home if Home is top activity and Recents is not Home. So when user // start home when recents animation is playing, the home activity can be resumed again // to let the transition controller collect Home activity. @@ -244,7 +248,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn options.setTransientLaunch(); } options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); - SystemUiProxy.INSTANCE.getNoCreate().startRecentsActivity(intent, options, mCallbacks); + UI_HELPER_EXECUTOR.execute(() -> mCtx.startActivity(intent, options.toBundle())); } else { UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() .startRecentsActivity(intent, eventTime, mCallbacks, null, null)); From cdac560c87c5980c046a5f22a28d3d8b84b0dd5a Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Tue, 30 May 2023 18:06:36 +0000 Subject: [PATCH 07/40] Enabled trackpad gestures To be cherrypicked (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:55140bd010203a60cdec8378bdacc90e010c6f02) Merged-In: Id6831da122398de34ddaae8134995aa42be5eae6 Change-Id: Id6831da122398de34ddaae8134995aa42be5eae6 --- 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 621c2abb90..edf41c34b3 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -381,7 +381,7 @@ public final class FeatureFlags { "Enable initiating split screen from workspace to workspace."); public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401, - "ENABLE_TRACKPAD_GESTURE", DISABLED, "Enables trackpad gesture."); + "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture."); // TODO(Block 29): Clean up flags public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897, From 5148c096e1f99d2c88158ba5b6deb3c50a01e587 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 26 Jun 2023 07:08:08 +0000 Subject: [PATCH 08/40] Fix NPE due to invalid RecentsView access Fixes: 288829919 Test: N/a, reorganizing code behind null check (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:cdf410f1349edf3451e1a8af64ea2d8cd676ffaa) Merged-In: I9efe48cab239b521f7e729af06706c352be2499c Change-Id: I9efe48cab239b521f7e729af06706c352be2499c --- .../com/android/quickstep/views/TaskView.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 17d83ec208..83a5c72127 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -841,17 +841,20 @@ public class TaskView extends FrameLayout implements Reusable { // the actual overview state failureListener.register(mActivity, mTask.key.id, () -> { notifyTaskLaunchFailed(TAG); - // Disable animations for now, as it is an edge case and the app usually covers - // launcher and also any state transition animation also gets clobbered by - // QuickstepTransitionManager.createWallpaperOpenAnimations when launcher - // shows again - getRecentsView().startHome(false /* animated */); RecentsView rv = getRecentsView(); - if (rv != null && rv.mSizeStrategy.getTaskbarController() != null) { - // LauncherTaskbarUIController depends on the launcher state when checking - // whether to handle resume, but that can come in before startHome() changes - // the state, so force-refresh here to ensure the taskbar is updated - rv.mSizeStrategy.getTaskbarController().refreshResumedState(); + if (rv != null) { + // Disable animations for now, as it is an edge case and the app usually + // covers launcher and also any state transition animation also gets + // clobbered by QuickstepTransitionManager.createWallpaperOpenAnimations + // when launcher shows again + rv.startHome(false /* animated */); + if (rv.mSizeStrategy.getTaskbarController() != null) { + // LauncherTaskbarUIController depends on the launcher state when + // checking whether to handle resume, but that can come in before + // startHome() changes the state, so force-refresh here to ensure the + // taskbar is updated + rv.mSizeStrategy.getTaskbarController().refreshResumedState(); + } } }); } From 18f815a7fda64344783c8b92be7d60153c2e50e8 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 23 Jun 2023 18:14:00 +0000 Subject: [PATCH 09/40] Restore strong reference to animation runner - The remote animation factory needs to be strongly referenced since the only other reference is a weakly held one from LauncherAnimationRunner, and if a gc happens in between starting the animation and the onAnimationStart() callback, then the animation will not play. Fixes: 284106887 Test: Force a gc after creating a remote app launch animation and ensure that the runner still exists when the animation starts (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6cfe2e6cbe52f5fc9aa0eb94afaeb7679810cc13) Merged-In: I5f584451b41c666916801b8ea0cb470c7ab9fc51 Change-Id: I5f584451b41c666916801b8ea0cb470c7ab9fc51 --- .../com/android/launcher3/QuickstepTransitionManager.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index e5fd605904..33a9f48a82 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -229,6 +229,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private RemoteAnimationProvider mRemoteAnimationProvider; // Strong refs to runners which are cleared when the launcher activity is destroyed private RemoteAnimationFactory mWallpaperOpenRunner; + private RemoteAnimationFactory mAppLaunchRunner; private RemoteAnimationFactory mKeyguardGoingAwayRunner; private RemoteAnimationFactory mWallpaperOpenTransitionRunner; @@ -298,17 +299,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener boolean fromRecents = isLaunchingFromRecents(v, null /* targets */); RunnableList onEndCallback = new RunnableList(); - RemoteAnimationFactory delegateRunner = new AppLaunchAnimationRunner(v, onEndCallback); + mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback); ItemInfo tag = (ItemInfo) v.getTag(); if (tag != null && tag.shouldUseBackgroundAnimation()) { ContainerAnimationRunner containerAnimationRunner = ContainerAnimationRunner.from(v, mStartingWindowListener, onEndCallback); if (containerAnimationRunner != null) { - delegateRunner = containerAnimationRunner; + mAppLaunchRunner = containerAnimationRunner; } } RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner( - mHandler, delegateRunner, true /* startAtFrontOfQueue */); + mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */); // Note that this duration is a guess as we do not know if the animation will be a // recents launch or not for sure until we know the opening app targets. @@ -1164,6 +1165,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // Also clear strong references to the runners registered with the remote animation // definition so we don't have to wait for the system gc mWallpaperOpenRunner = null; + mAppLaunchRunner = null; mKeyguardGoingAwayRunner = null; } } From 8a7181d45ea5f3d4e228a92c4113585337264461 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Mon, 26 Jun 2023 12:23:08 +0000 Subject: [PATCH 10/40] Prevent exception when quick switching between two split pairs When switching in between two split pairs just quick enough, it is possible to send the second entering split transition while it is animating the first entering split transition which is merged to a recents-during-split transition. The split parents might not be collected into the second transition because the split parents are already visible at that time, and there is no recents transition to merge to. So updates to not throwing when there is no split parent when composing a recents-to-split animation. Bug: 236226779 Test: repro steps of the bug and the Launcher won't throw (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:97eb471f2efda1eccce7eba8c68301b2fa34c5e7) Merged-In: I3a595722721186e8de7d60c9fb8c099ec799804a Change-Id: I3a595722721186e8de7d60c9fb8c099ec799804a --- quickstep/src/com/android/quickstep/TaskViewUtils.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 499a2601a9..1238819acd 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -473,16 +473,14 @@ public final class TaskViewUtils { throw new IllegalStateException( "Expected task to be showing, but it is " + mode); } - if (change.getParent() == null) { - throw new IllegalStateException("Initiating multi-split launch but the split" - + "root of " + taskId + " is already visible or has broken hierarchy."); - } } if (taskId == initialTaskId) { - splitRoot1 = transitionInfo.getChange(change.getParent()); + splitRoot1 = change.getParent() == null ? change : + transitionInfo.getChange(change.getParent()); } if (taskId == secondTaskId) { - splitRoot2 = transitionInfo.getChange(change.getParent()); + splitRoot2 = change.getParent() == null ? change : + transitionInfo.getChange(change.getParent()); } } From 5cdfa120df03fd9d983c8469508d7d73bd1198c7 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 23 Jun 2023 16:34:32 +0000 Subject: [PATCH 11/40] Workaround for handling the restart of an already visible task - If a task is already visible, then startActivity is a no-op and the remote transition that launcher expects to run is not started. As a workaround (until restarts are an actual transition), listen for the case where a task is restarted and invoke the end callbacks Fixes: 286016555 Test: Repro steps on the bug (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:57c2a79e47bc583306bd77ea3039d821a8d33dde) Merged-In: Iec3ab97c8817a5e95399cec90f891d65f369d234 Change-Id: Iec3ab97c8817a5e95399cec90f891d65f369d234 --- .../launcher3/QuickstepTransitionManager.java | 7 ++ .../uioverrides/QuickstepLauncher.java | 8 ++- .../TaskRestartedDuringLaunchListener.java | 72 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 33a9f48a82..dfea5159eb 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -144,6 +144,7 @@ import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; import com.android.quickstep.util.SurfaceTransactionApplier; +import com.android.quickstep.util.TaskRestartedDuringLaunchListener; import com.android.quickstep.util.WorkspaceRevealAnim; import com.android.quickstep.views.FloatingWidgetView; import com.android.quickstep.views.RecentsView; @@ -299,6 +300,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener boolean fromRecents = isLaunchingFromRecents(v, null /* targets */); RunnableList onEndCallback = new RunnableList(); + // Handle the case where an already visible task is launched which results in no transition + TaskRestartedDuringLaunchListener restartedListener = + new TaskRestartedDuringLaunchListener(); + restartedListener.register(onEndCallback::executeAllAndDestroy); + onEndCallback.add(restartedListener::unregister); + mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback); ItemInfo tag = (ItemInfo) v.getTag(); if (tag != null && tag.shouldUseBackgroundAnimation()) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 658bf2d865..2f13c5de44 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -345,11 +345,13 @@ public class QuickstepLauncher extends Launcher { @Override public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) { - // Only pause is taskbar controller is not present + // Only pause is taskbar controller is not present until the transition (if it exists) ends mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null); RunnableList result = super.startActivitySafely(v, intent, item); - if (getTaskbarUIController() == null && result == null) { - mHotseatPredictionController.setPauseUIUpdate(false); + if (result == null) { + if (getTaskbarUIController() == null) { + mHotseatPredictionController.setPauseUIUpdate(false); + } } else { result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false)); } diff --git a/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java new file mode 100644 index 0000000000..91e8376990 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.util; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + +import android.app.Activity; +import android.app.ActivityManager; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; +import com.android.quickstep.RecentsModel; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +/** + * This class tracks the failure of a task launch through the Launcher.startActivitySafely() call, + * in an edge case in which a task may already be visible on screen (ie. in PIP) and no transition + * will be run in WM, which results in expected callbacks to not be processed. + * + * We transiently register a task stack listener during a task launch and if the restart signal is + * received, then the registered callback will be notified. + */ +public class TaskRestartedDuringLaunchListener implements TaskStackChangeListener { + + private static final String TAG = "TaskRestartedDuringLaunchListener"; + + private @NonNull Runnable mTaskRestartedCallback = null; + + /** + * Registers a failure listener callback if it detects a scenario in which an app launch + * resulted in an already existing task to be "restarted". + */ + public void register(@NonNull Runnable taskRestartedCallback) { + TaskStackChangeListeners.getInstance().registerTaskStackListener(this); + mTaskRestartedCallback = taskRestartedCallback; + } + + /** + * Unregisters the failure listener. + */ + public void unregister() { + TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this); + mTaskRestartedCallback = null; + } + + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + if (wasVisible) { + Log.d(TAG, "Detected activity restart during launch for task=" + task.taskId); + mTaskRestartedCallback.run(); + unregister(); + } + } +} From 4fdf5684feebd99c4f0973db2df50be55934f5e8 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Tue, 27 Jun 2023 17:07:13 +0000 Subject: [PATCH 12/40] Polish home-key from split to pip transition Includes WINDOWING_MODE_MULTI_WINDOW closing target to the condition of playing fallback animation. So the remaining splitting task won't be play with iconview animation when home-key to auto-pip consumed another splitting task in pip transition handler. Bug: 281476331 Test: repro steps of the bug Test: pass existing tests Video: http://recall/-/fLARJNt42LVxc3tt86SneW/eelqATeE1REoOtOEDxeDVR (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:365105118220b218005225b038142273524877cc) Merged-In: If05d8841a6a940e61f71683422ef1a3d4e3597c7 Change-Id: If05d8841a6a940e61f71683422ef1a3d4e3597c7 --- .../launcher3/QuickstepTransitionManager.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index dfea5159eb..ca9c5771b1 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -18,6 +18,7 @@ package com.android.launcher3; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; @@ -1210,14 +1211,15 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener return false; } - private boolean hasMultipleTargetsWithMode(RemoteAnimationTarget[] targets, int mode) { + private boolean shouldPlayFallbackClosingAnimation(RemoteAnimationTarget[] targets) { int numTargets = 0; for (RemoteAnimationTarget target : targets) { - if (target.mode == mode) { + if (target.mode == MODE_CLOSING) { numTargets++; - } - if (numTargets > 1) { - return true; + if (numTargets > 1 || target.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_MULTI_WINDOW) { + return true; + } } } return false; @@ -1604,7 +1606,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener boolean playFallBackAnimation = (launcherView == null && launcherIsForceInvisibleOrOpening) || mLauncher.getWorkspace().isOverlayShown() - || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING); + || shouldPlayFallbackClosingAnimation(appTargets); boolean playWorkspaceReveal = true; boolean skipAllAppsScale = false; From 170ad5b4d61ed7df202c1633ebc00724320174cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ciche=C5=84ski?= Date: Tue, 27 Jun 2023 02:58:26 +0000 Subject: [PATCH 13/40] Remove the keep clear areas XML tag from Hotseat It reports its size through SystemUiProxy and this tag causes it to report the region twice. Additionally upon screen rotation the value is getting updated with a delay, so for a moment two keep clear areas for Launcher are present - one from the previous orientation (on the side), and one from the current orientation (matching the proxy value in unrestrictedKeepClearAreas). Bug: 285242520 Test: before http://recall/-/ekEuGtt9d9HWqkUtAzpHx8/cLtiXGYUyItm2kNCCEkkWA Test: after http://recall/-/ekEuGtt9d9HWqkUtAzpHx8/iPs6fwdSXG3TE0IERmxA8 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b5b218eea929f719703ca91df6b9868645909f4c) Merged-In: I40dfe08680c944f2be5db0f6b15515492f409565 Change-Id: I40dfe08680c944f2be5db0f6b15515492f409565 --- res/layout/hotseat.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml index 95ebd94d0e..82b0b8d74e 100644 --- a/res/layout/hotseat.xml +++ b/res/layout/hotseat.xml @@ -21,5 +21,4 @@ android:layout_height="match_parent" android:theme="@style/HomeScreenElementTheme" android:importantForAccessibility="no" - android:preferKeepClear="true" launcher:containerType="hotseat" /> \ No newline at end of file From df537ccdb7eee3dfab6eb8cda662d26110e86279 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 28 Jun 2023 19:29:19 -0700 Subject: [PATCH 14/40] Cleans up bad state when transient taskbar shows on home. Bug: 279514548 Test: open transparent activity on top of launcher unstash taskbar go home (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3bad3ebaad529a8be587b9c0eae61faf87ea8505) Merged-In: I13ab79b334e1f8feda441a82cc4d035c0142f513 Change-Id: I13ab79b334e1f8feda441a82cc4d035c0142f513 --- .../launcher3/taskbar/TaskbarLauncherStateController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 17e7e1b9bb..ddea51f737 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -202,6 +202,11 @@ public class TaskbarLauncherStateController { public void onStateTransitionComplete(LauncherState finalState) { mLauncherState = finalState; updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false); + // TODO(b/279514548) Cleans up bad state that can occur when user interacts with + // taskbar on top of transparent activity. + if (finalState == LauncherState.NORMAL && mLauncher.isResumed()) { + updateStateForFlag(FLAG_RESUMED, true); + } applyState(); boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT; com.android.launcher3.taskbar.Utilities.setOverviewDragState( From 7cc6f650113da7f4435399b9929eabc2b344bd4f Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Wed, 28 Jun 2023 11:51:18 -0700 Subject: [PATCH 15/40] Taskbar All Apps Shortcut Menu Acccessibility Focus Bug: 280657266 Test: manual Flag: not needed (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:53d953ccd24cd378418d9bd8c9964589fb1af44a) Merged-In: I2426636067ce3635b8b24b9f5d093dad7d9ddf4c Change-Id: I2426636067ce3635b8b24b9f5d093dad7d9ddf4c --- .../launcher3/taskbar/TaskbarPopupController.java | 1 - .../android/launcher3/popup/PopupContainerWithArrow.java | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index 5eec726e27..512b77a92a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -177,7 +177,6 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba systemShortcuts); } - icon.clearAccessibilityFocus(); container.addOnAttachStateChangeListener( new PopupLiveUpdateHandler(context, container) { @Override diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 8274789ef5..1f26bab502 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -125,6 +125,14 @@ public class PopupContainerWithArrow this(context, null, 0); } + @Override + protected View getAccessibilityInitialFocusView() { + if (mSystemShortcutContainer != null) { + return mSystemShortcutContainer.getChildAt(0); + } + return super.getAccessibilityInitialFocusView(); + } + public LauncherAccessibilityDelegate getAccessibilityDelegate() { return mAccessibilityDelegate; } @@ -242,7 +250,6 @@ public class PopupContainerWithArrow popupDataProvider.getNotificationKeysForItem(item), systemShortcuts); } - launcher.tryClearAccessibilityFocus(icon); launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item)); container.requestFocus(); return container; From 1f6a5f50e522cea404f414dbfe30940fce65532f Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 6 Jul 2023 04:34:54 +0000 Subject: [PATCH 16/40] Ignore recents transition if there are no closing tasks - In the case where Launcher calls startRecentsTransition while there are no other visible tasks, we should not be continuing with the transition as there are no tasks for Launcher to control. This was previously handled in RecentsAnimationController in legacy transitions, but the safer fix is to ignore it on the Launcher side for this release. Bug: 289175232 Test: Manually trigger empty targets and verify no issues (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6f7e15ff644bac2f11ecb779030255efebc18885) Merged-In: I3657c000cbc8c14c9ac989c2a57715515c96edb6 Change-Id: I3657c000cbc8c14c9ac989c2a57715515c96edb6 --- .../quickstep/RecentsAnimationCallbacks.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index 523a98ec4b..2256cbf14b 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -37,6 +38,7 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Set; @@ -101,9 +103,22 @@ public class RecentsAnimationCallbacks implements RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, Rect homeContentInsets, Rect minimizedHomeBounds) { + long appCount = Arrays.stream(appTargets) + .filter(app -> app.mode == MODE_CLOSING) + .count(); + if (appCount == 0) { + // Edge case, if there are no closing app targets, then Launcher has nothing to handle + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "RecentsAnimationCallbacks.onAnimationStart (canceled)", + /* extras= */ 0, + /* gestureEvent= */ START_RECENTS_ANIMATION); + notifyAnimationCanceled(); + animationController.finish(false /* toHome */, false /* sendUserLeaveHint */); + return; + } + mController = new RecentsAnimationController(animationController, mAllowMinimizeSplitScreen, this::onAnimationFinished); - if (mCancelled) { Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), mController::finishAnimationToApp); From 1672e8a367bf56b5e654e0492385b34e3c02ab63 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 6 Jul 2023 22:01:49 +0000 Subject: [PATCH 17/40] Fix an issue with nav bar translations not being updated - There are flows where the shared taskbar state is updated prior to being destroyed, and not updated to the latest values when the taskbar is recreated. ie. unfolded -> lock screen -> LauncherTaskbarUiController's mTaskbarInAppDisplayProgress[SYSUI_SURFACE_PROGRESS_INDEX] is set to 1 due to the notif shade (lockscreen) showing. This is written into TaskbarSharedState's sysuiStateFlags and inAppDisplayProgressMultiPropValues. fold -> TaskbarActivityContext is destroyed unlock -> TaskbarManager and TaskbarSharedState's sysuiStateFlags are updated while the device is folded unfold -> TaskbarActivityContext is recreated and initialized which restores from the shared state's inAppDisplayProgressMultiPropValues. It also tries to reapply the shared state's sysuiStateFlags, but this doesn't update inAppDisplayProgressMultiPropValues because the state's "enabled" state is not updated (default is no flag set, and lockscreen sysui state is not set anymore). -> The restored inAppDisplayProgressMultiPropValues value results in the wrong translation. - Note that after the above, the NavbarButtonsViewController state is actually correct and reflects the SysUI state, but the LauncherTaskbarUiController state is wrong. This CL tries to manually update the ui controller to the correct state when it is recreated. - CL also fixes a separate issue where LauncherTaskbarUIController could potentially overwrite the saved state progresses while restoring them due to the state callback being called Bug: 283346744 Test: Unfold -> Lockscreen -> Fold -> Unlock -> Unfold and ensure the buttons are translated correctly Signed-off-by: Winson Chung (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ccd359d76c7f42d4fc799f19d54a66c7c2fa462a) Merged-In: I43e473faf4fa2a493b9705506e3755df8f6264e7 Change-Id: I43e473faf4fa2a493b9705506e3755df8f6264e7 --- .../taskbar/LauncherTaskbarUIController.java | 5 +++++ .../taskbar/NavbarButtonsViewController.java | 9 +++++++++ .../launcher3/taskbar/TaskbarActivityContext.java | 4 +--- .../launcher3/taskbar/TaskbarControllers.java | 13 +++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index ba6f1651ed..abf49eb195 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -50,6 +50,7 @@ import com.android.quickstep.util.GroupTask; import com.android.quickstep.views.RecentsView; import java.io.PrintWriter; +import java.util.Arrays; /** * A data source which integrates with a Launcher instance @@ -105,6 +106,10 @@ public class LauncherTaskbarUIController extends TaskbarUIController { // Restore the in-app display progress from before Taskbar was recreated. float[] prevProgresses = mControllers.getSharedState().inAppDisplayProgressMultiPropValues; + // Make a copy of the previous progress to set since updating the multiprop will update + // the property which also calls onInAppDisplayProgressChanged() which writes the current + // values into the shared state + prevProgresses = Arrays.copyOf(prevProgresses, prevProgresses.length); for (int i = 0; i < prevProgresses.length; i++) { mTaskbarInAppDisplayProgressMultiProp.get(i).setValue(prevProgresses[i]); } diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 9a9e0ba70c..9c463cb68c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -923,6 +923,15 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } + /** + * Called whenever a new ui controller is set, and should update anything that depends on the + * ui controller. + */ + public void onUiControllerChanged() { + updateNavButtonInAppDisplayProgressForSysui(); + updateNavButtonTranslationY(); + } + @Override public void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "NavbarButtonsViewController:"); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 43feec716d..a1390aeabb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -591,9 +591,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext { * Sets a new data-source for this taskbar instance */ public void setUIController(@NonNull TaskbarUIController uiController) { - mControllers.uiController.onDestroy(); - mControllers.uiController = uiController; - mControllers.uiController.init(mControllers); + mControllers.setUiController(uiController); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index 66c2eb3314..d3f80e31cd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -196,6 +196,19 @@ public class TaskbarControllers { mPostInitCallbacks.clear(); } + /** + * Sets the ui controller. + */ + public void setUiController(@NonNull TaskbarUIController newUiController) { + uiController.onDestroy(); + uiController = newUiController; + uiController.init(this); + uiController.updateStateForSysuiFlags(mSharedState.sysuiStateFlags); + + // Notify that the ui controller has changed + navbarButtonsViewController.onUiControllerChanged(); + } + @Nullable public TaskbarSharedState getSharedState() { // This should only be null if called before init() and after destroy(). From fb8a789fc9d1d1877c04f173f64998c5898e6463 Mon Sep 17 00:00:00 2001 From: Andrew Cole Date: Thu, 6 Jul 2023 09:18:33 -0700 Subject: [PATCH 18/40] Updating materialColorSurfaceContainerHighest color access For any view files applying the WidgetConatinerTheme via widgetsTheme they were incorrectly inheriting themes and skipping the AppTheme provided but only in light mode. In dark mode the WidgetContainerTheme.Dark was correctly inheriting the theme. To avoid a risky theme update for all widgetsThemes we just modify the color accessor to use @color instead of ?attr as these colors should not be attributes AFAIKT Bug: b/289305591 Test: Follow repro steps on the bug for smartspace (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:a9af3bd03db3c19a07a4015471ee5c83d364fd36) Merged-In: I26cc3239763f8eac3dfe5f094c6757692f46d1bc Change-Id: I26cc3239763f8eac3dfe5f094c6757692f46d1bc --- res/color-v31/surface.xml | 24 ------------------- res/drawable/add_item_dialog_background.xml | 2 +- .../button_top_rounded_bordered_ripple.xml | 2 +- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 res/color-v31/surface.xml diff --git a/res/color-v31/surface.xml b/res/color-v31/surface.xml deleted file mode 100644 index da4571a61c..0000000000 --- a/res/color-v31/surface.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/res/drawable/add_item_dialog_background.xml b/res/drawable/add_item_dialog_background.xml index c3a82695c2..e279fa051a 100644 --- a/res/drawable/add_item_dialog_background.xml +++ b/res/drawable/add_item_dialog_background.xml @@ -1,7 +1,7 @@ - + diff --git a/res/drawable/button_top_rounded_bordered_ripple.xml b/res/drawable/button_top_rounded_bordered_ripple.xml index f15a4a0c5f..f5b68866cb 100644 --- a/res/drawable/button_top_rounded_bordered_ripple.xml +++ b/res/drawable/button_top_rounded_bordered_ripple.xml @@ -25,7 +25,7 @@ android:topRightRadius="12dp" android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp" /> - + From e0600c6bcae2207e7a6e179305d3298605070a8b Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Fri, 23 Jun 2023 15:43:39 -0700 Subject: [PATCH 19/40] Handle onFlingFinished onRecentsAnimationStart If the onRecentsAnimationStart callback runs after the user lifts their finger and onFlingFinished runs, then onFlingFinished never has another chance to run, leaving the user trapped in a state where the launcher is not started and the AllSetActivity is still present but invisible. Reverted to allow onFlingFinished to run onRecentsAnimationStart to handle this edge case. Flag: not needed Fixes: 285194839 Test: Ran AllSetActivty with a delay in onRecentsAnimationStart (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:1b2c2e694c6e233057570d3e8b4e5a0aa9c0c51a) Merged-In: I33ce5c1d4955b34d4b77d3b740dc599621bd4ed1 Change-Id: I33ce5c1d4955b34d4b77d3b740dc599621bd4ed1 --- .../inputconsumers/ProgressDelegateInputConsumer.java | 3 ++- .../android/quickstep/interaction/AllSetActivity.java | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java index ab70272852..eac09ad7dc 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java @@ -103,7 +103,8 @@ public class ProgressDelegateInputConsumer implements InputConsumer, mStateCallback = new MultiStateCallback(STATE_NAMES); mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED, this::endRemoteAnimation); - mStateCallback.runOnceAtState(STATE_FLING_FINISHED, this::onFlingFinished); + mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED, + this::onFlingFinished); mSwipeDetector = new SingleAxisSwipeDetector(mContext, this, VERTICAL); mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false); diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index 2eaff46715..6619dd86f6 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -254,6 +254,9 @@ public class AllSetActivity extends Activity { mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null); mBinder.setOverviewTargetChangeListener(mBinder::preloadOverviewForSUWAllSet); mBinder.preloadOverviewForSUWAllSet(); + if (mTaskbarManager != null) { + mLauncherStartAnim = mTaskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION); + } } @Override @@ -328,12 +331,9 @@ public class AllSetActivity extends Activity { mRootView.setAlpha(alpha); mRootView.setTranslationY((alpha - 1) * mSwipeUpShift); - if (mLauncherStartAnim == null && mTaskbarManager != null) { - mLauncherStartAnim = mTaskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION); - } if (mLauncherStartAnim != null) { - mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange( - mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN)); + mLauncherStartAnim.setPlayFraction( + FAST_OUT_SLOW_IN.getInterpolation(mSwipeProgress.value)); } maybeResumeOrPauseBackgroundAnimation(); } From ca4e39d402d6bc8453f51834461c1543ff5b84e4 Mon Sep 17 00:00:00 2001 From: Becky Qiu Date: Tue, 11 Jul 2023 17:22:59 -0700 Subject: [PATCH 20/40] Fix the issue that on tablet/unfold, the allapps background color is wrong. As the new spec, it should use materialColorSurfaceDim for all devices. Bug: 288493929 Test: manual, before: https://screenshot.googleplex.com/7YxpFGekwm6Gz6N, after: https://screenshot.googleplex.com/44Gr2dBfWSct7FK Flag: no flag needed. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:aa6612d3731a3e98f716747b0ede473e3bbca161) Merged-In: Ic33d9a758fe9fbaa0f735c874a545df711a18e2f Change-Id: Ic33d9a758fe9fbaa0f735c874a545df711a18e2f --- .../launcher3/allapps/ActivityAllAppsContainerView.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index d4140d851c..1977704588 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -43,7 +43,6 @@ import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; import android.util.SparseArray; -import android.util.TypedValue; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -272,9 +271,8 @@ public class ActivityAllAppsContainerView 0, 0 // Bottom left }; - final TypedValue value = new TypedValue(); - getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true); - mBottomSheetBackgroundColor = value.data; + mBottomSheetBackgroundColor = + Themes.getAttrColor(getContext(), R.attr.materialColorSurfaceDim); updateBackgroundVisibility(mActivityContext.getDeviceProfile()); mSearchUiManager.initializeSearch(this); } From e79837c0f6067e5d3c475ccb27a06fe3ac439fef Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Wed, 12 Jul 2023 12:46:53 -0700 Subject: [PATCH 21/40] Creating a correctly populated mOccupied grid when reordering on foldables On ag/21680045 I copy the previous mOccupied but the right thing to do is to create a new one with all the views information. Some test stoped running inadvertently that's why we didn't catch this issue. There is a separate cl with the test to ensure we can catch it later on. Fix: 289584301 Test: ReorderWidgets (cherry picked from commit 1c5514b56619d0ae76948689bb51479f131d0e30) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:52d9d9780fb5a83302dd6fb3d875635a2ee57e1c) Merged-In: I27b5a6e38a556d1c73ff8fbbdd552da6045e5b64 Change-Id: I27b5a6e38a556d1c73ff8fbbdd552da6045e5b64 --- .../celllayout/MulticellReorderAlgorithm.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java index cb1216109a..a2e26b37f3 100644 --- a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java +++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java @@ -19,8 +19,10 @@ import android.view.View; import com.android.launcher3.CellLayout; import com.android.launcher3.MultipageCellLayout; +import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.util.GridOccupancy; +import java.util.Arrays; import java.util.function.Supplier; /** @@ -79,7 +81,7 @@ public class MulticellReorderAlgorithm extends ReorderAlgorithm { lp.canReorder = false; mcl.setCountX(mcl.getCountX() + 1); mcl.getShortcutsAndWidgets().addViewInLayout(mSeam, lp); - mcl.setOccupied(createGridOccupancyWithSeam(mcl.getOccupied())); + mcl.setOccupied(createGridOccupancyWithSeam()); mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY()); } @@ -93,7 +95,8 @@ public class MulticellReorderAlgorithm extends ReorderAlgorithm { /** * The function supplied here will execute while the CellLayout has a simulated seam added. - * @param f function to run under simulation + * + * @param f function to run under simulation * @param return value of the supplied function * @return Value of supplied function */ @@ -110,18 +113,17 @@ public class MulticellReorderAlgorithm extends ReorderAlgorithm { return res; } - GridOccupancy createGridOccupancyWithSeam(GridOccupancy gridOccupancy) { + GridOccupancy createGridOccupancyWithSeam() { + ShortcutAndWidgetContainer shortcutAndWidgets = mCellLayout.getShortcutsAndWidgets(); GridOccupancy grid = new GridOccupancy(mCellLayout.getCountX(), mCellLayout.getCountY()); - for (int x = 0; x < mCellLayout.getCountX(); x++) { - for (int y = 0; y < mCellLayout.getCountY(); y++) { - int offset = x >= mCellLayout.getCountX() / 2 ? 1 : 0; - if (x == mCellLayout.getCountX() / 2) { - grid.cells[x][y] = true; - } else { - grid.cells[x][y] = gridOccupancy.cells[x - offset][y]; - } - } + for (int i = 0; i < shortcutAndWidgets.getChildCount(); i++) { + View view = shortcutAndWidgets.getChildAt(i); + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams(); + int seamOffset = lp.getCellX() >= mCellLayout.getCountX() / 2 && lp.canReorder ? 1 : 0; + grid.markCells(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan, lp.cellVSpan, + true); } + Arrays.fill(grid.cells[mCellLayout.getCountX() / 2], true); return grid; } } From faf861aae5fec56f4b9f1b99ac194491d0a2b457 Mon Sep 17 00:00:00 2001 From: fbaron Date: Wed, 12 Jul 2023 10:16:49 -0700 Subject: [PATCH 22/40] Fix inability to remove or interact with folder when removing 2nd item from folder Fix: 289960317 Test: Verify in unfolded felix that going from 2 icon folder and dragging 2nd icon out of folder lets you open the app that remains where the folder was Test: Verify in unfolded felix that going from 2 icon folder and dragging 2nd icon into remove droptarget removes the folder and turns it into a single clickable icon flag: no flag (cherry picked from commit 5a7ea3069b984aef151fc2486319583c769cf12c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:326ec97ab6a4d348708830cd2860d4dd90a59039) Merged-In: I26138ee9f8e7cdb45cafe2446dc4d1e3d6d8347f Change-Id: I26138ee9f8e7cdb45cafe2446dc4d1e3d6d8347f --- src/com/android/launcher3/Launcher.java | 2 ++ src/com/android/launcher3/folder/LauncherDelegate.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 25eb16079f..66690431ec 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2194,6 +2194,8 @@ public class Launcher extends StatefulActivity /** * Returns the CellLayout of the specified container at the specified screen. + * + * @param screenId must be presenterPos and not modelPos. */ public CellLayout getCellLayout(int container, int screenId) { return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index f15dc83387..7ac2472319 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -93,7 +93,7 @@ public class LauncherDelegate { // Move the item from the folder to the workspace, in the position of the // folder CellLayout cellLayout = mLauncher.getCellLayout(info.container, - info.screenId); + mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId); if (cellLayout == null) { return; } From 8a18bc0f12fc14e69f0e5398488cf989c99cbb49 Mon Sep 17 00:00:00 2001 From: fbaron Date: Wed, 12 Jul 2023 10:16:49 -0700 Subject: [PATCH 23/40] Fix inability to remove or interact with folder when removing 2nd item from folder Fix: 289960317 Test: Verify in unfolded felix that going from 2 icon folder and dragging 2nd icon out of folder lets you open the app that remains where the folder was Test: Verify in unfolded felix that going from 2 icon folder and dragging 2nd icon into remove droptarget removes the folder and turns it into a single clickable icon flag: no flag (cherry picked from commit 5a7ea3069b984aef151fc2486319583c769cf12c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:326ec97ab6a4d348708830cd2860d4dd90a59039) Merged-In: I26138ee9f8e7cdb45cafe2446dc4d1e3d6d8347f Change-Id: I26138ee9f8e7cdb45cafe2446dc4d1e3d6d8347f --- src/com/android/launcher3/Launcher.java | 2 ++ src/com/android/launcher3/folder/LauncherDelegate.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 25eb16079f..66690431ec 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2194,6 +2194,8 @@ public class Launcher extends StatefulActivity /** * Returns the CellLayout of the specified container at the specified screen. + * + * @param screenId must be presenterPos and not modelPos. */ public CellLayout getCellLayout(int container, int screenId) { return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index f15dc83387..7ac2472319 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -93,7 +93,7 @@ public class LauncherDelegate { // Move the item from the folder to the workspace, in the position of the // folder CellLayout cellLayout = mLauncher.getCellLayout(info.container, - info.screenId); + mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId); if (cellLayout == null) { return; } From c59599dc1970324c39e4b85bc6dfeb647baf2b74 Mon Sep 17 00:00:00 2001 From: randypfohl Date: Thu, 20 Jul 2023 12:53:31 -0700 Subject: [PATCH 24/40] Removing obsolete call to start home activity with shell transitions - The second start activity was causing issues with 3p launchers which may not expect another new intent (ie. if it handles gestures at the bottom of the screen). We can't completely remove this logic because for button navigation we don't want to fall through to the launch-next-task animation below, but we can can continue to finish the recents animation immediately. - With shell transitions, leashes for opening apps are always hidden by default so when transitioning to a 3p launcher from RecentsActivity we also need to show the surface if we want to animate it in Bug: 289609734 Test: Set 3p Launcher as default, in both gesture & button navigation - Go from 3p home -> overview, then overview -> 3p home - Go from app -> 3p home - Go from app -> overview, then overview -> 3p home - Quickswitch from app Signed-off-by: Winson Chung (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2b3dc6f73007f8b7d97ae2972e37c5d055659c8b) Merged-In: I6875083931de63a8097d23d180553885ed7cfb01 Change-Id: I6875083931de63a8097d23d180553885ed7cfb01 --- .../android/quickstep/FallbackSwipeHandler.java | 1 + .../android/quickstep/TaskAnimationManager.java | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index 1913091695..eff53f3c85 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -139,6 +139,7 @@ public class FallbackSwipeHandler extends mTmpMatrix.setScale(scale, scale, app.localBounds.exactCenterX(), app.localBounds.exactCenterY()); builder.setMatrix(mTmpMatrix).setAlpha(alpha); + builder.setShow(); } @Override diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 410ba21866..eacca0dbab 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -19,6 +19,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.launcher3.util.NavigationMode.NO_BUTTON; import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED; @@ -37,6 +38,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.DisplayController; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.views.DesktopTaskView; @@ -162,10 +164,16 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn for (RemoteAnimationTarget compat : appearedTaskTargets) { if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME - && activityInterface.getCreatedActivity() instanceof RecentsActivity) { - // When receive opening home activity while recents is running, enter home - // and dismiss recents. - ((RecentsActivity) activityInterface.getCreatedActivity()).startHome(); + && activityInterface.getCreatedActivity() instanceof RecentsActivity + && DisplayController.getNavigationMode(mCtx) != NO_BUTTON) { + // The only time we get onTasksAppeared() in button navigation with a + // 3p launcher is if the user goes to overview first, and in this case we + // can immediately finish the transition + RecentsView recentsView = + activityInterface.getCreatedActivity().getOverviewPanel(); + if (recentsView != null) { + recentsView.finishRecentsAnimation(true, null); + } return; } } From 628f74bd7183cc4a07266487d8b30f362bd80575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Franco?= Date: Thu, 27 Jul 2023 18:25:53 +0000 Subject: [PATCH 25/40] Disable FOLDABLE_SINGLE_PAGE The functionality should go back to the same as with phones. There shouldn't be issues with the reorder or similar behavior since we are switching form using the MultipageCellLayout to the regular CellLayout. The things we need to pay attention to is the the behavior of having two panels like adding the right number of panels when loading (folding, unfolding and rotating). Bug: 291822492 Test: ReorderWidgets (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8d606e89d3511a6f65e3a80add97c0fae580fb97) Merged-In: I903873e32f35c5ee9e0f3da8581a37d4087d021f Change-Id: I903873e32f35c5ee9e0f3da8581a37d4087d021f --- 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 f5f6769c33..b255ea8a18 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -194,7 +194,7 @@ public final class FeatureFlags { "In foldables, when reordering the icons and widgets, is now going to use both sides"); public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274, - "FOLDABLE_SINGLE_PAGE", ENABLED, "Use a single page for the workspace"); + "FOLDABLE_SINGLE_PAGE", DISABLED, "Use a single page for the workspace"); // TODO(Block 12): Clean up flags public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680, From 8e263cb930154eab6900a06a218cda2c0598a6f1 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 26/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 8d1e9f08dfbac3cc1ad2bafd8c4c17bfa111102a Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 27/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 30471b4d6c8e0b0caced395687628dc778f8441c Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Tue, 18 Jul 2023 13:34:07 -0700 Subject: [PATCH 28/40] Log appear animation's scale factor Test: Grabbed a bug report b/291974797 Flag: N/A Bug: 290320302 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2303acd7d4272b063352598d4ec65c5b92e0f0f7) Merged-In: Ic33dc94806b838a03a2203bdd5701a1eeaeeb7bf Change-Id: Ic33dc94806b838a03a2203bdd5701a1eeaeeb7bf --- .../launcher3/QuickstepTransitionManager.java | 4 + .../uioverrides/QuickstepLauncher.java | 5 + .../android/launcher3/util/EventLogArray.kt | 117 ++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 src/com/android/launcher3/util/EventLogArray.kt diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index ca9c5771b1..3966ca4319 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -158,6 +158,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.wm.shell.startingsurface.IStartingWindowListener; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -477,6 +478,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener }); } + /** Dump debug logs to bug report. */ + public void dump(@NonNull String prefix, @NonNull PrintWriter printWriter) {} + /** * Content is everything on screen except the background and the floating view (if any). * diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 3139e4d91a..b621a287c2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -204,6 +204,8 @@ public class QuickstepLauncher extends Launcher { public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; + protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t"; + private FixedContainerItems mAllAppsPredictions; private HotseatPredictionController mHotseatPredictionController; private DepthController mDepthController; @@ -1336,5 +1338,8 @@ public class QuickstepLauncher extends Launcher { if (recentsView != null) { recentsView.getSplitSelectController().dump(prefix, writer); } + if (mAppTransitionManager != null) { + mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer); + } } } diff --git a/src/com/android/launcher3/util/EventLogArray.kt b/src/com/android/launcher3/util/EventLogArray.kt new file mode 100644 index 0000000000..a17d6509e3 --- /dev/null +++ b/src/com/android/launcher3/util/EventLogArray.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.util + +import java.io.PrintWriter +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +/** + * A utility class to record and log events. Events are stored in a fixed size array and old logs + * are purged as new events come. + */ +class EventLogArray(private val name: String, size: Int) { + + companion object { + private const val TYPE_ONE_OFF = 0 + private const val TYPE_FLOAT = 1 + private const val TYPE_INTEGER = 2 + private const val TYPE_BOOL_TRUE = 3 + private const val TYPE_BOOL_FALSE = 4 + private fun isEntrySame(entry: EventEntry?, type: Int, event: String): Boolean { + return entry != null && entry.type == type && entry.event == event + } + } + + private val logs: Array + private var nextIndex = 0 + + init { + logs = arrayOfNulls(size) + } + + fun addLog(event: String) { + addLog(TYPE_ONE_OFF, event, 0f) + } + + fun addLog(event: String, extras: Int) { + addLog(TYPE_INTEGER, event, extras.toFloat()) + } + + fun addLog(event: String, extras: Float) { + addLog(TYPE_FLOAT, event, extras) + } + + fun addLog(event: String, extras: Boolean) { + addLog(if (extras) TYPE_BOOL_TRUE else TYPE_BOOL_FALSE, event, 0f) + } + + private fun addLog(type: Int, event: String, extras: Float) { + // Merge the logs if it's a duplicate + val last = (nextIndex + logs.size - 1) % logs.size + val secondLast = (nextIndex + logs.size - 2) % logs.size + if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) { + logs[last]!!.update(type, event, extras) + logs[secondLast]!!.duplicateCount++ + return + } + if (logs[nextIndex] == null) { + logs[nextIndex] = EventEntry() + } + logs[nextIndex]!!.update(type, event, extras) + nextIndex = (nextIndex + 1) % logs.size + } + + fun dump(prefix: String, writer: PrintWriter) { + writer.println("$prefix$name event history:") + val sdf = SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US) + val date = Date() + for (i in logs.indices) { + val log = logs[(nextIndex + logs.size - i - 1) % logs.size] ?: continue + date.time = log.time + val msg = StringBuilder(prefix).append(sdf.format(date)).append(log.event) + when (log.type) { + TYPE_BOOL_FALSE -> msg.append(": false") + TYPE_BOOL_TRUE -> msg.append(": true") + TYPE_FLOAT -> msg.append(": ").append(log.extras) + TYPE_INTEGER -> msg.append(": ").append(log.extras.toInt()) + else -> {} + } + if (log.duplicateCount > 0) { + msg.append(" & ").append(log.duplicateCount).append(" similar events") + } + writer.println(msg) + } + } + + /** A single event entry. */ + private class EventEntry { + var type = 0 + var event: String? = null + var extras = 0f + var time: Long = 0 + var duplicateCount = 0 + fun update(type: Int, event: String, extras: Float) { + this.type = type + this.event = event + this.extras = extras + time = System.currentTimeMillis() + duplicateCount = 0 + } + } +} From ed1503317c37f999c7214b9ef15ddaa34200e05a Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 29/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 5038853cd7e841dfacd6c428a5424c3ec702e583 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Tue, 18 Jul 2023 13:34:07 -0700 Subject: [PATCH 30/40] Log appear animation's scale factor Test: Grabbed a bug report b/291974797 Flag: N/A Bug: 290320302 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2303acd7d4272b063352598d4ec65c5b92e0f0f7) Merged-In: Ic33dc94806b838a03a2203bdd5701a1eeaeeb7bf Change-Id: Ic33dc94806b838a03a2203bdd5701a1eeaeeb7bf --- .../launcher3/QuickstepTransitionManager.java | 4 + .../uioverrides/QuickstepLauncher.java | 5 + .../android/launcher3/util/EventLogArray.kt | 117 ++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 src/com/android/launcher3/util/EventLogArray.kt diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index ca9c5771b1..3966ca4319 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -158,6 +158,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.wm.shell.startingsurface.IStartingWindowListener; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -477,6 +478,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener }); } + /** Dump debug logs to bug report. */ + public void dump(@NonNull String prefix, @NonNull PrintWriter printWriter) {} + /** * Content is everything on screen except the background and the floating view (if any). * diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 3139e4d91a..b621a287c2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -204,6 +204,8 @@ public class QuickstepLauncher extends Launcher { public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; + protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t"; + private FixedContainerItems mAllAppsPredictions; private HotseatPredictionController mHotseatPredictionController; private DepthController mDepthController; @@ -1336,5 +1338,8 @@ public class QuickstepLauncher extends Launcher { if (recentsView != null) { recentsView.getSplitSelectController().dump(prefix, writer); } + if (mAppTransitionManager != null) { + mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer); + } } } diff --git a/src/com/android/launcher3/util/EventLogArray.kt b/src/com/android/launcher3/util/EventLogArray.kt new file mode 100644 index 0000000000..a17d6509e3 --- /dev/null +++ b/src/com/android/launcher3/util/EventLogArray.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.launcher3.util + +import java.io.PrintWriter +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +/** + * A utility class to record and log events. Events are stored in a fixed size array and old logs + * are purged as new events come. + */ +class EventLogArray(private val name: String, size: Int) { + + companion object { + private const val TYPE_ONE_OFF = 0 + private const val TYPE_FLOAT = 1 + private const val TYPE_INTEGER = 2 + private const val TYPE_BOOL_TRUE = 3 + private const val TYPE_BOOL_FALSE = 4 + private fun isEntrySame(entry: EventEntry?, type: Int, event: String): Boolean { + return entry != null && entry.type == type && entry.event == event + } + } + + private val logs: Array + private var nextIndex = 0 + + init { + logs = arrayOfNulls(size) + } + + fun addLog(event: String) { + addLog(TYPE_ONE_OFF, event, 0f) + } + + fun addLog(event: String, extras: Int) { + addLog(TYPE_INTEGER, event, extras.toFloat()) + } + + fun addLog(event: String, extras: Float) { + addLog(TYPE_FLOAT, event, extras) + } + + fun addLog(event: String, extras: Boolean) { + addLog(if (extras) TYPE_BOOL_TRUE else TYPE_BOOL_FALSE, event, 0f) + } + + private fun addLog(type: Int, event: String, extras: Float) { + // Merge the logs if it's a duplicate + val last = (nextIndex + logs.size - 1) % logs.size + val secondLast = (nextIndex + logs.size - 2) % logs.size + if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) { + logs[last]!!.update(type, event, extras) + logs[secondLast]!!.duplicateCount++ + return + } + if (logs[nextIndex] == null) { + logs[nextIndex] = EventEntry() + } + logs[nextIndex]!!.update(type, event, extras) + nextIndex = (nextIndex + 1) % logs.size + } + + fun dump(prefix: String, writer: PrintWriter) { + writer.println("$prefix$name event history:") + val sdf = SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US) + val date = Date() + for (i in logs.indices) { + val log = logs[(nextIndex + logs.size - i - 1) % logs.size] ?: continue + date.time = log.time + val msg = StringBuilder(prefix).append(sdf.format(date)).append(log.event) + when (log.type) { + TYPE_BOOL_FALSE -> msg.append(": false") + TYPE_BOOL_TRUE -> msg.append(": true") + TYPE_FLOAT -> msg.append(": ").append(log.extras) + TYPE_INTEGER -> msg.append(": ").append(log.extras.toInt()) + else -> {} + } + if (log.duplicateCount > 0) { + msg.append(" & ").append(log.duplicateCount).append(" similar events") + } + writer.println(msg) + } + } + + /** A single event entry. */ + private class EventEntry { + var type = 0 + var event: String? = null + var extras = 0f + var time: Long = 0 + var duplicateCount = 0 + fun update(type: Int, event: String, extras: Float) { + this.type = type + this.event = event + this.extras = extras + time = System.currentTimeMillis() + duplicateCount = 0 + } + } +} From eb8fb49a963bba3284b7e56a36c5de04cf49e54d Mon Sep 17 00:00:00 2001 From: Neha Jain Date: Wed, 9 Aug 2023 23:19:38 +0000 Subject: [PATCH 31/40] Revert "Log appear animation's scale factor" DO NOT SUBMIT Revert submission 24154592-cherrypick-log-scale-factor-p3v8iq0onb Reason for revert: b/294401542 Reverted changes: /q/submissionid:24154592-cherrypick-log-scale-factor-p3v8iq0onb (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:306ca475386d61367a0b557b8f415eb5bfce5cc9) Merged-In: Ibd4d143838b7cafcfd85a40541295c3332b7be08 Change-Id: Ibd4d143838b7cafcfd85a40541295c3332b7be08 --- .../launcher3/QuickstepTransitionManager.java | 4 - .../uioverrides/QuickstepLauncher.java | 5 - .../android/launcher3/util/EventLogArray.kt | 117 ------------------ 3 files changed, 126 deletions(-) delete mode 100644 src/com/android/launcher3/util/EventLogArray.kt diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 3966ca4319..ca9c5771b1 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -158,7 +158,6 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.wm.shell.startingsurface.IStartingWindowListener; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -478,9 +477,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener }); } - /** Dump debug logs to bug report. */ - public void dump(@NonNull String prefix, @NonNull PrintWriter printWriter) {} - /** * Content is everything on screen except the background and the floating view (if any). * diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index b621a287c2..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -204,8 +204,6 @@ public class QuickstepLauncher extends Launcher { public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false; - protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t"; - private FixedContainerItems mAllAppsPredictions; private HotseatPredictionController mHotseatPredictionController; private DepthController mDepthController; @@ -1338,8 +1336,5 @@ public class QuickstepLauncher extends Launcher { if (recentsView != null) { recentsView.getSplitSelectController().dump(prefix, writer); } - if (mAppTransitionManager != null) { - mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer); - } } } diff --git a/src/com/android/launcher3/util/EventLogArray.kt b/src/com/android/launcher3/util/EventLogArray.kt deleted file mode 100644 index a17d6509e3..0000000000 --- a/src/com/android/launcher3/util/EventLogArray.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.launcher3.util - -import java.io.PrintWriter -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale - -/** - * A utility class to record and log events. Events are stored in a fixed size array and old logs - * are purged as new events come. - */ -class EventLogArray(private val name: String, size: Int) { - - companion object { - private const val TYPE_ONE_OFF = 0 - private const val TYPE_FLOAT = 1 - private const val TYPE_INTEGER = 2 - private const val TYPE_BOOL_TRUE = 3 - private const val TYPE_BOOL_FALSE = 4 - private fun isEntrySame(entry: EventEntry?, type: Int, event: String): Boolean { - return entry != null && entry.type == type && entry.event == event - } - } - - private val logs: Array - private var nextIndex = 0 - - init { - logs = arrayOfNulls(size) - } - - fun addLog(event: String) { - addLog(TYPE_ONE_OFF, event, 0f) - } - - fun addLog(event: String, extras: Int) { - addLog(TYPE_INTEGER, event, extras.toFloat()) - } - - fun addLog(event: String, extras: Float) { - addLog(TYPE_FLOAT, event, extras) - } - - fun addLog(event: String, extras: Boolean) { - addLog(if (extras) TYPE_BOOL_TRUE else TYPE_BOOL_FALSE, event, 0f) - } - - private fun addLog(type: Int, event: String, extras: Float) { - // Merge the logs if it's a duplicate - val last = (nextIndex + logs.size - 1) % logs.size - val secondLast = (nextIndex + logs.size - 2) % logs.size - if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) { - logs[last]!!.update(type, event, extras) - logs[secondLast]!!.duplicateCount++ - return - } - if (logs[nextIndex] == null) { - logs[nextIndex] = EventEntry() - } - logs[nextIndex]!!.update(type, event, extras) - nextIndex = (nextIndex + 1) % logs.size - } - - fun dump(prefix: String, writer: PrintWriter) { - writer.println("$prefix$name event history:") - val sdf = SimpleDateFormat(" HH:mm:ss.SSSZ ", Locale.US) - val date = Date() - for (i in logs.indices) { - val log = logs[(nextIndex + logs.size - i - 1) % logs.size] ?: continue - date.time = log.time - val msg = StringBuilder(prefix).append(sdf.format(date)).append(log.event) - when (log.type) { - TYPE_BOOL_FALSE -> msg.append(": false") - TYPE_BOOL_TRUE -> msg.append(": true") - TYPE_FLOAT -> msg.append(": ").append(log.extras) - TYPE_INTEGER -> msg.append(": ").append(log.extras.toInt()) - else -> {} - } - if (log.duplicateCount > 0) { - msg.append(" & ").append(log.duplicateCount).append(" similar events") - } - writer.println(msg) - } - } - - /** A single event entry. */ - private class EventEntry { - var type = 0 - var event: String? = null - var extras = 0f - var time: Long = 0 - var duplicateCount = 0 - fun update(type: Int, event: String, extras: Float) { - this.type = type - this.event = event - this.extras = extras - time = System.currentTimeMillis() - duplicateCount = 0 - } - } -} From 413a2346cbb6c3dcc4dd05e71a83c83c71994e04 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 32/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 0effcbca8480c08cdc1f2386d7a764b4fadae538 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 33/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 5dfa2976bf6ba2eae56dbec3066c0967c7caa0dd Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 34/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 781fcebcc8c1edbaee3edd85fc180cb0744daa65 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 35/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 4c96e5508268f3719576c5bd96746e2f005d2592 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 36/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 1f5e248da0b95ba396d30bf2ecb71da8a0841ab8 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 37/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From 14b4f9edcc92dfb6161272e17921c3271de9c12b Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 26 May 2023 11:50:13 +0100 Subject: [PATCH 38/40] [Unfold animation] Do not preemptively start the animation if it has run already Currently if we open an app, unfold the device and then go to home screen we will start the unfold animation preemptively in Launcher because Launcher activity will receive updated configuration change (where isTablet = true) only after going back to home screen, not when unfolding the device. This causes a problem because SystemUI won't send the unfold animation events after going back home as the animation has already run, so we end up with wrongly started animation in Launcher. This CL fixes the issues by checking if SystemUI has finished the animation (or if it is currently running) to avoid preemptive animation start in this case. This is done by subscribing to the original unfold transition progress provider which emits progress events sent through IPC from SystemUI. Bug: 285150685 Bug: 293131586 Test: open an app on folded screen, unfold, go to home screen => check that icons are not squished Test: fold/unfold when launcher is open (cherry picked from commit 6d756970e77e99ed434c58b815a6eaac1a023a4c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:2e53f5ef97a02d25f508774e82985e24dc2f4d2d) Merged-In: Ic437ff4d19cbd5764635f3007d99880622150f5b Change-Id: Ic437ff4d19cbd5764635f3007d99880622150f5b --- .../uioverrides/QuickstepLauncher.java | 4 ++ .../LauncherUnfoldAnimationController.java | 65 ++++++++++++++++++- .../launcher3/config/FeatureFlags.java | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 2f13c5de44..3139e4d91a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -472,6 +472,10 @@ public class QuickstepLauncher extends Launcher { public void onDestroy() { mAppTransitionManager.onActivityDestroyed(); if (mUnfoldTransitionProgressProvider != null) { + if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) { + SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null); + } + mUnfoldTransitionProgressProvider.destroy(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 6d15e8be98..e0b527268c 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -60,6 +60,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider; private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator; private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator; + private final TransitionStatusProvider mExternalTransitionStatusProvider = + new TransitionStatusProvider(); private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null; private Boolean mIsTablet = null; @@ -88,6 +90,8 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL unfoldTransitionProgressProvider); } + unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider); + mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager, rotationChangeProvider); mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher, @@ -166,11 +170,26 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL } if (mIsTablet != null && dp.isTablet != mIsTablet) { - if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) { + // We should preemptively start the animation only if: + // - We changed to the unfolded screen + // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't + // receive transition progress events from SystemUI later because there was no + // IPC connection established (e.g. because of SystemUI crash) + // - SystemUI has not already sent unfold animation progress events. This might happen + // if Launcher was not open during unfold, in this case we receive the configuration + // change only after we went back to home screen and we don't want to start the + // animation in this case. + if (dp.isTablet + && SystemUiProxy.INSTANCE.get(mLauncher).isActive() + && !mExternalTransitionStatusProvider.hasRun()) { // Preemptively start the unfold animation to make sure that we have drawn // the first frame of the animation before the screen gets unblocked preemptivelyStartAnimationOnNextFrame(); } + + if (!dp.isTablet) { + mExternalTransitionStatusProvider.onFolded(); + } } mIsTablet = dp.isTablet; @@ -222,4 +241,48 @@ public class LauncherUnfoldAnimationController implements OnDeviceProfileChangeL HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } + + /** + * Class to track the current status of the external transition provider (the events are coming + * from the SystemUI side through IPC), it allows to check if the transition has already + * finished or currently running on the SystemUI side since last unfold. + */ + private static class TransitionStatusProvider implements TransitionProgressListener { + + private boolean mHasRun = false; + + @Override + public void onTransitionStarted() { + markAsRun(); + } + + @Override + public void onTransitionProgress(float progress) { + markAsRun(); + } + + @Override + public void onTransitionFinished() { + markAsRun(); + } + + /** + * Called when the device is folded, so we can reset the status of the animation + */ + public void onFolded() { + mHasRun = false; + } + + /** + * Returns true if there was an animation already (or it is currently running) after + * unfolding the device + */ + public boolean hasRun() { + return mHasRun; + } + + private void markAsRun() { + mHasRun = true; + } + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b255ea8a18..76574b69c7 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -302,7 +302,7 @@ public final class FeatureFlags { "Enable widget transition animation when resizing the widgets"); public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209, - "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED, + "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED, "Enables starting the unfold animation preemptively when unfolding, without" + "waiting for SystemUI and then merging the SystemUI progress whenever we " + "start receiving the events"); From c02dabfb5973764fbf5dc3c271aaf31d592f61c0 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 3 Aug 2023 15:32:46 +0100 Subject: [PATCH 39/40] Fix return condition in getWorkspacePageTranslationProvider - When EDIT_MODE was introduced, it added a wrong condition to make it always return DEFAULT_PAGE_TRANSLATION_PROVIDER Bug: 294228521 Test: manual (cherry picked from commit c325c686c73498a7041a2bd85a97c03583f2526c) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4bfc0ee720e4ee80035c7ef59cf2d8d095cf9d72) Merged-In: If970949c8ab55bc67f98f987a7654ec2db89cdfb Change-Id: If970949c8ab55bc67f98f987a7654ec2db89cdfb --- src/com/android/launcher3/LauncherState.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 8b124dc847..6097fb1947 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -331,8 +331,7 @@ public abstract class LauncherState implements BaseState { * Gets the translation provider for workspace pages. */ public PageTranslationProvider getWorkspacePageTranslationProvider(Launcher launcher) { - if (this != SPRING_LOADED - || this != EDIT_MODE + if (!(this == SPRING_LOADED || this == EDIT_MODE) || !launcher.getDeviceProfile().isTwoPanels) { return DEFAULT_PAGE_TRANSLATION_PROVIDER; } From b38e61a3397806e23e57bfd8c2df0102e8bb64f2 Mon Sep 17 00:00:00 2001 From: Luca Zuccarini Date: Wed, 26 Jul 2023 15:24:43 +0000 Subject: [PATCH 40/40] Animate depth from the right value on Taskbar All Apps launches. `MyDepthController` in `QuickstepTransitionLauncher` assumes that we want the background to always animate the same way, matching the rest state of the workspace (depth == 0). However, in Taskbar All Apps the background is visible, and depth != 0. We now initialize the one-off `DepthController` for launches to take into account the latest depth set by the top level `DepthController`, so there is no jumpcut at the beginning of the animation. Note that in my opinion we should use the same `DepthController` for all cases, rather than having this one-off. I'm looking into the feasibility of that change, but for now this fixes the issue at hand. Fix: 292959100 Flag: N/A Test: manual, see videos in the bug (cherry picked from commit 627d67549f73c4c456537f814e450fa8a1318937) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3e91646d123abbe58ccc0544746145ce5024b923) Merged-In: Id90e8e728cc3e2ccf7d92148fbb0d6ff3e6fd6ca Change-Id: Id90e8e728cc3e2ccf7d92148fbb0d6ff3e6fd6ca --- .../android/launcher3/QuickstepTransitionManager.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index ca9c5771b1..75f25ac7c2 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1046,7 +1046,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW && BlurUtils.supportsBlursOnWindows(); - MyDepthController depthController = new MyDepthController(mLauncher); + LaunchDepthController depthController = new LaunchDepthController(mLauncher); ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth, MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher)) .setDuration(APP_LAUNCH_DURATION); @@ -2047,11 +2047,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - private static class MyDepthController extends DepthController { - MyDepthController(Launcher l) { - super(l); + private static class LaunchDepthController extends DepthController { + LaunchDepthController(QuickstepLauncher launcher) { + super(launcher); setCrossWindowBlursEnabled( CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled()); + // Make sure that the starting value matches the current depth set by the main + // controller. + stateDepth.setValue(launcher.getDepthController().stateDepth.getValue()); } } }