diff --git a/src/com/android/settings/search/IntentSearchViewHolder.java b/src/com/android/settings/search/IntentSearchViewHolder.java index 2722d56a91f..711d08e8a0b 100644 --- a/src/com/android/settings/search/IntentSearchViewHolder.java +++ b/src/com/android/settings/search/IntentSearchViewHolder.java @@ -17,17 +17,24 @@ package com.android.settings.search; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.UserHandle; +import android.util.Log; import android.view.View; import com.android.internal.logging.nano.MetricsProto; +import java.util.List; + /** * ViewHolder for intent based search results. * The DatabaseResultLoader is the primary use case for this ViewHolder. */ public class IntentSearchViewHolder extends SearchViewHolder { + private static final String TAG = "IntentSearchViewHolder"; + public IntentSearchViewHolder(View view) { super(view); } @@ -42,7 +49,7 @@ public class IntentSearchViewHolder extends SearchViewHolder { super.onBind(fragment, result); itemView.setOnClickListener(v -> { - fragment.onSearchResultClicked(this, result); + fragment.onSearchResultClicked(this, result); final Intent intent = result.payload.getIntent(); // Use app user id to support work profile use case. if (result instanceof AppSearchResult) { @@ -50,7 +57,14 @@ public class IntentSearchViewHolder extends SearchViewHolder { UserHandle userHandle = appResult.getAppUserHandle(); fragment.getActivity().startActivityAsUser(intent, userHandle); } else { - fragment.startActivity(intent); + final PackageManager pm = fragment.getActivity().getPackageManager(); + final List info = pm.queryIntentActivities(intent, 0 /* flags */); + if (info != null && !info.isEmpty()) { + fragment.startActivity(intent); + } else { + Log.e(TAG, "Cannot launch search result, title: " + + result.title + ", " + intent); + } } }); } diff --git a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java index 38e6285b7ff..a3826f66d6c 100644 --- a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java +++ b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java @@ -18,10 +18,10 @@ 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.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,6 +31,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.view.LayoutInflater; @@ -52,6 +53,7 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -82,7 +84,7 @@ public class IntentSearchViewHolderTest { mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); final Context context = RuntimeEnvironment.application; - View view = LayoutInflater.from(context).inflate(R.layout.search_intent_item, null); + final View view = LayoutInflater.from(context).inflate(R.layout.search_intent_item, null); mHolder = new IntentSearchViewHolder(view); mIcon = context.getDrawable(R.drawable.ic_search_24dp); @@ -100,7 +102,7 @@ public class IntentSearchViewHolderTest { @Test public void testBindViewElements_allUpdated() { - SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon); + final SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon); mHolder.onBind(mFragment, result); mHolder.itemView.performClick(); @@ -111,7 +113,6 @@ public class IntentSearchViewHolderTest { assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE); verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class)); - verify(mFragment).startActivity(any(Intent.class)); } @Test @@ -158,8 +159,8 @@ public class IntentSearchViewHolderTest { @Test public void testBindElements_placeholderSummary_visibilityIsGone() { - String nonBreakingSpace = mContext.getString(R.string.summary_placeholder); - SearchResult result = new Builder() + final String nonBreakingSpace = mContext.getString(R.string.summary_placeholder); + final SearchResult result = new Builder() .setTitle(TITLE) .setSummary(nonBreakingSpace) .setPayload(new ResultPayload(null)) @@ -173,8 +174,8 @@ public class IntentSearchViewHolderTest { @Test public void testBindElements_dynamicSummary_visibilityIsGone() { - String dynamicSummary = "%s"; - SearchResult result = new Builder() + final String dynamicSummary = "%s"; + final SearchResult result = new Builder() .setTitle(TITLE) .setSummary(dynamicSummary) .setPayload(new ResultPayload(null)) @@ -191,7 +192,7 @@ public class IntentSearchViewHolderTest { when(mPackageManager.getUserBadgedLabel(any(CharSequence.class), eq(new UserHandle(USER_ID)))).thenReturn(BADGED_LABEL); - SearchResult result = getAppSearchResult( + final SearchResult result = getAppSearchResult( TITLE, SUMMARY, mIcon, getApplicationInfo(USER_ID, TITLE, mIcon)); mHolder.onBind(mFragment, result); mHolder.itemView.performClick(); @@ -207,6 +208,38 @@ public class IntentSearchViewHolderTest { any(Intent.class), eq(new UserHandle(USER_ID))); } + @Test + public void testBindViewElements_validSubSettingIntent_shouldLaunch() { + final SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon); + when(mPackageManager.queryIntentActivities(result.payload.getIntent(), 0 /* flags */)) + .thenReturn(Arrays.asList(new ResolveInfo())); + + 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); + verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class)); + verify(mFragment).startActivity(result.payload.getIntent()); + } + + @Test + public void testBindViewElements_invalidSubSettingIntent_shouldNotLaunchAnything() { + final SearchResult result = getSearchResult(TITLE, SUMMARY, mIcon); + when(mPackageManager.queryIntentActivities(result.payload.getIntent(), 0 /* flags */)) + .thenReturn(null); + + 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); + verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class)); + verify(mFragment, never()).startActivity(any(Intent.class)); + } + private SearchResult getSearchResult(String title, String summary, Drawable icon) { Builder builder = new Builder(); builder.setStableId(Objects.hash(title, summary, icon))