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
This commit is contained in:
@@ -57,6 +57,8 @@ import com.android.settingslib.core.instrumentation.Instrumentable;
|
|||||||
import com.android.settingslib.search.Indexable;
|
import com.android.settingslib.search.Indexable;
|
||||||
import com.android.settingslib.widget.LayoutPreference;
|
import com.android.settingslib.widget.LayoutPreference;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,9 +114,8 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
ViewGroup mPinnedHeaderFrameLayout;
|
ViewGroup mPinnedHeaderFrameLayout;
|
||||||
|
private AppBarLayout mAppBarLayout;
|
||||||
private LayoutPreference mHeader;
|
private LayoutPreference mHeader;
|
||||||
|
|
||||||
private View mEmptyView;
|
private View mEmptyView;
|
||||||
private LinearLayoutManager mLayoutManager;
|
private LinearLayoutManager mLayoutManager;
|
||||||
private ArrayMap<String, Preference> mPreferenceCache;
|
private ArrayMap<String, Preference> mPreferenceCache;
|
||||||
@@ -145,6 +146,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
final View root = super.onCreateView(inflater, container, savedInstanceState);
|
final View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
mPinnedHeaderFrameLayout = root.findViewById(R.id.pinned_header);
|
mPinnedHeaderFrameLayout = root.findViewById(R.id.pinned_header);
|
||||||
|
mAppBarLayout = getActivity().findViewById(R.id.app_bar);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +252,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mAdapter != null) {
|
if (mAdapter != null) {
|
||||||
mAdapter.requestHighlight(getView(), getListView());
|
mAdapter.requestHighlight(getView(), getListView(), mAppBarLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,10 +39,14 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
|
||||||
public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
|
public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
|
||||||
|
|
||||||
private static final String TAG = "HighlightableAdapter";
|
private static final String TAG = "HighlightableAdapter";
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
static final long DELAY_COLLAPSE_DURATION_MILLIS = 300L;
|
||||||
|
@VisibleForTesting
|
||||||
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L;
|
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L;
|
||||||
private static final long HIGHLIGHT_DURATION = 15000L;
|
private static final long HIGHLIGHT_DURATION = 15000L;
|
||||||
private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
|
private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
|
||||||
@@ -115,7 +119,7 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter
|
|||||||
View v = holder.itemView;
|
View v = holder.itemView;
|
||||||
if (position == mHighlightPosition
|
if (position == mHighlightPosition
|
||||||
&& (mHighlightKey != null
|
&& (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.
|
// This position should be highlighted. If it's highlighted before - skip animation.
|
||||||
addHighlightBackground(v, !mFadeInAnimated);
|
addHighlightBackground(v, !mFadeInAnimated);
|
||||||
} else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
|
} 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)) {
|
if (mHighlightRequested || recyclerView == null || TextUtils.isEmpty(mHighlightKey)) {
|
||||||
return;
|
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(() -> {
|
root.postDelayed(() -> {
|
||||||
final int position = getPreferenceAdapterPosition(mHighlightKey);
|
|
||||||
if (position < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mHighlightRequested = true;
|
mHighlightRequested = true;
|
||||||
recyclerView.smoothScrollToPosition(position);
|
recyclerView.smoothScrollToPosition(position);
|
||||||
mHighlightPosition = position;
|
mHighlightPosition = position;
|
||||||
|
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
@@ -43,6 +44,8 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.SettingsActivity;
|
import com.android.settings.SettingsActivity;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
|
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -85,8 +88,11 @@ public class HighlightablePreferenceGroupAdapterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestHighlight_hasKey_notHighlightedBefore_shouldRequest() {
|
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(),
|
verify(mRoot).postDelayed(any(),
|
||||||
eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS));
|
eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS));
|
||||||
}
|
}
|
||||||
@@ -95,21 +101,21 @@ public class HighlightablePreferenceGroupAdapterTest {
|
|||||||
public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() {
|
public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() {
|
||||||
ReflectionHelpers.setField(mAdapter, "mHighlightKey", null);
|
ReflectionHelpers.setField(mAdapter, "mHighlightKey", null);
|
||||||
ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
|
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, "mHighlightKey", TEST_KEY);
|
||||||
ReflectionHelpers.setField(mAdapter, "mHighlightRequested", true);
|
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, "mHighlightKey", TEST_KEY);
|
||||||
ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
|
ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
|
||||||
mAdapter.requestHighlight(mRoot, null /* recyclerView */);
|
mAdapter.requestHighlight(mRoot, null /* recyclerView */, mock(AppBarLayout.class));
|
||||||
|
|
||||||
verifyZeroInteractions(mRoot);
|
verifyZeroInteractions(mRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void adjustInitialExpandedChildCount_invalidInput_shouldNotadjust() {
|
public void adjustInitialExpandedChildCount_invalidInput_shouldNotAdjust() {
|
||||||
HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(null /* host */);
|
HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(null /* host */);
|
||||||
HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment);
|
HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment);
|
||||||
final Bundle args = new Bundle();
|
final Bundle args = new Bundle();
|
||||||
|
Reference in New Issue
Block a user