Add two new samples and one new sample category.

- Added MediaBrowserService Android Auto compatible media sample;
- Added MessagingService Android Auto compatible notification sample;
- Renamed package names of both new samples;
- Created a new "notification" category;
- Moved existing notifications samples to the new "notification" category;

Bug: 18323953
Change-Id: Ibdc785ba68b6f9431d61525af5a4456a8598d5ce
diff --git a/build.gradle b/build.gradle
index 9615031..740e531 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,8 +24,8 @@
 "ui/actionbarcompat/ActionBarCompat-Styled",
 "ui/actionbarcompat/ActionBarCompat-ListPopupMenu",
 "ui/actionbarcompat/ActionBarCompat-ShareActionProvider",
-"ui/notifications/BasicNotifications",
-"ui/notifications/CustomNotifications",
+"notification/BasicNotifications",
+"notification/CustomNotifications",
 "ui/actionbar/DoneBar",
 "ui/graphics/PdfRendererBasic",
 "ui/window/BasicImmersiveMode",
@@ -47,6 +47,7 @@
 "ui/views/SwipeRefreshLayout/SwipeRefreshLayoutBasic",
 "ui/views/SwipeRefreshLayout/SwipeRefreshListFragment",
 "ui/views/SwipeRefreshLayout/SwipeRefreshMultipleViews",
+"media/MediaBrowserService",
 "media/MediaRouter",
 "media/MediaEffects",
 "admin/BasicManagedProfile",
@@ -62,7 +63,8 @@
 "background/JobScheduler",
 "ui/views/RecyclerView",
 "ui/views/CardView",
-"ui/notifications/LNotifications",
+"notification/LNotifications",
+"notification/MessagingService",
 "ui/DrawableTinting",
 "ui/Interpolator",
 "media/HdrViewfinder",
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/media/MediaBrowserService/Application/.gitignore
similarity index 100%
copy from ui/notifications/LNotifications/Application/.gitignore
copy to media/MediaBrowserService/Application/.gitignore
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/media/MediaBrowserService/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to media/MediaBrowserService/Application/proguard-project.txt
diff --git a/media/MediaBrowserService/Application/src/androidTest/java/com/example/android/mediabrowserservice/test/SampleTests.java b/media/MediaBrowserService/Application/src/androidTest/java/com/example/android/mediabrowserservice/test/SampleTests.java
new file mode 100644
index 0000000..7bf34eb
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/androidTest/java/com/example/android/mediabrowserservice/test/SampleTests.java
@@ -0,0 +1,76 @@
+/*
+* Copyright 2013 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.
+*/
+/*
+* Copyright (C) 2014 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.example.android.mediabrowserservice.test;
+
+import com.example.android.mediabrowserservice.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for MediaBrowserService sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private MediaBrowserServiceFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (MediaBrowserServiceFragment)
+            mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+    * Test if the test fixture has been set up correctly.
+    */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    /**
+    * Add more tests below.
+    */
+
+}
diff --git a/media/MediaBrowserService/Application/src/main/AndroidManifest.xml b/media/MediaBrowserService/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6d05c27
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.mediabrowserservice"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <uses-sdk
+        android:minSdkVersion="21"
+        android:targetSdkVersion="21" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme">
+
+        <meta-data android:name="com.google.android.gms.car.application"
+            android:resource="@xml/automotive_app_desc"/>
+
+
+        <activity android:name="com.example.android.mediabrowserservice.MusicPlayerActivity"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <!-- (OPTIONAL) use this meta data to indicate which icon should be used in media
+            notifications (for example, when the music changes and the user is
+            looking at another app) -->
+        <meta-data
+            android:name="com.google.android.gms.car.notification.SmallIcon"
+            android:resource="@drawable/ic_notification" />
+
+        <service
+            android:name="com.example.android.mediabrowserservice.MusicService"
+            android:exported="true"
+            >
+            <intent-filter>
+                <action android:name="android.media.browse.MediaBrowserService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java
new file mode 100644
index 0000000..726ae15
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014 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.example.android.mediabrowserservice;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Fragment that lists all the various browsable queues available
+ * from a {@link android.service.media.MediaBrowserService}.
+ * <p/>
+ * It uses a {@link MediaBrowser} to connect to the {@link MusicService}. Once connected,
+ * the fragment subscribes to get all the children. All {@link MediaBrowser.MediaItem}'s
+ * that can be browsed are shown in a ListView.
+ */
+public class BrowseFragment extends Fragment {
+
+    private static final String TAG = BrowseFragment.class.getSimpleName();
+
+    public static final String ARG_MEDIA_ID = "media_id";
+
+    public static interface FragmentDataHelper {
+        void onMediaItemSelected(MediaBrowser.MediaItem item);
+    }
+
+    // The mediaId to be used for subscribing for children using the MediaBrowser.
+    private String mMediaId;
+
+    private MediaBrowser mMediaBrowser;
+    private BrowseAdapter mBrowserAdapter;
+
+    private MediaBrowser.SubscriptionCallback mSubscriptionCallback = new MediaBrowser.SubscriptionCallback() {
+
+        @Override
+        public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+            mBrowserAdapter.clear();
+            mBrowserAdapter.notifyDataSetInvalidated();
+            for (MediaBrowser.MediaItem item : children) {
+                mBrowserAdapter.add(item);
+            }
+            mBrowserAdapter.notifyDataSetChanged();
+        }
+
+        @Override
+        public void onError(String id) {
+            Toast.makeText(getActivity(), R.string.error_loading_media,
+                    Toast.LENGTH_LONG).show();
+        }
+    };
+
+    private MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+        @Override
+        public void onConnected() {
+            LogHelper.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken());
+
+            if (mMediaId == null) {
+                mMediaId = mMediaBrowser.getRoot();
+            }
+            mMediaBrowser.subscribe(mMediaId, mSubscriptionCallback);
+            if (mMediaBrowser.getSessionToken() == null) {
+                throw new IllegalArgumentException("No Session token");
+            }
+            MediaController mediaController = new MediaController(getActivity(),
+                    mMediaBrowser.getSessionToken());
+            getActivity().setMediaController(mediaController);
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            LogHelper.d(TAG, "onConnectionFailed");
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            LogHelper.d(TAG, "onConnectionSuspended");
+            getActivity().setMediaController(null);
+        }
+    };
+
+    public static BrowseFragment newInstance(String mediaId) {
+        Bundle args = new Bundle();
+        args.putString(ARG_MEDIA_ID, mediaId);
+        BrowseFragment fragment = new BrowseFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_list, container, false);
+
+        mBrowserAdapter = new BrowseAdapter(getActivity());
+
+        View controls = rootView.findViewById(R.id.controls);
+        controls.setVisibility(View.GONE);
+
+        ListView listView = (ListView) rootView.findViewById(R.id.list_view);
+        listView.setAdapter(mBrowserAdapter);
+        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                MediaBrowser.MediaItem item = mBrowserAdapter.getItem(position);
+                try {
+                    FragmentDataHelper listener = (FragmentDataHelper) getActivity();
+                    listener.onMediaItemSelected(item);
+                } catch (ClassCastException ex) {
+                    Log.e(TAG, "Exception trying to cast to FragmentDataHelper", ex);
+                }
+            }
+        });
+
+        Bundle args = getArguments();
+        mMediaId = args.getString(ARG_MEDIA_ID, null);
+
+        mMediaBrowser = new MediaBrowser(getActivity(),
+                new ComponentName(getActivity(), MusicService.class),
+                mConnectionCallback, null);
+
+        return rootView;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mMediaBrowser.connect();
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mMediaBrowser.disconnect();
+    }
+
+    // An adapter for showing the list of browsed MediaItem's
+    private static class BrowseAdapter extends ArrayAdapter<MediaBrowser.MediaItem> {
+
+        public BrowseAdapter(Context context) {
+            super(context, R.layout.media_list_item, new ArrayList<MediaBrowser.MediaItem>());
+        }
+
+        static class ViewHolder {
+            ImageView mImageView;
+            TextView mTitleView;
+            TextView mDescriptionView;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+
+            ViewHolder holder;
+
+            if (convertView == null) {
+                convertView = LayoutInflater.from(getContext())
+                        .inflate(R.layout.media_list_item, parent, false);
+                holder = new ViewHolder();
+                holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
+                holder.mImageView.setVisibility(View.GONE);
+                holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
+                holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
+
+            MediaBrowser.MediaItem item = getItem(position);
+            holder.mTitleView.setText(item.getDescription().getTitle());
+            holder.mDescriptionView.setText(item.getDescription().getDescription());
+            if (item.isPlayable()) {
+                holder.mImageView.setImageDrawable(
+                        getContext().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+                holder.mImageView.setVisibility(View.VISIBLE);
+            }
+            return convertView;
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MediaNotification.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MediaNotification.java
new file mode 100644
index 0000000..7b8631a
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MediaNotification.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.mediabrowserservice;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.AsyncTask;
+import android.util.LruCache;
+import android.util.SparseArray;
+
+import com.example.android.mediabrowserservice.utils.BitmapHelper;
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.io.IOException;
+
+/**
+ * Keeps track of a notification and updates it automatically for a given
+ * MediaSession. Maintaining a visible notification (usually) guarantees that the music service
+ * won't be killed during playback.
+ */
+public class MediaNotification extends BroadcastReceiver {
+    private static final String TAG = "MediaNotification";
+
+    private static final int NOTIFICATION_ID = 412;
+
+    public static final String ACTION_PAUSE = "com.example.android.mediabrowserservice.pause";
+    public static final String ACTION_PLAY = "com.example.android.mediabrowserservice.play";
+    public static final String ACTION_PREV = "com.example.android.mediabrowserservice.prev";
+    public static final String ACTION_NEXT = "com.example.android.mediabrowserservice.next";
+
+    private static final int MAX_ALBUM_ART_CACHE_SIZE = 1024*1024;
+
+    private final MusicService mService;
+    private MediaSession.Token mSessionToken;
+    private MediaController mController;
+    private MediaController.TransportControls mTransportControls;
+    private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>();
+    private final LruCache<String, Bitmap> mAlbumArtCache;
+
+    private PlaybackState mPlaybackState;
+    private MediaMetadata mMetadata;
+
+    private Notification.Builder mNotificationBuilder;
+    private NotificationManager mNotificationManager;
+    private Notification.Action mPlayPauseAction;
+
+    private String mCurrentAlbumArt;
+    private int mNotificationColor;
+
+    private boolean mStarted = false;
+
+    public MediaNotification(MusicService service) {
+        mService = service;
+        updateSessionToken();
+
+        // simple album art cache that holds no more than
+        // MAX_ALBUM_ART_CACHE_SIZE bytes:
+        mAlbumArtCache = new LruCache<String, Bitmap>(MAX_ALBUM_ART_CACHE_SIZE) {
+            @Override
+            protected int sizeOf(String key, Bitmap value) {
+                return value.getByteCount();
+            }
+        };
+
+        mNotificationColor = getNotificationColor();
+
+        mNotificationManager = (NotificationManager) mService
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        String pkg = mService.getPackageName();
+        mIntents.put(R.drawable.ic_pause_white_24dp, PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_play_arrow_white_24dp, PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_skip_previous_white_24dp, PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_skip_next_white_24dp, PendingIntent.getBroadcast(mService, 100,
+                new Intent(ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT));
+    }
+
+    protected int getNotificationColor() {
+        int notificationColor = 0;
+        String packageName = mService.getPackageName();
+        try {
+            Context packageContext = mService.createPackageContext(packageName, 0);
+            ApplicationInfo applicationInfo =
+                    mService.getPackageManager().getApplicationInfo(packageName, 0);
+            packageContext.setTheme(applicationInfo.theme);
+            Resources.Theme theme = packageContext.getTheme();
+            TypedArray ta = theme.obtainStyledAttributes(
+                    new int[] {android.R.attr.colorPrimary});
+            notificationColor = ta.getColor(0, Color.DKGRAY);
+            ta.recycle();
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return notificationColor;
+    }
+
+    /**
+     * Posts the notification and starts tracking the session to keep it
+     * updated. The notification will automatically be removed if the session is
+     * destroyed before {@link #stopNotification} is called.
+     */
+    public void startNotification() {
+        if (!mStarted) {
+            mController.registerCallback(mCb);
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ACTION_NEXT);
+            filter.addAction(ACTION_PAUSE);
+            filter.addAction(ACTION_PLAY);
+            filter.addAction(ACTION_PREV);
+            mService.registerReceiver(this, filter);
+
+            mMetadata = mController.getMetadata();
+            mPlaybackState = mController.getPlaybackState();
+
+            mStarted = true;
+            // The notification must be updated after setting started to true
+            updateNotificationMetadata();
+        }
+    }
+
+    /**
+     * Removes the notification and stops tracking the session. If the session
+     * was destroyed this has no effect.
+     */
+    public void stopNotification() {
+        mStarted = false;
+        mController.unregisterCallback(mCb);
+        try {
+            mNotificationManager.cancel(NOTIFICATION_ID);
+            mService.unregisterReceiver(this);
+        } catch (IllegalArgumentException ex) {
+            // ignore if the receiver is not registered.
+        }
+        mService.stopForeground(true);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        LogHelper.d(TAG, "Received intent with action " + action);
+        if (ACTION_PAUSE.equals(action)) {
+            mTransportControls.pause();
+        } else if (ACTION_PLAY.equals(action)) {
+            mTransportControls.play();
+        } else if (ACTION_NEXT.equals(action)) {
+            mTransportControls.skipToNext();
+        } else if (ACTION_PREV.equals(action)) {
+            mTransportControls.skipToPrevious();
+        }
+    }
+
+    /**
+     * Update the state based on a change on the session token. Called either when
+     * we are running for the first time or when the media session owner has destroyed the session
+     * (see {@link android.media.session.MediaController.Callback#onSessionDestroyed()})
+     */
+    private void updateSessionToken() {
+        MediaSession.Token freshToken = mService.getSessionToken();
+        if (mSessionToken == null || !mSessionToken.equals(freshToken)) {
+            if (mController != null) {
+                mController.unregisterCallback(mCb);
+            }
+            mSessionToken = freshToken;
+            mController = new MediaController(mService, mSessionToken);
+            mTransportControls = mController.getTransportControls();
+            if (mStarted) {
+                mController.registerCallback(mCb);
+            }
+        }
+    }
+
+    private final MediaController.Callback mCb = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            mPlaybackState = state;
+            LogHelper.d(TAG, "Received new playback state", state);
+            updateNotificationPlaybackState();
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            mMetadata = metadata;
+            LogHelper.d(TAG, "Received new metadata ", metadata);
+            updateNotificationMetadata();
+        }
+
+        @Override
+        public void onSessionDestroyed() {
+            super.onSessionDestroyed();
+            LogHelper.d(TAG, "Session was destroyed, resetting to the new session token");
+            updateSessionToken();
+        }
+    };
+
+    private void updateNotificationMetadata() {
+        LogHelper.d(TAG, "updateNotificationMetadata. mMetadata=" + mMetadata);
+        if (mMetadata == null || mPlaybackState == null) {
+            return;
+        }
+
+        updatePlayPauseAction();
+
+        mNotificationBuilder = new Notification.Builder(mService);
+        int playPauseActionIndex = 0;
+
+        // If skip to previous action is enabled
+        if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
+            mNotificationBuilder
+                    .addAction(R.drawable.ic_skip_previous_white_24dp,
+                            mService.getString(R.string.label_previous),
+                            mIntents.get(R.drawable.ic_skip_previous_white_24dp));
+            playPauseActionIndex = 1;
+        }
+
+        mNotificationBuilder.addAction(mPlayPauseAction);
+
+        // If skip to next action is enabled
+        if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+            mNotificationBuilder.addAction(R.drawable.ic_skip_next_white_24dp,
+                    mService.getString(R.string.label_next),
+                    mIntents.get(R.drawable.ic_skip_next_white_24dp));
+        }
+
+        MediaDescription description = mMetadata.getDescription();
+
+        String fetchArtUrl = null;
+        Bitmap art = description.getIconBitmap();
+        if (art == null && description.getIconUri() != null) {
+            // This sample assumes the iconUri will be a valid URL formatted String, but
+            // it can actually be any valid Android Uri formatted String.
+            // async fetch the album art icon
+            String artUrl = description.getIconUri().toString();
+            art = mAlbumArtCache.get(artUrl);
+            if (art == null) {
+                fetchArtUrl = artUrl;
+                // use a placeholder art while the remote art is being downloaded
+                art = BitmapFactory.decodeResource(mService.getResources(), R.drawable.ic_default_art);
+            }
+        }
+
+        mNotificationBuilder
+                .setStyle(new Notification.MediaStyle()
+                        .setShowActionsInCompactView(playPauseActionIndex)  // only show play/pause in compact view
+                        .setMediaSession(mSessionToken))
+                .setColor(mNotificationColor)
+                .setSmallIcon(R.drawable.ic_notification)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setUsesChronometer(true)
+                .setContentTitle(description.getTitle())
+                .setContentText(description.getSubtitle())
+                .setLargeIcon(art);
+
+        updateNotificationPlaybackState();
+
+        mService.startForeground(NOTIFICATION_ID, mNotificationBuilder.build());
+        if (fetchArtUrl != null) {
+            fetchBitmapFromURLAsync(fetchArtUrl);
+        }
+    }
+
+    private void updatePlayPauseAction() {
+        LogHelper.d(TAG, "updatePlayPauseAction");
+        String playPauseLabel = "";
+        int playPauseIcon;
+        if (mPlaybackState.getState() == PlaybackState.STATE_PLAYING) {
+            playPauseLabel = mService.getString(R.string.label_pause);
+            playPauseIcon = R.drawable.ic_pause_white_24dp;
+        } else {
+            playPauseLabel = mService.getString(R.string.label_play);
+            playPauseIcon = R.drawable.ic_play_arrow_white_24dp;
+        }
+        if (mPlayPauseAction == null) {
+            mPlayPauseAction = new Notification.Action(playPauseIcon, playPauseLabel,
+                    mIntents.get(playPauseIcon));
+        } else {
+            mPlayPauseAction.icon = playPauseIcon;
+            mPlayPauseAction.title = playPauseLabel;
+            mPlayPauseAction.actionIntent = mIntents.get(playPauseIcon);
+        }
+    }
+
+    private void updateNotificationPlaybackState() {
+        LogHelper.d(TAG, "updateNotificationPlaybackState. mPlaybackState=" + mPlaybackState);
+        if (mPlaybackState == null || !mStarted) {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. cancelling notification!");
+            mService.stopForeground(true);
+            return;
+        }
+        if (mNotificationBuilder == null) {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. there is no notificationBuilder. Ignoring request to update state!");
+            return;
+        }
+        if (mPlaybackState.getPosition() >= 0) {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. updating playback position to ",
+                    (System.currentTimeMillis() - mPlaybackState.getPosition()) / 1000, " seconds");
+            mNotificationBuilder
+                    .setWhen(System.currentTimeMillis() - mPlaybackState.getPosition())
+                    .setShowWhen(true)
+                    .setUsesChronometer(true);
+            mNotificationBuilder.setShowWhen(true);
+        } else {
+            LogHelper.d(TAG, "updateNotificationPlaybackState. hiding playback position");
+            mNotificationBuilder
+                    .setWhen(0)
+                    .setShowWhen(false)
+                    .setUsesChronometer(false);
+        }
+
+        updatePlayPauseAction();
+
+        // Make sure that the notification can be dismissed by the user when we are not playing:
+        mNotificationBuilder.setOngoing(mPlaybackState.getState() == PlaybackState.STATE_PLAYING);
+
+        mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
+    }
+
+    public void fetchBitmapFromURLAsync(final String source) {
+        LogHelper.d(TAG, "getBitmapFromURLAsync: starting asynctask to fetch ", source);
+        new AsyncTask<Void, Void, Bitmap>() {
+            @Override
+            protected Bitmap doInBackground(Void[] objects) {
+                Bitmap bitmap = null;
+                try {
+                    bitmap = BitmapHelper.fetchAndRescaleBitmap(source,
+                            BitmapHelper.MEDIA_ART_BIG_WIDTH, BitmapHelper.MEDIA_ART_BIG_HEIGHT);
+                    mAlbumArtCache.put(source, bitmap);
+                } catch (IOException e) {
+                    LogHelper.e(TAG, e, "getBitmapFromURLAsync: " + source);
+                }
+                return bitmap;
+            }
+
+            @Override
+            protected void onPostExecute(Bitmap bitmap) {
+                if (bitmap != null && mMetadata != null &&
+                        mNotificationBuilder != null && mMetadata.getDescription() != null &&
+                        !source.equals(mMetadata.getDescription().getIconUri())) {
+                    // If the media is still the same, update the notification:
+                    LogHelper.d(TAG, "getBitmapFromURLAsync: set bitmap to ", source);
+                    mNotificationBuilder.setLargeIcon(bitmap);
+                    mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
+                }
+            }
+        }.execute();
+    }
+
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicPlayerActivity.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicPlayerActivity.java
new file mode 100644
index 0000000..648d268
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicPlayerActivity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 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.example.android.mediabrowserservice;
+
+import android.app.Activity;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.os.Bundle;
+
+/**
+ * Main activity for the music player.
+ */
+public class MusicPlayerActivity extends Activity
+        implements BrowseFragment.FragmentDataHelper {
+
+    private static final String TAG = MusicPlayerActivity.class.getSimpleName();
+
+    private MediaBrowser mMediaBrowser;
+    private MediaController mMediaController;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_player);
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction()
+                    .add(R.id.container, BrowseFragment.newInstance(null))
+                    .commit();
+        }
+    }
+
+    @Override
+    public void onMediaItemSelected(MediaBrowser.MediaItem item) {
+        if (item.isPlayable()) {
+            getMediaController().getTransportControls().playFromMediaId(item.getMediaId(), null);
+            QueueFragment queueFragment = QueueFragment.newInstance();
+            getFragmentManager().beginTransaction()
+                    .replace(R.id.container, queueFragment)
+                    .addToBackStack(null)
+                    .commit();
+        } else if (item.isBrowsable()) {
+            getFragmentManager().beginTransaction()
+                    .replace(R.id.container, BrowseFragment.newInstance(item.getMediaId()))
+                    .addToBackStack(null)
+                    .commit();
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicService.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicService.java
new file mode 100644
index 0000000..a7a9ae2
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/MusicService.java
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.mediabrowserservice;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.service.media.MediaBrowserService;
+
+import com.example.android.mediabrowserservice.model.MusicProvider;
+import com.example.android.mediabrowserservice.utils.LogHelper;
+import com.example.android.mediabrowserservice.utils.MediaIDHelper;
+import com.example.android.mediabrowserservice.utils.QueueHelper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_ROOT;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.createBrowseCategoryMediaID;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.extractBrowseCategoryFromMediaID;
+
+/**
+ * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
+ * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
+ * exposes it through its MediaSession.Token, which allows the client to create a MediaController
+ * that connects to and send control commands to the MediaSession remotely. This is useful for
+ * user interfaces that need to interact with your media session, like Android Auto. You can
+ * (should) also use the same service from your app's UI, which gives a seamless playback
+ * experience to the user.
+ *
+ * To implement a MediaBrowserService, you need to:
+ *
+ * <ul>
+ *
+ * <li> Extend {@link android.service.media.MediaBrowserService}, implementing the media browsing
+ *      related methods {@link android.service.media.MediaBrowserService#onGetRoot} and
+ *      {@link android.service.media.MediaBrowserService#onLoadChildren};
+ * <li> In onCreate, start a new {@link android.media.session.MediaSession} and notify its parent
+ *      with the session's token {@link android.service.media.MediaBrowserService#setSessionToken};
+ *
+ * <li> Set a callback on the
+ *      {@link android.media.session.MediaSession#setCallback(android.media.session.MediaSession.Callback)}.
+ *      The callback will receive all the user's actions, like play, pause, etc;
+ *
+ * <li> Handle all the actual music playing using any method your app prefers (for example,
+ *      {@link android.media.MediaPlayer})
+ *
+ * <li> Update playbackState, "now playing" metadata and queue, using MediaSession proper methods
+ *      {@link android.media.session.MediaSession#setPlaybackState(android.media.session.PlaybackState)}
+ *      {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata)} and
+ *      {@link android.media.session.MediaSession#setQueue(java.util.List)})
+ *
+ * <li> Declare and export the service in AndroidManifest with an intent receiver for the action
+ *      android.media.browse.MediaBrowserService
+ *
+ * </ul>
+ *
+ * To make your app compatible with Android Auto, you also need to:
+ *
+ * <ul>
+ *
+ * <li> Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+ *      with a &lt;automotiveApp&gt; root element. For a media app, this must include
+ *      an &lt;uses name="media"/&gt; element as a child.
+ *      For example, in AndroidManifest.xml:
+ *          &lt;meta-data android:name="com.google.android.gms.car.application"
+ *              android:resource="@xml/automotive_app_desc"/&gt;
+ *      And in res/values/automotive_app_desc.xml:
+ *          &lt;automotiveApp&gt;
+ *              &lt;uses name="media"/&gt;
+ *          &lt;/automotiveApp&gt;
+ *
+ * </ul>
+
+ * @see <a href="README.md">README.md</a> for more details.
+ *
+ */
+
+public class MusicService extends MediaBrowserService implements OnPreparedListener,
+        OnCompletionListener, OnErrorListener, AudioManager.OnAudioFocusChangeListener {
+
+    private static final String TAG = "MusicService";
+
+    // Action to thumbs up a media item
+    private static final String CUSTOM_ACTION_THUMBS_UP = "thumbs_up";
+    // Delay stopSelf by using a handler.
+    private static final int STOP_DELAY = 30000;
+
+    // The volume we set the media player to when we lose audio focus, but are
+    // allowed to reduce the volume instead of stopping playback.
+    public static final float VOLUME_DUCK = 0.2f;
+
+    // The volume we set the media player when we have audio focus.
+    public static final float VOLUME_NORMAL = 1.0f;
+    public static final String ANDROID_AUTO_PACKAGE_NAME = "com.google.android.projection.gearhead";
+    public static final String ANDROID_AUTO_EMULATOR_PACKAGE_NAME = "com.google.android.mediasimulator";
+
+    // Music catalog manager
+    private MusicProvider mMusicProvider;
+
+    private MediaSession mSession;
+    private MediaPlayer mMediaPlayer;
+
+    // "Now playing" queue:
+    private List<MediaSession.QueueItem> mPlayingQueue;
+    private int mCurrentIndexOnQueue;
+
+    // Current local media player state
+    private int mState = PlaybackState.STATE_NONE;
+
+    // Wifi lock that we hold when streaming files from the internet, in order
+    // to prevent the device from shutting off the Wifi radio
+    private WifiLock mWifiLock;
+
+    private MediaNotification mMediaNotification;
+
+    // Indicates whether the service was started.
+    private boolean mServiceStarted;
+
+    enum AudioFocus {
+        NoFocusNoDuck, // we don't have audio focus, and can't duck
+        NoFocusCanDuck, // we don't have focus, but can play at a low volume
+                        // ("ducking")
+        Focused // we have full audio focus
+    }
+
+    // Type of audio focus we have:
+    private AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck;
+    private AudioManager mAudioManager;
+
+    // Indicates if we should start playing immediately after we gain focus.
+    private boolean mPlayOnFocusGain;
+
+    private Handler mDelayedStopHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if ((mMediaPlayer != null && mMediaPlayer.isPlaying()) ||
+                    mPlayOnFocusGain) {
+                LogHelper.d(TAG, "Ignoring delayed stop since the media player is in use.");
+                return;
+            }
+            LogHelper.d(TAG, "Stopping service with delay handler.");
+            stopSelf();
+            mServiceStarted = false;
+        }
+    };
+
+    /*
+     * (non-Javadoc)
+     * @see android.app.Service#onCreate()
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        LogHelper.d(TAG, "onCreate");
+
+        mPlayingQueue = new ArrayList<>();
+
+        // Create the Wifi lock (this does not acquire the lock, this just creates it)
+        mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
+                .createWifiLock(WifiManager.WIFI_MODE_FULL, "MusicDemo_lock");
+
+
+        // Create the music catalog metadata provider
+        mMusicProvider = new MusicProvider();
+        mMusicProvider.retrieveMedia(new MusicProvider.Callback() {
+            @Override
+            public void onMusicCatalogReady(boolean success) {
+                mState = success ? PlaybackState.STATE_NONE : PlaybackState.STATE_ERROR;
+            }
+        });
+
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+
+        // Start a new MediaSession
+        mSession = new MediaSession(this, "MusicService");
+        setSessionToken(mSession.getSessionToken());
+        mSession.setCallback(new MediaSessionCallback());
+        mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
+                MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+
+        // Use these extras to reserve space for the corresponding actions, even when they are disabled
+        // in the playbackstate, so the custom actions don't reflow.
+        Bundle extras = new Bundle();
+        extras.putBoolean(
+            "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT",
+            true);
+        extras.putBoolean(
+            "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS",
+            true);
+        // If you want to reserve the Queue slot when there is no queue
+        // (mSession.setQueue(emptylist)), uncomment the lines below:
+        // extras.putBoolean(
+        //   "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE",
+        //   true);
+        mSession.setExtras(extras);
+
+        updatePlaybackState(null);
+
+        mMediaNotification = new MediaNotification(this);
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see android.app.Service#onDestroy()
+     */
+    @Override
+    public void onDestroy() {
+        LogHelper.d(TAG, "onDestroy");
+
+        // Service is being killed, so make sure we release our resources
+        handleStopRequest(null);
+
+        mDelayedStopHandler.removeCallbacksAndMessages(null);
+        // In particular, always release the MediaSession to clean up resources
+        // and notify associated MediaController(s).
+        mSession.release();
+    }
+
+
+    @Override
+    public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+        LogHelper.d(TAG, "OnGetRoot: clientPackageName=" + clientPackageName,
+                "; clientUid=" + clientUid + " ; rootHints=", rootHints);
+        // To ensure you are not allowing any arbitrary app to browse your app's contents, you
+        // need to check the origin:
+        if (!ANDROID_AUTO_PACKAGE_NAME.equals(clientPackageName) &&
+                !ANDROID_AUTO_EMULATOR_PACKAGE_NAME.equals(clientPackageName) &&
+                !getApplication().getPackageName().equals(clientPackageName)) {
+            // If the request comes from an untrusted package, return null. No further calls will
+            // be made to other media browsing methods.
+            LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package " + clientPackageName);
+            return null;
+        }
+        if (ANDROID_AUTO_PACKAGE_NAME.equals(clientPackageName)) {
+            // Optional: if your app needs to adapt ads, music library or anything else that
+            // needs to run differently when connected to the car, this is where you should handle
+            // it.
+        }
+        return new BrowserRoot(MEDIA_ID_ROOT, null);
+    }
+
+    @Override
+    public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+        if (!mMusicProvider.isInitialized()) {
+            // Use result.detach to allow calling result.sendResult from another thread:
+            result.detach();
+
+            mMusicProvider.retrieveMedia(new MusicProvider.Callback() {
+                @Override
+                public void onMusicCatalogReady(boolean success) {
+                    if (success) {
+                        loadChildrenImpl(parentMediaId, result);
+                    } else {
+                        updatePlaybackState(getString(R.string.error_no_metadata));
+                        result.sendResult(new ArrayList<MediaItem>());
+                    }
+                }
+            });
+
+        } else {
+            // If our music catalog is already loaded/cached, load them into result immediately
+            loadChildrenImpl(parentMediaId, result);
+        }
+    }
+
+    /**
+     * Actual implementation of onLoadChildren that assumes that MusicProvider is already
+     * initialized.
+     */
+    private void loadChildrenImpl(final String parentMediaId,
+                                  final Result<List<MediaBrowser.MediaItem>> result) {
+        LogHelper.d(TAG, "OnLoadChildren: parentMediaId=", parentMediaId);
+
+        List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>();
+
+        if (MEDIA_ID_ROOT.equals(parentMediaId)) {
+            LogHelper.d(TAG, "OnLoadChildren.ROOT");
+            mediaItems.add(new MediaBrowser.MediaItem(
+                    new MediaDescription.Builder()
+                        .setMediaId(MEDIA_ID_MUSICS_BY_GENRE)
+                        .setTitle(getString(R.string.browse_genres))
+                        .setIconUri(Uri.parse("android.resource://" +
+                                "com.example.android.mediabrowserservice/drawable/ic_by_genre"))
+                        .setSubtitle(getString(R.string.browse_genre_subtitle))
+                        .build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
+            ));
+
+        } else if (MEDIA_ID_MUSICS_BY_GENRE.equals(parentMediaId)) {
+            LogHelper.d(TAG, "OnLoadChildren.GENRES");
+            for (String genre: mMusicProvider.getGenres()) {
+                MediaBrowser.MediaItem item = new MediaBrowser.MediaItem(
+                    new MediaDescription.Builder()
+                        .setMediaId(createBrowseCategoryMediaID(MEDIA_ID_MUSICS_BY_GENRE, genre))
+                        .setTitle(genre)
+                        .setSubtitle(getString(R.string.browse_musics_by_genre_subtitle, genre))
+                        .build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
+                );
+                mediaItems.add(item);
+            }
+
+        } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_GENRE)) {
+            String genre = extractBrowseCategoryFromMediaID(parentMediaId)[1];
+            LogHelper.d(TAG, "OnLoadChildren.SONGS_BY_GENRE  genre=", genre);
+            for (MediaMetadata track: mMusicProvider.getMusicsByGenre(genre)) {
+                // Since mediaMetadata fields are immutable, we need to create a copy, so we
+                // can set a hierarchy-aware mediaID. We will need to know the media hierarchy
+                // when we get a onPlayFromMusicID call, so we can create the proper queue based
+                // on where the music was selected from (by artist, by genre, random, etc)
+                String hierarchyAwareMediaID = MediaIDHelper.createTrackMediaID(
+                        MEDIA_ID_MUSICS_BY_GENRE, genre, track);
+                MediaMetadata trackCopy = new MediaMetadata.Builder(track)
+                        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
+                        .build();
+                MediaBrowser.MediaItem bItem = new MediaBrowser.MediaItem(
+                        trackCopy.getDescription(), MediaItem.FLAG_PLAYABLE);
+                mediaItems.add(bItem);
+            }
+        } else {
+            LogHelper.w(TAG, "Skipping unmatched parentMediaId: ", parentMediaId);
+        }
+        result.sendResult(mediaItems);
+    }
+
+
+
+    private final class MediaSessionCallback extends MediaSession.Callback {
+        @Override
+        public void onPlay() {
+            LogHelper.d(TAG, "play");
+
+            if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
+                mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider);
+                mSession.setQueue(mPlayingQueue);
+                mSession.setQueueTitle(getString(R.string.random_queue_title));
+                // start playing from the beginning of the queue
+                mCurrentIndexOnQueue = 0;
+            }
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+                handlePlayRequest();
+            }
+        }
+
+        @Override
+        public void onSkipToQueueItem(long queueId) {
+            LogHelper.d(TAG, "OnSkipToQueueItem:" + queueId);
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+
+                // set the current index on queue from the music Id:
+                mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, queueId);
+
+                // play the music
+                handlePlayRequest();
+            }
+        }
+
+        @Override
+        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+            LogHelper.d(TAG, "playFromMediaId mediaId:", mediaId, "  extras=", extras);
+
+            // The mediaId used here is not the unique musicId. This one comes from the
+            // MediaBrowser, and is actually a "hierarchy-aware mediaID": a concatenation of
+            // the hierarchy in MediaBrowser and the actual unique musicID. This is necessary
+            // so we can build the correct playing queue, based on where the track was
+            // selected from.
+            mPlayingQueue = QueueHelper.getPlayingQueue(mediaId, mMusicProvider);
+            mSession.setQueue(mPlayingQueue);
+            String queueTitle = getString(R.string.browse_musics_by_genre_subtitle,
+                    MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaId));
+            mSession.setQueueTitle(queueTitle);
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+                String uniqueMusicID = MediaIDHelper.extractMusicIDFromMediaID(mediaId);
+                // set the current index on queue from the music Id:
+                mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(
+                        mPlayingQueue, uniqueMusicID);
+
+                // play the music
+                handlePlayRequest();
+            }
+        }
+
+        @Override
+        public void onPause() {
+            LogHelper.d(TAG, "pause. current state=" + mState);
+            handlePauseRequest();
+        }
+
+        @Override
+        public void onStop() {
+            LogHelper.d(TAG, "stop. current state=" + mState);
+            handleStopRequest(null);
+        }
+
+        @Override
+        public void onSkipToNext() {
+            LogHelper.d(TAG, "skipToNext");
+            mCurrentIndexOnQueue++;
+            if (mPlayingQueue != null && mCurrentIndexOnQueue >= mPlayingQueue.size()) {
+                mCurrentIndexOnQueue = 0;
+            }
+            if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+                mState = PlaybackState.STATE_PLAYING;
+                handlePlayRequest();
+            } else {
+                LogHelper.e(TAG, "skipToNext: cannot skip to next. next Index=" +
+                        mCurrentIndexOnQueue + " queue length=" +
+                        (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
+                handleStopRequest("Cannot skip");
+            }
+        }
+
+        @Override
+        public void onSkipToPrevious() {
+            LogHelper.d(TAG, "skipToPrevious");
+
+            mCurrentIndexOnQueue--;
+            if (mPlayingQueue != null && mCurrentIndexOnQueue < 0) {
+                // This sample's behavior: skipping to previous when in first song restarts the
+                // first song.
+                mCurrentIndexOnQueue = 0;
+            }
+            if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+                mState = PlaybackState.STATE_PLAYING;
+                handlePlayRequest();
+            } else {
+                LogHelper.e(TAG, "skipToPrevious: cannot skip to previous. previous Index=" +
+                        mCurrentIndexOnQueue + " queue length=" +
+                        (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
+                handleStopRequest("Cannot skip");
+            }
+        }
+
+        @Override
+        public void onCustomAction(String action, Bundle extras) {
+            if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
+                LogHelper.i(TAG, "onCustomAction: favorite for current track");
+                MediaMetadata track = getCurrentPlayingMusic();
+                if (track != null) {
+                    String mediaId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+                    mMusicProvider.setFavorite(mediaId, !mMusicProvider.isFavorite(mediaId));
+                }
+                updatePlaybackState(null);
+            } else {
+                LogHelper.e(TAG, "Unsupported action: ", action);
+            }
+
+        }
+
+        @Override
+        public void onPlayFromSearch(String query, Bundle extras) {
+            LogHelper.d(TAG, "playFromSearch  query=", query);
+
+            mPlayingQueue = QueueHelper.getPlayingQueueFromSearch(query, mMusicProvider);
+            LogHelper.d(TAG, "playFromSearch  playqueue.length=" + mPlayingQueue.size());
+            mSession.setQueue(mPlayingQueue);
+
+            if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+
+                // start playing from the beginning of the queue
+                mCurrentIndexOnQueue = 0;
+
+                handlePlayRequest();
+            }
+        }
+    }
+
+
+
+    /*
+     * Called when media player is done playing current song.
+     * @see android.media.MediaPlayer.OnCompletionListener
+     */
+    @Override
+    public void onCompletion(MediaPlayer player) {
+        LogHelper.d(TAG, "onCompletion from MediaPlayer");
+        // The media player finished playing the current song, so we go ahead
+        // and start the next.
+        if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+            // In this sample, we restart the playing queue when it gets to the end:
+            mCurrentIndexOnQueue++;
+            if (mCurrentIndexOnQueue >= mPlayingQueue.size()) {
+                mCurrentIndexOnQueue = 0;
+            }
+            handlePlayRequest();
+        } else {
+            // If there is nothing to play, we stop and release the resources:
+            handleStopRequest(null);
+        }
+    }
+
+    /*
+     * Called when media player is done preparing.
+     * @see android.media.MediaPlayer.OnPreparedListener
+     */
+    @Override
+    public void onPrepared(MediaPlayer player) {
+        LogHelper.d(TAG, "onPrepared from MediaPlayer");
+        // The media player is done preparing. That means we can start playing if we
+        // have audio focus.
+        configMediaPlayerState();
+    }
+
+    /**
+     * Called when there's an error playing media. When this happens, the media
+     * player goes to the Error state. We warn the user about the error and
+     * reset the media player.
+     *
+     * @see android.media.MediaPlayer.OnErrorListener
+     */
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        LogHelper.e(TAG, "Media player error: what=" + what + ", extra=" + extra);
+        handleStopRequest("MediaPlayer error " + what + " (" + extra + ")");
+        return true; // true indicates we handled the error
+    }
+
+
+
+
+    /**
+     * Called by AudioManager on audio focus changes.
+     */
+    @Override
+    public void onAudioFocusChange(int focusChange) {
+        LogHelper.d(TAG, "onAudioFocusChange. focusChange=" + focusChange);
+        if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+            // We have gained focus:
+            mAudioFocus = AudioFocus.Focused;
+
+        } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
+                focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
+                focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+            // We have lost focus. If we can duck (low playback volume), we can keep playing.
+            // Otherwise, we need to pause the playback.
+            boolean canDuck = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
+            mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck : AudioFocus.NoFocusNoDuck;
+
+            // If we are playing, we need to reset media player by calling configMediaPlayerState
+            // with mAudioFocus properly set.
+            if (mState == PlaybackState.STATE_PLAYING && !canDuck) {
+                // If we don't have audio focus and can't duck, we save the information that
+                // we were playing, so that we can resume playback once we get the focus back.
+                mPlayOnFocusGain = true;
+            }
+        } else {
+            LogHelper.e(TAG, "onAudioFocusChange: Ignoring unsupported focusChange: " + focusChange);
+        }
+
+        configMediaPlayerState();
+    }
+
+
+
+    /**
+     * Handle a request to play music
+     */
+    private void handlePlayRequest() {
+        LogHelper.d(TAG, "handlePlayRequest: mState=" + mState);
+
+        mDelayedStopHandler.removeCallbacksAndMessages(null);
+        if (!mServiceStarted) {
+            LogHelper.v(TAG, "Starting service");
+            // The MusicService needs to keep running even after the calling MediaBrowser
+            // is disconnected. Call startService(Intent) and then stopSelf(..) when we no longer
+            // need to play media.
+            startService(new Intent(getApplicationContext(), MusicService.class));
+            mServiceStarted = true;
+        }
+
+        mPlayOnFocusGain = true;
+        tryToGetAudioFocus();
+
+        if (!mSession.isActive()) {
+            mSession.setActive(true);
+        }
+
+        // actually play the song
+        if (mState == PlaybackState.STATE_PAUSED) {
+            // If we're paused, just continue playback and restore the
+            // 'foreground service' state.
+            configMediaPlayerState();
+        } else {
+            // If we're stopped or playing a song,
+            // just go ahead to the new song and (re)start playing
+            playCurrentSong();
+        }
+    }
+
+
+    /**
+     * Handle a request to pause music
+     */
+    private void handlePauseRequest() {
+        LogHelper.d(TAG, "handlePauseRequest: mState=" + mState);
+
+        if (mState == PlaybackState.STATE_PLAYING) {
+            // Pause media player and cancel the 'foreground service' state.
+            mState = PlaybackState.STATE_PAUSED;
+            if (mMediaPlayer.isPlaying()) {
+                mMediaPlayer.pause();
+            }
+            // while paused, retain the MediaPlayer but give up audio focus
+            relaxResources(false);
+            giveUpAudioFocus();
+        }
+        updatePlaybackState(null);
+    }
+
+    /**
+     * Handle a request to stop music
+     */
+    private void handleStopRequest(String withError) {
+        LogHelper.d(TAG, "handleStopRequest: mState=" + mState + " error=", withError);
+        mState = PlaybackState.STATE_STOPPED;
+
+        // let go of all resources...
+        relaxResources(true);
+        giveUpAudioFocus();
+        updatePlaybackState(withError);
+
+        mMediaNotification.stopNotification();
+
+        // service is no longer necessary. Will be started again if needed.
+        stopSelf();
+        mServiceStarted = false;
+    }
+
+    /**
+     * Releases resources used by the service for playback. This includes the
+     * "foreground service" status, the wake locks and possibly the MediaPlayer.
+     *
+     * @param releaseMediaPlayer Indicates whether the Media Player should also
+     *            be released or not
+     */
+    private void relaxResources(boolean releaseMediaPlayer) {
+        LogHelper.d(TAG, "relaxResources. releaseMediaPlayer=" + releaseMediaPlayer);
+        // stop being a foreground service
+        stopForeground(true);
+
+        // reset the delayed stop handler.
+        mDelayedStopHandler.removeCallbacksAndMessages(null);
+        mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
+
+        // stop and release the Media Player, if it's available
+        if (releaseMediaPlayer && mMediaPlayer != null) {
+            mMediaPlayer.reset();
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+
+        // we can also release the Wifi lock, if we're holding it
+        if (mWifiLock.isHeld()) {
+            mWifiLock.release();
+        }
+    }
+
+    /**
+     * Reconfigures MediaPlayer according to audio focus settings and
+     * starts/restarts it. This method starts/restarts the MediaPlayer
+     * respecting the current audio focus state. So if we have focus, it will
+     * play normally; if we don't have focus, it will either leave the
+     * MediaPlayer paused or set it to a low volume, depending on what is
+     * allowed by the current focus settings. This method assumes mPlayer !=
+     * null, so if you are calling it, you have to do so from a context where
+     * you are sure this is the case.
+     */
+    private void configMediaPlayerState() {
+        LogHelper.d(TAG, "configAndStartMediaPlayer. mAudioFocus=" + mAudioFocus);
+        if (mAudioFocus == AudioFocus.NoFocusNoDuck) {
+            // If we don't have audio focus and can't duck, we have to pause,
+            if (mState == PlaybackState.STATE_PLAYING) {
+                handlePauseRequest();
+            }
+        } else {  // we have audio focus:
+            if (mAudioFocus == AudioFocus.NoFocusCanDuck) {
+                mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK); // we'll be relatively quiet
+            } else {
+                mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL); // we can be loud again
+            }
+            // If we were playing when we lost focus, we need to resume playing.
+            if (mPlayOnFocusGain) {
+                if (!mMediaPlayer.isPlaying()) {
+                    LogHelper.d(TAG, "configAndStartMediaPlayer startMediaPlayer.");
+                    mMediaPlayer.start();
+                }
+                mPlayOnFocusGain = false;
+                mState = PlaybackState.STATE_PLAYING;
+            }
+        }
+        updatePlaybackState(null);
+    }
+
+    /**
+     * Makes sure the media player exists and has been reset. This will create
+     * the media player if needed, or reset the existing media player if one
+     * already exists.
+     */
+    private void createMediaPlayerIfNeeded() {
+        LogHelper.d(TAG, "createMediaPlayerIfNeeded. needed? " + (mMediaPlayer==null));
+        if (mMediaPlayer == null) {
+            mMediaPlayer = new MediaPlayer();
+
+            // Make sure the media player will acquire a wake-lock while
+            // playing. If we don't do that, the CPU might go to sleep while the
+            // song is playing, causing playback to stop.
+            mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+
+            // we want the media player to notify us when it's ready preparing,
+            // and when it's done playing:
+            mMediaPlayer.setOnPreparedListener(this);
+            mMediaPlayer.setOnCompletionListener(this);
+            mMediaPlayer.setOnErrorListener(this);
+        } else {
+            mMediaPlayer.reset();
+        }
+    }
+
+    /**
+     * Starts playing the current song in the playing queue.
+     */
+    void playCurrentSong() {
+        MediaMetadata track = getCurrentPlayingMusic();
+        if (track == null) {
+            LogHelper.e(TAG, "playSong:  ignoring request to play next song, because cannot" +
+                    " find it." +
+                    " currentIndex=" + mCurrentIndexOnQueue +
+                    " playQueue.size=" + (mPlayingQueue==null?"null": mPlayingQueue.size()));
+            return;
+        }
+        String source = track.getString(MusicProvider.CUSTOM_METADATA_TRACK_SOURCE);
+        LogHelper.d(TAG, "playSong:  current (" + mCurrentIndexOnQueue + ") in playingQueue. " +
+                " musicId=" + track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) +
+                " source=" + source);
+
+        mState = PlaybackState.STATE_STOPPED;
+        relaxResources(false); // release everything except MediaPlayer
+
+        try {
+            createMediaPlayerIfNeeded();
+
+            mState = PlaybackState.STATE_BUFFERING;
+
+            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mMediaPlayer.setDataSource(source);
+
+            // Starts preparing the media player in the background. When
+            // it's done, it will call our OnPreparedListener (that is,
+            // the onPrepared() method on this class, since we set the
+            // listener to 'this'). Until the media player is prepared,
+            // we *cannot* call start() on it!
+            mMediaPlayer.prepareAsync();
+
+            // If we are streaming from the internet, we want to hold a
+            // Wifi lock, which prevents the Wifi radio from going to
+            // sleep while the song is playing.
+            mWifiLock.acquire();
+
+            updatePlaybackState(null);
+            updateMetadata();
+
+        } catch (IOException ex) {
+            LogHelper.e(TAG, ex, "IOException playing song");
+            updatePlaybackState(ex.getMessage());
+        }
+    }
+
+
+
+    private void updateMetadata() {
+        if (!QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+            LogHelper.e(TAG, "Can't retrieve current metadata.");
+            mState = PlaybackState.STATE_ERROR;
+            updatePlaybackState(getResources().getString(R.string.error_no_metadata));
+            return;
+        }
+        MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
+        String mediaId = queueItem.getDescription().getMediaId();
+        MediaMetadata track = mMusicProvider.getMusic(mediaId);
+        String trackId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+        if (!mediaId.equals(trackId)) {
+            throw new IllegalStateException("track ID (" + trackId + ") " +
+                    "should match mediaId (" + mediaId + ")");
+        }
+        LogHelper.d(TAG, "Updating metadata for MusicID= " + mediaId);
+        mSession.setMetadata(track);
+    }
+
+
+    /**
+     * Update the current media player state, optionally showing an error message.
+     *
+     * @param error if not null, error message to present to the user.
+     *
+     */
+    private void updatePlaybackState(String error) {
+
+        LogHelper.d(TAG, "updatePlaybackState, setting session playback state to " + mState);
+        long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
+        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+            position = mMediaPlayer.getCurrentPosition();
+        }
+        PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
+                .setActions(getAvailableActions());
+
+        setCustomAction(stateBuilder);
+
+        // If there is an error message, send it to the playback state:
+        if (error != null) {
+            // Error states are really only supposed to be used for errors that cause playback to
+            // stop unexpectedly and persist until the user takes action to fix it.
+            stateBuilder.setErrorMessage(error);
+            mState = PlaybackState.STATE_ERROR;
+        }
+        stateBuilder.setState(mState, position, 1.0f, SystemClock.elapsedRealtime());
+
+        // Set the activeQueueItemId if the current index is valid.
+        if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+            MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+            stateBuilder.setActiveQueueItemId(item.getQueueId());
+        }
+
+        mSession.setPlaybackState(stateBuilder.build());
+
+        if (mState == PlaybackState.STATE_PLAYING || mState == PlaybackState.STATE_PAUSED) {
+            mMediaNotification.startNotification();
+        }
+    }
+
+    private void setCustomAction(PlaybackState.Builder stateBuilder) {
+        MediaMetadata currentMusic = getCurrentPlayingMusic();
+        if (currentMusic != null) {
+            // Set appropriate "Favorite" icon on Custom action:
+            String mediaId = currentMusic.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+            int favoriteIcon = R.drawable.ic_star_off;
+            if (mMusicProvider.isFavorite(mediaId)) {
+                favoriteIcon = R.drawable.ic_star_on;
+            }
+            LogHelper.d(TAG, "updatePlaybackState, setting Favorite custom action of music ",
+                    mediaId, " current favorite=", mMusicProvider.isFavorite(mediaId));
+            stateBuilder.addCustomAction(CUSTOM_ACTION_THUMBS_UP, getString(R.string.favorite),
+                    favoriteIcon);
+        }
+    }
+
+    private long getAvailableActions() {
+        long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
+                PlaybackState.ACTION_PLAY_FROM_SEARCH;
+        if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
+            return actions;
+        }
+        if (mState == PlaybackState.STATE_PLAYING) {
+            actions |= PlaybackState.ACTION_PAUSE;
+        }
+        if (mCurrentIndexOnQueue > 0) {
+            actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+        }
+        if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) {
+            actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
+        }
+        return actions;
+    }
+
+    private MediaMetadata getCurrentPlayingMusic() {
+        if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+            MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+            if (item != null) {
+                LogHelper.d(TAG, "getCurrentPlayingMusic for musicId=",
+                        item.getDescription().getMediaId());
+                return mMusicProvider.getMusic(item.getDescription().getMediaId());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Try to get the system audio focus.
+     */
+    void tryToGetAudioFocus() {
+        LogHelper.d(TAG, "tryToGetAudioFocus");
+        if (mAudioFocus != AudioFocus.Focused) {
+            int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
+                    AudioManager.AUDIOFOCUS_GAIN);
+            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+                mAudioFocus = AudioFocus.Focused;
+            }
+        }
+    }
+
+    /**
+     * Give up the audio focus.
+     */
+    void giveUpAudioFocus() {
+        LogHelper.d(TAG, "giveUpAudioFocus");
+        if (mAudioFocus == AudioFocus.Focused) {
+            if (mAudioManager.abandonAudioFocus(this) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+                mAudioFocus = AudioFocus.NoFocusNoDuck;
+            }
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueAdapter.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueAdapter.java
new file mode 100644
index 0000000..4f24e99
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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.example.android.mediabrowserservice;
+
+import android.app.Activity;
+import android.media.session.MediaSession;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A list adapter for items in a queue
+ */
+public class QueueAdapter extends ArrayAdapter<MediaSession.QueueItem> {
+
+    // The currently selected/active queue item Id.
+    private long mActiveQueueItemId = MediaSession.QueueItem.UNKNOWN_ID;
+
+    public QueueAdapter(Activity context) {
+        super(context, R.layout.media_list_item, new ArrayList<MediaSession.QueueItem>());
+    }
+
+    public void setActiveQueueItemId(long id) {
+        this.mActiveQueueItemId = id;
+    }
+
+    private static class ViewHolder {
+        ImageView mImageView;
+        TextView mTitleView;
+        TextView mDescriptionView;
+    }
+
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder holder;
+
+        if (convertView == null) {
+            convertView = LayoutInflater.from(getContext())
+                    .inflate(R.layout.media_list_item, parent, false);
+            holder = new ViewHolder();
+            holder.mImageView = (ImageView) convertView.findViewById(R.id.play_eq);
+            holder.mTitleView = (TextView) convertView.findViewById(R.id.title);
+            holder.mDescriptionView = (TextView) convertView.findViewById(R.id.description);
+            convertView.setTag(holder);
+        } else {
+            holder = (ViewHolder) convertView.getTag();
+        }
+
+        MediaSession.QueueItem item = getItem(position);
+        holder.mTitleView.setText(item.getDescription().getTitle());
+        if (item.getDescription().getDescription() != null) {
+            holder.mDescriptionView.setText(item.getDescription().getDescription());
+        }
+
+        // If the itemId matches the active Id then use a different icon
+        if (mActiveQueueItemId == item.getQueueId()) {
+            holder.mImageView.setImageDrawable(
+                    getContext().getDrawable(R.drawable.ic_equalizer_white_24dp));
+        } else {
+            holder.mImageView.setImageDrawable(
+                    getContext().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+        }
+        return convertView;
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueFragment.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueFragment.java
new file mode 100644
index 0000000..f6076bc
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/QueueFragment.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2014 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.example.android.mediabrowserservice;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.media.browse.MediaBrowser;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ImageButton;
+import android.widget.ListView;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.util.List;
+
+/**
+ * A class that shows the Media Queue to the user.
+ */
+public class QueueFragment extends Fragment {
+
+    private static final String TAG = QueueFragment.class.getSimpleName();
+
+    private ImageButton mSkipNext;
+    private ImageButton mSkipPrevious;
+    private ImageButton mPlayPause;
+
+    private MediaBrowser mMediaBrowser;
+    private MediaController.TransportControls mTransportControls;
+    private MediaController mMediaController;
+    private PlaybackState mPlaybackState;
+
+    private QueueAdapter mQueueAdapter;
+
+    private MediaBrowser.ConnectionCallback mConnectionCallback =
+            new MediaBrowser.ConnectionCallback() {
+        @Override
+        public void onConnected() {
+            LogHelper.d(TAG, "onConnected: session token ", mMediaBrowser.getSessionToken());
+
+            if (mMediaBrowser.getSessionToken() == null) {
+                throw new IllegalArgumentException("No Session token");
+            }
+
+            mMediaController = new MediaController(getActivity(),
+                    mMediaBrowser.getSessionToken());
+            mTransportControls = mMediaController.getTransportControls();
+            mMediaController.registerCallback(mSessionCallback);
+
+            getActivity().setMediaController(mMediaController);
+            mPlaybackState = mMediaController.getPlaybackState();
+
+            List<MediaSession.QueueItem> queue = mMediaController.getQueue();
+            if (queue != null) {
+                mQueueAdapter.clear();
+                mQueueAdapter.notifyDataSetInvalidated();
+                mQueueAdapter.addAll(queue);
+                mQueueAdapter.notifyDataSetChanged();
+            }
+            onPlaybackStateChanged(mPlaybackState);
+        }
+
+        @Override
+        public void onConnectionFailed() {
+            LogHelper.d(TAG, "onConnectionFailed");
+        }
+
+        @Override
+        public void onConnectionSuspended() {
+            LogHelper.d(TAG, "onConnectionSuspended");
+            mMediaController.unregisterCallback(mSessionCallback);
+            mTransportControls = null;
+            mMediaController = null;
+            getActivity().setMediaController(null);
+        }
+    };
+
+    // Receive callbacks from the MediaController. Here we update our state such as which queue
+    // is being shown, the current title and description and the PlaybackState.
+    private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+
+        @Override
+        public void onSessionDestroyed() {
+            LogHelper.d(TAG, "Session destroyed. Need to fetch a new Media Session");
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            if (state == null) {
+                return;
+            }
+            LogHelper.d(TAG, "Received playback state change to state ", state.getState());
+            mPlaybackState = state;
+            QueueFragment.this.onPlaybackStateChanged(state);
+        }
+
+        @Override
+        public void onQueueChanged(List<MediaSession.QueueItem> queue) {
+            LogHelper.d(TAG, "onQueueChanged ", queue);
+            if (queue != null) {
+                mQueueAdapter.clear();
+                mQueueAdapter.notifyDataSetInvalidated();
+                mQueueAdapter.addAll(queue);
+                mQueueAdapter.notifyDataSetChanged();
+            }
+        }
+    };
+
+    public static QueueFragment newInstance() {
+        return new QueueFragment();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_list, container, false);
+
+        mSkipPrevious = (ImageButton) rootView.findViewById(R.id.skip_previous);
+        mSkipPrevious.setEnabled(false);
+        mSkipPrevious.setOnClickListener(mButtonListener);
+
+        mSkipNext = (ImageButton) rootView.findViewById(R.id.skip_next);
+        mSkipNext.setEnabled(false);
+        mSkipNext.setOnClickListener(mButtonListener);
+
+        mPlayPause = (ImageButton) rootView.findViewById(R.id.play_pause);
+        mPlayPause.setEnabled(true);
+        mPlayPause.setOnClickListener(mButtonListener);
+
+        mQueueAdapter = new QueueAdapter(getActivity());
+
+        ListView mListView = (ListView) rootView.findViewById(R.id.list_view);
+        mListView.setAdapter(mQueueAdapter);
+        mListView.setFocusable(true);
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                MediaSession.QueueItem item = mQueueAdapter.getItem(position);
+                mTransportControls.skipToQueueItem(item.getQueueId());
+            }
+        });
+
+        mMediaBrowser = new MediaBrowser(getActivity(),
+                new ComponentName(getActivity(), MusicService.class),
+                mConnectionCallback, null);
+
+        return rootView;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mMediaBrowser != null) {
+            mMediaBrowser.connect();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mSessionCallback);
+        }
+        if (mMediaBrowser != null) {
+            mMediaBrowser.disconnect();
+        }
+    }
+
+
+    private void onPlaybackStateChanged(PlaybackState state) {
+        LogHelper.d(TAG, "onPlaybackStateChanged ", state);
+        if (state == null) {
+            return;
+        }
+        mQueueAdapter.setActiveQueueItemId(state.getActiveQueueItemId());
+        mQueueAdapter.notifyDataSetChanged();
+        boolean enablePlay = false;
+        StringBuilder statusBuilder = new StringBuilder();
+        switch (state.getState()) {
+            case PlaybackState.STATE_PLAYING:
+                statusBuilder.append("playing");
+                enablePlay = false;
+                break;
+            case PlaybackState.STATE_PAUSED:
+                statusBuilder.append("paused");
+                enablePlay = true;
+                break;
+            case PlaybackState.STATE_STOPPED:
+                statusBuilder.append("ended");
+                enablePlay = true;
+                break;
+            case PlaybackState.STATE_ERROR:
+                statusBuilder.append("error: ").append(state.getErrorMessage());
+                break;
+            case PlaybackState.STATE_BUFFERING:
+                statusBuilder.append("buffering");
+                break;
+            case PlaybackState.STATE_NONE:
+                statusBuilder.append("none");
+                enablePlay = false;
+                break;
+            case PlaybackState.STATE_CONNECTING:
+                statusBuilder.append("connecting");
+                break;
+            default:
+                statusBuilder.append(mPlaybackState);
+        }
+        statusBuilder.append(" -- At position: ").append(state.getPosition());
+        LogHelper.d(TAG, statusBuilder.toString());
+
+        if (enablePlay) {
+            mPlayPause.setImageDrawable(
+                    getActivity().getDrawable(R.drawable.ic_play_arrow_white_24dp));
+        } else {
+            mPlayPause.setImageDrawable(getActivity().getDrawable(R.drawable.ic_pause_white_24dp));
+        }
+
+        mSkipPrevious.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
+        mSkipNext.setEnabled((state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
+
+        LogHelper.d(TAG, "Queue From MediaController *** Title " +
+                mMediaController.getQueueTitle() + "\n: Queue: " + mMediaController.getQueue() +
+                "\n Metadata " + mMediaController.getMetadata());
+    }
+
+    private View.OnClickListener mButtonListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final int state = mPlaybackState == null ?
+                    PlaybackState.STATE_NONE : mPlaybackState.getState();
+            switch (v.getId()) {
+                case R.id.play_pause:
+                    LogHelper.d(TAG, "Play button pressed, in state " + state);
+                    if (state == PlaybackState.STATE_PAUSED ||
+                            state == PlaybackState.STATE_STOPPED ||
+                            state == PlaybackState.STATE_NONE) {
+                        playMedia();
+                    } else if (state == PlaybackState.STATE_PLAYING) {
+                        pauseMedia();
+                    }
+                    break;
+                case R.id.skip_previous:
+                    LogHelper.d(TAG, "Start button pressed, in state " + state);
+                    skipToPrevious();
+                    break;
+                case R.id.skip_next:
+                    skipToNext();
+                    break;
+            }
+        }
+    };
+
+    private void playMedia() {
+        if (mTransportControls != null) {
+            mTransportControls.play();
+        }
+    }
+
+    private void pauseMedia() {
+        if (mTransportControls != null) {
+            mTransportControls.pause();
+        }
+    }
+
+    private void skipToPrevious() {
+        if (mTransportControls != null) {
+            mTransportControls.skipToPrevious();
+        }
+    }
+
+    private void skipToNext() {
+        if (mTransportControls != null) {
+            mTransportControls.skipToNext();
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
new file mode 100644
index 0000000..ae90fb0
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/model/MusicProvider.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.mediabrowserservice.model;
+
+import android.media.MediaMetadata;
+import android.os.AsyncTask;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility class to get a list of MusicTrack's based on a server-side JSON
+ * configuration.
+ */
+public class MusicProvider {
+
+    private static final String TAG = "MusicProvider";
+
+    private static final String CATALOG_URL = "http://storage.googleapis.com/automotive-media/music.json";
+
+    public static final String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
+
+    private static String JSON_MUSIC = "music";
+    private static String JSON_TITLE = "title";
+    private static String JSON_ALBUM = "album";
+    private static String JSON_ARTIST = "artist";
+    private static String JSON_GENRE = "genre";
+    private static String JSON_SOURCE = "source";
+    private static String JSON_IMAGE = "image";
+    private static String JSON_TRACK_NUMBER = "trackNumber";
+    private static String JSON_TOTAL_TRACK_COUNT = "totalTrackCount";
+    private static String JSON_DURATION = "duration";
+
+    private final ReentrantLock initializationLock = new ReentrantLock();
+
+    // Categorized caches for music track data:
+    private final HashMap<String, List<MediaMetadata>> mMusicListByGenre;
+    private final HashMap<String, MediaMetadata> mMusicListById;
+
+    private final HashSet<String> mFavoriteTracks;
+
+    enum State {
+        NON_INITIALIZED, INITIALIZING, INITIALIZED;
+    }
+
+    private State mCurrentState = State.NON_INITIALIZED;
+
+
+    public interface Callback {
+        void onMusicCatalogReady(boolean success);
+    }
+
+    public MusicProvider() {
+        mMusicListByGenre = new HashMap<>();
+        mMusicListById = new HashMap<>();
+        mFavoriteTracks = new HashSet<>();
+    }
+
+    /**
+     * Get an iterator over the list of genres
+     *
+     * @return
+     */
+    public Iterable<String> getGenres() {
+        if (mCurrentState != State.INITIALIZED) {
+            return new ArrayList<String>(0);
+        }
+        return mMusicListByGenre.keySet();
+    }
+
+    /**
+     * Get music tracks of the given genre
+     *
+     * @return
+     */
+    public Iterable<MediaMetadata> getMusicsByGenre(String genre) {
+        if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
+            return new ArrayList<MediaMetadata>();
+        }
+        return mMusicListByGenre.get(genre);
+    }
+
+    /**
+     * Very basic implementation of a search that filter music tracks which title containing
+     * the given query.
+     *
+     * @return
+     */
+    public Iterable<MediaMetadata> searchMusics(String titleQuery) {
+        ArrayList<MediaMetadata> result = new ArrayList<>();
+        if (mCurrentState != State.INITIALIZED) {
+            return result;
+        }
+        titleQuery = titleQuery.toLowerCase();
+        for (MediaMetadata track: mMusicListById.values()) {
+            if (track.getString(MediaMetadata.METADATA_KEY_TITLE).toLowerCase()
+                    .contains(titleQuery)) {
+                result.add(track);
+            }
+        }
+        return result;
+    }
+
+    public MediaMetadata getMusic(String mediaId) {
+        return mMusicListById.get(mediaId);
+    }
+
+    public void setFavorite(String mediaId, boolean favorite) {
+        if (favorite) {
+            mFavoriteTracks.add(mediaId);
+        } else {
+            mFavoriteTracks.remove(mediaId);
+        }
+    }
+
+    public boolean isFavorite(String musicId) {
+        return mFavoriteTracks.contains(musicId);
+    }
+
+    public boolean isInitialized() {
+        return mCurrentState == State.INITIALIZED;
+    }
+
+    /**
+     * Get the list of music tracks from a server and caches the track information
+     * for future reference, keying tracks by mediaId and grouping by genre.
+     *
+     * @return
+     */
+    public void retrieveMedia(final Callback callback) {
+
+        if (mCurrentState == State.INITIALIZED) {
+            // Nothing to do, execute callback immediately
+            callback.onMusicCatalogReady(true);
+            return;
+        }
+
+        // Asynchronously load the music catalog in a separate thread
+        new AsyncTask() {
+            @Override
+            protected Object doInBackground(Object[] objects) {
+                retrieveMediaAsync(callback);
+                return null;
+            }
+        }.execute();
+    }
+
+    private void retrieveMediaAsync(Callback callback) {
+        initializationLock.lock();
+
+        try {
+            if (mCurrentState == State.NON_INITIALIZED) {
+                mCurrentState = State.INITIALIZING;
+
+                int slashPos = CATALOG_URL.lastIndexOf('/');
+                String path = CATALOG_URL.substring(0, slashPos + 1);
+                JSONObject jsonObj = parseUrl(CATALOG_URL);
+
+                JSONArray tracks = jsonObj.getJSONArray(JSON_MUSIC);
+                if (tracks != null) {
+                    for (int j = 0; j < tracks.length(); j++) {
+                        MediaMetadata item = buildFromJSON(tracks.getJSONObject(j), path);
+                        String genre = item.getString(MediaMetadata.METADATA_KEY_GENRE);
+                        List<MediaMetadata> list = mMusicListByGenre.get(genre);
+                        if (list == null) {
+                            list = new ArrayList<>();
+                        }
+                        list.add(item);
+                        mMusicListByGenre.put(genre, list);
+                        mMusicListById.put(item.getString(MediaMetadata.METADATA_KEY_MEDIA_ID),
+                                item);
+                    }
+                }
+                mCurrentState = State.INITIALIZED;
+            }
+        } catch (RuntimeException | JSONException e) {
+            LogHelper.e(TAG, e, "Could not retrieve music list");
+        } finally {
+            if (mCurrentState != State.INITIALIZED) {
+                // Something bad happened, so we reset state to NON_INITIALIZED to allow
+                // retries (eg if the network connection is temporary unavailable)
+                mCurrentState = State.NON_INITIALIZED;
+            }
+            initializationLock.unlock();
+            if (callback != null) {
+                callback.onMusicCatalogReady(mCurrentState == State.INITIALIZED);
+            }
+        }
+    }
+
+    private MediaMetadata buildFromJSON(JSONObject json, String basePath) throws JSONException {
+        String title = json.getString(JSON_TITLE);
+        String album = json.getString(JSON_ALBUM);
+        String artist = json.getString(JSON_ARTIST);
+        String genre = json.getString(JSON_GENRE);
+        String source = json.getString(JSON_SOURCE);
+        String iconUrl = json.getString(JSON_IMAGE);
+        int trackNumber = json.getInt(JSON_TRACK_NUMBER);
+        int totalTrackCount = json.getInt(JSON_TOTAL_TRACK_COUNT);
+        int duration = json.getInt(JSON_DURATION) * 1000; // ms
+
+        LogHelper.d(TAG, "Found music track: ", json);
+
+        // Media is stored relative to JSON file
+        if (!source.startsWith("http")) {
+            source = basePath + source;
+        }
+        if (!iconUrl.startsWith("http")) {
+            iconUrl = basePath + iconUrl;
+        }
+        // Since we don't have a unique ID in the server, we fake one using the hashcode of
+        // the music source. In a real world app, this could come from the server.
+        String id = String.valueOf(source.hashCode());
+
+        // Adding the music source to the MediaMetadata (and consequently using it in the
+        // mediaSession.setMetadata) is not a good idea for a real world music app, because
+        // the session metadata can be accessed by notification listeners. This is done in this
+        // sample for convenience only.
+        return new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, id)
+                .putString(CUSTOM_METADATA_TRACK_SOURCE, source)
+                .putString(MediaMetadata.METADATA_KEY_ALBUM, album)
+                .putString(MediaMetadata.METADATA_KEY_ARTIST, artist)
+                .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+                .putString(MediaMetadata.METADATA_KEY_GENRE, genre)
+                .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, iconUrl)
+                .putString(MediaMetadata.METADATA_KEY_TITLE, title)
+                .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, trackNumber)
+                .putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, totalTrackCount)
+                .build();
+    }
+
+    /**
+     * Download a JSON file from a server, parse the content and return the JSON
+     * object.
+     *
+     * @param urlString
+     * @return
+     */
+    private JSONObject parseUrl(String urlString) {
+        InputStream is = null;
+        try {
+            java.net.URL url = new java.net.URL(urlString);
+            URLConnection urlConnection = url.openConnection();
+            is = new BufferedInputStream(urlConnection.getInputStream());
+            BufferedReader reader = new BufferedReader(new InputStreamReader(
+                    urlConnection.getInputStream(), "iso-8859-1"));
+            StringBuilder sb = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                sb.append(line);
+            }
+            return new JSONObject(sb.toString());
+        } catch (Exception e) {
+            LogHelper.e(TAG, "Failed to parse the json for media list", e);
+            return null;
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/BitmapHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/BitmapHelper.java
new file mode 100644
index 0000000..5f0e767
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/BitmapHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 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.example.android.mediabrowserservice.utils;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+public class BitmapHelper {
+
+    // Bitmap size for album art in media notifications when there are more than 3 playback actions
+    public static final int MEDIA_ART_SMALL_WIDTH=64;
+    public static final int MEDIA_ART_SMALL_HEIGHT=64;
+
+    // Bitmap size for album art in media notifications when there are no more than 3 playback actions
+    public static final int MEDIA_ART_BIG_WIDTH=128;
+    public static final int MEDIA_ART_BIG_HEIGHT=128;
+
+    public static final Bitmap scaleBitmap(int scaleFactor, InputStream is) {
+        // Get the dimensions of the bitmap
+        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
+
+        // Decode the image file into a Bitmap sized to fill the View
+        bmOptions.inJustDecodeBounds = false;
+        bmOptions.inSampleSize = scaleFactor;
+
+        Bitmap bitmap = BitmapFactory.decodeStream(is, null, bmOptions);
+        return bitmap;
+    }
+
+    public static final int findScaleFactor(int targetW, int targetH, InputStream is) {
+        // Get the dimensions of the bitmap
+        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
+        bmOptions.inJustDecodeBounds = true;
+        BitmapFactory.decodeStream(is, null, bmOptions);
+        int actualW = bmOptions.outWidth;
+        int actualH = bmOptions.outHeight;
+
+        // Determine how much to scale down the image
+        return Math.min(actualW/targetW, actualH/targetH);
+    }
+
+    public static final Bitmap fetchAndRescaleBitmap(String uri, int width, int height)
+            throws IOException {
+        URL url = new URL(uri);
+        HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
+        httpConnection.setDoInput(true);
+        httpConnection.connect();
+        InputStream inputStream = httpConnection.getInputStream();
+        int scaleFactor = findScaleFactor(width, height, inputStream);
+
+        httpConnection = (HttpURLConnection) url.openConnection();
+        httpConnection.setDoInput(true);
+        httpConnection.connect();
+        inputStream = httpConnection.getInputStream();
+        Bitmap bitmap = scaleBitmap(scaleFactor, inputStream);
+        return bitmap;
+    }
+
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/LogHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/LogHelper.java
new file mode 100644
index 0000000..92b2e09
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/LogHelper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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.example.android.mediabrowserservice.utils;
+
+import android.util.Log;
+
+public class LogHelper {
+    public static void v(String tag, Object... messages) {
+        log(tag, Log.VERBOSE, null, messages);
+    }
+
+    public static void d(String tag, Object... messages) {
+        log(tag, Log.DEBUG, null, messages);
+    }
+
+    public static void i(String tag, Object... messages) {
+        log(tag, Log.INFO, null, messages);
+    }
+
+    public static void w(String tag, Object... messages) {
+        log(tag, Log.WARN, null, messages);
+    }
+
+    public static void w(String tag, Throwable t, Object... messages) {
+        log(tag, Log.WARN, t, messages);
+    }
+
+    public static void e(String tag, Object... messages) {
+        log(tag, Log.ERROR, null, messages);
+    }
+
+    public static void e(String tag, Throwable t, Object... messages) {
+        log(tag, Log.ERROR, t, messages);
+    }
+
+    public static void log(String tag, int level, Throwable t, Object... messages) {
+        if (messages != null && Log.isLoggable(tag, level)) {
+            String message;
+            if (messages.length == 1) {
+                message = messages[0] == null ? null : messages[0].toString();
+            } else {
+                StringBuilder sb = new StringBuilder();
+                for (Object m: messages) {
+                    sb.append(m);
+                }
+                if (t != null) {
+                    sb.append("\n").append(Log.getStackTraceString(t));
+                }
+                message = sb.toString();
+            }
+            Log.println(level, tag, message);
+        }
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/MediaIDHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/MediaIDHelper.java
new file mode 100644
index 0000000..68e6db9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/MediaIDHelper.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.mediabrowserservice.utils;
+
+import android.media.MediaMetadata;
+
+/**
+ * Utility class to help on queue related tasks.
+ */
+public class MediaIDHelper {
+
+    private static final String TAG = "MediaIDHelper";
+
+    // Media IDs used on browseable items of MediaBrowser
+    public static final String MEDIA_ID_ROOT = "__ROOT__";
+    public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
+
+    public static final String createTrackMediaID(String categoryType, String categoryValue,
+              MediaMetadata track) {
+        // MediaIDs are of the form <categoryType>/<categoryValue>|<musicUniqueId>, to make it easy to
+        // find the category (like genre) that a music was selected from, so we
+        // can correctly build the playing queue. This is specially useful when
+        // one music can appear in more than one list, like "by genre -> genre_1"
+        // and "by artist -> artist_1".
+        return categoryType + "/" + categoryValue + "|" +
+                track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+    }
+
+    public static final String createBrowseCategoryMediaID(String categoryType, String categoryValue) {
+        return categoryType + "/" + categoryValue;
+    }
+
+    /**
+     * Extracts unique musicID from the mediaID. mediaID is, by this sample's convention, a
+     * concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and unique
+     * musicID. This is necessary so we know where the user selected the music from, when the music
+     * exists in more than one music list, and thus we are able to correctly build the playing queue.
+     *
+     * @param musicID
+     * @return
+     */
+    public static final String extractMusicIDFromMediaID(String musicID) {
+        String[] segments = musicID.split("\\|", 2);
+        return segments.length == 2 ? segments[1] : null;
+    }
+
+    /**
+     * Extracts category and categoryValue from the mediaID. mediaID is, by this sample's
+     * convention, a concatenation of category (eg "by_genre"), categoryValue (eg "Classical") and
+     * mediaID. This is necessary so we know where the user selected the music from, when the music
+     * exists in more than one music list, and thus we are able to correctly build the playing queue.
+     *
+     * @param mediaID
+     * @return
+     */
+    public static final String[] extractBrowseCategoryFromMediaID(String mediaID) {
+        if (mediaID.indexOf('|') >= 0) {
+            mediaID = mediaID.split("\\|")[0];
+        }
+        if (mediaID.indexOf('/') == 0) {
+            return new String[]{mediaID, null};
+        } else {
+            return mediaID.split("/", 2);
+        }
+    }
+
+    public static final String extractBrowseCategoryValueFromMediaID(String mediaID) {
+        String[] categoryAndValue = extractBrowseCategoryFromMediaID(mediaID);
+        if (categoryAndValue != null && categoryAndValue.length == 2) {
+            return categoryAndValue[1];
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/QueueHelper.java b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/QueueHelper.java
new file mode 100644
index 0000000..abe3d34
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/java/com/example/android/mediabrowserservice/utils/QueueHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 Google Inc. All Rights Reserved.
+ *
+ * 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.example.android.mediabrowserservice.utils;
+
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+
+import com.example.android.mediabrowserservice.model.MusicProvider;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+
+/**
+ * Utility class to help on queue related tasks.
+ */
+public class QueueHelper {
+
+    private static final String TAG = "QueueHelper";
+
+    public static final List<MediaSession.QueueItem> getPlayingQueue(String mediaId,
+            MusicProvider musicProvider) {
+
+        // extract the category and unique music ID from the media ID:
+        String[] category = MediaIDHelper.extractBrowseCategoryFromMediaID(mediaId);
+
+        // This sample only supports genre category.
+        if (!category[0].equals(MEDIA_ID_MUSICS_BY_GENRE) || category.length != 2) {
+            LogHelper.e(TAG, "Could not build a playing queue for this mediaId: ", mediaId);
+            return null;
+        }
+
+        String categoryValue = category[1];
+        LogHelper.e(TAG, "Creating playing queue for musics of genre ", categoryValue);
+
+        List<MediaSession.QueueItem> queue = convertToQueue(
+                musicProvider.getMusicsByGenre(categoryValue));
+
+        return queue;
+    }
+
+    public static final List<MediaSession.QueueItem> getPlayingQueueFromSearch(String query,
+            MusicProvider musicProvider) {
+
+        LogHelper.e(TAG, "Creating playing queue for musics from search ", query);
+
+        return convertToQueue(musicProvider.searchMusics(query));
+    }
+
+
+    public static final int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
+             String mediaId) {
+        int index = 0;
+        for (MediaSession.QueueItem item: queue) {
+            if (mediaId.equals(item.getDescription().getMediaId())) {
+                return index;
+            }
+            index++;
+        }
+        return -1;
+    }
+
+    public static final int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
+             long queueId) {
+        int index = 0;
+        for (MediaSession.QueueItem item: queue) {
+            if (queueId == item.getQueueId()) {
+                return index;
+            }
+            index++;
+        }
+        return -1;
+    }
+
+    private static final List<MediaSession.QueueItem> convertToQueue(
+            Iterable<MediaMetadata> tracks) {
+        List<MediaSession.QueueItem> queue = new ArrayList<>();
+        int count = 0;
+        for (MediaMetadata track : tracks) {
+            // We don't expect queues to change after created, so we use the item index as the
+            // queueId. Any other number unique in the queue would work.
+            MediaSession.QueueItem item = new MediaSession.QueueItem(
+                    track.getDescription(), count++);
+            queue.add(item);
+        }
+        return queue;
+
+    }
+
+    /**
+     * Create a random queue. For simplicity sake, instead of a random queue, we create a
+     * queue using the first genre,
+     *
+     * @param musicProvider
+     * @return
+     */
+    public static final List<MediaSession.QueueItem> getRandomQueue(MusicProvider musicProvider) {
+        Iterator<String> genres = musicProvider.getGenres().iterator();
+        if (!genres.hasNext()) {
+            return new ArrayList<>();
+        }
+        String genre = genres.next();
+        Iterable<MediaMetadata> tracks = musicProvider.getMusicsByGenre(genre);
+
+        return convertToQueue(tracks);
+    }
+
+
+
+    public static final boolean isIndexPlayable(int index, List<MediaSession.QueueItem> queue) {
+        return (queue != null && index >= 0 && index < queue.size());
+    }
+}
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..47d6854
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_notification.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_notification.png
new file mode 100644
index 0000000..d8ea5a9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_notification.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_pause_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
new file mode 100644
index 0000000..b4bdbb5
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_pause_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..164385d
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_shuffle_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_shuffle_white_24dp.png
new file mode 100644
index 0000000..3eeb0ef
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_shuffle_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png
new file mode 100644
index 0000000..4eaf7ca
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_next_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png
new file mode 100644
index 0000000..e59dedb
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-hdpi/ic_skip_previous_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-mdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..01b53fd
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-night-xxhdpi/ic_star_off.png b/media/MediaBrowserService/Application/src/main/res/drawable-night-xxhdpi/ic_star_off.png
new file mode 100644
index 0000000..e435d2a
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-night-xxhdpi/ic_star_off.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-night-xxhdpi/ic_star_on.png b/media/MediaBrowserService/Application/src/main/res/drawable-night-xxhdpi/ic_star_on.png
new file mode 100644
index 0000000..0c75bb6
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-night-xxhdpi/ic_star_on.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png
new file mode 100644
index 0000000..dbba844
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_equalizer_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..af762f2
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png
new file mode 100644
index 0000000..14b6d17
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_pause_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..a55d199
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_shuffle_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_shuffle_white_24dp.png
new file mode 100644
index 0000000..8ce3a60
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_shuffle_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png
new file mode 100644
index 0000000..f282b92
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_next_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png
new file mode 100644
index 0000000..2522877
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xhdpi/ic_skip_previous_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_by_genre.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_by_genre.png
new file mode 100644
index 0000000..da3b4a7
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_by_genre.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_default_art.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_default_art.png
new file mode 100644
index 0000000..dfb9e67
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_default_art.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png
new file mode 100644
index 0000000..b82a8d9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_equalizer_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..eef47aa
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png
new file mode 100644
index 0000000..72dfa9f
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_pause_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
new file mode 100644
index 0000000..043acd8
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_play_arrow_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_shuffle_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_shuffle_white_24dp.png
new file mode 100644
index 0000000..718b6b5
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_shuffle_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png
new file mode 100644
index 0000000..4fe6088
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_next_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png
new file mode 100644
index 0000000..2c9310a
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_skip_previous_white_24dp.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_off.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_off.png
new file mode 100644
index 0000000..836085b
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_off.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_on.png b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_on.png
new file mode 100644
index 0000000..7cd6cfc
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/drawable-xxhdpi/ic_star_on.png
Binary files differ
diff --git a/media/MediaBrowserService/Application/src/main/res/layout/activity_player.xml b/media/MediaBrowserService/Application/src/main/res/layout/activity_player.xml
new file mode 100644
index 0000000..21cdbbd
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/layout/activity_player.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:tools="http://schemas.android.com/tools"
+             android:id="@+id/container"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             tools:context=".MusicPlayerActivity"
+             tools:ignore="MergeRootFrame" />
diff --git a/media/MediaBrowserService/Application/src/main/res/layout/fragment_list.xml b/media/MediaBrowserService/Application/src/main/res/layout/fragment_list.xml
new file mode 100644
index 0000000..c169fec
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/layout/fragment_list.xml
@@ -0,0 +1,60 @@
+<!--
+  Copyright (C) 2014 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"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:padding="@dimen/fragment_list_padding">
+
+    <LinearLayout
+        android:id="@+id/controls"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <ImageButton
+            android:id="@+id/skip_previous"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:src="@drawable/ic_skip_previous_white_24dp"
+            android:contentDescription="@string/skip_previous"/>
+
+        <ImageButton
+            android:id="@+id/play_pause"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:src="@drawable/ic_play_arrow_white_24dp"
+            android:contentDescription="@string/play_pause"/>
+
+        <ImageButton
+            android:id="@+id/skip_next"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:src="@drawable/ic_skip_next_white_24dp"
+            android:contentDescription="@string/skip_next"/>
+
+    </LinearLayout>
+
+    <ListView
+        android:id="@+id/list_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    </ListView>
+
+</LinearLayout>
diff --git a/media/MediaBrowserService/Application/src/main/res/layout/media_list_item.xml b/media/MediaBrowserService/Application/src/main/res/layout/media_list_item.xml
new file mode 100644
index 0000000..72c0ccf
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/layout/media_list_item.xml
@@ -0,0 +1,55 @@
+<!--
+  Copyright (C) 2014 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"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:minHeight="?android:listPreferredItemHeight"
+              android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/play_eq"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:contentDescription="@string/play_item"
+        android:src="@drawable/ic_play_arrow_white_24dp"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:mode="twoLine"
+        android:padding="@dimen/list_item_padding"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/margin_text_view"
+            android:layout_marginTop="@dimen/margin_text_view"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <TextView
+            android:id="@+id/description"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/margin_text_view"
+            android:layout_marginTop="@dimen/margin_text_view"
+            android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/media/MediaBrowserService/Application/src/main/res/values-v21/styles.xml b/media/MediaBrowserService/Application/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..21bb211
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values-v21/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+-->
+<resources>
+
+    <style name="AppBaseTheme" parent="android:Theme.Material">
+        <!-- colorPrimary is used for Notification icon and bottom facet bar icons
+        and overflow actions -->
+        <item name="android:colorPrimary">#ffff5722</item>
+
+        <!-- colorPrimaryDark is used for background -->
+        <item name="android:colorPrimaryDark">#ffbf360c</item>
+
+        <!-- colorAccent is sparingly used for accents, like floating action button highlight,
+        progress on playbar-->
+        <item name="android:colorAccent">#ffff5722</item>
+
+    </style>
+
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/dimens.xml b/media/MediaBrowserService/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..e57a8c9
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+    <dimen name="fragment_list_padding">16dp</dimen>
+    <dimen name="list_item_padding">4dp</dimen>
+    <dimen name="margin_text_view">6dp</dimen>
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/strings.xml b/media/MediaBrowserService/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7a012e8
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+
+    <string name="app_name">Auto Music Demo</string>
+    <string name="favorite">Favorite</string>
+    <string name="error_no_metadata">Unable to retrieve metadata.</string>
+    <string name="browse_genres">Genres</string>
+    <string name="browse_genre_subtitle">Songs by genre</string>
+    <string name="browse_musics_by_genre_subtitle">%1$s songs</string>
+    <string name="random_queue_title">Random music</string>
+    <string name="error_cannot_skip">Cannot skip</string>
+    <string name="error_loading_media">Error Loading Media</string>
+    <string name="play_item">Play item</string>
+    <string name="skip_previous">Skip to previous</string>
+    <string name="play_pause">play or pause</string>
+    <string name="skip_next">Skip to next</string>
+
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/strings_notifications.xml b/media/MediaBrowserService/Application/src/main/res/values/strings_notifications.xml
new file mode 100644
index 0000000..f406ba6
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/strings_notifications.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+
+    <string name="label_pause">Pause</string>
+    <string name="label_play">Play</string>
+    <string name="label_previous">Previous</string>
+    <string name="label_next">Next</string>
+    <string name="error_empty_metadata">Empty metadata!</string>
+</resources>
diff --git a/media/MediaBrowserService/Application/src/main/res/values/styles.xml b/media/MediaBrowserService/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3be59c1
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+-->
+<resources>
+
+
+    <style name="AppTheme" parent="AppBaseTheme">
+    </style>
+
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/media/MediaBrowserService/Application/src/main/res/xml/automotive_app_desc.xml b/media/MediaBrowserService/Application/src/main/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..a84750b
--- /dev/null
+++ b/media/MediaBrowserService/Application/src/main/res/xml/automotive_app_desc.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<automotiveApp>
+    <uses name="media"/>
+</automotiveApp>
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/media/MediaBrowserService/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to media/MediaBrowserService/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/media/MediaBrowserService/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to media/MediaBrowserService/LICENSE
diff --git a/media/MediaBrowserService/README.md b/media/MediaBrowserService/README.md
new file mode 100644
index 0000000..b61692e
--- /dev/null
+++ b/media/MediaBrowserService/README.md
@@ -0,0 +1,123 @@
+Android MediaBrowserService Sample
+==============================
+
+This sample shows how to implement an audio media app that provides
+media library metadata and playback controls through a standard
+service. This sample is compatible with Android Auto.
+
+Introduction
+------------
+
+This sample shows how to implement an audio media app that provides
+media library metadata and playback controls through a standard
+service. It exposes a simple music library through the new
+MediaBrowserService and provides MediaSession callbacks. This allows
+it to be used in Android Auto, for example.
+When not connected to a car, the app has a very simple UI that browses
+the media library and provides simple playback controls. When
+connected to Android Auto, the same service provides data and callback
+to the Android Auto UI in the same manner as it provides them to the
+local UI.
+
+To implement a MediaBrowserService, you need to:
+
+- extend android.service.media.MediaBrowserService, implementing the media
+  browsing related methods onGetRoot and onLoadChildren;
+
+- in onCreate, start a new MediaSession and call super.setSessionToken() with
+  this MediaSession's token;
+
+- set a MediaSession.Callback class on the MediaSession. The callback class
+  will receive all the user's actions, like play, pause, etc;
+
+- handle all the actual music playing using any method your app prefers
+  (for example, the Android MediaPlayer class)
+
+- whenever it changes, update info about the playing item and the playing
+  queue using MediaSession corresponding methods (setMetadata,
+  setPlaybackState, setQueue, setQueueTitle, etc)
+
+- handle AudioManager focus change events and react appropriately
+  (e.g. pause when audio focus is lost)
+
+
+To make it compatible with Android Auto, you also need to:
+
+- declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+  with a automotiveApp root element. For a media app, this must include
+  an &lt;uses name="media"/&gt; element as a child.
+
+  For example, in AndroidManifest.xml:
+```
+     <meta-data android:name="com.google.android.gms.car.application"
+       android:resource="@xml/automotive_app_desc"/>
+```
+
+  And in res/values/automotive\_app\_desc.xml:
+```
+      <?xml version="1.0" encoding="utf-8"?>
+      <automotiveApp>
+          <uses name="media"/>
+      </automotiveApp>
+```
+
+- declare and export the service in AndroidManifest:
+```
+    <service
+        android:name=".service.MusicService"
+        android:exported="true">
+      <intent-filter>
+         <action android:name="android.media.browse.MediaBrowserService" />
+      </intent-filter>
+    </service>
+```
+
+
+Pre-requisites
+--------------
+
+- Android SDK v21
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Screenshots
+-----------
+
+<!-- Update these to point to screenshots. Add more as needed. -->
+![Description](screenshots/image1.png) ![Description](screenshots/image2.png)
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MediaBrowserService
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you 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.
diff --git a/media/MediaBrowserService/build.gradle b/media/MediaBrowserService/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/media/MediaBrowserService/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/ui/notifications/BasicNotifications/buildSrc/build.gradle b/media/MediaBrowserService/buildSrc/build.gradle
similarity index 63%
rename from ui/notifications/BasicNotifications/buildSrc/build.gradle
rename to media/MediaBrowserService/buildSrc/build.gradle
index e344a8c..8c294c2 100644
--- a/ui/notifications/BasicNotifications/buildSrc/build.gradle
+++ b/media/MediaBrowserService/buildSrc/build.gradle
@@ -1,6 +1,3 @@
-
-
-
 repositories {
     mavenCentral()
 }
@@ -11,7 +8,7 @@
 sourceSets {
     main {
         groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
         }
     }
 }
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to media/MediaBrowserService/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties b/media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
copy from ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
copy to media/MediaBrowserService/gradle/wrapper/gradle-wrapper.properties
diff --git a/ui/notifications/BasicNotifications/gradlew b/media/MediaBrowserService/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to media/MediaBrowserService/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/media/MediaBrowserService/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to media/MediaBrowserService/gradlew.bat
diff --git a/media/MediaBrowserService/packaging.yaml b/media/MediaBrowserService/packaging.yaml
new file mode 100644
index 0000000..bcd9ac5
--- /dev/null
+++ b/media/MediaBrowserService/packaging.yaml
@@ -0,0 +1,15 @@
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+
+status:       PUBLISHED
+technologies: [Android]
+categories:   [None]
+languages:    [Java]
+solutions:    [Mobile]
+github:       googlesamples/android-MediaBrowserService
+level:        BEGINNER
+icon:         MediaBrowserServiceSample/src/main/res/drawable-xxhdpi/ic_launcher.png
+license:      apache2-android
diff --git a/media/MediaBrowserService/settings.gradle b/media/MediaBrowserService/settings.gradle
new file mode 100644
index 0000000..7f11c5f
--- /dev/null
+++ b/media/MediaBrowserService/settings.gradle
@@ -0,0 +1,2 @@
+include 'Application'
+
diff --git a/ui/notifications/BasicNotifications/template-params.xml b/media/MediaBrowserService/template-params.xml
similarity index 62%
copy from ui/notifications/BasicNotifications/template-params.xml
copy to media/MediaBrowserService/template-params.xml
index 7118c24..c129f6a 100644
--- a/ui/notifications/BasicNotifications/template-params.xml
+++ b/media/MediaBrowserService/template-params.xml
@@ -14,23 +14,23 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 -->
-
 <sample>
-    <name>BasicNotifications</name>
-    <group>UI</group>
-    <package>com.example.android.basicnotifications</package>
-    <!-- change minSdk if needed-->
-    <minSdk>8</minSdk>
+    <name>MediaBrowserService</name>
+    <group>Media</group>
+    <package>com.example.android.mediabrowserservice</package>
+
+    <minSdk>21</minSdk>
 
     <strings>
         <intro>
             <![CDATA[
-            This sample demonstrates how to display events in the system\'s notification bar. The
-            NotificationCompat API is used for compatibility with older devices, running Android
-            2.2 (Froyo) or newer.
+          This sample shows how to implement an audio media app that provides
+          media library metadata and playback controls through a standard
+          service.
             ]]>
         </intro>
     </strings>
 
-    <template src="base"/>
+    <!-- The basic templates have already been enabled. Uncomment more as desired. -->
+    <template src="basebuild" />
 </sample>
diff --git a/ui/notifications/BasicNotifications/Application/.gitignore b/notification/BasicNotifications/Application/.gitignore
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/.gitignore
rename to notification/BasicNotifications/Application/.gitignore
diff --git a/ui/notifications/BasicNotifications/Application/proguard-project.txt b/notification/BasicNotifications/Application/proguard-project.txt
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/proguard-project.txt
rename to notification/BasicNotifications/Application/proguard-project.txt
diff --git a/ui/notifications/BasicNotifications/Application/src/main/AndroidManifest.xml b/notification/BasicNotifications/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/AndroidManifest.xml
rename to notification/BasicNotifications/Application/src/main/AndroidManifest.xml
diff --git a/ui/notifications/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java b/notification/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java
rename to notification/BasicNotifications/Application/src/main/java/com/example/android/basicnotifications/MainActivity.java
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-hdpi/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-mdpi/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png b/notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_notification.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to notification/BasicNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/layout/sample_layout.xml b/notification/BasicNotifications/Application/src/main/res/layout/sample_layout.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/layout/sample_layout.xml
rename to notification/BasicNotifications/Application/src/main/res/layout/sample_layout.xml
diff --git a/ui/notifications/BasicNotifications/Application/src/main/res/values/strings.xml b/notification/BasicNotifications/Application/src/main/res/values/strings.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/src/main/res/values/strings.xml
rename to notification/BasicNotifications/Application/src/main/res/values/strings.xml
diff --git a/ui/notifications/BasicNotifications/Application/tests/AndroidManifest.xml b/notification/BasicNotifications/Application/tests/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/tests/AndroidManifest.xml
rename to notification/BasicNotifications/Application/tests/AndroidManifest.xml
diff --git a/ui/notifications/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java b/notification/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java
similarity index 100%
rename from ui/notifications/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java
rename to notification/BasicNotifications/Application/tests/src/com/example/android/basicnotifications/tests/SampleTests.java
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/notification/BasicNotifications/CONTRIB.md
similarity index 100%
rename from ui/notifications/BasicNotifications/CONTRIB.md
rename to notification/BasicNotifications/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/notification/BasicNotifications/LICENSE
similarity index 100%
rename from ui/notifications/BasicNotifications/LICENSE
rename to notification/BasicNotifications/LICENSE
diff --git a/ui/notifications/BasicNotifications/README.md b/notification/BasicNotifications/README.md
similarity index 100%
rename from ui/notifications/BasicNotifications/README.md
rename to notification/BasicNotifications/README.md
diff --git a/notification/BasicNotifications/build.gradle b/notification/BasicNotifications/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/BasicNotifications/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/ui/notifications/BasicNotifications/buildSrc/build.gradle b/notification/BasicNotifications/buildSrc/build.gradle
similarity index 63%
copy from ui/notifications/BasicNotifications/buildSrc/build.gradle
copy to notification/BasicNotifications/buildSrc/build.gradle
index e344a8c..8c294c2 100644
--- a/ui/notifications/BasicNotifications/buildSrc/build.gradle
+++ b/notification/BasicNotifications/buildSrc/build.gradle
@@ -1,6 +1,3 @@
-
-
-
 repositories {
     mavenCentral()
 }
@@ -11,7 +8,7 @@
 sourceSets {
     main {
         groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
         }
     }
 }
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
rename to notification/BasicNotifications/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
rename to notification/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
diff --git a/ui/notifications/BasicNotifications/gradlew b/notification/BasicNotifications/gradlew
similarity index 100%
rename from ui/notifications/BasicNotifications/gradlew
rename to notification/BasicNotifications/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/notification/BasicNotifications/gradlew.bat
similarity index 100%
rename from ui/notifications/BasicNotifications/gradlew.bat
rename to notification/BasicNotifications/gradlew.bat
diff --git a/ui/notifications/BasicNotifications/packaging.yaml b/notification/BasicNotifications/packaging.yaml
similarity index 100%
rename from ui/notifications/BasicNotifications/packaging.yaml
rename to notification/BasicNotifications/packaging.yaml
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/notification/BasicNotifications/settings.gradle
similarity index 100%
rename from ui/notifications/BasicNotifications/settings.gradle
rename to notification/BasicNotifications/settings.gradle
diff --git a/ui/notifications/BasicNotifications/template-params.xml b/notification/BasicNotifications/template-params.xml
similarity index 96%
rename from ui/notifications/BasicNotifications/template-params.xml
rename to notification/BasicNotifications/template-params.xml
index 7118c24..924a3d4 100644
--- a/ui/notifications/BasicNotifications/template-params.xml
+++ b/notification/BasicNotifications/template-params.xml
@@ -17,7 +17,7 @@
 
 <sample>
     <name>BasicNotifications</name>
-    <group>UI</group>
+    <group>Notification</group>
     <package>com.example.android.basicnotifications</package>
     <!-- change minSdk if needed-->
     <minSdk>8</minSdk>
diff --git a/ui/notifications/CustomNotifications/Application/.gitignore b/notification/CustomNotifications/Application/.gitignore
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/.gitignore
rename to notification/CustomNotifications/Application/.gitignore
diff --git a/ui/notifications/CustomNotifications/Application/proguard-project.txt b/notification/CustomNotifications/Application/proguard-project.txt
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/proguard-project.txt
rename to notification/CustomNotifications/Application/proguard-project.txt
diff --git a/ui/notifications/CustomNotifications/Application/src/main/AndroidManifest.xml b/notification/CustomNotifications/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/AndroidManifest.xml
rename to notification/CustomNotifications/Application/src/main/AndroidManifest.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java b/notification/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java
rename to notification/CustomNotifications/Application/src/main/java/com/example/android/customnotifications/MainActivity.java
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-hdpi/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-ldpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-mdpi/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v11/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi-v9/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/ic_stat_custom.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png b/notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xhdpi/robot_expanded.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to notification/CustomNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/layout/notification.xml b/notification/CustomNotifications/Application/src/main/res/layout/notification.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/layout/notification.xml
rename to notification/CustomNotifications/Application/src/main/res/layout/notification.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml b/notification/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml
rename to notification/CustomNotifications/Application/src/main/res/layout/notification_expanded.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/layout/sample_main.xml b/notification/CustomNotifications/Application/src/main/res/layout/sample_main.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/layout/sample_main.xml
rename to notification/CustomNotifications/Application/src/main/res/layout/sample_main.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml b/notification/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml
rename to notification/CustomNotifications/Application/src/main/res/values-sw720dp-land/dimens.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values-v9/styles.xml b/notification/CustomNotifications/Application/src/main/res/values-v9/styles.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values-v9/styles.xml
rename to notification/CustomNotifications/Application/src/main/res/values-v9/styles.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values/dimens.xml b/notification/CustomNotifications/Application/src/main/res/values/dimens.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values/dimens.xml
rename to notification/CustomNotifications/Application/src/main/res/values/dimens.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values/strings.xml b/notification/CustomNotifications/Application/src/main/res/values/strings.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values/strings.xml
rename to notification/CustomNotifications/Application/src/main/res/values/strings.xml
diff --git a/ui/notifications/CustomNotifications/Application/src/main/res/values/styles.xml b/notification/CustomNotifications/Application/src/main/res/values/styles.xml
similarity index 100%
rename from ui/notifications/CustomNotifications/Application/src/main/res/values/styles.xml
rename to notification/CustomNotifications/Application/src/main/res/values/styles.xml
diff --git a/ui/notifications/CustomNotifications/CONTRIB.md b/notification/CustomNotifications/CONTRIB.md
similarity index 100%
rename from ui/notifications/CustomNotifications/CONTRIB.md
rename to notification/CustomNotifications/CONTRIB.md
diff --git a/ui/notifications/CustomNotifications/LICENSE b/notification/CustomNotifications/LICENSE
similarity index 100%
rename from ui/notifications/CustomNotifications/LICENSE
rename to notification/CustomNotifications/LICENSE
diff --git a/ui/notifications/CustomNotifications/README.md b/notification/CustomNotifications/README.md
similarity index 100%
rename from ui/notifications/CustomNotifications/README.md
rename to notification/CustomNotifications/README.md
diff --git a/notification/CustomNotifications/build.gradle b/notification/CustomNotifications/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/CustomNotifications/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/ui/notifications/BasicNotifications/buildSrc/build.gradle b/notification/CustomNotifications/buildSrc/build.gradle
similarity index 63%
copy from ui/notifications/BasicNotifications/buildSrc/build.gradle
copy to notification/CustomNotifications/buildSrc/build.gradle
index e344a8c..8c294c2 100644
--- a/ui/notifications/BasicNotifications/buildSrc/build.gradle
+++ b/notification/CustomNotifications/buildSrc/build.gradle
@@ -1,6 +1,3 @@
-
-
-
 repositories {
     mavenCentral()
 }
@@ -11,7 +8,7 @@
 sourceSets {
     main {
         groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
         }
     }
 }
diff --git a/ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.jar
rename to notification/CustomNotifications/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from ui/notifications/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
rename to notification/CustomNotifications/gradle/wrapper/gradle-wrapper.properties
diff --git a/ui/notifications/CustomNotifications/gradlew b/notification/CustomNotifications/gradlew
similarity index 100%
rename from ui/notifications/CustomNotifications/gradlew
rename to notification/CustomNotifications/gradlew
diff --git a/ui/notifications/CustomNotifications/gradlew.bat b/notification/CustomNotifications/gradlew.bat
similarity index 100%
rename from ui/notifications/CustomNotifications/gradlew.bat
rename to notification/CustomNotifications/gradlew.bat
diff --git a/ui/notifications/CustomNotifications/packaging.yaml b/notification/CustomNotifications/packaging.yaml
similarity index 100%
rename from ui/notifications/CustomNotifications/packaging.yaml
rename to notification/CustomNotifications/packaging.yaml
diff --git a/ui/notifications/CustomNotifications/settings.gradle b/notification/CustomNotifications/settings.gradle
similarity index 100%
rename from ui/notifications/CustomNotifications/settings.gradle
rename to notification/CustomNotifications/settings.gradle
diff --git a/ui/notifications/CustomNotifications/template-params.xml b/notification/CustomNotifications/template-params.xml
similarity index 96%
rename from ui/notifications/CustomNotifications/template-params.xml
rename to notification/CustomNotifications/template-params.xml
index ff90ca3..1ded0d1 100644
--- a/ui/notifications/CustomNotifications/template-params.xml
+++ b/notification/CustomNotifications/template-params.xml
@@ -16,7 +16,7 @@
 -->
 <sample>
     <name>CustomNotifications</name>
-    <group>UI</group>
+    <group>Notification</group>
     <package>com.example.android.customnotifications</package>
 
     <!-- change minSdk if needed-->
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/notification/LNotifications/Application/.gitignore
similarity index 100%
rename from ui/notifications/LNotifications/Application/.gitignore
rename to notification/LNotifications/Application/.gitignore
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/notification/LNotifications/Application/proguard-project.txt
similarity index 100%
rename from ui/notifications/LNotifications/Application/proguard-project.txt
rename to notification/LNotifications/Application/proguard-project.txt
diff --git a/ui/notifications/LNotifications/Application/src/main/AndroidManifest.xml b/notification/LNotifications/Application/src/main/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/AndroidManifest.xml
rename to notification/LNotifications/Application/src/main/AndroidManifest.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/HeadsUpNotificationFragment.java
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/LNotificationActivity.java
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/OtherMetadataFragment.java
diff --git a/ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java b/notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java
rename to notification/LNotifications/Application/src/main/java/com/example/android/lnotifications/VisibilityMetadataFragment.java
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-hdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-mdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xhdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_launcher_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_private_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_public_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png b/notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png
rename to notification/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_secret_notification.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/activity_notification.xml b/notification/LNotifications/Application/src/main/res/layout/activity_notification.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/activity_notification.xml
rename to notification/LNotifications/Application/src/main/res/layout/activity_notification.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/contact_entry.xml b/notification/LNotifications/Application/src/main/res/layout/contact_entry.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/contact_entry.xml
rename to notification/LNotifications/Application/src/main/res/layout/contact_entry.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml b/notification/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml
rename to notification/LNotifications/Application/src/main/res/layout/fragment_heads_up_notification.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml b/notification/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml
rename to notification/LNotifications/Application/src/main/res/layout/fragment_other_metadata.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml b/notification/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml
rename to notification/LNotifications/Application/src/main/res/layout/fragment_visibility_metadata_notification.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/values/colors.xml b/notification/LNotifications/Application/src/main/res/values/colors.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/values/colors.xml
rename to notification/LNotifications/Application/src/main/res/values/colors.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/values/dimens.xml b/notification/LNotifications/Application/src/main/res/values/dimens.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/values/dimens.xml
rename to notification/LNotifications/Application/src/main/res/values/dimens.xml
diff --git a/ui/notifications/LNotifications/Application/src/main/res/values/strings.xml b/notification/LNotifications/Application/src/main/res/values/strings.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/src/main/res/values/strings.xml
rename to notification/LNotifications/Application/src/main/res/values/strings.xml
diff --git a/ui/notifications/LNotifications/Application/tests/AndroidManifest.xml b/notification/LNotifications/Application/tests/AndroidManifest.xml
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/AndroidManifest.xml
rename to notification/LNotifications/Application/tests/AndroidManifest.xml
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/HeadsUpNotificationFragmentTest.java
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/LNotificationActivityTest.java
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/OtherMetadataFragmentTest.java
diff --git a/ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java b/notification/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java
similarity index 100%
rename from ui/notifications/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java
rename to notification/LNotifications/Application/tests/src/com/example/android/lnotifications/VisibilityMetadataFragmentTest.java
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/notification/LNotifications/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to notification/LNotifications/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/notification/LNotifications/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to notification/LNotifications/LICENSE
diff --git a/ui/notifications/LNotifications/README.md b/notification/LNotifications/README.md
similarity index 100%
rename from ui/notifications/LNotifications/README.md
rename to notification/LNotifications/README.md
diff --git a/notification/LNotifications/build.gradle b/notification/LNotifications/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/LNotifications/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/ui/notifications/BasicNotifications/buildSrc/build.gradle b/notification/LNotifications/buildSrc/build.gradle
similarity index 63%
copy from ui/notifications/BasicNotifications/buildSrc/build.gradle
copy to notification/LNotifications/buildSrc/build.gradle
index e344a8c..8c294c2 100644
--- a/ui/notifications/BasicNotifications/buildSrc/build.gradle
+++ b/notification/LNotifications/buildSrc/build.gradle
@@ -1,6 +1,3 @@
-
-
-
 repositories {
     mavenCentral()
 }
@@ -11,7 +8,7 @@
 sourceSets {
     main {
         groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
         }
     }
 }
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/LNotifications/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
rename to notification/LNotifications/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/LNotifications/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.properties
rename to notification/LNotifications/gradle/wrapper/gradle-wrapper.properties
diff --git a/ui/notifications/LNotifications/gradlew b/notification/LNotifications/gradlew
similarity index 100%
rename from ui/notifications/LNotifications/gradlew
rename to notification/LNotifications/gradlew
diff --git a/ui/notifications/LNotifications/gradlew.bat b/notification/LNotifications/gradlew.bat
similarity index 100%
rename from ui/notifications/LNotifications/gradlew.bat
rename to notification/LNotifications/gradlew.bat
diff --git a/ui/notifications/LNotifications/packaging.yaml b/notification/LNotifications/packaging.yaml
similarity index 100%
rename from ui/notifications/LNotifications/packaging.yaml
rename to notification/LNotifications/packaging.yaml
diff --git a/ui/notifications/LNotifications/settings.gradle b/notification/LNotifications/settings.gradle
similarity index 100%
rename from ui/notifications/LNotifications/settings.gradle
rename to notification/LNotifications/settings.gradle
diff --git a/ui/notifications/LNotifications/template-params.xml b/notification/LNotifications/template-params.xml
similarity index 97%
rename from ui/notifications/LNotifications/template-params.xml
rename to notification/LNotifications/template-params.xml
index 43d5dc0..6257c81 100644
--- a/ui/notifications/LNotifications/template-params.xml
+++ b/notification/LNotifications/template-params.xml
@@ -17,7 +17,7 @@
 
 <sample>
     <name>LNotifications Sample</name>
-    <group>UI</group>
+    <group>Notification</group>
     <package>com.example.android.lnotifications</package>
 
     <!-- change minSdk if needed-->
diff --git a/ui/notifications/LNotifications/Application/.gitignore b/notification/MessagingService/Application/.gitignore
similarity index 100%
copy from ui/notifications/LNotifications/Application/.gitignore
copy to notification/MessagingService/Application/.gitignore
diff --git a/notification/MessagingService/Application/libs/android-auto-sdk.jar b/notification/MessagingService/Application/libs/android-auto-sdk.jar
new file mode 100644
index 0000000..8a9897d
--- /dev/null
+++ b/notification/MessagingService/Application/libs/android-auto-sdk.jar
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/proguard-project.txt b/notification/MessagingService/Application/proguard-project.txt
similarity index 100%
copy from ui/notifications/LNotifications/Application/proguard-project.txt
copy to notification/MessagingService/Application/proguard-project.txt
diff --git a/notification/MessagingService/Application/src/androidTest/java/com/example/android/messagingservice/test/SampleTests.java b/notification/MessagingService/Application/src/androidTest/java/com/example/android/messagingservice/test/SampleTests.java
new file mode 100644
index 0000000..79d618b
--- /dev/null
+++ b/notification/MessagingService/Application/src/androidTest/java/com/example/android/messagingservice/test/SampleTests.java
@@ -0,0 +1,76 @@
+/*
+* Copyright 2013 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.
+*/
+/*
+* Copyright (C) 2014 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.example.android.messagingservice.test;
+
+import com.example.android.messagingservice.*;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+* Tests for MessagingService sample.
+*/
+public class SampleTests extends ActivityInstrumentationTestCase2<MainActivity> {
+
+    private MainActivity mTestActivity;
+    private MessagingServiceFragment mTestFragment;
+
+    public SampleTests() {
+        super(MainActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Starts the activity under test using the default Intent with:
+        // action = {@link Intent#ACTION_MAIN}
+        // flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
+        // All other fields are null or empty.
+        mTestActivity = getActivity();
+        mTestFragment = (MessagingServiceFragment)
+            mTestActivity.getSupportFragmentManager().getFragments().get(1);
+    }
+
+    /**
+    * Test if the test fixture has been set up correctly.
+    */
+    public void testPreconditions() {
+        //Try to add a message to add context to your assertions. These messages will be shown if
+        //a tests fails and make it easy to understand why a test failed
+        assertNotNull("mTestActivity is null", mTestActivity);
+        assertNotNull("mTestFragment is null", mTestFragment);
+    }
+
+    /**
+    * Add more tests below.
+    */
+
+}
diff --git a/notification/MessagingService/Application/src/main/AndroidManifest.xml b/notification/MessagingService/Application/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f8a5850
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.messagingservice">
+
+    <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher"
+        android:theme="@style/AppTheme">
+
+        <meta-data android:name="com.google.android.gms.car.application"
+                   android:resource="@xml/automotive_app_desc"/>
+
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".MessagingService">
+        </service>
+
+        <receiver android:name=".MessageReadReceiver">
+            <intent-filter>
+                <action android:name="com.example.android.messagingservice.ACTION_MESSAGE_READ"/>
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name=".MessageReplyReceiver">
+            <intent-filter>
+                <action android:name="com.example.android.messagingservice.ACTION_MESSAGE_REPLY"/>
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/Conversations.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/Conversations.java
new file mode 100644
index 0000000..210e061
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/Conversations.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * A simple class that denotes unread conversations and messages. In a real world application,
+ * this would be replaced by a content provider that actually gets the unread messages to be
+ * shown to the user.
+ */
+public class Conversations {
+
+    /**
+     * Set of strings used as messages by the sample.
+     */
+    private static final String[] MESSAGES = new String[]{
+            "Are you at home?",
+            "Can you give me a call?",
+            "Hey yt?",
+            "Don't forget to get some milk on your way back home",
+            "Is that project done?",
+            "Did you finish the Messaging app yet?"
+    };
+
+    /**
+     * Senders of the said messages.
+     */
+    private static final String[] PARTICIPANTS = new String[]{
+            "John Rambo",
+            "Han Solo",
+            "Rocky Balboa",
+            "Lara Croft"
+    };
+
+    static class Conversation {
+
+        private final int conversationId;
+
+        private final String participantName;
+
+        /**
+         * A given conversation can have a single or multiple messages.
+         * Note that the messages are sorted from *newest* to *oldest*
+         */
+        private final List<String> messages;
+
+        private final long timestamp;
+
+        public Conversation(int conversationId, String participantName,
+                            List<String> messages) {
+            this.conversationId = conversationId;
+            this.participantName = participantName;
+            this.messages = messages == null ? Collections.<String>emptyList() : messages;
+            this.timestamp = System.currentTimeMillis();
+        }
+
+        public int getConversationId() {
+            return conversationId;
+        }
+
+        public String getParticipantName() {
+            return participantName;
+        }
+
+        public List<String> getMessages() {
+            return messages;
+        }
+
+        public long getTimestamp() {
+            return timestamp;
+        }
+
+        public String toString() {
+            return "[Conversation: conversationId=" + conversationId +
+                    ", participantName=" + participantName +
+                    ", messages=" + messages +
+                    ", timestamp=" + timestamp + "]";
+        }
+    }
+
+    private Conversations() {
+    }
+
+    public static Conversation[] getUnreadConversations(int howManyConversations,
+                                                        int messagesPerConversation) {
+        Conversation[] conversations = new Conversation[howManyConversations];
+        for (int i = 0; i < howManyConversations; i++) {
+            conversations[i] = new Conversation(
+                    ThreadLocalRandom.current().nextInt(),
+                    name(), makeMessages(messagesPerConversation));
+        }
+        return conversations;
+    }
+
+    private static List<String> makeMessages(int messagesPerConversation) {
+        int maxLen = MESSAGES.length;
+        List<String> messages = new ArrayList<>(messagesPerConversation);
+        for (int i = 0; i < messagesPerConversation; i++) {
+            messages.add(MESSAGES[ThreadLocalRandom.current().nextInt(0, maxLen)]);
+        }
+        return messages;
+    }
+
+    private static String name() {
+        return PARTICIPANTS[ThreadLocalRandom.current().nextInt(0, PARTICIPANTS.length)];
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MainActivity.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MainActivity.java
new file mode 100644
index 0000000..e558a64
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MainActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class MainActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        if (savedInstanceState == null) {
+            getFragmentManager().beginTransaction()
+                    .add(R.id.container, new MessagingFragment())
+                    .commit();
+        }
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageLogger.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageLogger.java
new file mode 100644
index 0000000..d1007b5
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageLogger.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * A simple logger that uses shared preferences to log messages, their reads
+ * and replies. Don't use this in a real world application. This logger is only
+ * used for displaying the messages in the text view.
+ */
+public class MessageLogger {
+
+    private static final String PREF_MESSAGE = "MESSAGE_LOGGER";
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    public static final String LOG_KEY = "message_data";
+    public static final String LINE_BREAKS = "\n\n";
+
+    public static void logMessage(Context context, String message) {
+        SharedPreferences prefs = getPrefs(context);
+        message = DATE_FORMAT.format(new Date(System.currentTimeMillis())) + ": " + message;
+        prefs.edit()
+                .putString(LOG_KEY, prefs.getString(LOG_KEY, "") + LINE_BREAKS + message)
+                .apply();
+    }
+
+    public static SharedPreferences getPrefs(Context context) {
+        return context.getSharedPreferences(PREF_MESSAGE, Context.MODE_PRIVATE);
+    }
+
+    public static String getAllMessages(Context context) {
+        return getPrefs(context).getString(LOG_KEY, "");
+    }
+
+    public static void clear(Context context) {
+        getPrefs(context).edit().remove(LOG_KEY).apply();
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReadReceiver.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReadReceiver.java
new file mode 100644
index 0000000..f28a3a7
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReadReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+public class MessageReadReceiver extends BroadcastReceiver {
+    private static final String TAG = MessageReadReceiver.class.getSimpleName();
+
+    private static final String CONVERSATION_ID = "conversation_id";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive");
+        int conversationId = intent.getIntExtra(CONVERSATION_ID, -1);
+        if (conversationId != -1) {
+            Log.d(TAG, "Conversation " + conversationId + " was read");
+            MessageLogger.logMessage(context, "Conversation " + conversationId + " was read.");
+            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+            notificationManager.cancel(conversationId);
+        }
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReplyReceiver.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReplyReceiver.java
new file mode 100644
index 0000000..0a3eba6
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessageReplyReceiver.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.RemoteInput;
+import android.util.Log;
+
+/**
+ * A receiver that gets called when a reply is sent to a given conversationId
+ */
+public class MessageReplyReceiver extends BroadcastReceiver {
+
+    private static final String TAG = MessageReplyReceiver.class.getSimpleName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (MessagingService.REPLY_ACTION.equals(intent.getAction())) {
+            int conversationId = intent.getIntExtra(MessagingService.CONVERSATION_ID, -1);
+            CharSequence reply = getMessageText(intent);
+            if (conversationId != -1) {
+                Log.d(TAG, "Got reply (" + reply + ") for ConversationId " + conversationId);
+                MessageLogger.logMessage(context, "ConversationId: " + conversationId +
+                        " received a reply: [" + reply + "]");
+            }
+        }
+    }
+
+    /**
+     * Get the message text from the intent.
+     * Note that you should call {@code RemoteInput#getResultsFromIntent(intent)} to process
+     * the RemoteInput.
+     */
+    private CharSequence getMessageText(Intent intent) {
+        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+        if (remoteInput != null) {
+            return remoteInput.getCharSequence(MessagingService.EXTRA_VOICE_REPLY);
+        }
+        return null;
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingFragment.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingFragment.java
new file mode 100644
index 0000000..f8efcc0
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingFragment.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * The main fragment that shows the buttons and the text view containing the log.
+ */
+public class MessagingFragment extends Fragment implements View.OnClickListener {
+
+    private static final String TAG = MessagingFragment.class.getSimpleName();
+
+    private Button mSendSingleConversation;
+    private Button mSendTwoConversations;
+    private Button mSendConversationWithThreeMessages;
+    private TextView mDataPortView;
+    private Button mClearLogButton;
+
+    private Messenger mService;
+    private boolean mBound;
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            mService = new Messenger(service);
+            mBound = true;
+            setButtonsState(true);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mService = null;
+            mBound = false;
+            setButtonsState(false);
+        }
+    };
+
+    private SharedPreferences.OnSharedPreferenceChangeListener listener =
+            new SharedPreferences.OnSharedPreferenceChangeListener() {
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+            if (MessageLogger.LOG_KEY.equals(key)) {
+                mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
+            }
+        }
+    };
+
+    public MessagingFragment() {
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_message_me, container, false);
+
+        mSendSingleConversation = (Button) rootView.findViewById(R.id.send_1_conversation);
+        mSendSingleConversation.setOnClickListener(this);
+
+        mSendTwoConversations = (Button) rootView.findViewById(R.id.send_2_conversations);
+        mSendTwoConversations.setOnClickListener(this);
+
+        mSendConversationWithThreeMessages =
+                (Button) rootView.findViewById(R.id.send_1_conversation_3_messages);
+        mSendConversationWithThreeMessages.setOnClickListener(this);
+
+        mDataPortView = (TextView) rootView.findViewById(R.id.data_port);
+        mDataPortView.setMovementMethod(new ScrollingMovementMethod());
+
+        mClearLogButton = (Button) rootView.findViewById(R.id.clear);
+        mClearLogButton.setOnClickListener(this);
+
+        setButtonsState(false);
+
+        return rootView;
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (view == mSendSingleConversation) {
+            sendMsg(1, 1);
+        } else if (view == mSendTwoConversations) {
+            sendMsg(2, 1);
+        } else if (view == mSendConversationWithThreeMessages) {
+            sendMsg(1, 3);
+        } else if (view == mClearLogButton) {
+            MessageLogger.clear(getActivity());
+            mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        getActivity().bindService(new Intent(getActivity(), MessagingService.class), mConnection,
+                Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        MessageLogger.getPrefs(getActivity()).unregisterOnSharedPreferenceChangeListener(listener);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mDataPortView.setText(MessageLogger.getAllMessages(getActivity()));
+        MessageLogger.getPrefs(getActivity()).registerOnSharedPreferenceChangeListener(listener);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mBound) {
+            getActivity().unbindService(mConnection);
+            mBound = false;
+        }
+    }
+
+    private void sendMsg(int howManyConversations, int messagesPerConversation) {
+        if (mBound) {
+            Message msg = Message.obtain(null, MessagingService.MSG_SEND_NOTIFICATION,
+                    howManyConversations, messagesPerConversation);
+            try {
+                mService.send(msg);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error sending a message", e);
+                MessageLogger.logMessage(getActivity(), "Error occurred while sending a message.");
+            }
+        }
+    }
+
+    private void setButtonsState(boolean enable) {
+        mSendSingleConversation.setEnabled(enable);
+        mSendTwoConversations.setEnabled(enable);
+        mSendConversationWithThreeMessages.setEnabled(enable);
+    }
+}
diff --git a/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingService.java b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingService.java
new file mode 100644
index 0000000..f980375
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/java/com/example/android/messagingservice/MessagingService.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 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.example.android.messagingservice;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.preview.support.v4.app.NotificationCompat.CarExtender;
+import android.preview.support.v4.app.NotificationCompat.CarExtender.UnreadConversation;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.RemoteInput;
+import android.util.Log;
+
+import java.util.Iterator;
+
+public class MessagingService extends Service {
+    private static final String TAG = MessagingService.class.getSimpleName();
+
+    public static final String READ_ACTION =
+            "com.example.android.messagingservice.ACTION_MESSAGE_READ";
+    public static final String REPLY_ACTION =
+            "com.example.android.messagingservice.ACTION_MESSAGE_REPLY";
+    public static final String CONVERSATION_ID = "conversation_id";
+    public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
+    public static final int MSG_SEND_NOTIFICATION = 1;
+    public static final String EOL = "\n";
+
+    private NotificationManagerCompat mNotificationManager;
+
+    private final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * Handler of incoming messages from clients.
+     */
+    class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SEND_NOTIFICATION:
+                    int howManyConversations = msg.arg1 <= 0 ? 1 : msg.arg1;
+                    int messagesPerConv = msg.arg2 <= 0 ? 1 : msg.arg2;
+                    sendNotification(howManyConversations, messagesPerConv);
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        Log.d(TAG, "onCreate");
+        mNotificationManager = NotificationManagerCompat.from(getApplicationContext());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "onBind");
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "onStartCommand");
+        return START_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.d(TAG, "onDestroy");
+    }
+
+    // Creates an intent that will be triggered when a message is marked as read.
+    private Intent getMessageReadIntent(int id) {
+        return new Intent()
+                .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+                .setAction(READ_ACTION)
+                .putExtra(CONVERSATION_ID, id);
+    }
+
+    // Creates an Intent that will be triggered when a voice reply is received.
+    private Intent getMessageReplyIntent(int conversationId) {
+        return new Intent()
+                .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+                .setAction(REPLY_ACTION)
+                .putExtra(CONVERSATION_ID, conversationId);
+    }
+
+    private void sendNotification(int howManyConversations, int messagesPerConversation) {
+        Conversations.Conversation[] conversations = Conversations.getUnreadConversations(
+                howManyConversations, messagesPerConversation);
+        for (Conversations.Conversation conv : conversations) {
+            sendNotificationForConversation(conv);
+        }
+    }
+
+    private void sendNotificationForConversation(Conversations.Conversation conversation) {
+        // A pending Intent for reads
+        PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
+                conversation.getConversationId(),
+                getMessageReadIntent(conversation.getConversationId()),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Build a RemoteInput for receiving voice input in a Car Notification
+        RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
+                .setLabel(getApplicationContext().getString(R.string.notification_reply))
+                .build();
+
+        // Building a Pending Intent for the reply action to trigger
+        PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(),
+                conversation.getConversationId(),
+                getMessageReplyIntent(conversation.getConversationId()),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+
+        // Create the UnreadConversation and populate it with the participant name,
+        // read and reply intents.
+        UnreadConversation.Builder unreadConvBuilder =
+                new UnreadConversation.Builder(conversation.getParticipantName())
+                .setLatestTimestamp(conversation.getTimestamp())
+                .setReadPendingIntent(readPendingIntent)
+                .setReplyAction(replyIntent, remoteInput);
+
+        // Note: Add messages from oldest to newest to the UnreadConversation.Builder
+        StringBuilder messageForNotification = new StringBuilder();
+        for (Iterator<String> messages = conversation.getMessages().iterator();
+             messages.hasNext(); ) {
+            String message = messages.next();
+            unreadConvBuilder.addMessage(message);
+            messageForNotification.append(message);
+            if (messages.hasNext()) {
+                messageForNotification.append(EOL);
+            }
+        }
+
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
+                .setSmallIcon(R.drawable.notification_icon)
+                .setLargeIcon(BitmapFactory.decodeResource(
+                        getApplicationContext().getResources(), R.drawable.android_contact))
+                .setContentText(messageForNotification.toString())
+                .setWhen(conversation.getTimestamp())
+                .setContentTitle(conversation.getParticipantName())
+                .setContentIntent(readPendingIntent)
+                .extend(new CarExtender()
+                        .setUnreadConversation(unreadConvBuilder.build())
+                        .setColor(getApplicationContext()
+                                .getResources().getColor(R.color.default_color_light)));
+
+        MessageLogger.logMessage(getApplicationContext(), "Sending notification "
+                + conversation.getConversationId() + " conversation: " + conversation);
+
+        mNotificationManager.notify(conversation.getConversationId(), builder.build());
+    }
+}
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-hdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-hdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-hdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-hdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..06d85f1
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-hdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-hdpi/notification_icon.png
new file mode 100644
index 0000000..9cdfca1
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-hdpi/notification_icon.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-mdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-mdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-mdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-mdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..4e1cc86
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-mdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-mdpi/notification_icon.png
new file mode 100644
index 0000000..d6069eb
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-mdpi/notification_icon.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-xhdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-xhdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..92f1e2d
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xhdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/notification_icon.png
new file mode 100644
index 0000000..786ed17
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xhdpi/notification_icon.png
Binary files differ
diff --git a/ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/android_contact.png
similarity index 100%
copy from ui/notifications/LNotifications/Application/src/main/res/drawable-xxhdpi/ic_contact_picture.png
copy to notification/MessagingService/Application/src/main/res/drawable-xxhdpi/android_contact.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2476cbd
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/notification_icon.png b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/notification_icon.png
new file mode 100644
index 0000000..005207c
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/drawable-xxhdpi/notification_icon.png
Binary files differ
diff --git a/notification/MessagingService/Application/src/main/res/layout-land/fragment_message_me.xml b/notification/MessagingService/Application/src/main/res/layout-land/fragment_message_me.xml
new file mode 100644
index 0000000..8f7b60a
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/layout-land/fragment_message_me.xml
@@ -0,0 +1,67 @@
+<!--
+  Copyright (C) 2014 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"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="center_horizontal"
+              android:orientation="horizontal"
+              android:paddingBottom="@dimen/activity_vertical_margin"
+              android:paddingLeft="@dimen/activity_horizontal_margin"
+              android:paddingRight="@dimen/activity_horizontal_margin"
+              android:paddingTop="@dimen/activity_vertical_margin">
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <Button
+            android:id="@+id/send_1_conversation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/send_1_conversation"/>
+
+        <Button
+            android:id="@+id/send_2_conversations"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/send_2_conversations"/>
+
+        <Button
+            android:id="@+id/send_1_conversation_3_messages"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/send_1_conv_3_messages"/>
+    </LinearLayout>
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="2">
+        <Button
+            android:id="@+id/clear"
+            android:layout_alignParentBottom="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/clear_log"/>
+
+        <TextView
+            android:id="@+id/data_port"
+            android:layout_above="@id/clear"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:maxLines="20"
+            android:scrollbars="vertical"/>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/notification/MessagingService/Application/src/main/res/layout/activity_main.xml b/notification/MessagingService/Application/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..59eec80
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,22 @@
+<!--
+  Copyright (C) 2014 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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity"
+    tools:ignore="MergeRootFrame" />
diff --git a/notification/MessagingService/Application/src/main/res/layout/fragment_message_me.xml b/notification/MessagingService/Application/src/main/res/layout/fragment_message_me.xml
new file mode 100644
index 0000000..d01c513
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/layout/fragment_message_me.xml
@@ -0,0 +1,58 @@
+<!--
+  Copyright (C) 2014 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:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:gravity="center_horizontal"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin">
+
+    <Button
+        android:id="@+id/send_1_conversation"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/send_1_conversation"/>
+
+    <Button
+        android:id="@+id/send_2_conversations"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/send_2_conversations"/>
+
+    <Button
+        android:id="@+id/send_1_conversation_3_messages"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/send_1_conv_3_messages"/>
+
+    <TextView
+        android:id="@+id/data_port"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:maxLines="20"
+        android:scrollbars="vertical"/>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/clear"
+        android:text="@string/clear_log"/>
+
+</LinearLayout>
diff --git a/notification/MessagingService/Application/src/main/res/values-v21/styles.xml b/notification/MessagingService/Application/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..f30c97a
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values-v21/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+    <style name="AppTheme" parent="android:Theme.Material.Light">
+        <item name="android:colorPrimary">@color/default_color_light</item>
+        <item name="android:colorPrimaryDark">@color/default_color_dark</item>
+    </style>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/colors.xml b/notification/MessagingService/Application/src/main/res/values/colors.xml
new file mode 100644
index 0000000..0e6825b
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+    <color name="default_color_light">#ff4092c3</color>
+    <color name="default_color_dark">#ff241c99</color>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/dimens.xml b/notification/MessagingService/Application/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..574a35d
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/strings.xml b/notification/MessagingService/Application/src/main/res/values/strings.xml
new file mode 100644
index 0000000..001b10e
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+    <string name="app_name">Messaging Sample</string>
+    <string name="action_settings">Settings</string>
+    <string name="title">Messaging Sample</string>
+    <string name="notification_reply">Reply by Voice</string>
+    <string name="send_2_conversations">Send 2 conversations with 1 message</string>
+    <string name="send_1_conversation">Send 1 conversation with 1 message</string>
+    <string name="send_1_conv_3_messages">Send 1 conversation with 3 messages</string>
+    <string name="clear_log">Clear Log</string>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/values/styles.xml b/notification/MessagingService/Application/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3f1a6af
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<resources>
+    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+    </style>
+</resources>
diff --git a/notification/MessagingService/Application/src/main/res/xml/automotive_app_desc.xml b/notification/MessagingService/Application/src/main/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..9e9f174
--- /dev/null
+++ b/notification/MessagingService/Application/src/main/res/xml/automotive_app_desc.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2014 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.
+  -->
+<automotiveApp>
+    <uses name="notification"/>
+</automotiveApp>
diff --git a/ui/notifications/BasicNotifications/CONTRIB.md b/notification/MessagingService/CONTRIB.md
similarity index 100%
copy from ui/notifications/BasicNotifications/CONTRIB.md
copy to notification/MessagingService/CONTRIB.md
diff --git a/ui/notifications/BasicNotifications/LICENSE b/notification/MessagingService/LICENSE
similarity index 100%
copy from ui/notifications/BasicNotifications/LICENSE
copy to notification/MessagingService/LICENSE
diff --git a/notification/MessagingService/README.md b/notification/MessagingService/README.md
new file mode 100644
index 0000000..4cb5bb8
--- /dev/null
+++ b/notification/MessagingService/README.md
@@ -0,0 +1,88 @@
+Android MessagingService Sample
+==============================
+
+This sample shows a simple service that sends notifications using
+NotificationCompat. In addition to sending a notification, it also extends
+the notification with a CarExtender to make it compatible with Android Auto.
+Each unread conversation from a user is sent as a distinct notification.
+
+CheckList while building a messaging app that supports Android Auto:
+-------------------------------------------------------------------
+1. Add or import the android-auto-sdk.jar into your app.
+2. Ensure that Message notifications are extended using
+NotificationCompat.Builder.extend(new CarExtender()...)
+3. Add meta-data to your AndroidManifest.xml to specify that your app
+is automotive enabled.
+```
+       <meta-data android:name="com.google.android.gms.car.application"
+                   android:resource="@xml/automotive_app_desc"/>
+```
+and include the following to indicate that the application wants to show notifications on
+the Android Auto overview screen.
+res/xml/automotive\_app\_desc.xml
+```
+<automotiveApp>
+    <uses name="notification"/>
+</automotiveApp>
+```
+
+Flow
+-----
+MessagingFragment is shown to the user. Depending on the button clicked, the MessagingService is
+sent a message. MessagingService inturn creates notifications which can be viewed either on the
+emulator or in a car.
+When a message is read, the associated PendingIntent is called and MessageReadReceiver is called
+with the appropriate conversationId. Similarly, when a reply is received, the MessageReplyReceiver
+is called with the appropriate conversationId. MessageLogger logs each event and shows them in a
+TextView in MessagingFragment for correlation.
+
+
+Pre-requisites
+--------------
+
+- Android SDK v21
+- Android Support Repository
+
+Getting Started
+---------------
+
+This sample uses the Gradle build system. To build this project, use the
+"gradlew build" command or use "Import Project" in Android Studio.
+
+Screenshots
+-----------
+
+<!-- Update these to point to screenshots. Add more as needed. -->
+![Description](screenshots/image1.png) ![Description](screenshots/image2.png)
+
+Support
+-------
+
+- Google+ Community: https://plus.google.com/communities/105153134372062985968
+- Stack Overflow: http://stackoverflow.com/questions/tagged/android
+
+If you've found an error in this sample, please file an issue:
+https://github.com/googlesamples/android-MessagingService
+
+Patches are encouraged, and may be submitted by forking this project and
+submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
+
+License
+-------
+
+Copyright 2014 The Android Open Source Project, Inc.
+
+Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements.  See the NOTICE file distributed with this work for
+additional information regarding copyright ownership.  The ASF licenses this
+file to you 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.
diff --git a/notification/MessagingService/build.gradle b/notification/MessagingService/build.gradle
new file mode 100644
index 0000000..2b8d1ef
--- /dev/null
+++ b/notification/MessagingService/build.gradle
@@ -0,0 +1,11 @@
+
+// BEGIN_EXCLUDE
+import com.example.android.samples.build.SampleGenPlugin
+apply plugin: SampleGenPlugin
+
+samplegen {
+  pathToBuild "../../../../build"
+  pathToSamplesCommon "../../common"
+}
+apply from: "../../../../build/build.gradle"
+// END_EXCLUDE
diff --git a/ui/notifications/BasicNotifications/buildSrc/build.gradle b/notification/MessagingService/buildSrc/build.gradle
similarity index 63%
copy from ui/notifications/BasicNotifications/buildSrc/build.gradle
copy to notification/MessagingService/buildSrc/build.gradle
index e344a8c..8c294c2 100644
--- a/ui/notifications/BasicNotifications/buildSrc/build.gradle
+++ b/notification/MessagingService/buildSrc/build.gradle
@@ -1,6 +1,3 @@
-
-
-
 repositories {
     mavenCentral()
 }
@@ -11,7 +8,7 @@
 sourceSets {
     main {
         groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
+            srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy")
         }
     }
 }
diff --git a/ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar b/notification/MessagingService/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
copy from ui/notifications/LNotifications/gradle/wrapper/gradle-wrapper.jar
copy to notification/MessagingService/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties b/notification/MessagingService/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
copy from ui/notifications/BasicNotifications/gradle/wrapper/gradle-wrapper.properties
copy to notification/MessagingService/gradle/wrapper/gradle-wrapper.properties
diff --git a/ui/notifications/BasicNotifications/gradlew b/notification/MessagingService/gradlew
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew
copy to notification/MessagingService/gradlew
diff --git a/ui/notifications/BasicNotifications/gradlew.bat b/notification/MessagingService/gradlew.bat
similarity index 100%
copy from ui/notifications/BasicNotifications/gradlew.bat
copy to notification/MessagingService/gradlew.bat
diff --git a/notification/MessagingService/packaging.yaml b/notification/MessagingService/packaging.yaml
new file mode 100644
index 0000000..38733c7
--- /dev/null
+++ b/notification/MessagingService/packaging.yaml
@@ -0,0 +1,15 @@
+# GOOGLE SAMPLE PACKAGING DATA
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+
+status:       PUBLISHED
+technologies: [Android]
+categories:   [None]
+languages:    [Java]
+solutions:    [Mobile]
+github:       googlesamples/android-MessagingService
+level:        BEGINNER
+icon:         MessagingServiceSample/src/main/res/drawable-xxhdpi/ic_launcher.png
+license:      apache2-android
diff --git a/ui/notifications/BasicNotifications/settings.gradle b/notification/MessagingService/settings.gradle
similarity index 100%
copy from ui/notifications/BasicNotifications/settings.gradle
copy to notification/MessagingService/settings.gradle
diff --git a/notification/MessagingService/template-params.xml b/notification/MessagingService/template-params.xml
new file mode 100644
index 0000000..edbeca9
--- /dev/null
+++ b/notification/MessagingService/template-params.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 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.
+-->
+<sample>
+    <name>MessagingService</name>
+    <group>Notification</group>
+    <package>com.example.android.messagingservice</package>
+
+    <minSdk>21</minSdk>
+
+    <dependency_external>'libs/android-auto-sdk.jar'</dependency_external>
+
+    <strings>
+        <intro>
+            <![CDATA[
+This sample shows a simple service that sends notifications using
+NotificationCompat. In addition to sending a notification, it also extends
+the notification with a CarExtender to make it compatible with Android Auto.
+Each unread conversation from a user is sent as a distinct notification.
+            ]]>
+        </intro>
+    </strings>
+
+    <template src="basebuild" />
+</sample>
diff --git a/ui/notifications/BasicNotifications/build.gradle b/ui/notifications/BasicNotifications/build.gradle
deleted file mode 100644
index cca9ac3..0000000
--- a/ui/notifications/BasicNotifications/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-// BEGIN_EXCLUDE
-import com.example.android.samples.build.SampleGenPlugin
-apply plugin: SampleGenPlugin
-
-samplegen {
-  pathToBuild "../../../../../build"
-  pathToSamplesCommon "../../../common"
-}
-apply from: "../../../../../build/build.gradle"
-// END_EXCLUDE
diff --git a/ui/notifications/CustomNotifications/build.gradle b/ui/notifications/CustomNotifications/build.gradle
deleted file mode 100644
index cca9ac3..0000000
--- a/ui/notifications/CustomNotifications/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-// BEGIN_EXCLUDE
-import com.example.android.samples.build.SampleGenPlugin
-apply plugin: SampleGenPlugin
-
-samplegen {
-  pathToBuild "../../../../../build"
-  pathToSamplesCommon "../../../common"
-}
-apply from: "../../../../../build/build.gradle"
-// END_EXCLUDE
diff --git a/ui/notifications/CustomNotifications/buildSrc/build.gradle b/ui/notifications/CustomNotifications/buildSrc/build.gradle
deleted file mode 100644
index e344a8c..0000000
--- a/ui/notifications/CustomNotifications/buildSrc/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    compile 'org.freemarker:freemarker:2.3.20'
-}
-
-sourceSets {
-    main {
-        groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
-        }
-    }
-}
-
diff --git a/ui/notifications/LNotifications/CONTRIB.md b/ui/notifications/LNotifications/CONTRIB.md
deleted file mode 100644
index 8ddb52d..0000000
--- a/ui/notifications/LNotifications/CONTRIB.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# How to become a contributor and submit your own code
-
-## Contributor License Agreements
-
-We'd love to accept your sample apps and patches! Before we can take them, we
-have to jump a couple of legal hurdles.
-
-Please fill out either the individual or corporate Contributor License Agreement (CLA).
-
-  * If you are an individual writing original source code and you're sure you
-    own the intellectual property, then you'll need to sign an [individual CLA]
-    (https://developers.google.com/open-source/cla/individual).
-  * If you work for a company that wants to allow you to contribute your work,
-    then you'll need to sign a [corporate CLA]
-    (https://developers.google.com/open-source/cla/corporate).
-
-Follow either of the two links above to access the appropriate CLA and
-instructions for how to sign and return it. Once we receive it, we'll be able to
-accept your pull requests.
-
-## Contributing A Patch
-
-1. Submit an issue describing your proposed change to the repo in question.
-1. The repo owner will respond to your issue promptly.
-1. If your proposed change is accepted, and you haven't already done so, sign a
-   Contributor License Agreement (see details above).
-1. Fork the desired repo, develop and test your code changes.
-1. Ensure that your code adheres to the existing style in the sample to which
-   you are contributing. Refer to the
-   [Google Cloud Platform Samples Style Guide]
-   (https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the
-   recommended coding standards for this organization.
-1. Ensure that your code has an appropriate set of unit tests which all pass.
-1. Submit a pull request.
-
diff --git a/ui/notifications/LNotifications/LICENSE b/ui/notifications/LNotifications/LICENSE
deleted file mode 100644
index c02ca2f..0000000
--- a/ui/notifications/LNotifications/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright 2014 Google Inc.
-
-   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.
diff --git a/ui/notifications/LNotifications/build.gradle b/ui/notifications/LNotifications/build.gradle
deleted file mode 100644
index be1fa82..0000000
--- a/ui/notifications/LNotifications/build.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-// BEGIN_EXCLUDE
-import com.example.android.samples.build.SampleGenPlugin
-apply plugin: SampleGenPlugin
-
-samplegen {
-  pathToBuild "../../../../../build"
-  pathToSamplesCommon "../../../common"
-}
-apply from: "../../../../../build/build.gradle"
-// END_EXCLUDE
diff --git a/ui/notifications/LNotifications/buildSrc/build.gradle b/ui/notifications/LNotifications/buildSrc/build.gradle
deleted file mode 100644
index e344a8c..0000000
--- a/ui/notifications/LNotifications/buildSrc/build.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-repositories {
-    mavenCentral()
-}
-dependencies {
-    compile 'org.freemarker:freemarker:2.3.20'
-}
-
-sourceSets {
-    main {
-        groovy {
-            srcDir new File(rootDir, "../../../../../../build/buildSrc/src/main/groovy")
-        }
-    }
-}
-