Snap for 6439596 from 7e14a22f019f8d49bb982c61709eee8db5652503 to qt-aml-tzdata-release

Change-Id: I43d5a03d1876bcd82ae4d3754edaa5b5bb2f206c
diff --git a/Android.mk b/Android.mk
index 28fd418..4e1658f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -51,7 +51,8 @@
 LOCAL_STATIC_ANDROID_LIBRARIES += \
     androidx-constraintlayout_constraintlayout \
     androidx.lifecycle_lifecycle-extensions \
-    car-media-common
+    car-media-common \
+    car-ui-lib
 
 # Including the resources for the static android libraries allows to pick up their static overlays.
 LOCAL_RESOURCE_DIR += \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6b3044a..f084f80 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -55,12 +55,7 @@
         <activity
             android:name=".AppGridActivity"
             android:launchMode="singleInstance"
-            android:noHistory="true">
-            <meta-data android:name="distractionOptimized" android:value="true"/>
-        </activity>
-        <activity
-            android:name=".AppSearchActivity"
-            android:launchMode="singleInstance"
+            android:exported="true"
             android:noHistory="true">
             <meta-data android:name="distractionOptimized" android:value="true"/>
         </activity>
diff --git a/OWNERS b/OWNERS
deleted file mode 100644
index 1686b3c..0000000
--- a/OWNERS
+++ /dev/null
@@ -1,7 +0,0 @@
-# Default code reviewers picked from top 3 or more developers.
-# Please update this list if you find better candidates.
-qiaozhang@google.com
-davidln@google.com
-ajchen@google.com
-arnaudberry@google.com
-stenning@google.com
diff --git a/res/layout/app_grid_activity.xml b/res/layout/app_grid_activity.xml
index af50bf1..e5d644d 100644
--- a/res/layout/app_grid_activity.xml
+++ b/res/layout/app_grid_activity.xml
@@ -27,10 +27,11 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"/>
 
-    <com.android.car.apps.common.widget.PagedRecyclerView
+    <com.android.car.ui.recyclerview.CarUiRecyclerView
         android:id="@+id/apps_grid"
+        app:layoutStyle="grid"
+        app:numOfColumns="4"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-    />
+        android:layout_height="match_parent"/>
 </LinearLayout>
 
diff --git a/res/layout/app_grid_activity_header.xml b/res/layout/app_grid_activity_header.xml
index cc38658..36aa10c 100644
--- a/res/layout/app_grid_activity_header.xml
+++ b/res/layout/app_grid_activity_header.xml
@@ -48,37 +48,13 @@
     </FrameLayout>
 
     <TextView
+        android:id="@+id/title"
         style="@style/TitleText"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/app_bar_height"
         android:layout_toEndOf="@id/exit_button_container"
         android:gravity="center_vertical"
-        android:text="@string/all_apps"
-    />
-
-    <FrameLayout
-        android:id="@+id/search_button_container"
-        android:layout_width="@dimen/panel_margin"
-        android:layout_height="@dimen/app_grid_touch_target_size"
-        android:layout_alignParentEnd="true"
-        android:layout_centerVertical="true"
-        android:background="?android:attr/selectableItemBackground"
-        android:clickable="true"
-        android:focusable="true"
-        android:visibility="gone"
-    >
-        <ImageButton
-            android:layout_width="@dimen/icon_size"
-            android:layout_height="@dimen/icon_size"
-            android:layout_gravity="center"
-            android:adjustViewBounds="true"
-            android:background="@null"
-            android:clickable="false"
-            android:focusable="false"
-            android:scaleType="fitXY"
-            android:src="@drawable/ic_search_black"
-            android:tint="@color/icon_tint"/>
-    </FrameLayout>
+        android:text="@string/app_launcher_title_all_apps"/>
 
     <View
         style="@style/HorizontalLineDivider"
diff --git a/res/layout/app_search_activity.xml b/res/layout/app_search_activity.xml
deleted file mode 100644
index 6b17641..0000000
--- a/res/layout/app_search_activity.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2018 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="?android:attr/colorPrimary"
-    android:gravity="center_horizontal"
-    android:orientation="vertical">
-
-    <RelativeLayout
-        android:id="@+id/app_search_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/app_bar_height">
-
-        <FrameLayout
-            android:id="@+id/exit_button_container"
-            android:layout_width="@dimen/panel_margin"
-            android:layout_height="@dimen/app_grid_touch_target_size"
-            android:background="?android:attr/selectableItemBackground"
-            android:clickable="true"
-            android:focusable="true">
-            <ImageButton
-                android:layout_width="@dimen/icon_size"
-                android:layout_height="@dimen/icon_size"
-                android:layout_gravity="center"
-                android:adjustViewBounds="true"
-                android:background="@null"
-                android:clickable="false"
-                android:focusable="false"
-                android:gravity="center"
-                android:src="@drawable/ic_arrow_back_black"
-                android:tint="@color/icon_tint"/>
-        </FrameLayout>
-
-        <EditText
-            android:id="@+id/app_search_bar"
-            android:layout_width="@dimen/search_item_width"
-            android:layout_height="@dimen/search_box_height"
-            android:layout_centerInParent="true"
-            android:layout_gravity="center_vertical"
-            android:background="?android:attr/colorPrimary"
-            android:drawablePadding="@dimen/search_bar_drawable_text_padding"
-            android:drawableStart="@drawable/ic_search_black"
-            android:drawableTint="@color/icon_tint"
-            android:inputType="text|textNoSuggestions"
-            android:maxLines="1"
-            android:paddingEnd="@dimen/search_bar_margin"
-            android:paddingStart="@dimen/search_bar_margin"
-            android:textAppearance="?android:attr/textAppearanceLarge"/>
-    </RelativeLayout>
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/search_result"
-        android:layout_width="@dimen/search_item_width"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/search_result_margin"/>
-
-</LinearLayout>
-
diff --git a/res/layout/app_search_result_item.xml b/res/layout/app_search_result_item.xml
deleted file mode 100644
index 5fdfcc1..0000000
--- a/res/layout/app_search_result_item.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2018 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/app_item"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="?android:attr/selectableItemBackground"
-    android:minHeight="@*android:dimen/car_single_line_list_item_height"
-    android:orientation="horizontal">
-
-    <ImageView
-        android:id="@+id/app_icon"
-        android:layout_width="@dimen/icon_size"
-        android:layout_height="@dimen/icon_size"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true"
-        android:layout_marginStart="@dimen/search_bar_margin"
-        android:layout_marginEnd="@dimen/search_bar_margin" />
-
-    <TextView
-        android:id="@+id/app_name"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true"
-        android:layout_marginStart="@dimen/search_result_text_margin"
-        android:layout_marginEnd="@dimen/search_result_text_margin"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:ellipsize="end"
-        android:maxLines="1" />
-</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 57e5376..a46a2af 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -16,7 +16,9 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_title">Car Launcher</string>
-    <string name="all_apps">All apps</string>
+
+    <string name="app_launcher_title_all_apps">All apps</string>
+    <string name="app_launcher_title_media_only">Media apps</string>
 
     <string name="driving_toast_text">
         <xliff:g id="app_name" example="Settings">%1$s</xliff:g> can\'t be used while driving.
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 5422778..3001aff 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -16,8 +16,7 @@
   -->
 
 <resources>
-    <!--Theme for the app, it's defined empty here so it can be overlaid easily -->
-    <style name="Theme.Launcher" parent="android:Theme.DeviceDefault.Wallpaper.NoTitleBar">
+    <style name="Theme.Launcher" parent="android:Theme.DeviceDefault.NoActionBar">
         <item name="textAppearanceGridItem">@android:style/TextAppearance.DeviceDefault.Medium</item>
         <item name="textAppearanceGridItemSecondary">@android:style/TextAppearance.DeviceDefault.Small</item>
     </style>
diff --git a/src/com/android/car/carlauncher/AppGridActivity.java b/src/com/android/car/carlauncher/AppGridActivity.java
index b0cb0d4..616bb5c 100644
--- a/src/com/android/car/carlauncher/AppGridActivity.java
+++ b/src/com/android/car/carlauncher/AppGridActivity.java
@@ -16,7 +16,9 @@
 
 package com.android.car.carlauncher;
 
-import android.annotation.Nullable;
+import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_LAUNCHABLES;
+import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_MEDIA_SERVICES;
+
 import android.app.Activity;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
@@ -32,13 +34,18 @@
 import android.content.ServiceConnection;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.View;
+import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
 import androidx.recyclerview.widget.RecyclerView;
@@ -57,12 +64,13 @@
  * Launcher activity that shows a grid of apps.
  */
 public final class AppGridActivity extends Activity {
-
     private static final String TAG = "AppGridActivity";
+    private static final String MODE_INTENT_EXTRA = "com.android.car.carlauncher.mode";
 
     private int mColumnNumber;
     private boolean mShowAllApps = true;
     private final Set<String> mHiddenApps = new HashSet<>();
+    private final Set<String> mCustomMediaComponents = new HashSet<>();
     private AppGridAdapter mGridAdapter;
     private PackageManager mPackageManager;
     private UsageStatsManager mUsageStatsManager;
@@ -70,6 +78,30 @@
     private Car mCar;
     private CarUxRestrictionsManager mCarUxRestrictionsManager;
     private CarPackageManager mCarPackageManager;
+    private Mode mMode;
+
+    private enum Mode {
+        ALL_APPS(R.string.app_launcher_title_all_apps,
+                APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
+                true),
+        MEDIA_ONLY(R.string.app_launcher_title_media_only,
+                APP_TYPE_MEDIA_SERVICES,
+                true),
+        MEDIA_POPUP(R.string.app_launcher_title_media_only,
+                APP_TYPE_MEDIA_SERVICES,
+                false),
+        ;
+        public final @StringRes int mTitleStringId;
+        public final @AppLauncherUtils.AppTypes int mAppTypes;
+        public final boolean mOpenMediaCenter;
+
+        Mode(@StringRes int titleStringId, @AppLauncherUtils.AppTypes int appTypes,
+                boolean openMediaCenter) {
+            mTitleStringId = titleStringId;
+            mAppTypes = appTypes;
+            mOpenMediaCenter = openMediaCenter;
+        }
+    }
 
     private ServiceConnection mCarConnectionListener = new ServiceConnection() {
         @Override
@@ -108,23 +140,19 @@
         mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
         mCar = Car.createCar(this, mCarConnectionListener);
         mHiddenApps.addAll(Arrays.asList(getResources().getStringArray(R.array.hidden_apps)));
+        mCustomMediaComponents.addAll(
+                Arrays.asList(getResources().getStringArray(R.array.custom_media_packages)));
 
         setContentView(R.layout.app_grid_activity);
 
+        updateMode();
+
         View exitView = findViewById(R.id.exit_button_container);
         exitView.setOnClickListener(v -> finish());
-        exitView.setOnLongClickListener(new View.OnLongClickListener() {
-            @Override
-            public boolean onLongClick(View v) {
-                mShowAllApps = !mShowAllApps;
-                updateAppsLists();
-                return true;
-            }
-        });
-
-        findViewById(R.id.search_button_container).setOnClickListener((View view) -> {
-            Intent intent = new Intent(this, AppSearchActivity.class);
-            startActivity(intent);
+        exitView.setOnLongClickListener(v -> {
+            mShowAllApps = !mShowAllApps;
+            updateAppsLists();
+            return true;
         });
 
         mGridAdapter = new AppGridAdapter(this);
@@ -138,11 +166,37 @@
             }
         });
         gridView.setLayoutManager(gridLayoutManager);
-
         gridView.setAdapter(mGridAdapter);
     }
 
     @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        setIntent(intent);
+        updateMode();
+    }
+
+    private void updateMode() {
+        mMode = parseMode(getIntent());
+        TextView titleView = findViewById(R.id.title);
+        titleView.setText(mMode.mTitleStringId);
+    }
+
+    /**
+     * Note: This activity is exported, meaning that it might receive intents from any source.
+     * Intent data parsing must be extra careful.
+     */
+    @NonNull
+    private Mode parseMode(@Nullable Intent intent) {
+        String mode = intent != null ? intent.getStringExtra(MODE_INTENT_EXTRA) : null;
+        try {
+            return mode != null ? Mode.valueOf(mode) : Mode.ALL_APPS;
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException("Received invalid mode: " + mode, e);
+        }
+    }
+
+    @Override
     protected void onResume() {
         super.onResume();
         // Using onResume() to refresh most recently used apps because we want to refresh even if
@@ -153,8 +207,13 @@
     /** Updates the list of all apps, and the list of the most recently used ones. */
     private void updateAppsLists() {
         Set<String> blackList = mShowAllApps ? Collections.emptySet() : mHiddenApps;
-        LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps(blackList,
-                getSystemService(LauncherApps.class), mCarPackageManager, mPackageManager);
+        LauncherAppsInfo appsInfo = AppLauncherUtils.getLauncherApps(blackList,
+                mCustomMediaComponents,
+                mMode.mAppTypes,
+                mMode.mOpenMediaCenter,
+                getSystemService(LauncherApps.class),
+                mCarPackageManager,
+                mPackageManager);
         mGridAdapter.setAllApps(appsInfo.getLaunchableComponentsList());
         mGridAdapter.setMostRecentApps(getMostRecentApps(appsInfo));
     }
diff --git a/src/com/android/car/carlauncher/AppItemViewHolder.java b/src/com/android/car/carlauncher/AppItemViewHolder.java
index ff3b651..418bb85 100644
--- a/src/com/android/car/carlauncher/AppItemViewHolder.java
+++ b/src/com/android/car/carlauncher/AppItemViewHolder.java
@@ -65,9 +65,12 @@
                 isLaunchable ? R.dimen.app_icon_opacity : R.dimen.app_icon_opacity_unavailable));
 
         if (isLaunchable) {
-            mAppItem.setOnClickListener(v -> AppLauncherUtils.launchApp(mContext, app));
-            mAppItem.setLongClickable(app.getAlternateLaunchIntent() != null);
-            mAppItem.setOnLongClickListener(v-> openAlternateLaunchIntent(app));
+            mAppItem.setOnClickListener(v -> app.getLaunchCallback().accept(mContext));
+            mAppItem.setLongClickable(app.getAlternateLaunchCallback() != null);
+            mAppItem.setOnLongClickListener(v-> {
+                app.getAlternateLaunchCallback().accept(mContext);
+                return true;
+            });
         } else {
             String warningText = mContext.getResources()
                     .getString(R.string.driving_toast_text, app.getDisplayName());
@@ -75,13 +78,4 @@
                     v -> Toast.makeText(mContext, warningText, Toast.LENGTH_LONG).show());
         }
     }
-
-    private boolean openAlternateLaunchIntent(AppMetaData app) {
-        Intent intent = app.getAlternateLaunchIntent();
-        if (intent != null) {
-            mContext.startActivity(intent);
-            return true;
-        }
-        return false;
-    }
 }
diff --git a/src/com/android/car/carlauncher/AppLauncherUtils.java b/src/com/android/car/carlauncher/AppLauncherUtils.java
index 91ccb44..223a551 100644
--- a/src/com/android/car/carlauncher/AppLauncherUtils.java
+++ b/src/com/android/car/carlauncher/AppLauncherUtils.java
@@ -16,11 +16,15 @@
 
 package com.android.car.carlauncher;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.annotation.Nullable;
+import android.app.Activity;
 import android.app.ActivityOptions;
 import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.content.pm.CarPackageManager;
+import android.car.media.CarMediaManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -33,8 +37,12 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.car.media.common.source.MediaSourceViewModel;
+
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 
+import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -50,6 +58,12 @@
 class AppLauncherUtils {
     private static final String TAG = "AppLauncherUtils";
 
+    @Retention(SOURCE)
+    @IntDef({APP_TYPE_LAUNCHABLES, APP_TYPE_MEDIA_SERVICES})
+    @interface AppTypes {}
+    static final int APP_TYPE_LAUNCHABLES = 1;
+    static final int APP_TYPE_MEDIA_SERVICES = 2;
+
     private AppLauncherUtils() {
     }
 
@@ -65,26 +79,26 @@
      *
      * @param app the requesting app's AppMetaData
      */
-    static void launchApp(Context context, AppMetaData app) {
+    static void launchApp(Context context, Intent intent) {
         ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchDisplayId(context.getDisplayId());
-        context.startActivity(app.getMainLaunchIntent(), options.toBundle());
+        context.startActivity(intent, options.toBundle());
     }
 
     /** Bundles application and services info. */
     static class LauncherAppsInfo {
         /*
          * Map of all car launcher components' (including launcher activities and media services)
-          * metadata keyed by ComponentName.
+         * metadata keyed by ComponentName.
          */
         private final Map<ComponentName, AppMetaData> mLaunchables;
 
         /** Map of all the media services keyed by ComponentName. */
         private final Map<ComponentName, ResolveInfo> mMediaServices;
 
-        LauncherAppsInfo(@NonNull Map<ComponentName, AppMetaData> components,
+        LauncherAppsInfo(@NonNull Map<ComponentName, AppMetaData> launchablesMap,
                 @NonNull Map<ComponentName, ResolveInfo> mediaServices) {
-            mLaunchables = components;
+            mLaunchables = launchablesMap;
             mMediaServices = mediaServices;
         }
 
@@ -143,15 +157,24 @@
      * Gets all the components that we want to see in the launcher in unsorted order, including
      * launcher activities and media services.
      *
-     * @param blackList         A (possibly empty) list of apps (package names) to hide
-     * @param launcherApps      The {@link LauncherApps} system service
-     * @param carPackageManager The {@link CarPackageManager} system service
-     * @param packageManager    The {@link PackageManager} system service
+     * @param blackList             A (possibly empty) list of apps (package names) to hide
+     * @param customMediaComponents A (possibly empty) list of media components (component names)
+     *                              that shouldn't be shown in Launcher because their applications'
+     *                              launcher activities will be shown
+     * @param appTypes              Types of apps to show (e.g.: all, or media sources only)
+     * @param openMediaCenter       Whether launcher should navigate to media center when the
+     *                              user selects a media source.
+     * @param launcherApps          The {@link LauncherApps} system service
+     * @param carPackageManager     The {@link CarPackageManager} system service
+     * @param packageManager        The {@link PackageManager} system service
      * @return a new {@link LauncherAppsInfo}
      */
     @NonNull
-    static LauncherAppsInfo getAllLauncherApps(
+    static LauncherAppsInfo getLauncherApps(
             @NonNull Set<String> blackList,
+            @NonNull Set<String> customMediaComponents,
+            @AppTypes int appTypes,
+            boolean openMediaCenter,
             LauncherApps launcherApps,
             CarPackageManager carPackageManager,
             PackageManager packageManager) {
@@ -166,73 +189,114 @@
         List<LauncherActivityInfo> availableActivities =
                 launcherApps.getActivityList(null, Process.myUserHandle());
 
-        Map<ComponentName, AppMetaData> components = new HashMap<>(
+        Map<ComponentName, AppMetaData> launchablesMap = new HashMap<>(
                 mediaServices.size() + availableActivities.size());
         Map<ComponentName, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
-        Set<String> mediaPackages = new HashSet<>();
 
         // Process media services
-        for (ResolveInfo info : mediaServices) {
-            String packageName = info.serviceInfo.packageName;
-            String className = info.serviceInfo.name;
-            ComponentName componentName = new ComponentName(packageName, className);
-            mediaServicesMap.put(componentName, info);
-            if (shouldAdd(componentName, components, blackList)) {
-                mediaPackages.add(packageName);
-                final boolean isDistractionOptimized = true;
+        if ((appTypes & APP_TYPE_MEDIA_SERVICES) != 0) {
+            for (ResolveInfo info : mediaServices) {
+                String packageName = info.serviceInfo.packageName;
+                String className = info.serviceInfo.name;
+                ComponentName componentName = new ComponentName(packageName, className);
+                mediaServicesMap.put(componentName, info);
+                if (shouldAddToLaunchables(componentName, blackList, customMediaComponents,
+                        appTypes, APP_TYPE_MEDIA_SERVICES)) {
+                    final boolean isDistractionOptimized = true;
 
-                Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
-                intent.putExtra(Car.CAR_EXTRA_MEDIA_COMPONENT, componentName.flattenToString());
+                    Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
+                    intent.putExtra(Car.CAR_EXTRA_MEDIA_COMPONENT, componentName.flattenToString());
 
-                AppMetaData appMetaData = new AppMetaData(
+                    AppMetaData appMetaData = new AppMetaData(
                         info.serviceInfo.loadLabel(packageManager),
                         componentName,
                         info.serviceInfo.loadIcon(packageManager),
                         isDistractionOptimized,
-                        intent,
-                        packageManager.getLaunchIntentForPackage(packageName));
-                components.put(componentName, appMetaData);
+                        context -> {
+                            if (openMediaCenter) {
+                                AppLauncherUtils.launchApp(context, intent);
+                            } else {
+                                selectMediaSourceAndFinish(context, componentName);
+                            }
+                        },
+                        context -> AppLauncherUtils.launchApp(context,
+                            packageManager.getLaunchIntentForPackage(packageName)));
+                    launchablesMap.put(componentName, appMetaData);
+                }
             }
         }
 
         // Process activities
-        for (LauncherActivityInfo info : availableActivities) {
-            ComponentName componentName = info.getComponentName();
-            String packageName = componentName.getPackageName();
-            // If a media service has been added to the map, don't add the activity belonging to the
-            // same package.
-            if (mediaPackages.contains(packageName)) {
-                continue;
-            }
-            if (shouldAdd(componentName, components, blackList)) {
-                boolean isDistractionOptimized =
+        if ((appTypes & APP_TYPE_LAUNCHABLES) != 0) {
+            for (LauncherActivityInfo info : availableActivities) {
+                ComponentName componentName = info.getComponentName();
+                String packageName = componentName.getPackageName();
+                if (shouldAddToLaunchables(componentName, blackList, customMediaComponents,
+                        appTypes, APP_TYPE_LAUNCHABLES)) {
+                    boolean isDistractionOptimized =
                         isActivityDistractionOptimized(carPackageManager, packageName,
-                                info.getName());
+                            info.getName());
 
-                Intent intent = new Intent(Intent.ACTION_MAIN)
+                    Intent intent = new Intent(Intent.ACTION_MAIN)
                         .setComponent(componentName)
                         .addCategory(Intent.CATEGORY_LAUNCHER)
                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                AppMetaData appMetaData = new AppMetaData(
+                    AppMetaData appMetaData = new AppMetaData(
                         info.getLabel(),
                         componentName,
                         info.getBadgedIcon(0),
                         isDistractionOptimized,
-                        intent,
-                        packageManager.getLaunchIntentForPackage(packageName));
-                components.put(componentName, appMetaData);
+                        context -> AppLauncherUtils.launchApp(context, intent),
+                        null);
+                    launchablesMap.put(componentName, appMetaData);
+                }
             }
         }
 
-        return new LauncherAppsInfo(components, mediaServicesMap);
+        return new LauncherAppsInfo(launchablesMap, mediaServicesMap);
     }
 
-    private static boolean shouldAdd(ComponentName componentName,
-            Map<ComponentName, AppMetaData> components,
-            @NonNull Set<String> blackList) {
-        return !components.containsKey(componentName) && !blackList.contains(
-                componentName.getPackageName());
+    private static boolean shouldAddToLaunchables(@NonNull ComponentName componentName,
+            @NonNull Set<String> blackList,
+            @NonNull Set<String> customMediaComponents,
+            @AppTypes int appTypesToShow,
+            @AppTypes int componentAppType) {
+        if (blackList.contains(componentName.getPackageName())) {
+            return false;
+        }
+        switch (componentAppType) {
+            // Process media services
+            case APP_TYPE_MEDIA_SERVICES:
+                // For a media service in customMediaComponents, if its application's launcher
+                // activity will be shown in the Launcher, don't show the service's icon in the
+                // Launcher.
+                if (customMediaComponents.contains(componentName.flattenToString())
+                        && (appTypesToShow & APP_TYPE_LAUNCHABLES) != 0) {
+                    return false;
+                }
+                return true;
+            // Process activities
+            case APP_TYPE_LAUNCHABLES:
+                return true;
+            default:
+                Log.e(TAG, "Invalid componentAppType : " + componentAppType);
+                return false;
+        }
+    }
+
+    private static void selectMediaSourceAndFinish(Context context, ComponentName componentName) {
+        try {
+            Car carApi = Car.createCar(context);
+            CarMediaManager manager = (CarMediaManager) carApi
+                    .getCarManager(Car.CAR_MEDIA_SERVICE);
+            manager.setMediaSource(componentName);
+            if (context instanceof Activity) {
+                ((Activity) context).finish();
+            }
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car not connected", e);
+        }
     }
 
     /**
diff --git a/src/com/android/car/carlauncher/AppMetaData.java b/src/com/android/car/carlauncher/AppMetaData.java
index 14c6a9f..5360df3 100644
--- a/src/com/android/car/carlauncher/AppMetaData.java
+++ b/src/com/android/car/carlauncher/AppMetaData.java
@@ -18,9 +18,12 @@
 
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 
+import java.util.function.Consumer;
+
 /**
  * Meta data of an app including the display name, the component name, the icon drawable, and an
  * intent to either open the app or the media center (for media services).
@@ -34,8 +37,8 @@
     private final ComponentName mComponentName;
     private final Drawable mIcon;
     private final boolean mIsDistractionOptimized;
-    private final Intent mMainLaunchIntent;
-    private final Intent mAlternateLaunchIntent;
+    private final Consumer<Context> mLaunchCallback;
+    private final Consumer<Context> mAlternateLaunchCallback;
 
     /**
      * AppMetaData
@@ -44,24 +47,23 @@
      * @param componentName          the component name
      * @param icon                   the application's icon
      * @param isDistractionOptimized whether mainLaunchIntent is safe for driving
-     * @param mainLaunchIntent       what to open by default (goes to the media center for media
-     *                               apps)
-     * @param alternateLaunchIntent  temporary allowance for media apps that still need to show UI
-     *                               beyond sign in and settings
+     * @param launchCallback         action to execute to launch this app
+     * @param alternateLaunchCallback  temporary alternative action to execute (e.g.: for media apps
+     *                               this allows opening their own UI).
      */
     AppMetaData(
             CharSequence displayName,
             ComponentName componentName,
             Drawable icon,
             boolean isDistractionOptimized,
-            Intent mainLaunchIntent,
-            @Nullable Intent alternateLaunchIntent) {
+            Consumer<Context> launchCallback,
+            Consumer<Context> alternateLaunchCallback) {
         mDisplayName = displayName == null ? "" : displayName.toString();
         mComponentName = componentName;
         mIcon = icon;
         mIsDistractionOptimized = isDistractionOptimized;
-        mMainLaunchIntent = mainLaunchIntent;
-        mAlternateLaunchIntent = alternateLaunchIntent;
+        mLaunchCallback = launchCallback;
+        mAlternateLaunchCallback = alternateLaunchCallback;
     }
 
     public String getDisplayName() {
@@ -76,12 +78,12 @@
         return mComponentName;
     }
 
-    Intent getMainLaunchIntent() {
-        return mMainLaunchIntent;
+    Consumer<Context> getLaunchCallback() {
+        return mLaunchCallback;
     }
 
-    Intent getAlternateLaunchIntent() {
-        return mAlternateLaunchIntent;
+    Consumer<Context> getAlternateLaunchCallback() {
+        return mAlternateLaunchCallback;
     }
 
     public Drawable getIcon() {
diff --git a/src/com/android/car/carlauncher/AppSearchActivity.java b/src/com/android/car/carlauncher/AppSearchActivity.java
deleted file mode 100644
index 2947906..0000000
--- a/src/com/android/car/carlauncher/AppSearchActivity.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.car.carlauncher;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.content.pm.CarPackageManager;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-
-import androidx.recyclerview.widget.RecyclerView;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Activity that allows user to search in apps.
- */
-public final class AppSearchActivity extends Activity {
-
-    private static final String TAG = "AppSearchActivity";
-
-    private PackageManager mPackageManager;
-    private SearchResultAdapter mSearchResultAdapter;
-    private AppInstallUninstallReceiver mInstallUninstallReceiver;
-    private Car mCar;
-    private CarPackageManager mCarPackageManager;
-    private CarUxRestrictionsManager mCarUxRestrictionsManager;
-
-    private ServiceConnection mCarConnectionListener = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
-                        Car.CAR_UX_RESTRICTION_SERVICE);
-                mSearchResultAdapter.setIsDistractionOptimizationRequired(
-                        mCarUxRestrictionsManager
-                                .getCurrentCarUxRestrictions()
-                                .isRequiresDistractionOptimization());
-                mCarUxRestrictionsManager.registerListener(
-                        restrictionInfo ->
-                                mSearchResultAdapter.setIsDistractionOptimizationRequired(
-                                        restrictionInfo.isRequiresDistractionOptimization()));
-
-                mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
-                mSearchResultAdapter.setAllApps(getAllApps());
-            } catch (CarNotConnectedException e) {
-                Log.e(TAG, "Car not connected in CarConnectionListener", e);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mCarUxRestrictionsManager = null;
-            mCarPackageManager = null;
-        }
-    };
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mPackageManager = getPackageManager();
-        mCar = Car.createCar(this, mCarConnectionListener);
-
-        setContentView(R.layout.app_search_activity);
-        findViewById(R.id.container).setOnTouchListener(
-                (view, event) -> {
-                    hideKeyboard();
-                    return false;
-                });
-        findViewById(R.id.exit_button_container).setOnClickListener(view -> finish());
-
-        RecyclerView searchResultView = findViewById(R.id.search_result);
-        searchResultView.setClipToOutline(true);
-        mSearchResultAdapter = new SearchResultAdapter(this);
-        searchResultView.setAdapter(mSearchResultAdapter);
-
-        EditText searchBar = findViewById(R.id.app_search_bar);
-        searchBar.addTextChangedListener(new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-            }
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (TextUtils.isEmpty(s)) {
-                    searchResultView.setVisibility(View.GONE);
-                    mSearchResultAdapter.clearResults();
-                } else {
-                    searchResultView.setVisibility(View.VISIBLE);
-                    mSearchResultAdapter.getFilter().filter(s.toString());
-                }
-            }
-        });
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        // register broadcast receiver for package installation and uninstallation
-        mInstallUninstallReceiver = new AppInstallUninstallReceiver();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        registerReceiver(mInstallUninstallReceiver, filter);
-
-        // Connect to car service
-        mCar.connect();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onPause();
-        // disconnect from app install/uninstall receiver
-        if (mInstallUninstallReceiver != null) {
-            unregisterReceiver(mInstallUninstallReceiver);
-            mInstallUninstallReceiver = null;
-        }
-        // disconnect from car listeners
-        try {
-            if (mCarUxRestrictionsManager != null) {
-                mCarUxRestrictionsManager.unregisterListener();
-            }
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Error unregistering listeners", e);
-        }
-        if (mCar != null) {
-            mCar.disconnect();
-        }
-    }
-
-    private List<AppMetaData> getAllApps() {
-        AppLauncherUtils.LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps(
-                Collections.emptySet(), getSystemService(LauncherApps.class), mCarPackageManager,
-                mPackageManager);
-        return appsInfo.getLaunchableComponentsList();
-    }
-
-    public void hideKeyboard() {
-        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(
-                Activity.INPUT_METHOD_SERVICE);
-        inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
-    }
-
-    private class AppInstallUninstallReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String packageName = intent.getData().getSchemeSpecificPart();
-
-            if (TextUtils.isEmpty(packageName)) {
-                Log.e(TAG, "System sent an empty app install/uninstall broadcast");
-                return;
-            }
-
-            mSearchResultAdapter.setAllApps(getAllApps());
-        }
-    }
-}
diff --git a/src/com/android/car/carlauncher/SearchResultAdapter.java b/src/com/android/car/carlauncher/SearchResultAdapter.java
deleted file mode 100644
index 6844dee..0000000
--- a/src/com/android/car/carlauncher/SearchResultAdapter.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.car.carlauncher;
-
-import android.app.Activity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Filter;
-import android.widget.Filterable;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.regex.Pattern;
-
-/**
- * The search result adapter that binds the filtered result to a list view.
- */
-
-final class SearchResultAdapter
-        extends RecyclerView.Adapter<SearchResultAdapter.ViewHolder> implements Filterable {
-    private final Activity mContext;
-    private List<AppMetaData> mAllApps;
-    private List<AppMetaData> mSearchResults;
-    private AppSearchFilter filter;
-    private boolean mIsDistractionOptimizationRequired;
-
-    public SearchResultAdapter(Activity context) {
-        mContext = context;
-        mSearchResults = null;
-    }
-
-    public void setIsDistractionOptimizationRequired(boolean isDistractionOptimizationRequired) {
-        mIsDistractionOptimizationRequired = isDistractionOptimizationRequired;
-        notifyDataSetChanged();
-    }
-
-    public void setAllApps(List<AppMetaData> apps) {
-        mAllApps = apps;
-        notifyDataSetChanged();
-    }
-
-    public static class ViewHolder extends RecyclerView.ViewHolder {
-        private View mAppItem;
-        private ImageView mAppIconView;
-        private TextView mAppNameView;
-
-        public ViewHolder(View view) {
-            super(view);
-            mAppItem = view.findViewById(R.id.app_item);
-            mAppIconView = mAppItem.findViewById(R.id.app_icon);
-            mAppNameView = mAppItem.findViewById(R.id.app_name);
-        }
-
-        public void bind(AppMetaData app, View.OnClickListener listener) {
-            if (app == null) {
-                // Empty out the view
-                mAppIconView.setImageDrawable(null);
-                mAppNameView.setText(null);
-                mAppItem.setClickable(false);
-                mAppItem.setOnClickListener(null);
-            } else {
-                mAppIconView.setImageDrawable(app.getIcon());
-                mAppNameView.setText(app.getDisplayName());
-                mAppItem.setClickable(true);
-                mAppItem.setOnClickListener(listener);
-            }
-        }
-    }
-
-    /**
-     * This filter does a simple case insensitive text match based on the application
-     * display name. e.g. "gm" -> "gmail".
-     * It only matches apps starting with the search query. It does not do sub-string matching.
-     */
-    private class AppSearchFilter extends Filter {
-
-        @Override
-        protected FilterResults performFiltering(CharSequence constraint) {
-            FilterResults results = new FilterResults();
-            if (constraint != null && constraint.length() > 0) {
-                List filterList = new ArrayList();
-                for (int i = 0; i < mAllApps.size(); i++) {
-                    AppMetaData app = mAllApps.get(i);
-                    if (shouldShowApp(
-                            app.getDisplayName(),
-                            constraint.toString(),
-                            app.getIsDistractionOptimized())) {
-                        filterList.add(app);
-                    }
-                }
-                Collections.sort(filterList, AppLauncherUtils.ALPHABETICAL_COMPARATOR);
-                results.count = filterList.size();
-                results.values = filterList;
-            } else {
-                results.count = 0;
-                results.values = null;
-            }
-
-            return results;
-        }
-
-        private boolean shouldShowApp(
-                String displayName, String constraint, boolean isDistractionOptimized) {
-            if (!mIsDistractionOptimizationRequired
-                    || (mIsDistractionOptimizationRequired && isDistractionOptimized)) {
-                Pattern pattern = Pattern.compile(
-                        "^" + constraint + ".*$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
-                return pattern.matcher(displayName).matches();
-            }
-            return false;
-        }
-
-        @Override
-        protected void publishResults(CharSequence constraint, FilterResults results) {
-            mSearchResults = (ArrayList) results.values;
-            notifyDataSetChanged();
-        }
-    }
-
-    @Override
-    public Filter getFilter() {
-        if (filter == null) {
-            filter = new AppSearchFilter();
-        }
-        return filter;
-    }
-
-    @NonNull
-    @Override
-    public SearchResultAdapter.ViewHolder onCreateViewHolder(
-            @NonNull ViewGroup parent, int viewType) {
-        View view = mContext.getLayoutInflater().inflate(R.layout.app_search_result_item, null);
-        return new ViewHolder(view);
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull SearchResultAdapter.ViewHolder holder, int position) {
-        // Get the data item for this position
-        AppMetaData app = mSearchResults.get(position);
-        holder.bind(app, v -> AppLauncherUtils.launchApp(mContext, app));
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return position;
-    }
-
-    @Override
-    public int getItemCount() {
-        if (mSearchResults != null) {
-            return mSearchResults.size();
-        } else {
-            return 0;
-        }
-    }
-
-    void clearResults() {
-        mSearchResults.clear();
-        notifyDataSetChanged();
-    }
-}