1/*
2 * Copyright (C) 2017 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
18package com.android.settings.search;
19
20import static com.google.common.truth.Truth.assertThat;
21import static org.mockito.Matchers.any;
22import static org.mockito.Matchers.eq;
23import static org.mockito.Mockito.doReturn;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.spy;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.content.ComponentName;
30import android.content.Context;
31import android.content.Intent;
32import android.content.pm.ApplicationInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
35import android.graphics.drawable.Drawable;
36import android.os.UserHandle;
37import android.view.LayoutInflater;
38import android.view.View;
39
40import com.android.settings.R;
41import com.android.settings.TestConfig;
42import com.android.settings.search.SearchResult.Builder;
43import com.android.settings.testutils.FakeFeatureFactory;
44import com.android.settings.testutils.SettingsRobolectricTestRunner;
45
46import org.junit.Before;
47import org.junit.Test;
48import org.junit.runner.RunWith;
49import org.mockito.Answers;
50import org.mockito.Mock;
51import org.mockito.MockitoAnnotations;
52import org.robolectric.RuntimeEnvironment;
53import org.robolectric.annotation.Config;
54
55import java.util.ArrayList;
56import java.util.Arrays;
57import java.util.List;
58import java.util.Objects;
59
60@RunWith(SettingsRobolectricTestRunner.class)
61@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
62public class IntentSearchViewHolderTest {
63
64    private static final String TITLE = "title";
65    private static final String SUMMARY = "summary";
66    private static final int USER_ID = 10;
67    private static final String BADGED_LABEL = "work title";
68
69    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
70    private Context mContext;
71    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
72    private SearchFragment mFragment;
73    @Mock
74    private PackageManager mPackageManager;
75    private FakeFeatureFactory mFeatureFactory;
76    private IntentSearchViewHolder mHolder;
77    private Drawable mIcon;
78    private Drawable mBadgedIcon;
79
80    @Before
81    public void setUp() {
82        MockitoAnnotations.initMocks(this);
83        FakeFeatureFactory.setupForTest(mContext);
84        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
85
86        final Context context = RuntimeEnvironment.application;
87        final View view = LayoutInflater.from(context).inflate(R.layout.search_intent_item, null);
88        mHolder = new IntentSearchViewHolder(view);
89
90        mIcon = context.getDrawable(R.drawable.ic_search_24dp);
91        mBadgedIcon = context.getDrawable(R.drawable.ic_add);
92        when(mFragment.getActivity().getPackageManager()).thenReturn(mPackageManager);
93    }
94
95    @Test
96    public void testConstructor_membersNotNull() {
97        assertThat(mHolder.titleView).isNotNull();
98        assertThat(mHolder.summaryView).isNotNull();
99        assertThat(mHolder.iconView).isNotNull();
100        assertThat(mHolder.breadcrumbView).isNotNull();
101    }
102
103    @Test
104    public void testBindViewElements_allUpdated() {
105        final SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon);
106        mHolder.onBind(mFragment, result);
107        mHolder.itemView.performClick();
108
109        assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
110        assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
111        assertThat(mHolder.iconView.getDrawable()).isEqualTo(mIcon);
112        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
113        assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE);
114
115        verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class));
116    }
117
118    @Test
119    public void testBindViewIcon_nullIcon_imageDrawableIsNull() {
120        final SearchResult result = getSearchResult(TITLE, SUMMARY, null);
121        mHolder.onBind(mFragment, result);
122
123        assertThat(mHolder.iconView.getDrawable()).isNull();
124    }
125
126    @Test
127    public void testBindViewElements_emptySummary_hideSummaryView() {
128        final SearchResult result = new Builder()
129                .setTitle(TITLE)
130                .setRank(1)
131                .setPayload(new ResultPayload(null))
132                .setIcon(mIcon)
133                .setStableId(1)
134                .build();
135
136        mHolder.onBind(mFragment, result);
137        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.GONE);
138    }
139
140    @Test
141    public void testBindViewElements_withBreadcrumb_shouldFormatBreadcrumb() {
142        final List<String> breadcrumbs = new ArrayList<>();
143        breadcrumbs.add("a");
144        breadcrumbs.add("b");
145        breadcrumbs.add("c");
146        final SearchResult result = new Builder()
147                .setTitle(TITLE)
148                .setRank(1)
149                .setPayload(new ResultPayload(null))
150                .addBreadcrumbs(breadcrumbs)
151                .setIcon(mIcon)
152                .setStableId(1)
153                .build();
154
155        mHolder.onBind(mFragment, result);
156        assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.VISIBLE);
157        assertThat(mHolder.breadcrumbView.getText()).isEqualTo("a > b > c");
158    }
159
160    @Test
161    public void testBindElements_placeholderSummary_visibilityIsGone() {
162        final String nonBreakingSpace = mContext.getString(R.string.summary_placeholder);
163        final SearchResult result = new Builder()
164                .setTitle(TITLE)
165                .setSummary(nonBreakingSpace)
166                .setPayload(new ResultPayload(null))
167                .setStableId(1)
168                .build();
169
170        mHolder.onBind(mFragment, result);
171
172        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.GONE);
173    }
174
175    @Test
176    public void testBindElements_dynamicSummary_visibilityIsGone() {
177        final String dynamicSummary = "%s";
178        final SearchResult result = new Builder()
179                .setTitle(TITLE)
180                .setSummary(dynamicSummary)
181                .setPayload(new ResultPayload(null))
182                .setStableId(1)
183                .build();
184
185        mHolder.onBind(mFragment, result);
186
187        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.GONE);
188    }
189
190    @Test
191    public void testBindViewElements_appSearchResult() {
192        when(mPackageManager.getUserBadgedLabel(any(CharSequence.class),
193                eq(new UserHandle(USER_ID)))).thenReturn(BADGED_LABEL);
194
195        final SearchResult result = getAppSearchResult(
196                TITLE, SUMMARY, mIcon, getApplicationInfo(USER_ID, TITLE, mIcon));
197        mHolder.onBind(mFragment, result);
198        mHolder.itemView.performClick();
199
200        assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
201        assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
202        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
203        assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE);
204        assertThat(mHolder.titleView.getContentDescription()).isEqualTo(BADGED_LABEL);
205
206        verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class));
207        verify(mFragment.getActivity()).startActivityAsUser(
208                any(Intent.class), eq(new UserHandle(USER_ID)));
209    }
210
211    @Test
212    public void testBindViewElements_validSubSettingIntent_shouldLaunch() {
213        final SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon);
214        when(mPackageManager.queryIntentActivities(result.payload.getIntent(), 0 /* flags */))
215                .thenReturn(Arrays.asList(new ResolveInfo()));
216
217        mHolder.onBind(mFragment, result);
218        mHolder.itemView.performClick();
219
220        assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
221        assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
222        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
223        verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class));
224        verify(mFragment).startActivity(result.payload.getIntent());
225    }
226
227    @Test
228    public void testBindViewElements_invalidSubSettingIntent_shouldNotLaunchAnything() {
229        final SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon);
230        when(mPackageManager.queryIntentActivities(result.payload.getIntent(), 0 /* flags */))
231                .thenReturn(null);
232
233        mHolder.onBind(mFragment, result);
234        mHolder.itemView.performClick();
235
236        assertThat(mHolder.titleView.getText()).isEqualTo(TITLE);
237        assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY);
238        assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
239        verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class));
240        verify(mFragment, never()).startActivity(any(Intent.class));
241    }
242
243    private SearchResult getSearchResult(String title, String summary, Drawable icon) {
244        Builder builder = new Builder();
245        builder.setStableId(Objects.hash(title, summary, icon))
246                .setTitle(title)
247                .setSummary(summary)
248                .setRank(1)
249                .setPayload(new ResultPayload(
250                        new Intent().setComponent(new ComponentName("pkg", "class"))))
251                .addBreadcrumbs(new ArrayList<>())
252                .setStableId(1)
253                .setIcon(icon);
254
255        return builder.build();
256    }
257
258    private SearchResult getAppSearchResult(
259            String title, String summary, Drawable icon, ApplicationInfo applicationInfo) {
260        AppSearchResult.Builder builder = new AppSearchResult.Builder();
261        builder.setTitle(title)
262                .setSummary(summary)
263                .setRank(1)
264                .setPayload(new ResultPayload(
265                        new Intent().setComponent(new ComponentName("pkg", "class"))))
266                .addBreadcrumbs(new ArrayList<>())
267                .setIcon(icon);
268        builder.setAppInfo(applicationInfo);
269        return builder.build();
270    }
271
272    private ApplicationInfo getApplicationInfo(int userId, CharSequence appLabel, Drawable icon) {
273        ApplicationInfo applicationInfo = spy(new ApplicationInfo());
274        applicationInfo.uid = UserHandle.getUid(userId, 12345);
275        doReturn(icon).when(applicationInfo).loadIcon(any(PackageManager.class));
276        doReturn(appLabel).when(applicationInfo).loadLabel(any(PackageManager.class));
277        return applicationInfo;
278    }
279}
280