diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index 75d95e6d85e30..f78046d381686 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -15,7 +15,6 @@ package com.android.systemui.plugins.qs; import android.view.View; -import android.view.View.OnClickListener; import com.android.systemui.plugins.FragmentBase; import com.android.systemui.plugins.annotations.DependsOn; @@ -34,7 +33,7 @@ public interface QS extends FragmentBase { String ACTION = "com.android.systemui.action.PLUGIN_QS"; - int VERSION = 13; + int VERSION = 14; String TAG = "QS"; @@ -68,7 +67,12 @@ public interface QS extends FragmentBase { void setHeaderListening(boolean listening); void notifyCustomizeChanged(); void setContainerController(QSContainerController controller); - void setExpandClickListener(OnClickListener onClickListener); + + /** + * Provide an action to collapse if expanded or expand if collapsed. + * @param action + */ + void setCollapseExpandAction(Runnable action); /** * Returns the height difference between the QSPanel container and the QuickQSPanel container diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java index 9a9683dbfb5ce..a8999ff31f8a1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java @@ -26,7 +26,7 @@ import com.android.systemui.plugins.qs.QSTile.State; @DependsOn(target = QSIconView.class) @DependsOn(target = QSTile.class) public abstract class QSTileView extends LinearLayout { - public static final int VERSION = 2; + public static final int VERSION = 3; public QSTileView(Context context) { super(context); @@ -71,4 +71,7 @@ public abstract class QSTileView extends LinearLayout { public View getSecondaryLabel() { return null; } + + /** Sets the index of this tile in its layout */ + public abstract void setPosition(int position); } diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 02c58e468c308..77523ec9229f7 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -24,6 +24,8 @@ android:orientation="vertical" android:layout_marginStart="@dimen/qs_label_container_margin" android:layout_marginEnd="0dp" + android:focusable="false" + android:importantForAccessibility="no" android:layout_gravity="center_vertical | start"> diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index d20141b338389..34f771ce04310 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -15,6 +15,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.Scroller; @@ -552,6 +553,51 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { postInvalidateOnAnimation(); } + private int sanitizePageAction(int action) { + int pageLeftId = AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT.getId(); + int pageRightId = AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT.getId(); + if (action == pageLeftId || action == pageRightId) { + if (!isLayoutRtl()) { + if (action == pageLeftId) { + return AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD; + } else { + return AccessibilityNodeInfo.ACTION_SCROLL_FORWARD; + } + } else { + if (action == pageLeftId) { + return AccessibilityNodeInfo.ACTION_SCROLL_FORWARD; + } else { + return AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD; + } + } + } + return action; + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + action = sanitizePageAction(action); + boolean performed = super.performAccessibilityAction(action, arguments); + if (performed && (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD + || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) { + requestAccessibilityFocus(); + } + return performed; + } + + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); + // getCurrentItem does not respect RTL, so it works well together with page actions that + // use left/right positioning. + if (getCurrentItem() != 0) { + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT); + } + if (getCurrentItem() != mPages.size() - 1) { + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT); + } + } + private static Animator setupBounceAnimator(View view, int ordinal) { view.setAlpha(0f); view.setScaleX(0f); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java index aac5672aa3f93..bcf60d1c1f4f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java @@ -48,10 +48,5 @@ public interface QSFooter { */ void setKeyguardShowing(boolean keyguardShowing); - /** - * Sets the {@link android.view.View.OnClickListener to be used on elements that expend QS. - */ - void setExpandClickListener(View.OnClickListener onClickListener); - default void disable(int state1, int state2, boolean animate) {} } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java index 6c0ca49c81a80..61905ae00be81 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java @@ -23,13 +23,11 @@ import android.content.res.Configuration; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; import android.view.View; -import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.TextView; @@ -168,23 +166,6 @@ public class QSFooterView extends FrameLayout { super.onDetachedFromWindow(); } - @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (action == AccessibilityNodeInfo.ACTION_EXPAND) { - if (mExpandClickListener != null) { - mExpandClickListener.onClick(null); - return true; - } - } - return super.performAccessibilityAction(action, arguments); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); - } - void disable(int state2) { final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; if (disabled == mQsDisabled) return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index bef4f43b9d11d..0d29a1abca9fb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -114,12 +114,6 @@ public class QSFooterViewController extends ViewController impleme mView.setKeyguardShowing(); } - /** */ - @Override - public void setExpandClickListener(View.OnClickListener onClickListener) { - mView.setExpandClickListener(onClickListener); - } - @Override public void disable(int state1, int state2, boolean animate) { mView.disable(state2); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index b5e1c5ee1a3e7..795a606fe7e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -31,7 +31,6 @@ import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -717,8 +716,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @Override - public void setExpandClickListener(OnClickListener onClickListener) { - mFooter.setExpandClickListener(onClickListener); + public void setCollapseExpandAction(Runnable action) { + mQSPanelController.setCollapseExpandAction(action); + mQuickQSPanelController.setCollapseExpandAction(action); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 11a36ada4e36d..8c4dedc577d1f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -34,6 +34,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.LinearLayout; import androidx.annotation.VisibleForTesting; @@ -62,6 +63,8 @@ public class QSPanel extends LinearLayout implements Tunable { private final int mMediaTopMargin; private final int mMediaTotalBottomMargin; + private Runnable mCollapseExpandAction; + /** * The index where the content starts that needs to be moved between parents */ @@ -678,6 +681,28 @@ public class QSPanel extends LinearLayout implements Tunable { mShouldMoveMediaOnExpansion = shouldMoveMediaOnExpansion; } + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (action == AccessibilityNodeInfo.ACTION_EXPAND + || action == AccessibilityNodeInfo.ACTION_COLLAPSE) { + if (mCollapseExpandAction != null) { + mCollapseExpandAction.run(); + return true; + } + } + return super.performAccessibilityAction(action, arguments); + } + + public void setCollapseExpandAction(Runnable action) { + mCollapseExpandAction = action; + } + private class H extends Handler { private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 74d1a3d43ab32..9c8fc4761f980 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -424,6 +424,14 @@ public abstract class QSPanelControllerBase extends ViewContr return mView.getBrightnessView(); } + /** + * Set a listener to collapse/expand QS. + * @param action + */ + public void setCollapseExpandAction(Runnable action) { + mView.setCollapseExpandAction(action); + } + /** Sets whether we are currently on lock screen. */ public void setIsOnKeyguard(boolean isOnKeyguard) { boolean isOnSplitShadeLockscreen = mShouldUseSplitNotificationShade && isOnKeyguard; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index f5ae019c20aba..3c95da8732735 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Configuration; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.LinearLayout; import com.android.internal.logging.UiEventLogger; @@ -167,6 +168,14 @@ public class QuickQSPanel extends QSPanel { return QSEvent.QQS_TILE_VISIBLE; } + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + // Remove the collapse action from QSPanel + info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); + } + static class QQSSideLabelTileLayout extends SideLabelTileLayout { private boolean mLastSelected; diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index da82d2cb7e8d1..9ef90ecf50a73 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -8,6 +8,7 @@ import android.provider.Settings; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.Nullable; @@ -240,6 +241,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { } else { record.tileView.setLeftTopRightBottom(left, top, right, bottom); } + record.tileView.setPosition(i); mLastTileBottom = bottom; } } @@ -296,4 +298,11 @@ public class TileLayout extends ViewGroup implements QSTileLayout { } } } + + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); + info.setCollectionInfo( + new AccessibilityNodeInfo.CollectionInfo(mRecords.size(), 1, false)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index a712ce2e6394b..72dad0608a3a0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -69,6 +69,12 @@ open class QSTileViewImpl @JvmOverloads constructor( internal const val TILE_STATE_RES_PREFIX = "tile_states_" } + private var _position: Int = INVALID + + override fun setPosition(position: Int) { + _position = position + } + override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE set(value) { if (field == value) return @@ -404,6 +410,10 @@ open class QSTileViewImpl @JvmOverloads constructor( } } } + if (_position != INVALID) { + info.collectionItemInfo = + AccessibilityNodeInfo.CollectionItemInfo(_position, 1, 0, 1, false) + } } override fun toString(): String { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 20fc39d6482fe..600732373caed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -239,7 +239,7 @@ public class NotificationPanelViewController extends PanelViewController { private final DozeParameters mDozeParameters; private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener(); - private final OnClickListener mOnClickListener = new OnClickListener(); + private final Runnable mCollapseExpandAction = new CollapseExpandAction(); private final OnOverscrollTopChangedListener mOnOverscrollTopChangedListener = new OnOverscrollTopChangedListener(); @@ -3573,7 +3573,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onFragmentViewCreated(String tag, Fragment fragment) { mQs = (QS) fragment; mQs.setPanelView(mHeightListener); - mQs.setExpandClickListener(mOnClickListener); + mQs.setCollapseExpandAction(mCollapseExpandAction); mQs.setHeaderClickable(isQsExpansionEnabled()); mQs.setOverscrolling(mStackScrollerOverscrolling); mQs.setInSplitShade(mShouldUseSplitNotificationShade); @@ -4216,9 +4216,9 @@ public class NotificationPanelViewController extends PanelViewController { } } - private class OnClickListener implements View.OnClickListener { + private class CollapseExpandAction implements Runnable { @Override - public void onClick(View v) { + public void run() { onQsExpansionStarted(); if (mQsExpanded) { flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 63f8641ad16da..829445eb92bea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Fragment; @@ -304,6 +305,16 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { assertThat(mQsFragmentView.getY()).isEqualTo(-qsAbsoluteBottom); } + @Test + public void setCollapseExpandAction_passedToControllers() { + Runnable action = () -> {}; + QSFragment fragment = resumeAndGetFragment(); + fragment.setCollapseExpandAction(action); + + verify(mQSPanelController).setCollapseExpandAction(action); + verify(mQuickQSPanelController).setCollapseExpandAction(action); + } + @Override protected Fragment instantiate(Context context, String className, Bundle arguments) { MockitoAnnotations.initMocks(this); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index 04bbd60b8d90b..ba02a828d337d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -21,24 +21,19 @@ import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityNodeInfo import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.qs.QSTileView -import com.android.systemui.qs.QSPanelControllerBase.TileRecord -import com.android.systemui.qs.logging.QSLogger -import com.android.systemui.qs.tileimpl.QSTileImpl import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper @@ -47,22 +42,8 @@ class QSPanelTest : SysuiTestCase() { private lateinit var mTestableLooper: TestableLooper private lateinit var mQsPanel: QSPanel - @Mock - private lateinit var mHost: QSTileHost - - @Mock - private lateinit var dndTile: QSTileImpl<*> - - @Mock - private lateinit var mDndTileRecord: TileRecord - - @Mock - private lateinit var mQSLogger: QSLogger private lateinit var mParentView: ViewGroup - @Mock - private lateinit var mQSTileView: QSTileView - private lateinit var mFooter: View @Before @@ -71,8 +52,6 @@ class QSPanelTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) mTestableLooper = TestableLooper.get(this) - mDndTileRecord.tile = dndTile - mDndTileRecord.tileView = mQSTileView mTestableLooper.runWithLooper { mQsPanel = QSPanel(mContext, null) mQsPanel.initialize() @@ -86,11 +65,6 @@ class QSPanelTest : SysuiTestCase() { mParentView = FrameLayout(mContext).apply { addView(mQsPanel) } - - whenever(dndTile.tileSpec).thenReturn("dnd") - whenever(mHost.tiles).thenReturn(emptyList()) - whenever(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView) - mQsPanel.addTile(mDndTileRecord) } } @@ -137,6 +111,24 @@ class QSPanelTest : SysuiTestCase() { assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1) } + @Test + fun testHasCollapseAccessibilityAction() { + val info = AccessibilityNodeInfo(mQsPanel) + mQsPanel.onInitializeAccessibilityNodeInfo(info) + + assertThat(info.actions and AccessibilityNodeInfo.ACTION_COLLAPSE).isNotEqualTo(0) + assertThat(info.actions and AccessibilityNodeInfo.ACTION_EXPAND).isEqualTo(0) + } + + @Test + fun testCollapseActionCallsRunnable() { + val mockRunnable = mock(Runnable::class.java) + mQsPanel.setCollapseExpandAction(mockRunnable) + + mQsPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE, null) + verify(mockRunnable).run() + } + private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) = context.resources.configuration.apply { orientation = newOrientation } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt new file mode 100644 index 0000000000000..60c2bde947a68 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt @@ -0,0 +1,66 @@ +package com.android.systemui.qs + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import android.view.ViewGroup +import android.view.accessibility.AccessibilityNodeInfo +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +@SmallTest +class QuickQSPanelTest : SysuiTestCase() { + + private lateinit var testableLooper: TestableLooper + private lateinit var quickQSPanel: QuickQSPanel + + private lateinit var parentView: ViewGroup + + @Before + @Throws(Exception::class) + fun setup() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + + testableLooper.runWithLooper { + quickQSPanel = QuickQSPanel(mContext, null) + quickQSPanel.initialize() + + quickQSPanel.onFinishInflate() + quickQSPanel.setSecurityFooter(View(mContext), false) + quickQSPanel.setHeaderContainer(LinearLayout(mContext)) + // Provides a parent with non-zero size for QSPanel + parentView = FrameLayout(mContext).apply { + addView(quickQSPanel) + } + } + } + + @Test + fun testHasExpandAccessibilityAction() { + val info = AccessibilityNodeInfo(quickQSPanel) + quickQSPanel.onInitializeAccessibilityNodeInfo(info) + + Truth.assertThat(info.actions and AccessibilityNodeInfo.ACTION_EXPAND).isNotEqualTo(0) + Truth.assertThat(info.actions and AccessibilityNodeInfo.ACTION_COLLAPSE).isEqualTo(0) + } + + @Test + fun testExpandActionCallsRunnable() { + val mockRunnable = Mockito.mock(Runnable::class.java) + quickQSPanel.setCollapseExpandAction(mockRunnable) + + quickQSPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_EXPAND, null) + Mockito.verify(mockRunnable).run() + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java index bd4bfff4c18a1..5abc0e1f9c890 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.test.suitebuilder.annotation.SmallTest; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.test.runner.AndroidJUnit4; @@ -170,4 +171,36 @@ public class TileLayoutTest extends SysuiTestCase { mTileLayout.measure(mLayoutSizeForOneTile, mLayoutSizeForOneTile); assertEquals(0, mTileLayout.getMeasuredHeight()); } + + @Test + public void testCollectionInfo() { + QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord(); + AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(mTileLayout); + mTileLayout.addTile(tileRecord1); + + mTileLayout.onInitializeAccessibilityNodeInfo(info); + AccessibilityNodeInfo.CollectionInfo collectionInfo = info.getCollectionInfo(); + assertEquals(1, collectionInfo.getRowCount()); + assertEquals(1, collectionInfo.getColumnCount()); // always use one column + + mTileLayout.addTile(tileRecord2); + mTileLayout.onInitializeAccessibilityNodeInfo(info); + collectionInfo = info.getCollectionInfo(); + assertEquals(2, collectionInfo.getRowCount()); + assertEquals(1, collectionInfo.getColumnCount()); // always use one column + } + + @Test + public void testSetPositionOnTiles() { + QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord(); + mTileLayout.addTile(tileRecord1); + mTileLayout.addTile(tileRecord2); + mTileLayout.measure(mLayoutSizeForOneTile * 2, mLayoutSizeForOneTile * 2); + mTileLayout.layout(0, 0, mLayoutSizeForOneTile * 2, mLayoutSizeForOneTile * 2); + + verify(tileRecord1.tileView).setPosition(0); + verify(tileRecord2.tileView).setPosition(1); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt index a2b5013c8c2cd..9fdc2fd608705 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt @@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.text.TextUtils import android.view.View +import android.view.accessibility.AccessibilityNodeInfo import android.widget.TextView import androidx.test.filters.SmallTest import com.android.systemui.R @@ -257,6 +258,28 @@ class QSTileViewImplTest : SysuiTestCase() { assertThat((tileView.secondaryLabel as TextView).text).isEqualTo(onString) } + @Test + fun testCollectionItemInfoHasPosition() { + val position = 5 + tileView.setPosition(position) + + val info = AccessibilityNodeInfo(tileView) + tileView.onInitializeAccessibilityNodeInfo(info) + + assertThat(info.collectionItemInfo.rowIndex).isEqualTo(position) + assertThat(info.collectionItemInfo.rowSpan).isEqualTo(1) + assertThat(info.collectionItemInfo.columnIndex).isEqualTo(0) + assertThat(info.collectionItemInfo.columnSpan).isEqualTo(1) + } + + @Test + fun testCollectionItemInfoNoPosition() { + val info = AccessibilityNodeInfo(tileView) + tileView.onInitializeAccessibilityNodeInfo(info) + + assertThat(info.collectionItemInfo).isNull() + } + class FakeTileView( context: Context, icon: QSIconView,