1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.widget; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.times; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.verifyZeroInteractions; 30 import static org.mockito.Mockito.when; 31 32 import android.content.Context; 33 import android.graphics.drawable.ColorDrawable; 34 import android.os.Bundle; 35 import android.view.View; 36 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceCategory; 39 import androidx.preference.PreferenceScreen; 40 import androidx.preference.PreferenceViewHolder; 41 import androidx.recyclerview.widget.RecyclerView; 42 43 import com.android.settings.R; 44 import com.android.settings.SettingsActivity; 45 import com.android.settings.SettingsPreferenceFragment; 46 47 import com.google.android.material.appbar.AppBarLayout; 48 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 import org.robolectric.RobolectricTestRunner; 55 import org.robolectric.RuntimeEnvironment; 56 import org.robolectric.util.ReflectionHelpers; 57 58 @RunWith(RobolectricTestRunner.class) 59 public class HighlightablePreferenceGroupAdapterTest { 60 61 private static final String TEST_KEY = "key"; 62 63 @Mock 64 private View mRoot; 65 @Mock 66 private PreferenceCategory mPreferenceCatetory; 67 @Mock 68 private SettingsPreferenceFragment mFragment; 69 70 private Context mContext; 71 private HighlightablePreferenceGroupAdapter mAdapter; 72 private PreferenceViewHolder mViewHolder; 73 private Preference mPreference; 74 75 @Before setUp()76 public void setUp() { 77 MockitoAnnotations.initMocks(this); 78 mContext = RuntimeEnvironment.application; 79 mPreference = new Preference(mContext); 80 mPreference.setKey(TEST_KEY); 81 when(mPreferenceCatetory.getContext()).thenReturn(mContext); 82 mAdapter = spy(new HighlightablePreferenceGroupAdapter(mPreferenceCatetory, TEST_KEY, 83 false /* highlighted*/)); 84 when(mAdapter.getItem(anyInt())).thenReturn(mPreference); 85 mViewHolder = PreferenceViewHolder.createInstanceForTests( 86 View.inflate(mContext, R.layout.app_preference_item, null)); 87 } 88 89 @Test requestHighlight_hasKey_notHighlightedBefore_shouldRequest()90 public void requestHighlight_hasKey_notHighlightedBefore_shouldRequest() { 91 when(mAdapter.getPreferenceAdapterPosition(anyString())).thenReturn(1); 92 mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class)); 93 94 verify(mRoot).postDelayed(any(), 95 eq(HighlightablePreferenceGroupAdapter.DELAY_COLLAPSE_DURATION_MILLIS)); 96 verify(mRoot).postDelayed(any(), 97 eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS)); 98 } 99 100 @Test requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest()101 public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() { 102 ReflectionHelpers.setField(mAdapter, "mHighlightKey", null); 103 ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false); 104 mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class)); 105 106 ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY); 107 ReflectionHelpers.setField(mAdapter, "mHighlightRequested", true); 108 mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class)); 109 110 ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY); 111 ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false); 112 mAdapter.requestHighlight(mRoot, null /* recyclerView */, mock(AppBarLayout.class)); 113 114 verifyZeroInteractions(mRoot); 115 } 116 117 @Test adjustInitialExpandedChildCount_invalidInput_shouldNotAdjust()118 public void adjustInitialExpandedChildCount_invalidInput_shouldNotAdjust() { 119 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(null /* host */); 120 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 121 final Bundle args = new Bundle(); 122 when(mFragment.getArguments()).thenReturn(args); 123 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 124 final PreferenceScreen screen = mock(PreferenceScreen.class); 125 when(mFragment.getArguments()).thenReturn(null); 126 when(mFragment.getPreferenceScreen()).thenReturn(screen); 127 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 128 verifyZeroInteractions(screen); 129 } 130 131 @Test adjustInitialExpandedChildCount_hasHightlightKey_shouldExpandAllChildren()132 public void adjustInitialExpandedChildCount_hasHightlightKey_shouldExpandAllChildren() { 133 final Bundle args = new Bundle(); 134 when(mFragment.getArguments()).thenReturn(args); 135 args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, "testkey"); 136 final PreferenceScreen screen = mock(PreferenceScreen.class); 137 when(mFragment.getPreferenceScreen()).thenReturn(screen); 138 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 139 140 verify(screen).setInitialExpandedChildrenCount(Integer.MAX_VALUE); 141 } 142 143 @Test adjustInitialExpandedChildCount_noKeyOrChildCountOverride_shouldDoNothing()144 public void adjustInitialExpandedChildCount_noKeyOrChildCountOverride_shouldDoNothing() { 145 final Bundle args = new Bundle(); 146 when(mFragment.getArguments()).thenReturn(args); 147 when(mFragment.getInitialExpandedChildCount()).thenReturn(-1); 148 final PreferenceScreen screen = mock(PreferenceScreen.class); 149 when(mFragment.getPreferenceScreen()).thenReturn(screen); 150 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 151 152 verify(mFragment).getInitialExpandedChildCount(); 153 verifyZeroInteractions(screen); 154 } 155 156 @Test adjustInitialExpandedChildCount_hasCountOverride_shouldDoNothing()157 public void adjustInitialExpandedChildCount_hasCountOverride_shouldDoNothing() { 158 when(mFragment.getInitialExpandedChildCount()).thenReturn(10); 159 final PreferenceScreen screen = mock(PreferenceScreen.class); 160 when(mFragment.getPreferenceScreen()).thenReturn(screen); 161 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 162 163 verify(mFragment).getInitialExpandedChildCount(); 164 165 verify(screen).setInitialExpandedChildrenCount(10); 166 } 167 168 @Test updateBackground_notHighlightedRow_shouldNotSetHighlightedTag()169 public void updateBackground_notHighlightedRow_shouldNotSetHighlightedTag() { 170 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 171 172 mAdapter.updateBackground(mViewHolder, 0); 173 174 assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull(); 175 } 176 177 @Test updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag()178 public void updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag() { 179 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 180 assertThat(mAdapter.mFadeInAnimated).isFalse(); 181 182 mAdapter.updateBackground(mViewHolder, 10); 183 184 assertThat(mAdapter.mFadeInAnimated).isTrue(); 185 assertThat(mViewHolder.itemView.getBackground()).isInstanceOf(ColorDrawable.class); 186 assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isEqualTo(true); 187 verify(mAdapter).requestRemoveHighlightDelayed(mViewHolder); 188 } 189 190 @Test updateBackgroundTwice_highlight_shouldAnimateOnce()191 public void updateBackgroundTwice_highlight_shouldAnimateOnce() { 192 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 193 ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView)); 194 assertThat(mAdapter.mFadeInAnimated).isFalse(); 195 mAdapter.updateBackground(mViewHolder, 10); 196 // mFadeInAnimated change from false to true - indicating background change is scheduled 197 // through animation. 198 assertThat(mAdapter.mFadeInAnimated).isTrue(); 199 // remove highlight should be requested. 200 verify(mAdapter).requestRemoveHighlightDelayed(mViewHolder); 201 202 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 203 mAdapter.updateBackground(mViewHolder, 10); 204 // only sets background color once - if it's animation this would be called many times 205 verify(mViewHolder.itemView).setBackgroundColor(mAdapter.mHighlightColor); 206 // remove highlight should be requested. 207 verify(mAdapter, times(2)).requestRemoveHighlightDelayed(mViewHolder); 208 } 209 210 @Test updateBackground_reuseHightlightedRowForNormalRow_shouldResetBackgroundAndTag()211 public void updateBackground_reuseHightlightedRowForNormalRow_shouldResetBackgroundAndTag() { 212 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 213 mViewHolder.itemView.setTag(R.id.preference_highlighted, true); 214 215 mAdapter.updateBackground(mViewHolder, 0); 216 217 assertThat(mViewHolder.itemView.getBackground()).isNotInstanceOf(ColorDrawable.class); 218 assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isEqualTo(false); 219 } 220 } 221