diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 0592510338..c7325bad9d 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 9afcd2ab81..d379d6d6f7 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/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 24d2b0bc6e..87538f77f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -519,10 +519,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;
}
@@ -1080,6 +1078,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));
}
}
@@ -1172,7 +1173,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
private void onTaskbarTimeout(Alarm alarm) {
- if (mControllers.taskbarAutohideSuspendController.isSuspended()) {
+ if (mControllers.taskbarAutohideSuspendController.isTransientTaskbarStashingSuspended()) {
return;
}
updateAndAnimateTransientTaskbarForTimeout();
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();
}
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 275a3b81a6..80df78ae54 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/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);
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;
}
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);
}
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))
+ }
}