1 /* 2 * Copyright (c) 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.car.media.localmediaplayer; 17 18 import android.content.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.media.MediaDescription; 23 import android.media.browse.MediaBrowser; 24 import android.media.session.MediaSession; 25 import android.os.Bundle; 26 import android.service.media.MediaBrowserService; 27 import androidx.annotation.Nullable; 28 import android.util.Log; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 public class LocalMediaBrowserService extends MediaBrowserService { 34 private static final String TAG = "LMBService"; 35 private static final String ROOT_ID = "__ROOT__"; 36 private static final String MEDIA_SESSION_TAG = "LOCAL_MEDIA_SESSION"; 37 38 static final String FOLDERS_ID = "__FOLDERS__"; 39 static final String ARTISTS_ID = "__ARTISTS__"; 40 static final String ALBUMS_ID = "__ALBUMS__"; 41 static final String GENRES_ID = "__GENRES__"; 42 43 static final String ACTION_PLAY = "com.android.car.media.localmediaplayer.ACTION_PLAY"; 44 static final String ACTION_PAUSE = "com.android.car.media.localmediaplayer.ACTION_PAUSE"; 45 static final String ACTION_NEXT = "com.android.car.media.localmediaplayer.ACTION_NEXT"; 46 static final String ACTION_PREV = "com.android.car.media.localmediaplayer.ACTION_PREV"; 47 48 private BrowserRoot mRoot = new BrowserRoot(ROOT_ID, null); 49 List<MediaBrowser.MediaItem> mRootItems = new ArrayList<>(); 50 51 private DataModel mDataModel; 52 private Player mPlayer; 53 private MediaSession mSession; 54 private String mLastCategory; 55 56 private BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() { 57 @Override 58 public void onReceive(Context context, Intent intent) { 59 if (intent.getAction() == null) { 60 return; 61 } 62 63 switch (intent.getAction()) { 64 case ACTION_PLAY: 65 mPlayer.onPlay(); 66 break; 67 case ACTION_PAUSE: 68 mPlayer.onPause(); 69 break; 70 case ACTION_NEXT: 71 mPlayer.onSkipToNext(); 72 break; 73 case ACTION_PREV: 74 mPlayer.onSkipToPrevious(); 75 break; 76 default: 77 Log.w(TAG, "Ingoring intent with unknown action=" + intent); 78 } 79 } 80 }; 81 addRootItems()82 private void addRootItems() { 83 MediaDescription folders = new MediaDescription.Builder() 84 .setMediaId(FOLDERS_ID) 85 .setTitle(getString(R.string.folders_title)) 86 .setIconUri(Utils.getUriForResource(this, R.drawable.ic_folder)) 87 .build(); 88 mRootItems.add(new MediaBrowser.MediaItem(folders, MediaBrowser.MediaItem.FLAG_BROWSABLE)); 89 90 MediaDescription albums = new MediaDescription.Builder() 91 .setMediaId(ALBUMS_ID) 92 .setTitle(getString(R.string.albums_title)) 93 .setIconUri(Utils.getUriForResource(this, R.drawable.ic_album)) 94 .build(); 95 mRootItems.add(new MediaBrowser.MediaItem(albums, MediaBrowser.MediaItem.FLAG_BROWSABLE)); 96 97 MediaDescription artists = new MediaDescription.Builder() 98 .setMediaId(ARTISTS_ID) 99 .setTitle(getString(R.string.artists_title)) 100 .setIconUri(Utils.getUriForResource(this, R.drawable.ic_artist)) 101 .build(); 102 mRootItems.add(new MediaBrowser.MediaItem(artists, MediaBrowser.MediaItem.FLAG_BROWSABLE)); 103 104 MediaDescription genres = new MediaDescription.Builder() 105 .setMediaId(GENRES_ID) 106 .setTitle(getString(R.string.genres_title)) 107 .setIconUri(Utils.getUriForResource(this, R.drawable.ic_genre)) 108 .build(); 109 mRootItems.add(new MediaBrowser.MediaItem(genres, MediaBrowser.MediaItem.FLAG_BROWSABLE)); 110 } 111 112 @Override onCreate()113 public void onCreate() { 114 super.onCreate(); 115 116 mDataModel = new DataModel(this); 117 addRootItems(); 118 mSession = new MediaSession(this, MEDIA_SESSION_TAG); 119 setSessionToken(mSession.getSessionToken()); 120 mPlayer = new Player(this, mSession, mDataModel); 121 mSession.setCallback(mPlayer); 122 mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS 123 | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 124 mPlayer.maybeRestoreState(); 125 126 IntentFilter filter = new IntentFilter(); 127 filter.addAction(ACTION_PLAY); 128 filter.addAction(ACTION_PAUSE); 129 filter.addAction(ACTION_NEXT); 130 filter.addAction(ACTION_PREV); 131 registerReceiver(mNotificationReceiver, filter); 132 } 133 134 @Override onDestroy()135 public void onDestroy() { 136 mPlayer.saveState(); 137 mPlayer.destroy(); 138 mSession.release(); 139 unregisterReceiver(mNotificationReceiver); 140 super.onDestroy(); 141 } 142 143 @Nullable 144 @Override onGetRoot(String clientName, int clientUid, Bundle rootHints)145 public BrowserRoot onGetRoot(String clientName, int clientUid, Bundle rootHints) { 146 if (Log.isLoggable(TAG, Log.DEBUG)) { 147 Log.d(TAG, "onGetRoot clientName=" + clientName); 148 } 149 return mRoot; 150 } 151 152 @Override onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result)153 public void onLoadChildren(String parentId, Result<List<MediaBrowser.MediaItem>> result) { 154 if (Log.isLoggable(TAG, Log.DEBUG)) { 155 Log.d(TAG, "onLoadChildren parentId=" + parentId); 156 } 157 158 switch (parentId) { 159 case ROOT_ID: 160 result.sendResult(mRootItems); 161 mLastCategory = parentId; 162 break; 163 case FOLDERS_ID: 164 mDataModel.onQueryByFolder(parentId, result); 165 mLastCategory = parentId; 166 break; 167 case ALBUMS_ID: 168 mDataModel.onQueryByAlbum(parentId, result); 169 mLastCategory = parentId; 170 break; 171 case ARTISTS_ID: 172 mDataModel.onQueryByArtist(parentId, result); 173 mLastCategory = parentId; 174 break; 175 case GENRES_ID: 176 mDataModel.onQueryByGenre(parentId, result); 177 mLastCategory = parentId; 178 break; 179 default: 180 mDataModel.onQueryByKey(mLastCategory, parentId, result); 181 } 182 } 183 } 184