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(); 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; } 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/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 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/Launcher.java b/src/com/android/launcher3/Launcher.java index 29b0f08d37..ffd56cc979 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/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 8250a990c7..8dc120402d 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -416,6 +416,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/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); + } + } + } +} 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" +