From 374b417b68171829cc7fb119b40ec49b9206d87b Mon Sep 17 00:00:00 2001 From: Behnam Heydarshahi Date: Tue, 5 Sep 2023 13:50:03 +0000 Subject: [PATCH] PanelSlicesAdapter: load slice label safely Load the slice action label string safely when action label is set, and no longer at adapter instantiation time. Bug: b/298571448 Test: atest PanelSlicesAdapterTest Change-Id: I0d5b6e0b2db2fa78d3f58ae2cb96f14437923ffd --- .../settings/panel/PanelSlicesAdapter.java | 14 ++--- .../panel/PanelSlicesAdapterTest.java | 63 ++++++++++++++++++- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/com/android/settings/panel/PanelSlicesAdapter.java b/src/com/android/settings/panel/PanelSlicesAdapter.java index 6dd1a30ec71..fb41879316f 100644 --- a/src/com/android/settings/panel/PanelSlicesAdapter.java +++ b/src/com/android/settings/panel/PanelSlicesAdapter.java @@ -61,15 +61,12 @@ public class PanelSlicesAdapter private final List> mSliceLiveData; private final int mMetricsCategory; private final PanelFragment mPanelFragment; - private final String mSliceClickActionLabel; public PanelSlicesAdapter( PanelFragment fragment, Map> sliceLiveData, int metricsCategory) { mPanelFragment = fragment; mSliceLiveData = new ArrayList<>(sliceLiveData.values()); mMetricsCategory = metricsCategory; - mSliceClickActionLabel = mPanelFragment.getContext().getString( - R.string.accessibility_action_label_panel_slice); } @NonNull @@ -77,7 +74,7 @@ public class PanelSlicesAdapter public SliceRowViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { final Context context = viewGroup.getContext(); final LayoutInflater inflater = LayoutInflater.from(context); - View view; + final View view; if (viewType == PanelContent.VIEW_TYPE_SLIDER) { view = inflater.inflate(R.layout.panel_slice_slider_row, viewGroup, false); } else { @@ -189,7 +186,6 @@ public class PanelSlicesAdapter return; } sliceView.setTag(ROW_VIEW_TAG, new Object()); - sliceView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, @@ -208,15 +204,17 @@ public class PanelSlicesAdapter * Update the action label for TalkBack to be more specific * @param view the RowView within the Slice */ - private void setActionLabel(View view) { + @VisibleForTesting void setActionLabel(View view) { view.setAccessibilityDelegate(new View.AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); + AccessibilityNodeInfo.AccessibilityAction customClick = - new AccessibilityNodeInfo.AccessibilityAction( - ACTION_CLICK, mSliceClickActionLabel); + new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, host + .getResources() + .getString(R.string.accessibility_action_label_panel_slice)); info.addAction(customClick); } }); diff --git a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java index 516d088634a..9322317080b 100644 --- a/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java +++ b/tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java @@ -33,9 +33,13 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.Uri; +import android.text.TextUtils; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; +import android.widget.LinearLayout; import androidx.lifecycle.LiveData; import androidx.slice.Slice; @@ -44,6 +48,7 @@ import com.android.settings.R; import com.android.settings.panel.PanelSlicesAdapter.SliceRowViewHolder; import com.android.settings.testutils.FakeFeatureFactory; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -94,7 +99,6 @@ public class PanelSlicesAdapterTest { .get() .getSupportFragmentManager() .findFragmentById(R.id.main_content)); - } private void addTestLiveData(Uri uri) { @@ -106,6 +110,61 @@ public class PanelSlicesAdapterTest { mData.put(uri, liveData); } + /** + * Edge case where fragment context is not available. + */ + @Test + public void withPanelFragmentContextNull_createAdapter_noExceptionThrown() { + when(mPanelFragment.getContext()).thenReturn(null); + + final PanelSlicesAdapter adapter = spy(new PanelSlicesAdapter(mPanelFragment, mData, 0)); + + Assert.assertNotNull(adapter); + } + + /** + * ViewHolder should load and set the action label correctly. + */ + @Test + public void setActionLabel_loadsActionLabel() { + addTestLiveData(VOLUME_NOTIFICATION_URI); + final PanelSlicesAdapter adapter = new PanelSlicesAdapter(mPanelFragment, mData, 0); + final ViewGroup view = new FrameLayout(mContext); + final SliceRowViewHolder viewHolder = adapter.onCreateViewHolder(view, VIEW_TYPE_SLIDER); + + // now let's see if setActionLabel can load and set the label correctly. + LinearLayout llRow = new LinearLayout(mContext); + viewHolder.setActionLabel(llRow); + + boolean isLabelSet = isActionLabelSet(llRow); + Assert.assertTrue("Action label was not set correctly.", isLabelSet); + } + + /** + * @param rowView the view with id row_view + * @return whether the accessibility action label is set + */ + private boolean isActionLabelSet(View rowView) { + View.AccessibilityDelegate delegate = rowView.getAccessibilityDelegate(); + if (delegate == null) { + return false; + } + AccessibilityNodeInfo node = new AccessibilityNodeInfo(rowView); + delegate.onInitializeAccessibilityNodeInfo(rowView, node); + + boolean foundLabel = false; + final String expectedLabel = + mContext.getString(R.string.accessibility_action_label_panel_slice); + for (AccessibilityNodeInfo.AccessibilityAction action : node.getActionList()) { + if (action.equals(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK) + && TextUtils.equals(action.getLabel(), expectedLabel)) { + foundLabel = true; + break; + } + } + return foundLabel; + } + @Test public void sizeOfAdapter_shouldNotExceedMaxNum() { for (int i = 0; i < MAX_NUM_OF_SLICES + 2; i++) { @@ -141,7 +200,7 @@ public class PanelSlicesAdapterTest { } @Test - public void onCreateViewHolder_viewTypeSlider_verifyActionLabelSet() { + public void onBindViewHolder_viewTypeSlider_verifyActionLabelSet() { addTestLiveData(VOLUME_NOTIFICATION_URI); final PanelSlicesAdapter adapter =