From ef87755c34e06c9f0ed47203db3f76a181096617 Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Wed, 24 Feb 2021 16:37:39 +0800 Subject: [PATCH] Fix the higlighted settings not work properly from search Since the Andorid S, we introduce the CollapsingToolbarLayout , the highlighted funcation might break for unkown reason. I observed that broken cases with overlapping issue on tool bar. The possible root cause is the interation bwtween CoordinatorLayout v.s CollapsingToolbarLayout v.s Recycler view. This cl is definetly a workaround to prevent this issue. I try to collapse the tool bar with an animation before we start to scroll the list. This makes the overall transition smooth but always collapse the tool bar. Fix: 177968297 Test: Click a lot of search results, and screen should highlight settings correctly. Change-Id: Id9c32b642433dcc39c179a2cc83a06e77cc47888 --- .../settings/SettingsPreferenceFragment.java | 8 +++--- .../HighlightablePreferenceGroupAdapter.java | 27 ++++++++++++++----- ...ghlightablePreferenceGroupAdapterTest.java | 16 +++++++---- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java index 2c628f6f375..0c1ace905c2 100644 --- a/src/com/android/settings/SettingsPreferenceFragment.java +++ b/src/com/android/settings/SettingsPreferenceFragment.java @@ -57,6 +57,8 @@ import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settingslib.search.Indexable; import com.android.settingslib.widget.LayoutPreference; +import com.google.android.material.appbar.AppBarLayout; + import java.util.UUID; /** @@ -112,9 +114,8 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF @VisibleForTesting ViewGroup mPinnedHeaderFrameLayout; - + private AppBarLayout mAppBarLayout; private LayoutPreference mHeader; - private View mEmptyView; private LinearLayoutManager mLayoutManager; private ArrayMap mPreferenceCache; @@ -145,6 +146,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF Bundle savedInstanceState) { final View root = super.onCreateView(inflater, container, savedInstanceState); mPinnedHeaderFrameLayout = root.findViewById(R.id.pinned_header); + mAppBarLayout = getActivity().findViewById(R.id.app_bar); return root; } @@ -250,7 +252,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF return; } if (mAdapter != null) { - mAdapter.requestHighlight(getView(), getListView()); + mAdapter.requestHighlight(getView(), getListView(), mAppBarLayout); } } diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java index e89e79365f5..b3ae6096b78 100644 --- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java +++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java @@ -39,10 +39,14 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; +import com.google.android.material.appbar.AppBarLayout; + public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter { private static final String TAG = "HighlightableAdapter"; @VisibleForTesting + static final long DELAY_COLLAPSE_DURATION_MILLIS = 300L; + @VisibleForTesting static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L; private static final long HIGHLIGHT_DURATION = 15000L; private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L; @@ -115,7 +119,7 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter View v = holder.itemView; if (position == mHighlightPosition && (mHighlightKey != null - && TextUtils.equals(mHighlightKey, getItem(position).getKey()))) { + && TextUtils.equals(mHighlightKey, getItem(position).getKey()))) { // This position should be highlighted. If it's highlighted before - skip animation. addHighlightBackground(v, !mFadeInAnimated); } else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) { @@ -124,15 +128,26 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter } } - public void requestHighlight(View root, RecyclerView recyclerView) { + /** + * A function can highlight a specific setting in recycler view. + * note: Before highlighting a setting, screen collapses tool bar with an animation. + */ + public void requestHighlight(View root, RecyclerView recyclerView, AppBarLayout appBarLayout) { if (mHighlightRequested || recyclerView == null || TextUtils.isEmpty(mHighlightKey)) { return; } + final int position = getPreferenceAdapterPosition(mHighlightKey); + if (position < 0) { + return; + } + + if (appBarLayout != null) { + root.postDelayed(() -> { + appBarLayout.setExpanded(false, true); + }, DELAY_COLLAPSE_DURATION_MILLIS); + } + root.postDelayed(() -> { - final int position = getPreferenceAdapterPosition(mHighlightKey); - if (position < 0) { - return; - } mHighlightRequested = true; recyclerView.smoothScrollToPosition(position); mHighlightPosition = position; diff --git a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java index 23b7edcd107..f5e2a50c6fc 100644 --- a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java +++ b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -43,6 +44,8 @@ import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; +import com.google.android.material.appbar.AppBarLayout; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -85,8 +88,11 @@ public class HighlightablePreferenceGroupAdapterTest { @Test public void requestHighlight_hasKey_notHighlightedBefore_shouldRequest() { - mAdapter.requestHighlight(mRoot, mock(RecyclerView.class)); + when(mAdapter.getPreferenceAdapterPosition(anyString())).thenReturn(1); + mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class)); + verify(mRoot).postDelayed(any(), + eq(HighlightablePreferenceGroupAdapter.DELAY_COLLAPSE_DURATION_MILLIS)); verify(mRoot).postDelayed(any(), eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS)); } @@ -95,21 +101,21 @@ public class HighlightablePreferenceGroupAdapterTest { public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() { ReflectionHelpers.setField(mAdapter, "mHighlightKey", null); ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false); - mAdapter.requestHighlight(mRoot, mock(RecyclerView.class)); + mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class)); ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY); ReflectionHelpers.setField(mAdapter, "mHighlightRequested", true); - mAdapter.requestHighlight(mRoot, mock(RecyclerView.class)); + mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class)); ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY); ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false); - mAdapter.requestHighlight(mRoot, null /* recyclerView */); + mAdapter.requestHighlight(mRoot, null /* recyclerView */, mock(AppBarLayout.class)); verifyZeroInteractions(mRoot); } @Test - public void adjustInitialExpandedChildCount_invalidInput_shouldNotadjust() { + public void adjustInitialExpandedChildCount_invalidInput_shouldNotAdjust() { HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(null /* host */); HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); final Bundle args = new Bundle();