From b9f1e73087b1f8e3558ae52bfabd3d55bdd3f2e3 Mon Sep 17 00:00:00 2001 From: Marcello Galhardo Date: Fri, 5 May 2023 14:42:28 +0000 Subject: [PATCH 1/6] Move Note Shortcut from Settings to SystemUI * Change Note Shortcut category package name from Settings to SystemUI Test: manual Flag: not needed Fixes: b/280431176 Change-Id: I50f4550ad98f4c4959731b456a846faa2ec5a1a9 --- res/xml/widget_sections.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/xml/widget_sections.xml b/res/xml/widget_sections.xml index 6165bf7051..742991f580 100644 --- a/res/xml/widget_sections.xml +++ b/res/xml/widget_sections.xml @@ -26,6 +26,6 @@ launcher:category="1" launcher:sectionDrawable="@drawable/ic_note_taking_widget_category" launcher:sectionTitle="@string/widget_category_note_taking"> - + \ No newline at end of file From ec5abba93b3f3c430da68071817d80ae0e79c201 Mon Sep 17 00:00:00 2001 From: Thales Lima Date: Wed, 5 Apr 2023 16:33:50 +0100 Subject: [PATCH 2/6] Add feature flag and attribute for responsive grid This shouldn't change anything in the grids, only support the creation of responsive grids in the future. For now, to check that the flag works, turn it on and check the dumpsys. There is also a dump test created for this. Fix: 277064696 Fix: 277064702 Test: DeviceProfileResponsiveDumpTest Flag: ENABLE_RESPONSIVE_WORKSPACE Change-Id: I1bef87043a100234bd661cd6ac00007fdc654116 --- res/values/attrs.xml | 3 +++ src/com/android/launcher3/DeviceProfile.java | 6 ++++++ .../launcher3/InvariantDeviceProfile.java | 16 ++++++++++++++-- .../android/launcher3/config/FeatureFlags.java | 4 ++++ .../launcher3/AbstractDeviceProfileTest.kt | 4 ++-- .../nonquickstep/DeviceProfileDumpTest.kt | 12 ++++++++++++ 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/res/values/attrs.xml b/res/values/attrs.xml index ce8d90166b..813653446d 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -199,6 +199,9 @@ + + diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 867522699f..0231090d7e 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -101,6 +101,7 @@ public class DeviceProfile { public final float aspectRatio; public final boolean isScalableGrid; + public final boolean isResponsiveGrid; private final int mTypeIndex; /** @@ -293,6 +294,10 @@ public class DeviceProfile { this.rotationHint = windowBounds.rotationHint; mInsets.set(windowBounds.insets); + // TODO(b/241386436): + // for testing that the flag works only, shouldn't change any launcher behaviour + isResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE; + isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode; // Determine device posture. mInfo = info; @@ -1577,6 +1582,7 @@ public class DeviceProfile { writer.println(prefix + "\taspectRatio:" + aspectRatio); + writer.println(prefix + "\tisResponsiveGrid:" + isResponsiveGrid); writer.println(prefix + "\tisScalableGrid:" + isScalableGrid); writer.println(prefix + "\tinv.numRows: " + inv.numRows); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 3aa582d074..376f54dc4c 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -48,6 +48,7 @@ import androidx.annotation.VisibleForTesting; import androidx.annotation.XmlRes; import androidx.core.content.res.ResourcesCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.model.DeviceGridState; import com.android.launcher3.provider.RestoreDbTask; @@ -105,7 +106,7 @@ public class InvariantDeviceProfile { static final int INDEX_TWO_PANEL_PORTRAIT = 2; static final int INDEX_TWO_PANEL_LANDSCAPE = 3; - /** These resources are used to override the device profile */ + /** These resources are used to override the device profile */ private static final String RES_GRID_NUM_ROWS = "grid_num_rows"; private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns"; private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp"; @@ -177,6 +178,8 @@ public class InvariantDeviceProfile { protected boolean isScalable; @XmlRes public int devicePaddingId = INVALID_RESOURCE_HANDLE; + @XmlRes + public int workspaceSpecsId = INVALID_RESOURCE_HANDLE; public String dbFile; public int defaultLayoutId; @@ -350,6 +353,7 @@ public class InvariantDeviceProfile { isScalable = closestProfile.isScalable; devicePaddingId = closestProfile.devicePaddingId; + workspaceSpecsId = closestProfile.mWorkspaceSpecsId; this.deviceType = deviceType; inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing; @@ -795,6 +799,7 @@ public class InvariantDeviceProfile { private final boolean isScalable; private final int devicePaddingId; + private final int mWorkspaceSpecsId; public GridOption(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes( @@ -836,7 +841,7 @@ public class InvariantDeviceProfile { inlineNavButtonsEndSpacing = a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing, - R.dimen.taskbar_button_margin_default); + R.dimen.taskbar_button_margin_default); numFolderRows = a.getInt( R.styleable.GridDisplayOption_numFolderRows, numRows); @@ -856,6 +861,13 @@ public class InvariantDeviceProfile { deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory, DEVICE_CATEGORY_ALL); + if (FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE.get()) { + mWorkspaceSpecsId = a.getResourceId( + R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE); + } else { + mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE; + } + int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb, DONT_INLINE_QSB); inlineQsb[INDEX_DEFAULT] = diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 065122aa30..ef57953e9c 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -420,6 +420,10 @@ public final class FeatureFlags { // TODO(Block 32): Empty block + public static final BooleanFlag ENABLE_RESPONSIVE_WORKSPACE = getDebugFlag(241386436, + "ENABLE_RESPONSIVE_WORKSPACE", DISABLED, + "Enables new workspace grid calculations method."); + public static class BooleanFlag { private final boolean mCurrentValue; diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt index 0fe8bee230..01f494b202 100644 --- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt +++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt @@ -50,7 +50,7 @@ abstract class AbstractDeviceProfileTest { private lateinit var originalWindowManagerProxy: WindowManagerProxy @Before - fun setUp() { + open fun setUp() { val appContext: Context = ApplicationProvider.getApplicationContext() originalWindowManagerProxy = WindowManagerProxy.INSTANCE.get(appContext) originalDisplayController = DisplayController.INSTANCE.get(appContext) @@ -59,7 +59,7 @@ abstract class AbstractDeviceProfileTest { } @After - fun tearDown() { + open fun tearDown() { WindowManagerProxy.INSTANCE.initializeForTesting(originalWindowManagerProxy) DisplayController.INSTANCE.initializeForTesting(originalDisplayController) } diff --git a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt index 13db6c76c8..a81413e1d3 100644 --- a/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt +++ b/tests/src/com/android/launcher3/nonquickstep/DeviceProfileDumpTest.kt @@ -58,6 +58,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 126.0px (48.0dp)\n" + "\taspectRatio:2.2222223\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 5\n" + @@ -193,6 +194,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 63.0px (24.0dp)\n" + "\taspectRatio:2.2222223\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 5\n" + @@ -328,6 +330,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 126.0px (48.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:2.2222223\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 5\n" + @@ -463,6 +466,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 63.0px (24.0dp)\n" + "\taspectRatio:2.2222223\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 5\n" + @@ -599,6 +603,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.6\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:true\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 6\n" + @@ -735,6 +740,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.6\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:true\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 6\n" + @@ -871,6 +877,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.6\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:true\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 6\n" + @@ -1007,6 +1014,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.6\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:true\n" + "\tinv.numRows: 5\n" + "\tinv.numColumns: 6\n" + @@ -1148,6 +1156,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.2\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 4\n" + "\tinv.numColumns: 4\n" + @@ -1288,6 +1297,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.2\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 4\n" + "\tinv.numColumns: 4\n" + @@ -1428,6 +1438,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.2\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 4\n" + "\tinv.numColumns: 4\n" + @@ -1564,6 +1575,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() { "\tmInsets.right: 0.0px (0.0dp)\n" + "\tmInsets.bottom: 0.0px (0.0dp)\n" + "\taspectRatio:1.2\n" + + "\tisResponsiveGrid:false\n" + "\tisScalableGrid:false\n" + "\tinv.numRows: 4\n" + "\tinv.numColumns: 4\n" + From 3c0f30a2bbf0dd353e180005657d6dbf64aceb0d Mon Sep 17 00:00:00 2001 From: Xiaowen Lei Date: Wed, 12 Apr 2023 16:59:45 -0700 Subject: [PATCH 3/6] Add Lifecycle and SavedState support in Launcher ActivityContext. And call initCompose() in Launcher.java. Therefore there is no behavior change for BaseActivity, or other ActivityContext subtypes that don't call initCompose(). This should work for non-activity subtypes of ActivityContext. It's likely that the Launcher activity will require more complete supoort for Lifecycle and SavedState. Note: In order to test the ComposeView, need to build this CL together with ag/22461463. 1. `ViewTreeLifecycleOwner.set(..., ...);` is necessary to fix the following crash: java.lang.IllegalStateException: ViewTreeLifecycleOwner not found This error comes from WindowRecomposer.android.kt - http://cs/androidx-platform-dev/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/WindowRecomposer.android.kt;l=351-354;rcl=5673af1441ebe04c776f528193dfc5f85c0c9a66 2. `ViewTreeSavedStateRegistryOwner.set(..., ...);` is necessary to fix the following crash: java.lang.IllegalStateException: Composed into the View which doesn't propagateViewTreeSavedStateRegistryOwner! This error comes from AndroidComposeView.android.kt. - http://cs/androidx-platform-dev/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt;l=1178-1183;rcl=5673af1441ebe04c776f528193dfc5f85c0c9a66 3. The lifecycle needs to be STARTED. Otherwise the ComposeView doesn't show up in its parent (i.e., isn't drawn). - In ComponentActivity, `onSaveInstanceState` starts the lifecycle. - In ComposeInitializeImpl (from SystemUI), ViewLifecycleOwner.updateState() starts the lifecycle when the window is visible and in focus. The implementation in this CL does the same thing as the ComposeInitializeImpl. Bug: 275905704 Test: on device, trigger a card such as the timer Test: RUN_ERROR_PRONE=True m out/soong/.intermediates/packages/apps/Launcher3/Launcher3/android_common/lint/lint-report.xml Test: RUN_ERROR_PRONE=True m out/soong/.intermediates/vendor/google/gms/packages/SearchLauncher/SearchLauncher/android_common/lint/lint-report.html Test: Presubmit Change-Id: Ieced66ad5238916a82ea13c01f69146bbdcf287a --- src/com/android/launcher3/Launcher.java | 3 + .../launcher3/views/ComposeInitializer.java | 229 ++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 src/com/android/launcher3/views/ComposeInitializer.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 86a5f9f2a9..6e98bda7ca 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -214,6 +214,7 @@ import com.android.launcher3.util.TouchController; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.ViewOnDrawExecutor; import com.android.launcher3.views.ActivityContext; +import com.android.launcher3.views.ComposeInitializer; import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.views.FloatingSurfaceView; import com.android.launcher3.views.OptionsPopupView; @@ -553,6 +554,8 @@ public class Launcher extends StatefulActivity setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); setContentView(getRootView()); + ComposeInitializer.initCompose(this); + if (mOnInitialBindListener != null) { getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener); } diff --git a/src/com/android/launcher3/views/ComposeInitializer.java b/src/com/android/launcher3/views/ComposeInitializer.java new file mode 100644 index 0000000000..092988591e --- /dev/null +++ b/src/com/android/launcher3/views/ComposeInitializer.java @@ -0,0 +1,229 @@ +/* + * 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.views; + +import android.os.Build; +import android.view.View; +import android.view.ViewParent; +import android.view.ViewTreeObserver; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; +import androidx.lifecycle.ViewTreeLifecycleOwner; +import androidx.savedstate.SavedStateRegistry; +import androidx.savedstate.SavedStateRegistryController; +import androidx.savedstate.SavedStateRegistryOwner; +import androidx.savedstate.ViewTreeSavedStateRegistryOwner; + +import com.android.launcher3.Utilities; + +/** + * An initializer to use Compose for classes implementing {@code ActivityContext}. This allows + * adding ComposeView to ViewTree outside a {@link androidx.activity.ComponentActivity}. + */ +public final class ComposeInitializer { + /** + * Performs the initialization to use Compose in the ViewTree of {@code target}. + */ + public static void initCompose(ActivityContext target) { + getContentChild(target).addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + + @Override + public void onViewAttachedToWindow(View v) { + ComposeInitializer.onAttachedToWindow(v); + } + + @Override + public void onViewDetachedFromWindow(View v) { + ComposeInitializer.onDetachedFromWindow(v); + } + }); + } + + /** + * Find the "content child" for {@code target}. + * + * @see "WindowRecomposer.android.kt: [View.contentChild]" + */ + private static View getContentChild(ActivityContext target) { + View self = target.getDragLayer(); + ViewParent parent = self.getParent(); + while (parent instanceof View parentView) { + if (parentView.getId() == android.R.id.content) return self; + self = parentView; + parent = self.getParent(); + } + return self; + } + + /** + * Function to be called on your window root view's [View.onAttachedToWindow] function. + */ + private static void onAttachedToWindow(View root) { + if (ViewTreeLifecycleOwner.get(root) != null) { + throw new IllegalStateException( + "View " + root + " already has a LifecycleOwner"); + } + + ViewParent parent = root.getParent(); + if (parent instanceof View && ((View) parent).getId() != android.R.id.content) { + throw new IllegalStateException( + "ComposeInitializer.onContentChildAttachedToWindow(View) must be called on " + + "the content child. Outside of activities and dialogs, this is " + + "usually the top-most View of a window."); + } + + // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] + // is both visible and focused. + ViewLifecycleOwner lifecycleOwner = new ViewLifecycleOwner(root); + + // We must call [ViewLifecycleOwner.onCreate] after creating the + // [SavedStateRegistryOwner] because `onCreate` might move the lifecycle state to STARTED + // which will make [SavedStateRegistryController.performRestore] throw. + lifecycleOwner.onCreate(); + + // Set the owners on the root. They will be reused by any ComposeView inside the root + // hierarchy. + ViewTreeLifecycleOwner.set(root, lifecycleOwner); + ViewTreeSavedStateRegistryOwner.set(root, lifecycleOwner); + } + + /** + * Function to be called on your window root view's [View.onDetachedFromWindow] function. + */ + private static void onDetachedFromWindow(View root) { + final LifecycleOwner lifecycleOwner = ViewTreeLifecycleOwner.get(root); + if (lifecycleOwner != null) { + ((ViewLifecycleOwner) lifecycleOwner).onDestroy(); + } + ViewTreeLifecycleOwner.set(root, null); + ViewTreeSavedStateRegistryOwner.set(root, null); + } + + /** + * A [LifecycleOwner] for a [View] that updates lifecycle state based on window state. + * + * Also a trivial implementation of [SavedStateRegistryOwner] that does not do any save or + * restore. This works for processes similar to the SystemUI process, which is always running + * and top-level windows using this initialization are created once, when the process is + * started. + * + * The implementation requires the caller to call [onCreate] and [onDestroy] when the view is + * attached to or detached from a view hierarchy. After [onCreate] and before [onDestroy] is + * called, the implementation monitors window state in the following way + * * If the window is not visible, we are in the [Lifecycle.State.CREATED] state + * * If the window is visible but not focused, we are in the [Lifecycle.State.STARTED] state + * * If the window is visible and focused, we are in the [Lifecycle.State.RESUMED] state + * + * Or in table format: + * ``` + * ┌───────────────┬───────────────────┬──────────────┬─────────────────┐ + * │ View attached │ Window Visibility │ Window Focus │ Lifecycle State │ + * ├───────────────┼───────────────────┴──────────────┼─────────────────┤ + * │ Not attached │ Any │ N/A │ + * ├───────────────┼───────────────────┬──────────────┼─────────────────┤ + * │ │ Not visible │ Any │ CREATED │ + * │ ├───────────────────┼──────────────┼─────────────────┤ + * │ Attached │ │ No focus │ STARTED │ + * │ │ Visible ├──────────────┼─────────────────┤ + * │ │ │ Has focus │ RESUMED │ + * └───────────────┴───────────────────┴──────────────┴─────────────────┘ + * ``` + */ + private static class ViewLifecycleOwner implements SavedStateRegistryOwner { + private final ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusListener = + hasFocus -> updateState(); + private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); + + private final SavedStateRegistryController mSavedStateRegistryController = + SavedStateRegistryController.create(this); + + private final View mView; + private final Api34Impl mApi34Impl; + + ViewLifecycleOwner(View view) { + mView = view; + if (Utilities.ATLEAST_U) { + mApi34Impl = new Api34Impl(); + } else { + mApi34Impl = null; + } + + mSavedStateRegistryController.performRestore(null); + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycleRegistry; + } + + @NonNull + @Override + public SavedStateRegistry getSavedStateRegistry() { + return mSavedStateRegistryController.getSavedStateRegistry(); + } + + void onCreate() { + mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); + if (Utilities.ATLEAST_U) { + mApi34Impl.addOnWindowVisibilityChangeListener(); + } + mView.getViewTreeObserver().addOnWindowFocusChangeListener( + mWindowFocusListener); + updateState(); + } + + void onDestroy() { + if (Utilities.ATLEAST_U) { + mApi34Impl.removeOnWindowVisibilityChangeListener(); + } + mView.getViewTreeObserver().removeOnWindowFocusChangeListener( + mWindowFocusListener); + mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED); + } + + private void updateState() { + Lifecycle.State state = + mView.getWindowVisibility() != View.VISIBLE ? Lifecycle.State.CREATED + : (!mView.hasWindowFocus() ? Lifecycle.State.STARTED + : Lifecycle.State.RESUMED); + mLifecycleRegistry.setCurrentState(state); + } + + @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + private class Api34Impl { + private final ViewTreeObserver.OnWindowVisibilityChangeListener + mWindowVisibilityListener = + visibility -> updateState(); + + void addOnWindowVisibilityChangeListener() { + mView.getViewTreeObserver().addOnWindowVisibilityChangeListener( + mWindowVisibilityListener); + } + + void removeOnWindowVisibilityChangeListener() { + mView.getViewTreeObserver().removeOnWindowVisibilityChangeListener( + mWindowVisibilityListener); + } + } + } +} From 4e909d07ac90706885b357e4e33234cee5c88842 Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Tue, 9 May 2023 14:58:25 -0400 Subject: [PATCH 4/6] Animate the position of the selected bubble arrow in the expanded bubble bar. Fixes: 280605070 Test: Tested on a physical device Change-Id: I961254527ba9a26d211c2c348a937a424fa2fb4d --- .../taskbar/bubbles/BubbleBarBackground.kt | 3 +- .../taskbar/bubbles/BubbleBarView.java | 40 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt index 667c6f5b6c..7397159f42 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt @@ -43,7 +43,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun private var shadowBlur = 0f private var keyShadowDistance = 0f - private var arrowPositionX: Float = 0f + var arrowPositionX: Float = 0f + private set private var showingArrow: Boolean = false private var arrowDrawable: ShapeDrawable diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index 07de3b8c15..0e1e0e1a1b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar.bubbles; +import android.animation.ValueAnimator; import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; @@ -41,14 +42,14 @@ import java.util.List; * - stashed as a handle * - unstashed but collapsed, in this state the bar is showing but the bubbles are stacked within it * - unstashed and expanded, in this state the bar is showing and the bubbles are shown in a row - * with one of the bubbles being selected. Additionally, WMShell will display the expanded bubble - * view above the bar. + * with one of the bubbles being selected. Additionally, WMShell will display the expanded bubble + * view above the bar. *

* The bubble bar has some behavior related to taskbar: * - When taskbar is unstashed, bubble bar will also become unstashed (but in its "collapsed" - * state) + * state) * - When taskbar is stashed, bubble bar will also become stashed (unless bubble bar is in its - * "expanded" state) + * "expanded" state) * - When bubble bar is in its "expanded" state, taskbar becomes stashed *

* If there are no bubbles, the bubble bar and bubble stashed handle are not shown. Additionally @@ -64,6 +65,7 @@ public class BubbleBarView extends FrameLayout { // TODO: (b/273594744) calculate the amount of space we have and base the max on that // if it's smaller than 5. private static final int MAX_BUBBLES = 5; + private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200; private final TaskbarActivityContext mActivityContext; private final BubbleBarBackground mBubbleBarBackground; @@ -209,14 +211,18 @@ public class BubbleBarView extends FrameLayout { /** * Sets which bubble view should be shown as selected. */ - // TODO: (b/273592694) animate it public void setSelectedBubble(BubbleView view) { mSelectedBubbleView = view; - updateArrowForSelected(); - invalidate(); + updateArrowForSelected(/* shouldAnimate= */ true); } - private void updateArrowForSelected() { + /** + * Update the arrow position to match the selected bubble. + * + * @param shouldAnimate whether or not to animate the arrow. If the bar was just expanded, this + * should be set to {@code false}. Otherwise set this to {@code true}. + */ + private void updateArrowForSelected(boolean shouldAnimate) { if (mSelectedBubbleView == null) { Log.w(TAG, "trying to update selection arrow without a selected view!"); return; @@ -224,7 +230,21 @@ public class BubbleBarView extends FrameLayout { final int index = indexOfChild(mSelectedBubbleView); // Find the center of the bubble when it's expanded, set the arrow position to it. final float tx = getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f; - mBubbleBarBackground.setArrowPosition(tx); + + if (shouldAnimate) { + final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX(); + ValueAnimator animator = ValueAnimator.ofFloat(currentArrowPosition, tx); + animator.setDuration(ARROW_POSITION_ANIMATION_DURATION_MS); + animator.addUpdateListener(animation -> { + float x = (float) animation.getAnimatedValue(); + mBubbleBarBackground.setArrowPosition(x); + invalidate(); + }); + animator.start(); + } else { + mBubbleBarBackground.setArrowPosition(tx); + invalidate(); + } } @Override @@ -248,7 +268,7 @@ public class BubbleBarView extends FrameLayout { public void setExpanded(boolean isBarExpanded) { if (mIsBarExpanded != isBarExpanded) { mIsBarExpanded = isBarExpanded; - updateArrowForSelected(); + updateArrowForSelected(/* shouldAnimate= */ false); setOrUnsetClickListener(); if (!isBarExpanded && mReorderRunnable != null) { mReorderRunnable.run(); From 4aa44e516900fce1110782cbb1140c28ab2fd1f2 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Tue, 9 May 2023 14:18:02 -0700 Subject: [PATCH 5/6] Fixing NPE in TransfromParams.java - we were not actually using mRecentsSurface so we no longer need to compute that. By removing this we will also fix the NPE. Test: Pre-Submit Fixes: 269716918 Flag: not needed Change-Id: Ief4be341061a1c914456d6740f175662ccebc364 --- .../quickstep/util/TransformParams.java | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java index aa9a45bd8b..0f20e430fc 100644 --- a/quickstep/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/src/com/android/quickstep/util/TransformParams.java @@ -17,11 +17,9 @@ package com.android.quickstep.util; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import android.util.FloatProperty; import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; @@ -61,7 +59,6 @@ public class TransformParams { private float mCornerRadius; private RemoteAnimationTargets mTargetSet; private SurfaceTransactionApplier mSyncTransactionApplier; - private SurfaceControl mRecentsSurface; private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; @@ -141,8 +138,9 @@ public class TransformParams { public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) { RemoteAnimationTargets targets = mTargetSet; SurfaceTransaction transaction = new SurfaceTransaction(); - mRecentsSurface = getRecentsSurface(targets); - + if (targets == null) { + return transaction; + } for (int i = 0; i < targets.unfilteredApps.length; i++) { RemoteAnimationTarget app = targets.unfilteredApps[i]; SurfaceProperties builder = transaction.forSurface(app.leash); @@ -176,20 +174,6 @@ public class TransformParams { return transaction; } - private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) { - for (int i = 0; i < targets.unfilteredApps.length; i++) { - RemoteAnimationTarget app = targets.unfilteredApps[i]; - if (app.mode == targets.targetMode) { - if (app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS) { - return app.leash; - } - } else { - return app.leash; - } - } - return null; - } - // Pubic getters so outside packages can read the values. public float getProgress() { @@ -204,10 +188,6 @@ public class TransformParams { return mCornerRadius; } - public SurfaceControl getRecentsSurface() { - return mRecentsSurface; - } - public RemoteAnimationTargets getTargetSet() { return mTargetSet; } From 6de875483035bdb4a497ffa50bb2af60f69ab64a Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 9 May 2023 16:07:31 -0700 Subject: [PATCH 6/6] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I7dd50c73ab58bc08e112cb134653fe68b6fb4a2a --- res/values-cs/strings.xml | 2 +- res/values-mr/strings.xml | 4 ++-- res/values-or/strings.xml | 2 +- res/values-zh-rCN/strings.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 0f981317f0..69cc3a8937 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -27,7 +27,7 @@ "V nouzovém režimu jsou widgety zakázány." "Zkratka není k dispozici" "Domů" - "Rozdělená obrazovka" + "Rozdělit obrazovku" "Informace o aplikaci %1$s" "Widget přesunete klepnutím a podržením." "Dvojitým klepnutím a podržením přesunete widget, případně použijte vlastní akce." diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index 8371648673..bbb2b3e89e 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -88,9 +88,9 @@ "नाव संपादित करा" "%1$s अक्षम केला आहे" "{count,plural, =1{{app_name} संबंधित # सूचना आहे}other{{app_name} संबंधित # सूचना आहेत}}" - "%2$d पैकी %1$d पृष्ठ" + "%2$d पैकी %1$d पेज" "%2$d पैकी %1$d मुख्य स्क्रीन" - "नवीन मुख्य स्क्रीन पृष्ठ" + "नवीन मुख्य स्क्रीन पेज" "फोल्डर उघडले, %1$d बाय %2$d" "फोल्डर बंद करण्यासाठी टॅप करा" "पुनर्नामित करणे सेव्ह करण्यासाठी टॅप करा" diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 3c28d8dbb1..32f3cd843e 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -68,7 +68,7 @@ "ସନ୍ଧାନ ଫଳାଫଳ" "ବ୍ୟକ୍ତିଗତ ଆପ୍ ତାଲିକା" "କାର୍ଯ୍ୟକାରୀ ଆପ୍‌ ତାଲିକା" - "ବାହାର କରନ୍ତୁ" + "କାଢ଼ି ଦିଅନ୍ତୁ" "ଅନଇନଷ୍ଟଲ କରନ୍ତୁ" "ଆପ୍‌ ସୂଚନା" "ଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index ca937735c0..69a3718500 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -99,7 +99,7 @@ "文件夹:%1$s%2$d 个项目" "文件夹:%1$s%2$d 个或更多项目" "壁纸" - "壁纸和样式" + "壁纸与个性化" "主屏幕设置" "已被您的管理员停用" "允许旋转主屏幕"