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)) + } }