From 9f3f5f4fed23eac98cf7441c025fd6100e020ed2 Mon Sep 17 00:00:00 2001 From: Tony Mak Date: Mon, 12 Jun 2017 18:02:53 +0100 Subject: [PATCH] Fix searching work app in settings Test: m -j RunSettingsRoboTests Test: Observe search result with badged icon and showing work app info when tapping on it. Test: personal app search result is still working Test: Non app search result is working Fix: 62366873 Merged-in: I333372699b263d02cc4083289dc746c7aacd414d Change-Id: I333372699b263d02cc4083289dc746c7aacd414d (cherry picked from commit 3bef8ce30a43d068aafd4756dc2db7eb9f869781) --- .../settings/search/AppSearchResult.java | 5 ++ .../search/InstalledAppResultLoader.java | 3 +- .../search/IntentSearchViewHolder.java | 16 ++++- .../settings/search/SearchViewHolder.java | 12 +++- .../search/InstalledAppResultLoaderTest.java | 39 ++++++++++ .../search/IntentSearchViewHolderTest.java | 71 +++++++++++++++++-- 6 files changed, 134 insertions(+), 12 deletions(-) diff --git a/src/com/android/settings/search/AppSearchResult.java b/src/com/android/settings/search/AppSearchResult.java index fcb83dc73d0..b59e32e5666 100644 --- a/src/com/android/settings/search/AppSearchResult.java +++ b/src/com/android/settings/search/AppSearchResult.java @@ -18,6 +18,7 @@ package com.android.settings.search; import android.content.pm.ApplicationInfo; +import android.os.UserHandle; public class AppSearchResult extends SearchResult { /** @@ -30,6 +31,10 @@ public class AppSearchResult extends SearchResult { info = builder.mInfo; } + public UserHandle getAppUserHandle() { + return new UserHandle(UserHandle.getUserId(info.uid)); + } + public static class Builder extends SearchResult.Builder { protected ApplicationInfo mInfo; diff --git a/src/com/android/settings/search/InstalledAppResultLoader.java b/src/com/android/settings/search/InstalledAppResultLoader.java index 81b96a17ed9..70a39eeedc7 100644 --- a/src/com/android/settings/search/InstalledAppResultLoader.java +++ b/src/com/android/settings/search/InstalledAppResultLoader.java @@ -40,6 +40,7 @@ import com.android.settings.utils.AsyncLoader; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -99,7 +100,7 @@ public class InstalledAppResultLoader extends AsyncLoader { fragment.onSearchResultClicked(this, result); - fragment.startActivity(result.payload.getIntent()); + final Intent intent = result.payload.getIntent(); + // Use app user id to support work profile use case. + if (result instanceof AppSearchResult) { + AppSearchResult appResult = (AppSearchResult) result; + UserHandle userHandle = appResult.getAppUserHandle(); + fragment.getActivity().startActivityAsUser(intent, userHandle); + } else { + fragment.startActivity(intent); + } }); } } diff --git a/src/com/android/settings/search/SearchViewHolder.java b/src/com/android/settings/search/SearchViewHolder.java index 72fd0232024..ed72940f8b6 100644 --- a/src/com/android/settings/search/SearchViewHolder.java +++ b/src/com/android/settings/search/SearchViewHolder.java @@ -18,8 +18,11 @@ package com.android.settings.search; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; +import android.util.IconDrawableFactory; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -46,6 +49,7 @@ public abstract class SearchViewHolder extends RecyclerView.ViewHolder { protected final MetricsFeatureProvider mMetricsFeatureProvider; protected final SearchFeatureProvider mSearchFeatureProvider; + private final IconDrawableFactory mIconDrawableFactory; public SearchViewHolder(View view) { super(view); @@ -59,6 +63,7 @@ public abstract class SearchViewHolder extends RecyclerView.ViewHolder { breadcrumbView = view.findViewById(R.id.breadcrumb); mPlaceholderSummary = view.getContext().getString(R.string.summary_placeholder); + mIconDrawableFactory = IconDrawableFactory.newInstance(view.getContext()); } public abstract int getClickActionMetricName(); @@ -78,7 +83,12 @@ public abstract class SearchViewHolder extends RecyclerView.ViewHolder { if (result instanceof AppSearchResult) { AppSearchResult appResult = (AppSearchResult) result; PackageManager pm = fragment.getActivity().getPackageManager(); - iconView.setImageDrawable(appResult.info.loadIcon(pm)); + UserHandle userHandle = appResult.getAppUserHandle(); + Drawable badgedIcon = + mIconDrawableFactory.getBadgedIcon(appResult.info, userHandle.getIdentifier()); + iconView.setImageDrawable(badgedIcon); + titleView.setContentDescription( + pm.getUserBadgedLabel(appResult.info.loadLabel(pm), userHandle)); } else { // Valid even when result.icon is null. iconView.setImageDrawable(result.icon); diff --git a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java index 645b9868967..d0a200dbccd 100644 --- a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java +++ b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.os.UserHandle; import android.os.UserManager; import com.android.settings.R; @@ -47,6 +48,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; @@ -387,4 +389,41 @@ public class InstalledAppResultLoaderTest { assertThat(mLoader.loadInBackground().size()).isEqualTo(0); } + + @Test + public void query_appExistsInBothProfiles() { + final String query = "carrot"; + final String packageName = "carrot"; + final int user1 = 0; + final int user2 = 10; + final int uid = 67672; + List infos = new ArrayList<>(); + infos.add(new UserInfo(user1, "user 1", 0)); + infos.add(new UserInfo(user2, "user 2", UserInfo.FLAG_MANAGED_PROFILE)); + + when(mUserManager.getProfiles(anyInt())).thenReturn(infos); + + when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), eq(user1))) + .thenReturn(Arrays.asList( + ApplicationTestUtils.buildInfo(UserHandle.getUid(user1, uid) /* uid */, + packageName, 0 /* flags */, + 0 /* targetSdkVersion */))); + when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), eq(user2))) + .thenReturn(Arrays.asList( + ApplicationTestUtils.buildInfo(UserHandle.getUid(user2, uid) /* uid */, + packageName, 0 /* flags */, + 0 /* targetSdkVersion */))); + + mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query, + mSiteMapManager); + + Set searchResults = (Set) mLoader.loadInBackground(); + assertThat(searchResults).hasSize(2); + + Set uidResults = searchResults.stream().map(result -> result.info.uid).collect( + Collectors.toSet()); + assertThat(uidResults).containsExactly( + UserHandle.getUid(user1, uid), + UserHandle.getUid(user2, uid)); + } } diff --git a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java index 574e4f7f0c2..cee3c7831c0 100644 --- a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java +++ b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java @@ -17,18 +17,30 @@ package com.android.settings.search; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.os.UserHandle; import android.view.LayoutInflater; import android.view.View; import com.android.settings.R; -import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.search.SearchResult.Builder; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.testutils.SettingsRobolectricTestRunner; import org.junit.Before; import org.junit.Test; @@ -43,25 +55,25 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.verify; - @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class IntentSearchViewHolderTest { private static final String TITLE = "title"; private static final String SUMMARY = "summary"; + private static final int USER_ID = 10; + private static final String BADGED_LABEL = "work title"; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; - @Mock + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private SearchFragment mFragment; + @Mock + private PackageManager mPackageManager; private FakeFeatureFactory mFeatureFactory; private IntentSearchViewHolder mHolder; private Drawable mIcon; + private Drawable mBadgedIcon; @Before public void setUp() { @@ -74,6 +86,8 @@ public class IntentSearchViewHolderTest { mHolder = new IntentSearchViewHolder(view); mIcon = context.getDrawable(R.drawable.ic_search_history); + mBadgedIcon = context.getDrawable(R.drawable.ic_add); + when(mFragment.getActivity().getPackageManager()).thenReturn(mPackageManager); } @Test @@ -172,6 +186,27 @@ public class IntentSearchViewHolderTest { assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.GONE); } + @Test + public void testBindViewElements_appSearchResult() { + when(mPackageManager.getUserBadgedLabel(any(CharSequence.class), + eq(new UserHandle(USER_ID)))).thenReturn(BADGED_LABEL); + + SearchResult result = getAppSearchResult( + TITLE, SUMMARY, mIcon, getApplicationInfo(USER_ID, TITLE, mIcon)); + mHolder.onBind(mFragment, result); + mHolder.itemView.performClick(); + + assertThat(mHolder.titleView.getText()).isEqualTo(TITLE); + assertThat(mHolder.summaryView.getText()).isEqualTo(SUMMARY); + assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE); + assertThat(mHolder.titleView.getContentDescription()).isEqualTo(BADGED_LABEL); + + verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class)); + verify(mFragment.getActivity()).startActivityAsUser( + any(Intent.class), eq(new UserHandle(USER_ID))); + } + private SearchResult getSearchResult(String title, String summary, Drawable icon) { Builder builder = new Builder(); builder.setStableId(Objects.hash(title, summary, icon)) @@ -186,4 +221,26 @@ public class IntentSearchViewHolderTest { return builder.build(); } + + private SearchResult getAppSearchResult( + String title, String summary, Drawable icon, ApplicationInfo applicationInfo) { + AppSearchResult.Builder builder = new AppSearchResult.Builder(); + builder.setTitle(title) + .setSummary(summary) + .setRank(1) + .setPayload(new ResultPayload( + new Intent().setComponent(new ComponentName("pkg", "class")))) + .addBreadcrumbs(new ArrayList<>()) + .setIcon(icon); + builder.setAppInfo(applicationInfo); + return builder.build(); + } + + private ApplicationInfo getApplicationInfo(int userId, CharSequence appLabel, Drawable icon) { + ApplicationInfo applicationInfo = spy(new ApplicationInfo()); + applicationInfo.uid = UserHandle.getUid(userId, 12345); + doReturn(icon).when(applicationInfo).loadIcon(any(PackageManager.class)); + doReturn(appLabel).when(applicationInfo).loadLabel(any(PackageManager.class)); + return applicationInfo; + } }