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