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.eq; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.verifyZeroInteractions; 28 import static org.mockito.Mockito.when; 29 30 import android.content.Context; 31 import android.graphics.drawable.ColorDrawable; 32 import android.os.Bundle; 33 import android.view.View; 34 35 import androidx.preference.PreferenceCategory; 36 import androidx.preference.PreferenceScreen; 37 import androidx.preference.PreferenceViewHolder; 38 import androidx.recyclerview.widget.RecyclerView; 39 40 import com.android.settings.R; 41 import com.android.settings.SettingsActivity; 42 import com.android.settings.SettingsPreferenceFragment; 43 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.mockito.Mock; 48 import org.mockito.MockitoAnnotations; 49 import org.robolectric.RobolectricTestRunner; 50 import org.robolectric.RuntimeEnvironment; 51 import org.robolectric.util.ReflectionHelpers; 52 53 @RunWith(RobolectricTestRunner.class) 54 public class HighlightablePreferenceGroupAdapterTest { 55 56 private static final String TEST_KEY = "key"; 57 58 @Mock 59 private View mRoot; 60 @Mock 61 private PreferenceCategory mPreferenceCatetory; 62 @Mock 63 private SettingsPreferenceFragment mFragment; 64 65 private Context mContext; 66 private HighlightablePreferenceGroupAdapter mAdapter; 67 private PreferenceViewHolder mViewHolder; 68 69 @Before setUp()70 public void setUp() { 71 MockitoAnnotations.initMocks(this); 72 mContext = RuntimeEnvironment.application; 73 when(mPreferenceCatetory.getContext()).thenReturn(mContext); 74 mAdapter = spy(new HighlightablePreferenceGroupAdapter(mPreferenceCatetory, TEST_KEY, 75 false /* highlighted*/)); 76 mViewHolder = PreferenceViewHolder.createInstanceForTests( 77 View.inflate(mContext, R.layout.app_preference_item, null)); 78 } 79 80 @Test requestHighlight_hasKey_notHighlightedBefore_shouldRequest()81 public void requestHighlight_hasKey_notHighlightedBefore_shouldRequest() { 82 mAdapter.requestHighlight(mRoot, mock(RecyclerView.class)); 83 84 verify(mRoot).postDelayed(any(), 85 eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS)); 86 } 87 88 @Test requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest()89 public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() { 90 ReflectionHelpers.setField(mAdapter, "mHighlightKey", null); 91 ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false); 92 mAdapter.requestHighlight(mRoot, mock(RecyclerView.class)); 93 94 ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY); 95 ReflectionHelpers.setField(mAdapter, "mHighlightRequested", true); 96 mAdapter.requestHighlight(mRoot, mock(RecyclerView.class)); 97 98 ReflectionHelpers.setField(mAdapter, "mHighlightKey", TEST_KEY); 99 ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false); 100 mAdapter.requestHighlight(mRoot, null /* recyclerView */); 101 102 verifyZeroInteractions(mRoot); 103 } 104 105 @Test adjustInitialExpandedChildCount_invalidInput_shouldNotadjust()106 public void adjustInitialExpandedChildCount_invalidInput_shouldNotadjust() { 107 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(null /* host */); 108 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 109 final Bundle args = new Bundle(); 110 when(mFragment.getArguments()).thenReturn(args); 111 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 112 final PreferenceScreen screen = mock(PreferenceScreen.class); 113 when(mFragment.getArguments()).thenReturn(null); 114 when(mFragment.getPreferenceScreen()).thenReturn(screen); 115 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 116 verifyZeroInteractions(screen); 117 } 118 119 @Test adjustInitialExpandedChildCount_hasHightlightKey_shouldExpandAllChildren()120 public void adjustInitialExpandedChildCount_hasHightlightKey_shouldExpandAllChildren() { 121 final Bundle args = new Bundle(); 122 when(mFragment.getArguments()).thenReturn(args); 123 args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, "testkey"); 124 final PreferenceScreen screen = mock(PreferenceScreen.class); 125 when(mFragment.getPreferenceScreen()).thenReturn(screen); 126 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 127 128 verify(screen).setInitialExpandedChildrenCount(Integer.MAX_VALUE); 129 } 130 131 @Test adjustInitialExpandedChildCount_noKeyOrChildCountOverride_shouldDoNothing()132 public void adjustInitialExpandedChildCount_noKeyOrChildCountOverride_shouldDoNothing() { 133 final Bundle args = new Bundle(); 134 when(mFragment.getArguments()).thenReturn(args); 135 when(mFragment.getInitialExpandedChildCount()).thenReturn(-1); 136 final PreferenceScreen screen = mock(PreferenceScreen.class); 137 when(mFragment.getPreferenceScreen()).thenReturn(screen); 138 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 139 140 verify(mFragment).getInitialExpandedChildCount(); 141 verifyZeroInteractions(screen); 142 } 143 144 @Test adjustInitialExpandedChildCount_hasCountOverride_shouldDoNothing()145 public void adjustInitialExpandedChildCount_hasCountOverride_shouldDoNothing() { 146 when(mFragment.getInitialExpandedChildCount()).thenReturn(10); 147 final PreferenceScreen screen = mock(PreferenceScreen.class); 148 when(mFragment.getPreferenceScreen()).thenReturn(screen); 149 HighlightablePreferenceGroupAdapter.adjustInitialExpandedChildCount(mFragment); 150 151 verify(mFragment).getInitialExpandedChildCount(); 152 153 verify(screen).setInitialExpandedChildrenCount(10); 154 } 155 156 @Test updateBackground_notHighlightedRow_shouldNotSetHighlightedTag()157 public void updateBackground_notHighlightedRow_shouldNotSetHighlightedTag() { 158 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 159 160 mAdapter.updateBackground(mViewHolder, 0); 161 162 assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull(); 163 } 164 165 @Test updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag()166 public void updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag() { 167 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 168 assertThat(mAdapter.mFadeInAnimated).isFalse(); 169 170 mAdapter.updateBackground(mViewHolder, 10); 171 172 assertThat(mAdapter.mFadeInAnimated).isTrue(); 173 assertThat(mViewHolder.itemView.getBackground()).isInstanceOf(ColorDrawable.class); 174 assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isEqualTo(true); 175 verify(mAdapter).requestRemoveHighlightDelayed(mViewHolder.itemView); 176 } 177 178 @Test updateBackgroundTwice_highlight_shouldAnimateOnce()179 public void updateBackgroundTwice_highlight_shouldAnimateOnce() { 180 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 181 ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView)); 182 assertThat(mAdapter.mFadeInAnimated).isFalse(); 183 mAdapter.updateBackground(mViewHolder, 10); 184 // mFadeInAnimated change from false to true - indicating background change is scheduled 185 // through animation. 186 assertThat(mAdapter.mFadeInAnimated).isTrue(); 187 // remove highlight should be requested. 188 verify(mAdapter).requestRemoveHighlightDelayed(mViewHolder.itemView); 189 190 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 191 mAdapter.updateBackground(mViewHolder, 10); 192 // only sets background color once - if it's animation this would be called many times 193 verify(mViewHolder.itemView).setBackgroundColor(mAdapter.mHighlightColor); 194 // remove highlight should be requested. 195 verify(mAdapter, times(2)).requestRemoveHighlightDelayed(mViewHolder.itemView); 196 } 197 198 @Test updateBackground_reuseHightlightedRowForNormalRow_shouldResetBackgroundAndTag()199 public void updateBackground_reuseHightlightedRowForNormalRow_shouldResetBackgroundAndTag() { 200 ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10); 201 mViewHolder.itemView.setTag(R.id.preference_highlighted, true); 202 203 mAdapter.updateBackground(mViewHolder, 0); 204 205 assertThat(mViewHolder.itemView.getBackground()).isNotInstanceOf(ColorDrawable.class); 206 assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isEqualTo(false); 207 } 208 } 209