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 import static org.mockito.Matchers.any; 21 import static org.mockito.Matchers.anyInt; 22 import static org.mockito.Matchers.eq; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.app.ActionBar; 29 import android.app.Activity; 30 import android.app.Fragment; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.pm.ActivityInfo; 34 import android.content.pm.ApplicationInfo; 35 import android.content.pm.PackageInfo; 36 import android.content.pm.ResolveInfo; 37 import android.graphics.drawable.ColorDrawable; 38 import android.os.UserHandle; 39 import android.support.v7.preference.Preference; 40 import android.view.LayoutInflater; 41 import android.view.View; 42 import android.widget.ImageButton; 43 import android.widget.ImageView; 44 import android.widget.TextView; 45 46 import com.android.settings.R; 47 import com.android.settings.applications.LayoutPreference; 48 import com.android.settings.testutils.SettingsRobolectricTestRunner; 49 import com.android.settings.testutils.ShadowIconDrawableFactory; 50 import com.android.settingslib.applications.ApplicationsState; 51 52 import org.junit.Before; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.mockito.Answers; 56 import org.mockito.Mock; 57 import org.mockito.MockitoAnnotations; 58 import org.robolectric.RuntimeEnvironment; 59 import org.robolectric.annotation.Config; 60 61 @RunWith(SettingsRobolectricTestRunner.class) 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 Activity 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(mActivity); 92 93 assertThat(view).isNotNull(); 94 } 95 96 @Test testBuildView_withContext_shouldBuildPreference()97 public void testBuildView_withContext_shouldBuildPreference() { 98 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 99 Preference preference = mController.done(mActivity, mShadowContext); 100 101 assertThat(preference instanceof LayoutPreference).isTrue(); 102 } 103 104 @Test testBuildView_constructedWithView_shouldReturnSameView()105 public void testBuildView_constructedWithView_shouldReturnSameView() { 106 View inputView = mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */); 107 mController = EntityHeaderController.newInstance(mActivity, mFragment, inputView); 108 View view = mController.done(mActivity); 109 110 assertThat(view).isSameAs(inputView); 111 } 112 113 @Test bindViews_shouldBindAllData()114 public void bindViews_shouldBindAllData() { 115 final String testString = "test"; 116 final View header = 117 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */); 118 final TextView label = header.findViewById(R.id.entity_header_title); 119 final TextView summary = header.findViewById(R.id.entity_header_summary); 120 final TextView secondSummary = header.findViewById(R.id.entity_header_second_summary); 121 122 mController = EntityHeaderController.newInstance(mActivity, mFragment, header); 123 mController.setLabel(testString); 124 mController.setSummary(testString); 125 mController.setSecondSummary(testString); 126 mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add)); 127 mController.done(mActivity); 128 129 assertThat(label).isNotNull(); 130 assertThat(label.getText()).isEqualTo(testString); 131 assertThat(summary).isNotNull(); 132 assertThat(summary.getText()).isEqualTo(testString); 133 assertThat(secondSummary).isNotNull(); 134 assertThat(secondSummary.getText()).isEqualTo(testString); 135 } 136 137 @Test bindButton_hasEditRuleNameClickListener_shouldShowButton()138 public void bindButton_hasEditRuleNameClickListener_shouldShowButton() { 139 final ResolveInfo info = new ResolveInfo(); 140 info.activityInfo = new ActivityInfo(); 141 info.activityInfo.packageName = "123"; 142 info.activityInfo.name = "321"; 143 final View view = mLayoutInflater 144 .inflate(R.layout.settings_entity_header, null /* root */); 145 when(mActivity.getApplicationContext()).thenReturn(mContext); 146 147 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 148 mController.setEditZenRuleNameListener(new View.OnClickListener() { 149 public void onClick(View v) { 150 // do nothing 151 } 152 }); 153 mController.setButtonActions( 154 EntityHeaderController.ActionType.ACTION_DND_RULE_PREFERENCE, 155 EntityHeaderController.ActionType.ACTION_NONE); 156 mController.done(mActivity); 157 158 final ImageButton button1 = view.findViewById(android.R.id.button1); 159 assertThat(button1).isNotNull(); 160 assertThat(button1.getVisibility()).isEqualTo(View.VISIBLE); 161 assertThat(button1.getDrawable()).isNotNull(); 162 assertThat(view.findViewById(android.R.id.button2).getVisibility()).isEqualTo(View.GONE); 163 } 164 165 @Test bindButton_noEditRuleNameClickListener_shouldNotShowButton()166 public void bindButton_noEditRuleNameClickListener_shouldNotShowButton() { 167 final ResolveInfo info = new ResolveInfo(); 168 info.activityInfo = new ActivityInfo(); 169 info.activityInfo.packageName = "123"; 170 info.activityInfo.name = "321"; 171 final View view = mLayoutInflater.inflate(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_DND_RULE_PREFERENCE, 177 EntityHeaderController.ActionType.ACTION_NONE); 178 mController.done(mActivity); 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 = 188 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */); 189 final Activity activity = mock(Activity.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(mActivity); 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(R.id.entity_header_content).performClick(); 206 verify(mFragment, never()).getActivity(); 207 verify(activity, never()).startActivity(any(Intent.class)); 208 } 209 210 @Test bindButton_hasAppInfo_shouldAttachClickListener()211 public void bindButton_hasAppInfo_shouldAttachClickListener() { 212 final View appLinks = 213 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */); 214 final Activity activity = mock(Activity.class); 215 when(mFragment.getActivity()).thenReturn(activity); 216 when(mContext.getString(eq(R.string.application_info_label))).thenReturn("App Info"); 217 218 mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks); 219 mController.setPackageName("123") 220 .setUid(123321) 221 .setHasAppInfoLink(true) 222 .setButtonActions( 223 EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE, 224 EntityHeaderController.ActionType.ACTION_NONE); 225 mController.done(mActivity); 226 227 appLinks.findViewById(R.id.entity_header_content).performClick(); 228 verify(activity) 229 .startActivityForResultAsUser(any(Intent.class), anyInt(), any(UserHandle.class)); 230 } 231 232 @Test iconContentDescription_shouldWorkWithSetIcon()233 public void iconContentDescription_shouldWorkWithSetIcon() { 234 final View view = 235 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */); 236 when(mFragment.getActivity()).thenReturn(mock(Activity.class)); 237 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 238 String description = "Fake Description"; 239 mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add)); 240 mController.setIconContentDescription(description); 241 mController.done(mActivity); 242 assertThat(view.findViewById(R.id.entity_header_icon).getContentDescription().toString()) 243 .isEqualTo(description); 244 } 245 246 @Test iconContentDescription_shouldWorkWithoutSetIcon()247 public void iconContentDescription_shouldWorkWithoutSetIcon() { 248 final View view = mLayoutInflater 249 .inflate(R.layout.settings_entity_header, null /* root */); 250 when(mFragment.getActivity()).thenReturn(mock(Activity.class)); 251 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 252 String description = "Fake Description"; 253 mController.setIconContentDescription(description); 254 mController.done(mActivity); 255 assertThat(view.findViewById(R.id.entity_header_icon).getContentDescription().toString()) 256 .isEqualTo(description); 257 } 258 259 @Test 260 @Config(shadows = ShadowIconDrawableFactory.class) setIcon_usingAppEntry_shouldLoadIconFromDrawableFactory()261 public void setIcon_usingAppEntry_shouldLoadIconFromDrawableFactory() { 262 final View view = mLayoutInflater 263 .inflate(R.layout.settings_entity_header, null /* root */); 264 final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class); 265 entry.info = new ApplicationInfo(); 266 mController = EntityHeaderController.newInstance(mActivity, mFragment, view); 267 mController.setIcon(entry).done(mActivity); 268 final ImageView iconView = view.findViewById(R.id.entity_header_icon); 269 270 // Icon is set 271 assertThat(iconView.getDrawable()).isNotNull(); 272 // ... and entry.icon is still empty. This means the icon didn't come from cache. 273 assertThat(entry.icon).isNull(); 274 } 275 276 @Test bindButton_hasAppNotifIntent_shouldShowButton()277 public void bindButton_hasAppNotifIntent_shouldShowButton() { 278 final View appLinks = mLayoutInflater 279 .inflate(R.layout.settings_entity_header, null /* root */); 280 281 mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks); 282 mController.setAppNotifPrefIntent(new Intent()) 283 .setButtonActions( 284 EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE, 285 EntityHeaderController.ActionType.ACTION_NONE); 286 mController.done(mActivity); 287 288 assertThat(appLinks.findViewById(android.R.id.button1).getVisibility()) 289 .isEqualTo(View.VISIBLE); 290 assertThat(appLinks.findViewById(android.R.id.button2).getVisibility()) 291 .isEqualTo(View.GONE); 292 } 293 294 // Ensure that the instant app label does not show up when we haven't told the controller the 295 // app is instant. 296 @Test instantApps_normalAppsDontGetLabel()297 public void instantApps_normalAppsDontGetLabel() { 298 final View header = mLayoutInflater.inflate( 299 R.layout.settings_entity_header, null /* root */); 300 mController = EntityHeaderController.newInstance(mActivity, mFragment, header); 301 mController.done(mActivity); 302 303 assertThat(header.findViewById(R.id.install_type).getVisibility()) 304 .isEqualTo(View.GONE); 305 } 306 307 // Test that the "instant apps" label is present in the header when we have an instant app. 308 @Test instantApps_expectedHeaderItem()309 public void instantApps_expectedHeaderItem() { 310 final View header = mLayoutInflater.inflate( 311 R.layout.settings_entity_header, null /* root */); 312 mController = EntityHeaderController.newInstance(mActivity, mFragment, header); 313 mController.setIsInstantApp(true); 314 mController.done(mActivity); 315 TextView label = header.findViewById(R.id.install_type); 316 317 assertThat(label.getVisibility()).isEqualTo(View.VISIBLE); 318 assertThat(label.getText()).isEqualTo( 319 header.getResources().getString(R.string.install_type_instant)); 320 assertThat(header.findViewById(R.id.entity_header_summary).getVisibility()) 321 .isEqualTo(View.GONE); 322 } 323 324 @Test styleActionBar_invalidObjects_shouldNotCrash()325 public void styleActionBar_invalidObjects_shouldNotCrash() { 326 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 327 mController.styleActionBar(null); 328 329 when(mActivity.getActionBar()).thenReturn(null); 330 mController.styleActionBar(mActivity); 331 332 verify(mActivity).getActionBar(); 333 } 334 335 @Test styleActionBar_setElevationAndBackground()336 public void styleActionBar_setElevationAndBackground() { 337 final ActionBar actionBar = mActivity.getActionBar(); 338 339 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 340 mController.styleActionBar(mActivity); 341 342 verify(actionBar).setElevation(0); 343 // Enforce a color drawable as background here, as image based drawables might not be 344 // wide enough to cover entire action bar. 345 verify(actionBar).setBackgroundDrawable(any(ColorDrawable.class)); 346 } 347 348 @Test initAppHeaderController_appHeaderNull_useFragmentContext()349 public void initAppHeaderController_appHeaderNull_useFragmentContext() { 350 mController = EntityHeaderController.newInstance(mActivity, mFragment, null); 351 352 // Fragment.getContext() is invoked to inflate the view 353 verify(mFragment).getContext(); 354 } 355 } 356