• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.ActionBar;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageInfo;
35 import android.content.pm.ResolveInfo;
36 import android.graphics.drawable.ColorDrawable;
37 import android.os.UserHandle;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.widget.ImageButton;
41 import android.widget.ImageView;
42 import android.widget.TextView;
43 
44 import androidx.fragment.app.Fragment;
45 import androidx.fragment.app.FragmentActivity;
46 import androidx.preference.Preference;
47 
48 import com.android.settings.R;
49 import com.android.settingslib.applications.ApplicationsState;
50 import com.android.settingslib.widget.LayoutPreference;
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.RobolectricTestRunner;
59 import org.robolectric.RuntimeEnvironment;
60 
61 @RunWith(RobolectricTestRunner.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 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(mActivity);
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(mActivity, 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(R.layout.settings_entity_header, null /* root */);
108         mController = EntityHeaderController.newInstance(mActivity, mFragment, inputView);
109         View view = mController.done(mActivity);
110 
111         assertThat(view).isSameAs(inputView);
112     }
113 
114     @Test
bindViews_shouldBindAllData()115     public void bindViews_shouldBindAllData() {
116         final String testString = "test";
117         final View header =
118                 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */);
119         final TextView label = header.findViewById(R.id.entity_header_title);
120         final TextView summary = header.findViewById(R.id.entity_header_summary);
121         final TextView secondSummary = header.findViewById(R.id.entity_header_second_summary);
122 
123         mController = EntityHeaderController.newInstance(mActivity, mFragment, header);
124         mController.setLabel(testString);
125         mController.setSummary(testString);
126         mController.setSecondSummary(testString);
127         mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add_24dp));
128         mController.done(mActivity);
129 
130         assertThat(label).isNotNull();
131         assertThat(label.getText()).isEqualTo(testString);
132         assertThat(summary).isNotNull();
133         assertThat(summary.getText()).isEqualTo(testString);
134         assertThat(secondSummary).isNotNull();
135         assertThat(secondSummary.getText()).isEqualTo(testString);
136     }
137 
138     @Test
bindButton_hasEditClickListener_shouldShowButton()139     public void bindButton_hasEditClickListener_shouldShowButton() {
140         final ResolveInfo info = new ResolveInfo();
141         info.activityInfo = new ActivityInfo();
142         info.activityInfo.packageName = "123";
143         info.activityInfo.name = "321";
144         final View view = mLayoutInflater
145                 .inflate(R.layout.settings_entity_header, null /* root */);
146         when(mActivity.getApplicationContext()).thenReturn(mContext);
147 
148         mController = EntityHeaderController.newInstance(mActivity, mFragment, view);
149         mController.setEditListener(new View.OnClickListener() {
150             public void onClick(View v) {
151                 // do nothing
152             }
153         });
154         mController.setButtonActions(
155                 EntityHeaderController.ActionType.ACTION_EDIT_PREFERENCE,
156                 EntityHeaderController.ActionType.ACTION_NONE);
157         mController.done(mActivity);
158 
159         final ImageButton button1 = view.findViewById(android.R.id.button1);
160         assertThat(button1).isNotNull();
161         assertThat(button1.getVisibility()).isEqualTo(View.VISIBLE);
162         assertThat(button1.getDrawable()).isNotNull();
163         assertThat(view.findViewById(android.R.id.button2).getVisibility()).isEqualTo(View.GONE);
164     }
165 
166     @Test
bindButton_noEditClickListener_shouldNotShowButton()167     public void bindButton_noEditClickListener_shouldNotShowButton() {
168         final ResolveInfo info = new ResolveInfo();
169         info.activityInfo = new ActivityInfo();
170         info.activityInfo.packageName = "123";
171         info.activityInfo.name = "321";
172         final View view = mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */);
173         when(mActivity.getApplicationContext()).thenReturn(mContext);
174 
175         mController = EntityHeaderController.newInstance(mActivity, mFragment, view);
176         mController.setButtonActions(
177                 EntityHeaderController.ActionType.ACTION_EDIT_PREFERENCE,
178                 EntityHeaderController.ActionType.ACTION_NONE);
179         mController.done(mActivity);
180 
181         assertThat(view.findViewById(android.R.id.button1).getVisibility()).isEqualTo(View.GONE);
182         assertThat(view.findViewById(android.R.id.button2).getVisibility()).isEqualTo(View.GONE);
183     }
184 
185 
186     @Test
bindButton_noAppInfo_shouldNotAttachClickListener()187     public void bindButton_noAppInfo_shouldNotAttachClickListener() {
188         final View appLinks =
189                 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */);
190         final FragmentActivity activity = mock(FragmentActivity.class);
191         when(mFragment.getActivity()).thenReturn(activity);
192 
193         mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks);
194         mController.setPackageName(null)
195                 .setHasAppInfoLink(true)
196                 .setButtonActions(
197                         EntityHeaderController.ActionType.ACTION_NONE,
198                         EntityHeaderController.ActionType.ACTION_NONE);
199         mController.done(mActivity);
200 
201         assertThat(appLinks.findViewById(android.R.id.button1).getVisibility())
202                 .isEqualTo(View.GONE);
203         assertThat(appLinks.findViewById(android.R.id.button2).getVisibility())
204                 .isEqualTo(View.GONE);
205 
206         appLinks.findViewById(R.id.entity_header_content).performClick();
207         verify(mFragment, never()).getActivity();
208         verify(activity, never()).startActivity(any(Intent.class));
209     }
210 
211     @Test
bindButton_hasAppInfo_shouldAttachClickListener()212     public void bindButton_hasAppInfo_shouldAttachClickListener() {
213         final View appLinks =
214                 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */);
215         final FragmentActivity activity = mock(FragmentActivity.class);
216         when(mFragment.getActivity()).thenReturn(activity);
217         when(mContext.getString(eq(R.string.application_info_label))).thenReturn("App Info");
218 
219         mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks);
220         mController.setPackageName("123")
221                 .setUid(123321)
222                 .setHasAppInfoLink(true)
223                 .setButtonActions(
224                         EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
225                         EntityHeaderController.ActionType.ACTION_NONE);
226         mController.done(mActivity);
227 
228         appLinks.findViewById(R.id.entity_header_content).performClick();
229         verify(activity)
230                 .startActivityForResultAsUser(any(Intent.class), anyInt(), any(UserHandle.class));
231     }
232 
233     @Test
iconContentDescription_shouldWorkWithSetIcon()234     public void iconContentDescription_shouldWorkWithSetIcon() {
235         final View view =
236                 mLayoutInflater.inflate(R.layout.settings_entity_header, null /* root */);
237         when(mFragment.getActivity()).thenReturn(mock(FragmentActivity.class));
238         mController = EntityHeaderController.newInstance(mActivity, mFragment, view);
239         String description = "Fake Description";
240         mController.setIcon(mShadowContext.getDrawable(R.drawable.ic_add_24dp));
241         mController.setIconContentDescription(description);
242         mController.done(mActivity);
243         assertThat(view.findViewById(R.id.entity_header_icon).getContentDescription().toString())
244                 .isEqualTo(description);
245     }
246 
247     @Test
iconContentDescription_shouldWorkWithoutSetIcon()248     public void iconContentDescription_shouldWorkWithoutSetIcon() {
249         final View view = mLayoutInflater
250                 .inflate(R.layout.settings_entity_header, null /* root */);
251         when(mFragment.getActivity()).thenReturn(mock(FragmentActivity.class));
252         mController = EntityHeaderController.newInstance(mActivity, mFragment, view);
253         String description = "Fake Description";
254         mController.setIconContentDescription(description);
255         mController.done(mActivity);
256         assertThat(view.findViewById(R.id.entity_header_icon).getContentDescription().toString())
257                 .isEqualTo(description);
258     }
259 
260     @Test
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         // ... entry.icon is still empty. This means the icon didn't come from cache.
271         assertThat(entry.icon).isNull();
272     }
273 
274     @Test
bindButton_hasAppNotifIntent_shouldShowButton()275     public void bindButton_hasAppNotifIntent_shouldShowButton() {
276         final View appLinks = mLayoutInflater
277                 .inflate(R.layout.settings_entity_header, null /* root */);
278 
279         mController = EntityHeaderController.newInstance(mActivity, mFragment, appLinks);
280         mController.setAppNotifPrefIntent(new Intent())
281                 .setButtonActions(
282                         EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
283                         EntityHeaderController.ActionType.ACTION_NONE);
284         mController.done(mActivity);
285 
286         assertThat(appLinks.findViewById(android.R.id.button1).getVisibility())
287                 .isEqualTo(View.VISIBLE);
288         assertThat(appLinks.findViewById(android.R.id.button2).getVisibility())
289                 .isEqualTo(View.GONE);
290     }
291 
292     // Ensure that the instant app label does not show up when we haven't told the controller the
293     // app is instant.
294     @Test
instantApps_normalAppsDontGetLabel()295     public void instantApps_normalAppsDontGetLabel() {
296         final View header = mLayoutInflater.inflate(
297                 R.layout.settings_entity_header, null /* root */);
298         mController = EntityHeaderController.newInstance(mActivity, mFragment, header);
299         mController.done(mActivity);
300 
301         assertThat(header.findViewById(R.id.install_type).getVisibility())
302                 .isEqualTo(View.GONE);
303     }
304 
305     // Test that the "instant apps" label is present in the header when we have an instant app.
306     @Test
instantApps_expectedHeaderItem()307     public void instantApps_expectedHeaderItem() {
308         final View header = mLayoutInflater.inflate(
309                 R.layout.settings_entity_header, null /* root */);
310         mController = EntityHeaderController.newInstance(mActivity, mFragment, header);
311         mController.setIsInstantApp(true);
312         mController.done(mActivity);
313         TextView label = header.findViewById(R.id.install_type);
314 
315         assertThat(label.getVisibility()).isEqualTo(View.VISIBLE);
316         assertThat(label.getText()).isEqualTo(
317                 header.getResources().getString(R.string.install_type_instant));
318         assertThat(header.findViewById(R.id.entity_header_summary).getVisibility())
319                 .isEqualTo(View.GONE);
320     }
321 
322     @Test
styleActionBar_invalidObjects_shouldNotCrash()323     public void styleActionBar_invalidObjects_shouldNotCrash() {
324         mController = EntityHeaderController.newInstance(mActivity, mFragment, null);
325         mController.styleActionBar(null);
326 
327         when(mActivity.getActionBar()).thenReturn(null);
328         mController.styleActionBar(mActivity);
329 
330         verify(mActivity).getActionBar();
331     }
332 
333     @Test
styleActionBar_setElevationAndBackground()334     public void styleActionBar_setElevationAndBackground() {
335         final ActionBar actionBar = mActivity.getActionBar();
336 
337         mController = EntityHeaderController.newInstance(mActivity, mFragment, null);
338         mController.styleActionBar(mActivity);
339 
340         verify(actionBar).setElevation(0);
341         // Enforce a color drawable as background here, as image based drawables might not be
342         // wide enough to cover entire action bar.
343         verify(actionBar).setBackgroundDrawable(any(ColorDrawable.class));
344     }
345 
346     @Test
initAppHeaderController_appHeaderNull_useFragmentContext()347     public void initAppHeaderController_appHeaderNull_useFragmentContext() {
348         mController = EntityHeaderController.newInstance(mActivity, mFragment, null);
349 
350         // Fragment.getContext() is invoked to inflate the view
351         verify(mFragment).getContext();
352     }
353 }
354