1 /* 2 * Copyright (C) 2016 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.eq; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.never; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.ActivityInfo; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.ResolveInfo; 34 import android.os.UserHandle; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.widget.ImageButton; 38 import android.widget.TextView; 39 40 import androidx.fragment.app.Fragment; 41 import androidx.fragment.app.FragmentActivity; 42 import androidx.preference.Preference; 43 44 import com.android.settings.R; 45 import com.android.settingslib.widget.LayoutPreference; 46 47 import org.junit.Before; 48 import org.junit.Ignore; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.Answers; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 import org.robolectric.RobolectricTestRunner; 55 import org.robolectric.RuntimeEnvironment; 56 import org.robolectric.annotation.Config; 57 58 @RunWith(RobolectricTestRunner.class) 59 @Config(shadows = { 60 com.android.settings.testutils.shadow.ShadowFragment.class, 61 }) 62 public class EntityHeaderControllerTest { 63 64 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 65 private Context mContext; 66 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 67 private FragmentActivity mActivity; 68 @Mock 69 private Fragment mFragment; 70 71 private Context mShadowContext; 72 private LayoutInflater mLayoutInflater; 73 private PackageInfo mInfo; 74 private EntityHeaderController mController; 75 76 @Before setUp()77 public void setUp() { 78 MockitoAnnotations.initMocks(this); 79 mShadowContext = RuntimeEnvironment.application; 80 when(mActivity.getApplicationContext()).thenReturn(mShadowContext); 81 when(mContext.getApplicationContext()).thenReturn(mContext); 82 when(mFragment.getContext()).thenReturn(mShadowContext); 83 mLayoutInflater = LayoutInflater.from(mShadowContext); 84 mInfo = new PackageInfo(); 85 mInfo.versionName = "1234"; 86 } 87 88 @Test testBuildView_constructedWithoutView_shouldCreateNewView()89 public void testBuildView_constructedWithoutView_shouldCreateNewView() { 90 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 91 View view = mController.done(); 92 93 assertThat(view).isNotNull(); 94 } 95 96 @Test testBuildView_withContext_shouldBuildPreferenceAllowedBelowDivider()97 public void testBuildView_withContext_shouldBuildPreferenceAllowedBelowDivider() { 98 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 99 Preference preference = mController.done(mShadowContext); 100 101 assertThat(preference instanceof LayoutPreference).isTrue(); 102 assertThat(((LayoutPreference)preference).isAllowDividerBelow()).isTrue(); 103 } 104 105 @Test testBuildView_constructedWithView_shouldReturnSameView()106 public void testBuildView_constructedWithView_shouldReturnSameView() { 107 View inputView = mLayoutInflater.inflate( 108 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 109 mController = EntityHeaderController.newInstance(mActivity, mFragment, inputView); 110 View view = mController.done(); 111 112 assertThat(view).isSameInstanceAs(inputView); 113 } 114 115 @Test bindViews_shouldBindAllData()116 public void bindViews_shouldBindAllData() { 117 final String testString = "test"; 118 final View header = mLayoutInflater.inflate( 119 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 120 final TextView label = header.findViewById(R.id.entity_header_title); 121 final TextView summary = header.findViewById(R.id.entity_header_summary); 122 final TextView secondSummary = header.findViewById( 123 com.android.settingslib.widget.preference.layout.R.id.entity_header_second_summary); 124 125 mController = EntityHeaderController.newInstance(mActivity, mFragment, header); 126 mController.setLabel(testString); 127 mController.setSummary(testString); 128 mController.setSecondSummary(testString); 129 mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add_24dp)); 130 mController.done(); 131 132 assertThat(label).isNotNull(); 133 assertThat(label.getText()).isEqualTo(testString); 134 assertThat(summary).isNotNull(); 135 assertThat(summary.getText()).isEqualTo(testString); 136 assertThat(secondSummary).isNotNull(); 137 assertThat(secondSummary.getText()).isEqualTo(testString); 138 } 139 140 @Test bindButton_hasEditClickListener_shouldShowButton()141 public void bindButton_hasEditClickListener_shouldShowButton() { 142 final ResolveInfo info = new ResolveInfo(); 143 info.activityInfo = new ActivityInfo(); 144 info.activityInfo.packageName = "123"; 145 info.activityInfo.name = "321"; 146 final View view = mLayoutInflater.inflate( 147 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 148 when(mActivity.getApplicationContext()).thenReturn(mContext); 149 150 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 151 mController.setEditListener(v -> {/* do nothing */}); 152 mController.setButtonActions( 153 EntityHeaderController.ActionType.ACTION_EDIT_PREFERENCE, 154 EntityHeaderController.ActionType.ACTION_NONE); 155 mController.done(); 156 157 final ImageButton button1 = view.findViewById(android.R.id.button1); 158 assertThat(button1).isNotNull(); 159 assertThat(button1.getVisibility()).isEqualTo(View.VISIBLE); 160 assertThat(button1.getDrawable()).isNotNull(); 161 assertThat(view.findViewById(android.R.id.button2).getVisibility()).isEqualTo(View.GONE); 162 } 163 164 @Test bindButton_noEditClickListener_shouldNotShowButton()165 public void bindButton_noEditClickListener_shouldNotShowButton() { 166 final ResolveInfo info = new ResolveInfo(); 167 info.activityInfo = new ActivityInfo(); 168 info.activityInfo.packageName = "123"; 169 info.activityInfo.name = "321"; 170 final View view = mLayoutInflater.inflate( 171 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 172 when(mActivity.getApplicationContext()).thenReturn(mContext); 173 174 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 175 mController.setButtonActions( 176 EntityHeaderController.ActionType.ACTION_EDIT_PREFERENCE, 177 EntityHeaderController.ActionType.ACTION_NONE); 178 mController.done(); 179 180 assertThat(view.findViewById(android.R.id.button1).getVisibility()).isEqualTo(View.GONE); 181 assertThat(view.findViewById(android.R.id.button2).getVisibility()).isEqualTo(View.GONE); 182 } 183 184 185 @Test bindButton_noAppInfo_shouldNotAttachClickListener()186 public void bindButton_noAppInfo_shouldNotAttachClickListener() { 187 final View appLinks = mLayoutInflater.inflate( 188 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 189 final FragmentActivity activity = mock(FragmentActivity.class); 190 when(mFragment.getActivity()).thenReturn(activity); 191 192 mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks); 193 mController.setPackageName(null) 194 .setHasAppInfoLink(true) 195 .setButtonActions( 196 EntityHeaderController.ActionType.ACTION_NONE, 197 EntityHeaderController.ActionType.ACTION_NONE); 198 mController.done(); 199 200 assertThat(appLinks.findViewById(android.R.id.button1).getVisibility()) 201 .isEqualTo(View.GONE); 202 assertThat(appLinks.findViewById(android.R.id.button2).getVisibility()) 203 .isEqualTo(View.GONE); 204 205 appLinks.findViewById(com.android.settingslib.widget.preference.layout.R.id.entity_header_content) 206 .performClick(); 207 verify(mFragment, never()).getActivity(); 208 verify(activity, never()).startActivity(any(Intent.class)); 209 } 210 211 @Ignore("b/313616350") 212 @Test bindButton_hasAppInfo_shouldAttachClickListener()213 public void bindButton_hasAppInfo_shouldAttachClickListener() { 214 final View appLinks = mLayoutInflater.inflate( 215 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 216 final FragmentActivity activity = mock(FragmentActivity.class); 217 when(mFragment.getActivity()).thenReturn(activity); 218 when(mContext.getString(eq(R.string.application_info_label))).thenReturn("App Info"); 219 220 mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks); 221 mController.setPackageName("123") 222 .setUid(123321) 223 .setHasAppInfoLink(true) 224 .setButtonActions( 225 EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE, 226 EntityHeaderController.ActionType.ACTION_NONE); 227 mController.done(); 228 229 appLinks.findViewById(com.android.settingslib.widget.preference.layout.R.id.entity_header_content) 230 .performClick(); 231 verify(activity) 232 .startActivityForResultAsUser(any(Intent.class), anyInt(), any(UserHandle.class)); 233 } 234 235 @Test iconContentDescription_shouldWorkWithSetIcon()236 public void iconContentDescription_shouldWorkWithSetIcon() { 237 final View view = mLayoutInflater.inflate( 238 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 239 when(mFragment.getActivity()).thenReturn(mock(FragmentActivity.class)); 240 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 241 String description = "Fake Description"; 242 mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add_24dp)); 243 mController.setIconContentDescription(description); 244 mController.done(); 245 assertThat(view.findViewById(R.id.entity_header_icon).getContentDescription().toString()) 246 .isEqualTo(description); 247 } 248 249 @Test iconContentDescription_shouldWorkWithoutSetIcon()250 public void iconContentDescription_shouldWorkWithoutSetIcon() { 251 final View view = mLayoutInflater.inflate( 252 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 253 when(mFragment.getActivity()).thenReturn(mock(FragmentActivity.class)); 254 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 255 String description = "Fake Description"; 256 mController.setIconContentDescription(description); 257 mController.done(); 258 assertThat(view.findViewById(R.id.entity_header_icon).getContentDescription().toString()) 259 .isEqualTo(description); 260 } 261 262 @Test bindButton_hasAppNotifIntent_shouldShowButton()263 public void bindButton_hasAppNotifIntent_shouldShowButton() { 264 final View appLinks = mLayoutInflater.inflate( 265 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 266 267 mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks); 268 mController.setAppNotifPrefIntent(new Intent()) 269 .setButtonActions( 270 EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE, 271 EntityHeaderController.ActionType.ACTION_NONE); 272 mController.done(); 273 274 assertThat(appLinks.findViewById(android.R.id.button1).getVisibility()) 275 .isEqualTo(View.VISIBLE); 276 assertThat(appLinks.findViewById(android.R.id.button2).getVisibility()) 277 .isEqualTo(View.GONE); 278 } 279 280 // Ensure that the instant app label does not show up when we haven't told the controller the 281 // app is instant. 282 @Test instantApps_normalAppsDontGetLabel()283 public void instantApps_normalAppsDontGetLabel() { 284 final View header = mLayoutInflater.inflate( 285 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 286 mController = EntityHeaderController.newInstance(mActivity, mFragment, header); 287 mController.done(); 288 289 View installType = header.findViewById(com.android.settingslib.widget.preference.layout.R.id.install_type); 290 assertThat(installType.getVisibility()).isEqualTo(View.GONE); 291 } 292 293 // Test that the "instant apps" label is present in the header when we have an instant app. 294 @Test instantApps_expectedHeaderItem()295 public void instantApps_expectedHeaderItem() { 296 final View header = mLayoutInflater.inflate( 297 com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header, null /* root */); 298 mController = EntityHeaderController.newInstance(mActivity, mFragment, header); 299 mController.setIsInstantApp(true); 300 mController.done(); 301 TextView label = header.findViewById(com.android.settingslib.widget.preference.layout.R.id.install_type); 302 303 assertThat(label.getVisibility()).isEqualTo(View.VISIBLE); 304 assertThat(label.getText()).isEqualTo( 305 header.getResources().getString(R.string.install_type_instant)); 306 assertThat(header.findViewById(R.id.entity_header_summary).getVisibility()) 307 .isEqualTo(View.GONE); 308 } 309 310 @Test initAppHeaderController_appHeaderNull_useFragmentContext()311 public void initAppHeaderController_appHeaderNull_useFragmentContext() { 312 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 313 314 // Fragment.getContext() is invoked to inflate the view 315 verify(mFragment).getContext(); 316 } 317 } 318