Re-use MediaSourceUtil in AppLauncherUtils

As part of ag/25009174 we added a library to Car media Common to
determine if a app is a media templated app for automotive.This code can
be reused in the launcher logic.

Additionally remove the custom filtering in the MediaSourceViewModel for
ignoring non audio apps since filtering will happen at the lower level
at MediaViewModel

Bug: 302593315
Test: atest AppLauncherUtilsTest && atest MediaSourceViewModelTest

Change-Id: Ifb46b3db3f1e9e2420914617df43f6112de3cef5
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java b/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java
index 2aafd60..5becb9f 100644
--- a/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java
@@ -21,7 +21,6 @@
 
 import android.app.Application;
 import android.car.media.CarMediaIntents;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -34,7 +33,6 @@
 import androidx.lifecycle.Observer;
 
 import com.android.car.apps.common.imaging.ImageBinder;
-import com.android.car.carlauncher.AppLauncherUtils;
 import com.android.car.carlauncher.homescreen.HomeCardInterface;
 import com.android.car.carlauncher.homescreen.ui.CardContent;
 import com.android.car.carlauncher.homescreen.ui.CardHeader;
@@ -49,9 +47,6 @@
 import com.android.car.media.common.source.MediaSourceViewModel;
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.Arrays;
-import java.util.List;
-
 
 /**
  * ViewModel for media. Uses both a {@link MediaSourceViewModel} and a {@link PlaybackViewModel}
@@ -247,8 +242,7 @@
     private void updateModel() {
         MediaSource mediaSource = mSourceViewModel.getPrimaryMediaSource().getValue();
         if (mediaSourceChanged()) {
-            if (mediaSource != null
-                    && supportsMediaWidget(mediaSource.getBrowseServiceComponentName())) {
+            if (mediaSource != null) {
                 if (Log.isLoggable(TAG, Log.INFO)) {
                     Log.i(TAG, "Setting Media view to source "
                             + mediaSource.getDisplayName(mContext));
@@ -271,17 +265,6 @@
     }
 
     /**
-     * Ensure the app is supported in media widget. This should either be a media templated
-     * app or a custom media component
-     */
-    private boolean supportsMediaWidget(ComponentName componentName) {
-        List<String> customMediaComponents = Arrays.asList(
-                mContext.getResources().getStringArray(R.array.custom_media_packages));
-        return AppLauncherUtils.isMediaTemplate(getApplication().getPackageManager(), componentName)
-                || customMediaComponents.contains(componentName.flattenToString());
-    }
-
-    /**
      * Callback for the observer of the PlaybackViewModel
      */
     private void updateModelMetadata() {
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java
index 450f4bc..f5e0b01 100644
--- a/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java
+++ b/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java
@@ -45,7 +45,6 @@
 import com.android.car.media.common.source.MediaSource;
 import com.android.car.media.common.source.MediaSourceColors;
 import com.android.car.media.common.source.MediaSourceViewModel;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
 
 import org.junit.After;
 import org.junit.Before;
@@ -155,10 +154,6 @@
         when(mMediaSource.getBrowseServiceComponentName())
                 .thenReturn(ComponentName.createRelative("com.test", ".mbs"));
 
-        // ensure media source is considered a legacy media app
-        ExtendedMockito.doReturn(Boolean.TRUE)
-                .when(() -> AppLauncherUtils.isMediaTemplate(any(), any()));
-
         mLiveMediaSource.setValue(mMediaSource);
         mLiveMetadata.setValue(mMetadata);
 
@@ -182,55 +177,6 @@
         when(mMediaSource.getBrowseServiceComponentName())
                 .thenReturn(ComponentName.createRelative("com.test", ".mbs"));
 
-        // ensure media source is considered a legacy media app
-        ExtendedMockito.doReturn(Boolean.TRUE)
-                .when(() -> AppLauncherUtils.isMediaTemplate(any(), any()));
-
-        mLiveMediaSource.setValue(mMediaSource);
-
-        verify(mOnModelUpdateListener).onModelUpdate(mMediaViewModel);
-        CardHeader header = mMediaViewModel.getCardHeader();
-        assertEquals(header.getCardTitle(), APP_NAME);
-        assertNull(header.getCardIcon());
-        DescriptiveTextWithControlsView content =
-                (DescriptiveTextWithControlsView) mMediaViewModel.getCardContent();
-        assertEquals(content.getTitle().toString(), "");
-        assertEquals(content.getTitle().toString(), "");
-
-    }
-
-    @Test
-    public void changeSourceOnlyNonLegacyMediaApp_doesNotCallPresenter() {
-        when(mMediaSource.getDisplayName(any())).thenReturn(APP_NAME);
-        when(mMediaSource.getIcon()).thenReturn(APP_ICON);
-
-        when(mMediaSource.getBrowseServiceComponentName())
-                .thenReturn(ComponentName.createRelative("com.test", ".mbs"));
-
-        // not a legacy media app
-        ExtendedMockito.doReturn(Boolean.FALSE)
-                .when(() -> AppLauncherUtils.isMediaTemplate(any(), any()));
-
-        mLiveMediaSource.setValue(mMediaSource);
-
-        verify(mOnModelUpdateListener, never()).onModelUpdate(any());
-        // Card does not get updated.
-        assertNull(mMediaViewModel.getCardHeader());
-
-    }
-    @Test
-    public void changeSourceToCustomMediaComponentApp_updatesModel() {
-        when(mMediaSource.getDisplayName(any())).thenReturn(APP_NAME);
-        when(mMediaSource.getIcon()).thenReturn(APP_ICON);
-
-        // Radio is a custom component app
-        when(mMediaSource.getBrowseServiceComponentName())
-                .thenReturn(ComponentName.createRelative("com.android.car.radio", ".service"));
-
-        // not legacy media app
-        ExtendedMockito.doReturn(Boolean.TRUE)
-                .when(() -> AppLauncherUtils.isMediaTemplate(any(), any()));
-
         mLiveMediaSource.setValue(mMediaSource);
 
         verify(mOnModelUpdateListener).onModelUpdate(mMediaViewModel);
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java b/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java
index a37c45f..c4e92c7 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java
@@ -40,7 +40,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -56,6 +55,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.car.media.common.source.MediaSourceUtil;
 import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup;
 
 import com.google.common.collect.Sets;
@@ -254,7 +254,7 @@
                 ComponentName componentName = new ComponentName(packageName, className);
                 mediaServicesMap.put(componentName, info);
                 mEnabledPackages.add(packageName);
-                if (shouldAddToLaunchables(packageManager, componentName, appsToHide,
+                if (shouldAddToLaunchables(context, componentName, appsToHide,
                         customMediaComponents, appTypes, APP_TYPE_MEDIA_SERVICES)) {
                     final boolean isDistractionOptimized = true;
                     boolean isDisabledByTos = tosDisabledPackages.contains(packageName);
@@ -290,7 +290,7 @@
                 ComponentName componentName = info.getComponentName();
                 String packageName = componentName.getPackageName();
                 mEnabledPackages.add(packageName);
-                if (shouldAddToLaunchables(packageManager, componentName, appsToHide,
+                if (shouldAddToLaunchables(context, componentName, appsToHide,
                         customMediaComponents, appTypes, APP_TYPE_LAUNCHABLES)) {
                     boolean isDistractionOptimized =
                             isActivityDistractionOptimized(carPackageManager, packageName,
@@ -330,7 +330,7 @@
                 String packageName = info.activityInfo.packageName;
                 String className = info.activityInfo.name;
                 ComponentName componentName = new ComponentName(packageName, className);
-                if (!shouldAddToLaunchables(packageManager, componentName, appsToHide,
+                if (!shouldAddToLaunchables(context, componentName, appsToHide,
                         customMediaComponents, appTypes, APP_TYPE_LAUNCHABLES)) {
                     continue;
                 }
@@ -423,52 +423,6 @@
         }
     }
 
-    /**
-     * Determine if the given media browse service is supported through media templates
-     */
-    public static boolean isMediaTemplate(PackageManager pm, ComponentName mbsComponentName) {
-        Bundle metaData = null;
-        try {
-            metaData = pm.getServiceInfo(
-                    mbsComponentName,
-                    PackageManager.GET_META_DATA)
-                    .metaData;
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Service for Component " + mbsComponentName + " was not found");
-            return false;
-        }
-
-        if (metaData != null && metaData.containsKey(ANDROIDX_CAR_APP_LAUNCHABLE)) {
-            boolean launchable = metaData.getBoolean(ANDROIDX_CAR_APP_LAUNCHABLE);
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "MBS for " + mbsComponentName
-                        + " is opted " + (launchable ? "in" : "out"));
-            }
-            return launchable;
-        }
-
-        // No explicit declaration. For backward compatibility, keep MBS only for media apps
-        String packageName = mbsComponentName.getPackageName();
-        try {
-            if (isLegacyMediaApp(pm, packageName)) {
-                Log.d(TAG, "Including " + mbsComponentName + "  for media app " + packageName);
-                return true;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Package " + packageName + " was not found");
-        }
-        Log.d(TAG, "Skipping MBS for " + mbsComponentName
-                + " belonging to non media app " + packageName);
-        return false;
-    }
-
-    /** Determine if it's a legacy media app that doesn't have a launcher activity*/
-    private static boolean isLegacyMediaApp(
-            PackageManager pm, String packageName) throws PackageManager.NameNotFoundException {
-        // a media app doesn't have a launcher activity
-        return pm.getLaunchIntentForPackage(packageName) == null;
-    }
-
     private static Consumer<Pair<Context, View>> buildShortcuts(String packageName,
             CharSequence displayName, ShortcutsListener shortcutsListener) {
         return pair -> {
@@ -718,7 +672,7 @@
         return activities;
     }
 
-    private static boolean shouldAddToLaunchables(PackageManager packageManager,
+    private static boolean shouldAddToLaunchables(Context context,
             @NonNull ComponentName componentName,
             @NonNull Set<String> appsToHide,
             @NonNull Set<String> customMediaComponents,
@@ -749,7 +703,7 @@
                     return true;
                 }
                 // Only Keep MBS that is a media template
-                return isMediaTemplate(packageManager, componentName);
+                return new MediaSourceUtil(context).isMediaTemplate(componentName);
             // Process activities
             case APP_TYPE_LAUNCHABLES:
                 return true;
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java
index 613be5d..c027a72 100644
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java
+++ b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
@@ -210,6 +211,7 @@
         // Media apps should do only switching and not launch activity
         verify(mMockContext, never()).startActivity(any(), any());
     }
+
     @Test
     public void testGetLauncherApps_Launcher() {
         mockSettingsStringCalls();
@@ -270,7 +272,6 @@
     }
 
 
-
     @Test
     public void testGetLauncherAppsWithEnableAndTosDisabledApps() {
         mockSettingsStringCalls();
@@ -349,7 +350,8 @@
     }
 
     private void forceStopInit(ActivityManager activityManager, CarMediaManager carMediaManager,
-            ComponentName currentMediaComponentName, ComponentName previousMediaComponentName,
+            ComponentName currentMediaComponentName,
+            ComponentName previousMediaComponentName,
             Map<Integer, Boolean> currentModes, boolean isMedia) {
         when(mMockContext.getSystemService(
                 ArgumentMatchers.<Class<ActivityManager>>any())).thenReturn(activityManager);
@@ -430,7 +432,8 @@
         CarMediaManager carMediaManager = mock(CarMediaManager.class);
         ComponentName currentMediaComponentName = new ComponentName(packageName,
                 "com.example.service");
-        ComponentName otherMediaComponentName = new ComponentName("other.package", "other.test");
+        ComponentName otherMediaComponentName = new ComponentName("other.package",
+                "other.test");
         Map<Integer, Boolean> currentModes = new HashMap<>();
         currentModes.put(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK, true);
         currentModes.put(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE, true);
@@ -476,12 +479,20 @@
         ApplicationInfo mbsAppInfo = new ApplicationInfo();
         mbsAppInfo.category = CATEGORY_AUDIO;
         ResolveInfo mbs = constructServiceResolveInfo(TEST_MEDIA_TEMPLATE_MBS);
+
         try {
+            Intent mbsIntent = new Intent();
+            mbsIntent.setComponent(mbs.getComponentInfo().getComponentName());
+            mbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
+
             when(mMockPackageManager.getApplicationInfo(mbs.getComponentInfo().packageName, 0))
                     .thenReturn(mbsAppInfo);
-            when(mMockPackageManager.getServiceInfo(mbs.getComponentInfo().getComponentName(),
-                    PackageManager.GET_META_DATA))
-                    .thenReturn(new ServiceInfo());
+
+            doReturn(Arrays.asList(mbs)).when(mMockPackageManager).queryIntentServices(
+                    argThat((Intent i) -> i != null
+                            && mbs.getComponentInfo().getComponentName().equals(i.getComponent())),
+                    eq(PackageManager.GET_META_DATA));
+
             when(mMockPackageManager.getLaunchIntentForPackage(mbs.getComponentInfo().packageName))
                     .thenReturn(null);
         } catch (PackageManager.NameNotFoundException e) {
@@ -493,12 +504,20 @@
         videoAppInfo.category = CATEGORY_VIDEO;
         ResolveInfo videoApp = constructServiceResolveInfo(TEST_VIDEO_MBS);
         try {
+            Intent videoMbsIntent = new Intent();
+            videoMbsIntent.setComponent(videoApp.getComponentInfo().getComponentName());
+            videoMbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
+
             when(mMockPackageManager.getApplicationInfo(videoApp.getComponentInfo().packageName,
                     0))
                     .thenReturn(videoAppInfo);
-            when(mMockPackageManager.getServiceInfo(videoApp.getComponentInfo().getComponentName(),
-                    PackageManager.GET_META_DATA))
-                    .thenReturn(new ServiceInfo());
+
+            doReturn(Arrays.asList(videoApp)).when(mMockPackageManager).queryIntentServices(
+                    argThat((Intent i) -> i != null
+                            && videoApp.getComponentInfo().getComponentName()
+                                    .equals(i.getComponent())),
+                    eq(PackageManager.GET_META_DATA));
+
             when(mMockPackageManager.getLaunchIntentForPackage(
                     videoApp.getComponentInfo().packageName))
                     .thenReturn(new Intent());
@@ -506,48 +525,33 @@
             throw new RuntimeException(e);
         }
 
-        // setup a NDO app that has MBS opted in to launch in car
-        ApplicationInfo launchableMBSInfo = new ApplicationInfo();
-        launchableMBSInfo.category = CATEGORY_VIDEO;
-        ResolveInfo launchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_LAUNCHABLE);
-        try {
-            when(mMockPackageManager.getApplicationInfo(
-                    launchableMBSApp.getComponentInfo().packageName,
-                    0))
-                    .thenReturn(launchableMBSInfo);
-            ServiceInfo value = new ServiceInfo();
-            value.metaData = new Bundle();
-
-            value.metaData.putBoolean("androidx.car.app.launchable", true);
-
-            when(mMockPackageManager.getServiceInfo(
-                    launchableMBSApp.getComponentInfo().getComponentName(),
-                    PackageManager.GET_META_DATA))
-                    .thenReturn(value);
-            when(mMockPackageManager.getLaunchIntentForPackage(
-                    launchableMBSApp.getComponentInfo().packageName))
-                    .thenReturn(new Intent());
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-
         // setup a NDO app that has MBS opted out of launch in car
         ApplicationInfo notlaunchableMBSInfo = new ApplicationInfo();
         notlaunchableMBSInfo.category = CATEGORY_VIDEO;
         ResolveInfo notlaunchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_NOT_LAUNCHABLE);
+
         try {
+            Intent notlaunachableMbsIntent = new Intent();
+            notlaunachableMbsIntent.setComponent(
+                    notlaunchableMBSApp.getComponentInfo().getComponentName());
+            notlaunachableMbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
+
             when(mMockPackageManager.getApplicationInfo(
                     notlaunchableMBSApp.getComponentInfo().packageName, 0))
                     .thenReturn(notlaunchableMBSInfo);
-            ServiceInfo value = new ServiceInfo();
-            value.metaData = new Bundle();
 
-            value.metaData.putBoolean("androidx.car.app.launchable", false);
 
-            when(mMockPackageManager.getServiceInfo(
-                    notlaunchableMBSApp.getComponentInfo().getComponentName(),
-                    PackageManager.GET_META_DATA))
-                    .thenReturn(value);
+            notlaunchableMBSApp.serviceInfo.metaData = new Bundle();
+            notlaunchableMBSApp.serviceInfo.metaData
+                    .putBoolean("androidx.car.app.launchable", false);
+
+            doReturn(Arrays.asList(notlaunchableMBSApp))
+                    .when(mMockPackageManager).queryIntentServices(
+                    argThat((Intent i) -> i != null
+                            && notlaunchableMBSApp.getComponentInfo().getComponentName()
+                                    .equals(i.getComponent())),
+                    eq(PackageManager.GET_META_DATA));
+
             when(mMockPackageManager.getLaunchIntentForPackage(
                     notlaunchableMBSApp.getComponentInfo().packageName))
                     .thenReturn(new Intent());
@@ -555,7 +559,40 @@
             throw new RuntimeException(e);
         }
 
-        when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenAnswer(args -> {
+
+        // setup a NDO app that has MBS opted in to launch in car
+        ApplicationInfo launchableMBSInfo = new ApplicationInfo();
+        launchableMBSInfo.category = CATEGORY_VIDEO;
+        ResolveInfo launchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_LAUNCHABLE);
+        try {
+            Intent mbsIntent = new Intent();
+            mbsIntent.setComponent(launchableMBSApp.getComponentInfo().getComponentName());
+            mbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
+
+            when(mMockPackageManager.getApplicationInfo(
+                    launchableMBSApp.getComponentInfo().packageName,
+                    0))
+                    .thenReturn(launchableMBSInfo);
+
+
+            launchableMBSApp.serviceInfo.metaData = new Bundle();
+            launchableMBSApp.serviceInfo.metaData.putBoolean("androidx.car.app.launchable", true);
+
+            doReturn(Arrays.asList(launchableMBSApp)).when(mMockPackageManager).queryIntentServices(
+                    argThat((Intent i) -> i != null
+                            && launchableMBSApp.getComponentInfo().getComponentName()
+                            .equals(i.getComponent())),
+                    eq(PackageManager.GET_META_DATA));
+
+            when(mMockPackageManager.getLaunchIntentForPackage(
+                    launchableMBSApp.getComponentInfo().packageName))
+                    .thenReturn(new Intent());
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+
+        when(mMockPackageManager.queryIntentServices(any(), eq(PackageManager.GET_RESOLVED_FILTER)))
+                .thenAnswer(args -> {
             Intent intent = args.getArgument(0);
             if (intent.getAction().equals(MediaBrowserService.SERVICE_INTERFACE)) {
                 return Arrays.asList(mbs, videoApp, notlaunchableMBSApp, launchableMBSApp,
@@ -587,19 +624,20 @@
     }
 
     private void mockTosPackageManagerQueries() {
+        ResolveInfo resolveInfo = constructServiceResolveInfo(TEST_ENABLED_APP);
         try {
             when(mMockPackageManager.getServiceInfo(
-                    constructServiceResolveInfo(TEST_ENABLED_APP)
+                    resolveInfo
                             .getComponentInfo().getComponentName(),
                     PackageManager.GET_META_DATA))
                     .thenReturn(new ServiceInfo());
-        }  catch (PackageManager.NameNotFoundException e) {
+        } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException(e);
         }
         when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenAnswer(args -> {
             Intent intent = args.getArgument(0);
             if (intent.getAction().equals(MediaBrowserService.SERVICE_INTERFACE)) {
-                return Collections.singletonList(constructServiceResolveInfo(TEST_ENABLED_APP));
+                return Collections.singletonList(resolveInfo);
             }
             return new ArrayList<>();
         });
@@ -641,7 +679,7 @@
         doReturn(TEST_TOS_DISABLED_APP_1 + TOS_DISABLED_APPS_SEPARATOR
                 + TEST_TOS_DISABLED_APP_2)
                 .when(() -> Settings.Secure.getString(any(ContentResolver.class),
-                       eq(KEY_UNACCEPTED_TOS_DISABLED_APPS)));
+                        eq(KEY_UNACCEPTED_TOS_DISABLED_APPS)));
     }
 
     private void launchAllApps(List<AppMetaData> appMetaData) {