From a89f4f9b9cc155306f2c4b85c7342b879608e9fc Mon Sep 17 00:00:00 2001 From: fbaron Date: Mon, 5 Jun 2023 15:42:52 -0700 Subject: [PATCH 1/6] Fix crash on first open of widget picker when changing orientation Bug: 285909636 Test: on first use of widget picker a tooltip edu shows up, rotate from landscape to portrait or vice-versa and make sure widget picker doesn't crash Change-Id: Iaa066a41633d3efe48b67a867f3f267f613ea5fe --- src/com/android/launcher3/widget/picker/WidgetsFullSheet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 33c4f8d7e2..a3ef6e1828 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -774,7 +774,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet super.onCloseComplete(); removeCallbacks(mShowEducationTipTask); if (mLatestEducationalTip != null) { - mLatestEducationalTip.close(false); + mLatestEducationalTip.close(true); } AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL); } From caf2c0ffb0821b54adab9b8a8e09ace773c375d5 Mon Sep 17 00:00:00 2001 From: Anushree Ganjam Date: Tue, 6 Jun 2023 18:56:44 +0000 Subject: [PATCH 2/6] Remove SEARCH_ONBOARDING_COUNT & QSB_SEARCH_ONBOARDING_CARD_DISMISSED. Launcher Search related educards is being sent from AiAi now and we don't need to hold the count in launcher. Flag: NA Bug: 265476821 Test: Manual Change-Id: I5374e7cab6551d44fc995adb3b3a46173169ab6c --- src/com/android/launcher3/util/OnboardingPrefs.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java index 65736916d8..348c8d8a4c 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -40,17 +40,14 @@ public class OnboardingPrefs { public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen"; public static final String SEARCH_KEYBOARD_EDU_SEEN = "launcher.search_edu_seen"; public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count"; - public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count"; public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count"; - public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss"; public static final String TASKBAR_EDU_TOOLTIP_STEP = "launcher.taskbar_edu_tooltip_step"; // When adding a new key, add it here as well, to be able to reset it from Developer Options. public static final Map ALL_PREF_KEYS = Map.of( "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT }, "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT, HOTSEAT_LONGPRESS_TIP_SEEN }, - "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT, - SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED}, + "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT}, "Taskbar Education", new String[] { TASKBAR_EDU_TOOLTIP_STEP }, "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT} ); @@ -62,7 +59,6 @@ public class OnboardingPrefs { HOME_BOUNCE_SEEN, HOTSEAT_LONGPRESS_TIP_SEEN, SEARCH_KEYBOARD_EDU_SEEN, - QSB_SEARCH_ONBOARDING_CARD_DISMISSED }) @Retention(RetentionPolicy.SOURCE) public @interface EventBoolKey {} @@ -74,7 +70,6 @@ public class OnboardingPrefs { HOME_BOUNCE_COUNT, HOTSEAT_DISCOVERY_TIP_COUNT, SEARCH_SNACKBAR_COUNT, - SEARCH_ONBOARDING_COUNT, ALL_APPS_VISITED_COUNT, TASKBAR_EDU_TOOLTIP_STEP, }) @@ -88,8 +83,6 @@ public class OnboardingPrefs { maxCounts.put(HOME_BOUNCE_COUNT, 3); maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5); maxCounts.put(SEARCH_SNACKBAR_COUNT, 3); - // This is the sum of all onboarding cards. Currently there is only 1 card shown 3 times. - maxCounts.put(SEARCH_ONBOARDING_COUNT, 3); maxCounts.put(ALL_APPS_VISITED_COUNT, 20); maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2); MAX_COUNTS = Collections.unmodifiableMap(maxCounts); From 521c2a038bb90a0e73d1e606319c15e5c14bd92e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 8 Jun 2023 13:17:20 -0700 Subject: [PATCH 3/6] Fising launcher stuck on blur The same surface can become invalid, in which case we need to wait until the next draw to apply blur Bug: 284411563 Test: Verified on device Change-Id: Ifb6be94756fd00c5925e11a6b7d57f206e063a17 --- .../launcher3/QuickstepTransitionManager.java | 5 ----- .../launcher3/statehandlers/DepthController.java | 6 ++++++ .../quickstep/util/BaseDepthController.java | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 114965fcab..e5fd605904 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -2042,10 +2042,5 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener setCrossWindowBlursEnabled( CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled()); } - - @Override - public void setSurface(SurfaceControl surface) { - super.setSurface(surface); - } } } diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 7c62763943..70aa5d77b8 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -158,6 +158,12 @@ public class DepthController extends BaseDepthController implements StateHandler super.applyDepthAndBlur(); } + @Override + protected void onInvalidSurface() { + // Lets wait for surface to become valid again + mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener); + } + @Override public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { mIgnoreStateChangesDuringMultiWindowAnimation = true; diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java index 23cfb39ec2..931e46876e 100644 --- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java +++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java @@ -88,6 +88,8 @@ public class BaseDepthController { */ protected boolean mInEarlyWakeUp; + private boolean mWaitingOnSurfaceValidity; + public BaseDepthController(Launcher activity) { mLauncher = activity; mMaxBlurRadius = activity.getResources().getInteger(R.integer.max_depth_blur_radius); @@ -115,6 +117,8 @@ public class BaseDepthController { } } + protected void onInvalidSurface() { } + protected void applyDepthAndBlur() { float depth = mDepth; IBinder windowToken = mLauncher.getRootView().getWindowToken(); @@ -128,9 +132,15 @@ public class BaseDepthController { if (!BlurUtils.supportsBlursOnWindows()) { return; } - if (mSurface == null || !mSurface.isValid()) { + if (mSurface == null) { return; } + if (!mSurface.isValid()) { + mWaitingOnSurfaceValidity = true; + onInvalidSurface(); + return; + } + mWaitingOnSurfaceValidity = false; boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque(); boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg && !mPauseBlurs; @@ -174,7 +184,7 @@ public class BaseDepthController { * Sets the specified app target surface to apply the blur to. */ protected void setSurface(SurfaceControl surface) { - if (mSurface != surface) { + if (mSurface != surface || mWaitingOnSurfaceValidity) { mSurface = surface; applyDepthAndBlur(); } From edd9c96e70119054993f06bb82531c50abac6988 Mon Sep 17 00:00:00 2001 From: Andreas Agvard Date: Wed, 7 Jun 2023 20:36:15 +0200 Subject: [PATCH 4/6] Added utility function to VibratorWrapper for generating Assist hapic Test: Manual Change-Id: I14c1c4e5a5c1aef54de12524282d21f16c4021c0 --- .../launcher3/util/VibratorWrapper.java | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java index ceba0db384..91945caa15 100644 --- a/src/com/android/launcher3/util/VibratorWrapper.java +++ b/src/com/android/launcher3/util/VibratorWrapper.java @@ -17,6 +17,7 @@ package com.android.launcher3.util; import static android.os.VibrationEffect.createPredefined; import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED; + import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; @@ -68,6 +69,9 @@ public class VibratorWrapper { @Nullable private final VibrationEffect mBumpEffect; + @Nullable + private final VibrationEffect mAssistEffect; + private long mLastDragTime; private final int mThresholdUntilNextDragCallMillis; @@ -125,12 +129,25 @@ public class VibratorWrapper { mBumpEffect = null; mThresholdUntilNextDragCallMillis = 0; } + + if (Utilities.ATLEAST_R && mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, + VibrationEffect.Composition.PRIMITIVE_TICK)) { + // quiet ramp, short pause, then sharp tick + mAssistEffect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) + .compose(); + } else { + // fallback for devices without composition support + mAssistEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK); + } } /** - * This is called when the user swipes to/from all apps. This is meant to be used in between - * long animation progresses so that it gives a dragging texture effect. For a better - * experience, this should be used in combination with vibrateForDragCommit(). + * This is called when the user swipes to/from all apps. This is meant to be used in between + * long animation progresses so that it gives a dragging texture effect. For a better + * experience, this should be used in combination with vibrateForDragCommit(). */ public void vibrateForDragTexture() { if (mDragEffect == null) { @@ -145,7 +162,7 @@ public class VibratorWrapper { } /** - * This is used when user reaches the commit threshold when swiping to/from from all apps. + * This is used when user reaches the commit threshold when swiping to/from from all apps. */ public void vibrateForDragCommit() { if (mCommitEffect != null) { @@ -156,9 +173,9 @@ public class VibratorWrapper { } /** - * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a - * FLING going to/from all apps. Client can just call this method elsewhere just for the - * effect. + * The bump haptic is used to be called at the end of a swipe and only if it the gesture is a + * FLING going to/from all apps. Client can just call this method elsewhere just for the + * effect. */ public void vibrateForDragBump() { if (mBumpEffect != null) { @@ -166,6 +183,15 @@ public class VibratorWrapper { } } + /** + * The assist haptic is used to be called when an assistant is invoked + */ + public void vibrateForAssist() { + if (mAssistEffect != null) { + vibrate(mAssistEffect); + } + } + /** * This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For * example, when no animation is happening but a vibrator happens to be vibrating still. Need @@ -176,6 +202,7 @@ public class VibratorWrapper { // reset dragTexture timestamp to be able to play dragTexture again whenever cancelled mLastDragTime = 0; } + private boolean isHapticFeedbackEnabled(ContentResolver resolver) { return Settings.System.getInt(resolver, HAPTIC_FEEDBACK_ENABLED, 0) == 1; } From 2b8556ce1a955f2b15d6c16878b01f034394145f Mon Sep 17 00:00:00 2001 From: Jordan Silva Date: Wed, 7 Jun 2023 12:10:59 +0100 Subject: [PATCH 5/6] Adding match workspace to SizeSpec for responsive grid support Added matchWorkspace property to support responsive grid implementation for AllApps and Folders. This property indicates whether the attribute value will be used from the workspace instead of defining a fixed value in the AllApps and Folders XML. The class was updated to be a data class and added a auxiliar constructor new make it more flexible for testing and to add other initializers to support AllApps and Folders. Bug: 284155638 Flag: ENABLE_RESPONSIVE_WORKSPACE Test: WorkspaceSpecsTest Test: SizeSpecTest Change-Id: I65b71e66be7b8236a1dee62b56a487b87881d991 --- res/values/attrs.xml | 4 +- .../android/launcher3/responsive/SizeSpec.kt | 73 +++++++++++++++ .../launcher3/workspace/WorkspaceSpecs.kt | 76 +++------------- tests/res/values/attrs.xml | 4 +- .../res/xml/invalid_workspace_file_case_4.xml | 58 ++++++++++++ .../launcher3/responsive/SizeSpecTest.kt | 88 +++++++++++++++++++ .../launcher3/util/TestResourceHelper.kt | 2 +- .../launcher3/workspace/WorkspaceSpecsTest.kt | 53 +++++++---- 8 files changed, 275 insertions(+), 83 deletions(-) create mode 100644 src/com/android/launcher3/responsive/SizeSpec.kt create mode 100644 tests/res/xml/invalid_workspace_file_case_4.xml create mode 100644 tests/src/com/android/launcher3/responsive/SizeSpecTest.kt diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 2e74d7d7dd..fc23eaa02d 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -204,6 +204,7 @@ + @@ -251,10 +252,11 @@ - + + diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt new file mode 100644 index 0000000000..bf5ca1cb89 --- /dev/null +++ b/src/com/android/launcher3/responsive/SizeSpec.kt @@ -0,0 +1,73 @@ +package com.android.launcher3.responsive + +import android.content.res.TypedArray +import android.util.AttributeSet +import android.util.Log +import android.util.TypedValue +import com.android.launcher3.R +import com.android.launcher3.util.ResourceHelper + +data class SizeSpec( + val fixedSize: Float, + val ofAvailableSpace: Float, + val ofRemainderSpace: Float, + val matchWorkspace: Boolean +) { + + fun isValid(): Boolean { + // All attributes are empty + if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) { + Log.e(TAG, "SizeSpec#isValid - all attributes are empty") + return false + } + + // More than one attribute is filled + val attrCount = + (if (fixedSize > 0) 1 else 0) + + (if (ofAvailableSpace > 0) 1 else 0) + + (if (ofRemainderSpace > 0) 1 else 0) + + (if (matchWorkspace) 1 else 0) + if (attrCount > 1) { + Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled") + return false + } + + // Values should be between 0 and 1 + if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) { + Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1") + return false + } + + // Invalid fixed size + if (fixedSize < 0f) { + Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.") + return false + } + + return true + } + + companion object { + private const val TAG = "WorkspaceSpecs::SizeSpec" + private fun getValue(a: TypedArray, index: Int): Float { + return when (a.getType(index)) { + TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat() + TypedValue.TYPE_FLOAT -> a.getFloat(index, 0f) + else -> 0f + } + } + + fun create(resourceHelper: ResourceHelper, attrs: AttributeSet): SizeSpec { + val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SizeSpec) + + val fixedSize = getValue(styledAttrs, R.styleable.SizeSpec_fixedSize) + val ofAvailableSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofAvailableSpace) + val ofRemainderSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofRemainderSpace) + val matchWorkspace = styledAttrs.getBoolean(R.styleable.SizeSpec_matchWorkspace, false) + + styledAttrs.recycle() + + return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace) + } + } +} diff --git a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt index dc5ae47b16..48159249a0 100644 --- a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt +++ b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt @@ -16,13 +16,12 @@ package com.android.launcher3.workspace -import android.content.res.TypedArray import android.content.res.XmlResourceParser import android.util.AttributeSet import android.util.Log -import android.util.TypedValue import android.util.Xml import com.android.launcher3.R +import com.android.launcher3.responsive.SizeSpec import com.android.launcher3.util.ResourceHelper import java.io.IOException import kotlin.math.roundToInt @@ -95,16 +94,16 @@ class WorkspaceSpecs(resourceHelper: ResourceHelper) { if (type == XmlPullParser.START_TAG) { when (parser.name) { XmlTags.START_PADDING -> { - startPadding = SizeSpec(resourceHelper, attr) + startPadding = SizeSpec.create(resourceHelper, attr) } XmlTags.END_PADDING -> { - endPadding = SizeSpec(resourceHelper, attr) + endPadding = SizeSpec.create(resourceHelper, attr) } XmlTags.GUTTER -> { - gutter = SizeSpec(resourceHelper, attr) + gutter = SizeSpec.create(resourceHelper, attr) } XmlTags.CELL_SIZE -> { - cellSize = SizeSpec(resourceHelper, attr) + cellSize = SizeSpec.create(resourceHelper, attr) } } } @@ -270,61 +269,12 @@ data class WorkspaceSpec( } private fun allSpecsAreValid(): Boolean = - startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid() -} - -class SizeSpec(resourceHelper: ResourceHelper, attrs: AttributeSet) { - val fixedSize: Float - val ofAvailableSpace: Float - val ofRemainderSpace: Float - - init { - val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SpecSize) - - fixedSize = getValue(styledAttrs, R.styleable.SpecSize_fixedSize) - ofAvailableSpace = getValue(styledAttrs, R.styleable.SpecSize_ofAvailableSpace) - ofRemainderSpace = getValue(styledAttrs, R.styleable.SpecSize_ofRemainderSpace) - - styledAttrs.recycle() - } - - private fun getValue(a: TypedArray, index: Int): Float { - if (a.getType(index) == TypedValue.TYPE_DIMENSION) { - return a.getDimensionPixelSize(index, 0).toFloat() - } else if (a.getType(index) == TypedValue.TYPE_FLOAT) { - return a.getFloat(index, 0f) - } - return 0f - } - - fun isValid(): Boolean { - // All attributes are empty - if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f) { - Log.e(TAG, "SizeSpec#isValid - all attributes are empty") - return false - } - - // More than one attribute is filled - val attrCount = - (if (fixedSize > 0) 1 else 0) + - (if (ofAvailableSpace > 0) 1 else 0) + - (if (ofRemainderSpace > 0) 1 else 0) - if (attrCount > 1) { - Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled") - return false - } - - // Values should be between 0 and 1 - if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) { - Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1") - return false - } - - return true - } - - override fun toString(): String { - return "SizeSpec(fixedSize=$fixedSize, ofAvailableSpace=$ofAvailableSpace, " + - "ofRemainderSpace=$ofRemainderSpace)" - } + startPadding.isValid() && + endPadding.isValid() && + gutter.isValid() && + cellSize.isValid() && + !startPadding.matchWorkspace && + !endPadding.matchWorkspace && + !gutter.matchWorkspace && + !cellSize.matchWorkspace } diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml index 2310d9ef66..cb6da3ba3f 100644 --- a/tests/res/values/attrs.xml +++ b/tests/res/values/attrs.xml @@ -26,10 +26,10 @@ - + + - diff --git a/tests/res/xml/invalid_workspace_file_case_4.xml b/tests/res/xml/invalid_workspace_file_case_4.xml new file mode 100644 index 0000000000..9e74c852b3 --- /dev/null +++ b/tests/res/xml/invalid_workspace_file_case_4.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt new file mode 100644 index 0000000000..426777de26 --- /dev/null +++ b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt @@ -0,0 +1,88 @@ +/* + * 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.responsive + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.AbstractDeviceProfileTest +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SizeSpecTest : AbstractDeviceProfileTest() { + override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context + + @Before + fun setup() { + initializeVarsForPhone(deviceSpecs["phone"]!!) + } + + @Test + fun valid_values() { + val combinations = + listOf( + SizeSpec(100f, 0f, 0f, false), + SizeSpec(0f, 1f, 0f, false), + SizeSpec(0f, 0f, 1f, false), + SizeSpec(0f, 0f, 0f, false), + SizeSpec(0f, 0f, 0f, true) + ) + + for (instance in combinations) { + assertThat(instance.isValid()).isEqualTo(true) + } + } + + @Test + fun multiple_values_assigned() { + val combinations = + listOf( + SizeSpec(1f, 1f, 0f, false), + SizeSpec(1f, 0f, 1f, false), + SizeSpec(1f, 0f, 0f, true), + SizeSpec(0f, 1f, 1f, false), + SizeSpec(0f, 1f, 0f, true), + SizeSpec(0f, 0f, 1f, true), + SizeSpec(1f, 1f, 1f, true) + ) + + for (instance in combinations) { + assertThat(instance.isValid()).isEqualTo(false) + } + } + + @Test + fun invalid_values() { + val combinations = + listOf( + SizeSpec(0f, 1.1f, 0f, false), + SizeSpec(0f, -0.1f, 0f, false), + SizeSpec(0f, 0f, 1.1f, false), + SizeSpec(0f, 0f, -0.1f, false), + SizeSpec(-1f, 0f, 0f, false) + ) + + for (instance in combinations) { + assertThat(instance.isValid()).isEqualTo(false) + } + } +} diff --git a/tests/src/com/android/launcher3/util/TestResourceHelper.kt b/tests/src/com/android/launcher3/util/TestResourceHelper.kt index fb03fe1b53..3f0054e0c3 100644 --- a/tests/src/com/android/launcher3/util/TestResourceHelper.kt +++ b/tests/src/com/android/launcher3/util/TestResourceHelper.kt @@ -27,7 +27,7 @@ class TestResourceHelper(private val context: Context, private val specsFileId: ResourceHelper(context, specsFileId) { override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray { var clone = styleId.clone() - if (styleId == R.styleable.SpecSize) clone = TestR.styleable.SpecSize + if (styleId == R.styleable.SizeSpec) clone = TestR.styleable.SizeSpec else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec return context.obtainStyledAttributes(attrs, clone) } diff --git a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt index 9cd0a2e4a3..51808e3e53 100644 --- a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt +++ b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt @@ -50,16 +50,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "specType=HEIGHT, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "endPadding=SizeSpec(fixedSize=84.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "cellSize=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.15808, " + - "ofRemainderSpace=0.0)" + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false)" + ")" ) assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString()) @@ -69,16 +73,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "specType=HEIGHT, " + "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "endPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=1.0), " + + "ofRemainderSpace=1.0, " + + "matchWorkspace=false), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "cellSize=SizeSpec(fixedSize=273.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0)" + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false)" + ")" ) assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString()) @@ -88,16 +96,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "specType=HEIGHT, " + "startPadding=SizeSpec(fixedSize=21.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "endPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=1.0), " + + "ofRemainderSpace=1.0, " + + "matchWorkspace=false), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "cellSize=SizeSpec(fixedSize=273.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0)" + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false)" + ")" ) assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1) @@ -108,16 +120,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "specType=WIDTH, " + "startPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "endPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.0), " + + "ofRemainderSpace=0.0, " + + "matchWorkspace=false), " + "cellSize=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + - "ofRemainderSpace=0.25)" + + "ofRemainderSpace=0.25, " + + "matchWorkspace=false)" + ")" ) } @@ -136,4 +152,9 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { fun parseInvalidFile_valueBiggerThan1_throwsError() { WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3)) } + + @Test(expected = IllegalStateException::class) + fun parseInvalidFile_matchWorkspace_true_throwsError() { + WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4)) + } } From 0a074648c29cb74b8b227deccb8187cf31e0d156 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 24 Mar 2023 23:20:21 +0000 Subject: [PATCH 6/6] Suspend immersive mode autohide while pending transient taskbar timeout Test: open Youtube fullscreen video, swipe up on taskbar; ensure taskbar autostashes before immersive mode autohides Fixes: 260765455 Flag: none Change-Id: I05c61de911f1bf326962d84c4f7c3e12135f23cf --- .../TaskbarAutohideSuspendController.java | 20 +++++++++++++------ .../taskbar/TaskbarStashController.java | 11 +++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java index 2517ff671c..c9d331bb7f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java @@ -41,8 +41,10 @@ public class TaskbarAutohideSuspendController implements public static final int FLAG_AUTOHIDE_SUSPEND_TOUCHING = 1 << 2; // Taskbar EDU overlay is open above the Taskbar. */ public static final int FLAG_AUTOHIDE_SUSPEND_EDU_OPEN = 1 << 3; - // Taskbar in immersive mode in overview + // Taskbar is in immersive mode in overview. public static final int FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER = 1 << 4; + // Transient Taskbar is temporarily unstashed (pending a timeout). + public static final int FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR = 1 << 5; @IntDef(flag = true, value = { FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, @@ -50,6 +52,7 @@ public class TaskbarAutohideSuspendController implements FLAG_AUTOHIDE_SUSPEND_TOUCHING, FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, + FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, }) @Retention(RetentionPolicy.SOURCE) public @interface AutohideSuspendFlag {} @@ -85,18 +88,21 @@ public class TaskbarAutohideSuspendController implements boolean isSuspended = isSuspended(); mSystemUiProxy.notifyTaskbarAutohideSuspend(isSuspended); - mActivity.onTransientAutohideSuspendFlagChanged(isSuspended); + mActivity.onTransientAutohideSuspendFlagChanged(isTransientTaskbarStashingSuspended()); } /** - * Returns true iff taskbar autohide is currently suspended. + * Returns true iff taskbar autohide is currently suspended for immersive mode. */ - public boolean isSuspended() { + private boolean isSuspended() { return mAutohideSuspendFlags != 0; } - public boolean isSuspendedForTransientTaskbarInOverview() { - return (mAutohideSuspendFlags & FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER) != 0; + /** + * Returns whether Transient Taskbar should avoid auto-stashing. + */ + public boolean isTransientTaskbarStashingSuspended() { + return (mAutohideSuspendFlags & ~FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR) != 0; } @Override @@ -115,6 +121,8 @@ public class TaskbarAutohideSuspendController implements appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_EDU_OPEN, "FLAG_AUTOHIDE_SUSPEND_EDU_OPEN"); appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, "FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER"); + appendFlag(str, flags, FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, + "FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR"); return str.toString(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 00e14adf07..4710721463 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -508,10 +508,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba return; } - if (stash && mControllers.taskbarAutohideSuspendController.isSuspended() - && !mControllers.taskbarAutohideSuspendController - .isSuspendedForTransientTaskbarInOverview()) { - // Avoid stashing if autohide is currently suspended. + if (stash && mControllers.taskbarAutohideSuspendController + .isTransientTaskbarStashingSuspended()) { return; } @@ -1038,6 +1036,9 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) ? LAUNCHER_TRANSIENT_TASKBAR_HIDE : LAUNCHER_TRANSIENT_TASKBAR_SHOW); + mControllers.taskbarAutohideSuspendController.updateFlag( + TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TRANSIENT_TASKBAR, + !hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)); } } @@ -1130,7 +1131,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } private void onTaskbarTimeout(Alarm alarm) { - if (mControllers.taskbarAutohideSuspendController.isSuspended()) { + if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) { return; } updateAndAnimateTransientTaskbar(true);