Reverted "MediaSession update" back to RemoteControlClient. Added back the "x" to close the notification. Notification now also colored below Lollipop. Its now possible to play music from Google Now voice search.
This commit is contained in:
parent
554bc25f83
commit
1072a3bf85
13 changed files with 712 additions and 329 deletions
|
|
@ -26,8 +26,8 @@ android {
|
|||
applicationId "com.kabouzeid.gramophone"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 38
|
||||
versionName "0.9.22b dev-1"
|
||||
versionCode 39
|
||||
versionName "0.9.23b dev-1"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public class MusicPlayerRemote {
|
|||
private static final String TAG = MusicPlayerRemote.class.getSimpleName();
|
||||
|
||||
private static int position = -1;
|
||||
private static boolean startAfterConnected = false;
|
||||
|
||||
private static ArrayList<Song> playingQueue;
|
||||
private static ArrayList<Song> restoredOriginalQueue;
|
||||
|
|
@ -44,6 +45,7 @@ public class MusicPlayerRemote {
|
|||
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
|
||||
musicService = binder.getService();
|
||||
musicService.restorePreviousState(restoredOriginalQueue, playingQueue, position);
|
||||
if(startAfterConnected) resumePlaying();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -121,6 +123,8 @@ public class MusicPlayerRemote {
|
|||
|
||||
public static void openQueue(final ArrayList<Song> playingQueue, final int startPosition, final boolean startPlaying) {
|
||||
MusicPlayerRemote.playingQueue = playingQueue;
|
||||
position = startPosition;
|
||||
startAfterConnected = startPlaying;
|
||||
if (musicService != null) {
|
||||
musicService.openQueue(MusicPlayerRemote.playingQueue, startPosition, startPlaying);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ package com.kabouzeid.gramophone.helper;
|
|||
*/
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskStackBuilder;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v7.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v7.graphics.Palette;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.kabouzeid.gramophone.R;
|
||||
import com.kabouzeid.gramophone.model.Song;
|
||||
|
|
@ -24,87 +24,199 @@ import com.kabouzeid.gramophone.ui.activities.MusicControllerActivity;
|
|||
import com.kabouzeid.gramophone.util.MusicUtil;
|
||||
import com.kabouzeid.gramophone.util.PreferenceUtils;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.assist.ImageSize;
|
||||
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
|
||||
import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
|
||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||
|
||||
public class PlayingNotificationHelper {
|
||||
|
||||
public static final String TAG = PlayingNotificationHelper.class.getSimpleName();
|
||||
public static final int NOTIFICATION_ID = 1337;
|
||||
|
||||
public static Notification buildNotification(final Context context, MediaSessionCompat.Token sessionToken, final Song song, final boolean isPlaying) {
|
||||
private final MusicService service;
|
||||
|
||||
NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle()
|
||||
.setMediaSession(sessionToken)
|
||||
.setShowActionsInCompactView(0, 1, 2);
|
||||
private final NotificationManager notificationManager;
|
||||
private Notification notification = null;
|
||||
|
||||
style.setShowCancelButton(true);
|
||||
style.setCancelButtonIntent(retrievePlaybackAction(context, 3));
|
||||
private RemoteViews notificationLayout;
|
||||
private RemoteViews notificationLayoutExpanded;
|
||||
|
||||
Bitmap albumArt = ImageLoader.getInstance().loadImageSync(MusicUtil.getAlbumArtUri(song.albumId).toString());
|
||||
if (albumArt == null) {
|
||||
albumArt = BitmapFactory.decodeResource(context.getResources(), R.drawable.default_album_art);
|
||||
}
|
||||
int notificationColor = PreferenceUtils.getInstance(context).coloredNotification() ?
|
||||
Palette.from(albumArt).generate().getVibrantColor(Color.TRANSPARENT) :
|
||||
Color.TRANSPARENT;
|
||||
private Song currentSong;
|
||||
private String currentAlbumArtUri;
|
||||
|
||||
return new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setLargeIcon(albumArt)
|
||||
.setContentIntent(getOpenMusicControllerPendingIntent(context))
|
||||
.setContentTitle(song.title)
|
||||
.setContentText(song.artistName)
|
||||
.setSubText(song.albumName)
|
||||
.setWhen(0)
|
||||
.setShowWhen(false)
|
||||
.setStyle(style)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
|
||||
.addAction(R.drawable.ic_skip_previous_white_36dp,
|
||||
"",
|
||||
retrievePlaybackAction(context, 2))
|
||||
|
||||
.addAction(isPlaying ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_arrow_white_36dp,
|
||||
"",
|
||||
retrievePlaybackAction(context, 0))
|
||||
|
||||
.addAction(R.drawable.ic_skip_next_white_36dp,
|
||||
"",
|
||||
retrievePlaybackAction(context, 1))
|
||||
|
||||
.setOnlyAlertOnce(true)
|
||||
.setColor(notificationColor)
|
||||
.build();
|
||||
public PlayingNotificationHelper(final MusicService service) {
|
||||
this.service = service;
|
||||
notificationManager = (NotificationManager) service
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
private static PendingIntent getOpenMusicControllerPendingIntent(final Context context) {
|
||||
Intent result = new Intent(context, MusicControllerActivity.class);
|
||||
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
|
||||
public void buildNotification(final Song song, final boolean isPlaying) {
|
||||
currentSong = song;
|
||||
notificationLayout = new RemoteViews(service.getPackageName(),
|
||||
R.layout.notification_controller);
|
||||
notificationLayoutExpanded = new RemoteViews(service.getPackageName(),
|
||||
R.layout.notification_controller_big);
|
||||
|
||||
notification = new NotificationCompat.Builder(service)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentIntent(getOpenMusicControllerPendingIntent())
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContent(notificationLayout)
|
||||
.build();
|
||||
|
||||
notification.bigContentView = notificationLayoutExpanded;
|
||||
|
||||
setUpCollapsedLayout();
|
||||
setUpExpandedLayout();
|
||||
loadAlbumArt();
|
||||
setUpPlaybackActions(isPlaying);
|
||||
setUpExpandedPlaybackActions(isPlaying);
|
||||
|
||||
service.startForeground(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
private PendingIntent getOpenMusicControllerPendingIntent() {
|
||||
Intent result = new Intent(service, MusicControllerActivity.class);
|
||||
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(service);
|
||||
taskStackBuilder.addParentStack(MusicControllerActivity.class);
|
||||
taskStackBuilder.addNextIntent(result);
|
||||
return taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
private static PendingIntent retrievePlaybackAction(final Context context, final int which) {
|
||||
String actionString = null;
|
||||
private void setUpExpandedPlaybackActions(boolean isPlaying) {
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.action_play_pause,
|
||||
retrievePlaybackActions(1));
|
||||
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.action_next,
|
||||
retrievePlaybackActions(2));
|
||||
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.action_prev,
|
||||
retrievePlaybackActions(3));
|
||||
|
||||
notificationLayoutExpanded.setOnClickPendingIntent(R.id.action_quit,
|
||||
retrievePlaybackActions(4));
|
||||
|
||||
notificationLayoutExpanded.setImageViewResource(R.id.action_play_pause,
|
||||
isPlaying ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_arrow_white_36dp);
|
||||
}
|
||||
|
||||
private void setUpPlaybackActions(boolean isPlaying) {
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause,
|
||||
retrievePlaybackActions(1));
|
||||
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_next,
|
||||
retrievePlaybackActions(2));
|
||||
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_prev,
|
||||
retrievePlaybackActions(3));
|
||||
|
||||
notificationLayout.setImageViewResource(R.id.action_play_pause,
|
||||
isPlaying ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_arrow_white_36dp);
|
||||
}
|
||||
|
||||
private PendingIntent retrievePlaybackActions(final int which) {
|
||||
Intent action;
|
||||
PendingIntent pendingIntent;
|
||||
final ComponentName serviceName = new ComponentName(service, MusicService.class);
|
||||
switch (which) {
|
||||
case 0:
|
||||
actionString = MusicService.ACTION_TOGGLE_PLAYBACK;
|
||||
break;
|
||||
case 1:
|
||||
actionString = MusicService.ACTION_SKIP;
|
||||
break;
|
||||
action = new Intent(MusicService.ACTION_TOGGLE_PLAYBACK);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 1, action, 0);
|
||||
return pendingIntent;
|
||||
case 2:
|
||||
actionString = MusicService.ACTION_REWIND;
|
||||
break;
|
||||
action = new Intent(MusicService.ACTION_SKIP);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 2, action, 0);
|
||||
return pendingIntent;
|
||||
case 3:
|
||||
actionString = MusicService.ACTION_QUIT;
|
||||
action = new Intent(MusicService.ACTION_REWIND);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 3, action, 0);
|
||||
return pendingIntent;
|
||||
case 4:
|
||||
action = new Intent(MusicService.ACTION_QUIT);
|
||||
action.setComponent(serviceName);
|
||||
pendingIntent = PendingIntent.getService(service, 4, action, 0);
|
||||
return pendingIntent;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (actionString != null) {
|
||||
final ComponentName serviceName = new ComponentName(context, MusicService.class);
|
||||
Intent actionIntent = new Intent(actionString);
|
||||
actionIntent.setComponent(serviceName);
|
||||
return PendingIntent.getService(context, 0, actionIntent, 0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setUpCollapsedLayout() {
|
||||
if (currentSong != null) {
|
||||
notificationLayout.setTextViewText(R.id.title, currentSong.title);
|
||||
notificationLayout.setTextViewText(R.id.text, currentSong.artistName);
|
||||
notificationLayout.setTextViewText(R.id.text2, currentSong.albumName);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpExpandedLayout() {
|
||||
if (currentSong != null) {
|
||||
notificationLayoutExpanded.setTextViewText(R.id.title, currentSong.title);
|
||||
notificationLayoutExpanded.setTextViewText(R.id.text, currentSong.artistName);
|
||||
notificationLayoutExpanded.setTextViewText(R.id.text2, currentSong.albumName);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAlbumArt() {
|
||||
currentAlbumArtUri = MusicUtil.getAlbumArtUri(currentSong.albumId).toString();
|
||||
ImageLoader.getInstance().displayImage(currentAlbumArtUri, new NonViewAware(new ImageSize(-1, -1), ViewScaleType.CROP), new SimpleImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||
if (currentAlbumArtUri.equals(imageUri))
|
||||
setAlbumArt(loadedImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
if (currentAlbumArtUri.equals(imageUri))
|
||||
setAlbumArt(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setAlbumArt(Bitmap albumArt) {
|
||||
int defaultColor = service.getResources().getColor(R.color.default_notification_color);
|
||||
int newColor = defaultColor;
|
||||
if (albumArt != null) {
|
||||
notificationLayout.setImageViewBitmap(R.id.icon, albumArt);
|
||||
notificationLayoutExpanded.setImageViewBitmap(R.id.icon, albumArt);
|
||||
if (PreferenceUtils.getInstance(service).coloredNotification())
|
||||
newColor = Palette.from(albumArt).generate().getVibrantColor(defaultColor);
|
||||
} else {
|
||||
notificationLayout.setImageViewResource(R.id.icon, R.drawable.default_album_art);
|
||||
notificationLayoutExpanded.setImageViewResource(R.id.icon, R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
notificationLayout.setInt(R.id.root, "setBackgroundColor", newColor);
|
||||
notificationLayoutExpanded.setInt(R.id.root, "setBackgroundColor", newColor);
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
public void killNotification() {
|
||||
service.stopForeground(true);
|
||||
notification = null;
|
||||
}
|
||||
|
||||
public void updatePlayState(final boolean isPlaying) {
|
||||
if (notification == null || notificationManager == null) {
|
||||
return;
|
||||
}
|
||||
if (notificationLayout != null) {
|
||||
notificationLayout.setImageViewResource(R.id.action_play_pause,
|
||||
isPlaying ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_arrow_white_36dp);
|
||||
}
|
||||
if (notificationLayoutExpanded != null) {
|
||||
notificationLayoutExpanded.setImageViewResource(R.id.action_play_pause,
|
||||
isPlaying ? R.drawable.ic_pause_white_36dp : R.drawable.ic_play_arrow_white_36dp);
|
||||
}
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
package com.kabouzeid.gramophone.helper;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.kabouzeid.gramophone.loader.SongLoader;
|
||||
import com.kabouzeid.gramophone.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
public class SearchQueryHelper {
|
||||
private static final String TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?";
|
||||
private static final String ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?";
|
||||
private static final String ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?";
|
||||
private static final String AND = " AND ";
|
||||
|
||||
public static ArrayList<Song> getSongs(final Context context, final Bundle extras) {
|
||||
final String query = extras.getString(SearchManager.QUERY, null);
|
||||
final String artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null);
|
||||
final String albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null);
|
||||
final String titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null);
|
||||
|
||||
ArrayList<Song> songs = new ArrayList<>();
|
||||
|
||||
if (artistName != null && albumName != null && titleName != null) {
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), albumName.toLowerCase(), titleName.toLowerCase()}));
|
||||
}
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
if (artistName != null && titleName != null) {
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION + AND + TITLE_SELECTION, new String[]{artistName.toLowerCase(), titleName.toLowerCase()}));
|
||||
}
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
if (albumName != null && titleName != null) {
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION + AND + TITLE_SELECTION, new String[]{albumName.toLowerCase(), titleName.toLowerCase()}));
|
||||
}
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
if (artistName != null) {
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{artistName.toLowerCase()}));
|
||||
}
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
if (albumName != null) {
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{albumName.toLowerCase()}));
|
||||
}
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
if (titleName != null) {
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{titleName.toLowerCase()}));
|
||||
}
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ARTIST_SELECTION, new String[]{query.toLowerCase()}));
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, ALBUM_SELECTION, new String[]{query.toLowerCase()}));
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
songs = SongLoader.getSongs(SongLoader.makeSongCursor(context, TITLE_SELECTION, new String[]{query.toLowerCase()}));
|
||||
if (!songs.isEmpty()) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
return SongLoader.getSongs(context, query);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,12 +17,26 @@ import java.util.List;
|
|||
public class AlbumLoader {
|
||||
|
||||
public static List<Album> getAllAlbums(Context context) {
|
||||
Cursor cursor = makeAlbumCursor(context);
|
||||
Cursor cursor = makeAlbumCursor(context, null, null);
|
||||
return getAlbums(cursor);
|
||||
}
|
||||
|
||||
public static List<Album> getAlbums(Context context, String query) {
|
||||
Cursor cursor = makeAlbumCursor(context, MediaStore.Audio.AlbumColumns.ALBUM + " LIKE ?", new String[]{"%" + query + "%"});
|
||||
return getAlbums(cursor);
|
||||
}
|
||||
|
||||
public static Album getAlbum(Context context, int albumId) {
|
||||
Cursor cursor = makeAlbumCursor(context, BaseColumns._ID + "=?", new String[]{String.valueOf(albumId)});
|
||||
return getAlbum(cursor);
|
||||
}
|
||||
|
||||
public static List<Album> getAlbums(Cursor cursor) {
|
||||
List<Album> albums = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String albumName = cursor.getString(1);
|
||||
final int id = cursor.getInt(0);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
|
|
@ -38,8 +52,23 @@ public class AlbumLoader {
|
|||
return albums;
|
||||
}
|
||||
|
||||
public static Cursor makeAlbumCursor(final Context context) {
|
||||
return makeAlbumCursor(context, null, null);
|
||||
public static Album getAlbum(Cursor cursor) {
|
||||
Album album = new Album();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String albumName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
final int year = cursor.getInt(5);
|
||||
|
||||
album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return album;
|
||||
}
|
||||
|
||||
public static Cursor makeAlbumCursor(final Context context, final String selection, final String[] values) {
|
||||
|
|
@ -59,46 +88,4 @@ public class AlbumLoader {
|
|||
MediaStore.Audio.AlbumColumns.FIRST_YEAR,
|
||||
}, selection, values, PreferenceUtils.getInstance(context).getAlbumSortOrder());
|
||||
}
|
||||
|
||||
public static Album getAlbum(Context context, int albumId) {
|
||||
Cursor cursor = makeAlbumCursor(context, BaseColumns._ID + "=?", new String[]{String.valueOf(albumId)});
|
||||
Album album = new Album();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String albumName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
final int year = cursor.getInt(5);
|
||||
|
||||
album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return album;
|
||||
}
|
||||
|
||||
public static List<Album> getAlbums(Context context, String query) {
|
||||
Cursor cursor = makeAlbumCursor(context, MediaStore.Audio.AlbumColumns.ALBUM + " LIKE ?", new String[]{"%" + query + "%"});
|
||||
List<Album> albums = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final String albumName = cursor.getString(1);
|
||||
final int id = cursor.getInt(0);
|
||||
final String artist = cursor.getString(2);
|
||||
final int artistId = cursor.getInt(3);
|
||||
final int songCount = cursor.getInt(4);
|
||||
final int year = cursor.getInt(5);
|
||||
|
||||
final Album album = new Album(id, albumName, artist, artistId, songCount, year);
|
||||
albums.add(album);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return albums;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,26 @@ import java.util.List;
|
|||
public class ArtistLoader {
|
||||
|
||||
public static List<Artist> getAllArtists(Context context) {
|
||||
Cursor cursor = makeArtistCursor(context);
|
||||
Cursor cursor = makeArtistCursor(context, null, null);
|
||||
return getArtists(cursor);
|
||||
}
|
||||
|
||||
public static List<Artist> getArtists(Context context, String query) {
|
||||
Cursor cursor = makeArtistCursor(context, MediaStore.Audio.ArtistColumns.ARTIST + " LIKE ?", new String[]{"%" + query + "%"});
|
||||
return getArtists(cursor);
|
||||
}
|
||||
|
||||
public static Artist getArtist(Context context, int artistId) {
|
||||
Cursor cursor = makeArtistCursor(context, BaseColumns._ID + "=?", new String[]{String.valueOf(artistId)});
|
||||
return getArtist(cursor);
|
||||
}
|
||||
|
||||
public static List<Artist> getArtists(Cursor cursor) {
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String artistName = cursor.getString(1);
|
||||
final int id = cursor.getInt(0);
|
||||
final int albumCount = cursor.getInt(2);
|
||||
final int songCount = cursor.getInt(3);
|
||||
|
||||
|
|
@ -30,31 +44,14 @@ public class ArtistLoader {
|
|||
artists.add(artist);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null)
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return artists;
|
||||
}
|
||||
|
||||
public static Cursor makeArtistCursor(final Context context) {
|
||||
return makeArtistCursor(context, null, null);
|
||||
}
|
||||
|
||||
public static Cursor makeArtistCursor(final Context context, final String selection, final String[] values) {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.ArtistColumns.ARTIST,
|
||||
/* 2 */
|
||||
MediaStore.Audio.ArtistColumns.NUMBER_OF_ALBUMS,
|
||||
/* 3 */
|
||||
MediaStore.Audio.ArtistColumns.NUMBER_OF_TRACKS
|
||||
}, selection, values, PreferenceUtils.getInstance(context).getArtistSortOrder());
|
||||
}
|
||||
|
||||
public static Artist getArtist(Context context, int artistId) {
|
||||
Cursor cursor = makeArtistCursor(context, BaseColumns._ID + "=?", new String[]{String.valueOf(artistId)});
|
||||
public static Artist getArtist(Cursor cursor) {
|
||||
Artist artist = new Artist();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int id = cursor.getInt(0);
|
||||
|
|
@ -71,24 +68,17 @@ public class ArtistLoader {
|
|||
return artist;
|
||||
}
|
||||
|
||||
public static List<Artist> getArtists(Context context, String query) {
|
||||
Cursor cursor = makeArtistCursor(context, MediaStore.Audio.ArtistColumns.ARTIST + " LIKE ?", new String[]{"%" + query + "%"});
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final String artistName = cursor.getString(1);
|
||||
final int id = cursor.getInt(0);
|
||||
final int albumCount = cursor.getInt(2);
|
||||
final int songCount = cursor.getInt(3);
|
||||
|
||||
final Artist artist = new Artist(id, artistName, albumCount, songCount);
|
||||
artists.add(artist);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return artists;
|
||||
public static Cursor makeArtistCursor(final Context context, final String selection, final String[] values) {
|
||||
return context.getContentResolver().query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
new String[]{
|
||||
/* 0 */
|
||||
BaseColumns._ID,
|
||||
/* 1 */
|
||||
MediaStore.Audio.ArtistColumns.ARTIST,
|
||||
/* 2 */
|
||||
MediaStore.Audio.ArtistColumns.NUMBER_OF_ALBUMS,
|
||||
/* 3 */
|
||||
MediaStore.Audio.ArtistColumns.NUMBER_OF_TRACKS
|
||||
}, selection, values, PreferenceUtils.getInstance(context).getArtistSortOrder());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import com.kabouzeid.gramophone.model.Song;
|
|||
import com.kabouzeid.gramophone.util.PreferenceUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Karim Abou Zeid (kabouzeid)
|
||||
|
|
@ -18,12 +17,26 @@ public class SongLoader {
|
|||
private static final String BASE_SELECTION = MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''";
|
||||
|
||||
public static ArrayList<Song> getAllSongs(Context context) {
|
||||
Cursor cursor = makeSongCursor(context);
|
||||
Cursor cursor = makeSongCursor(context, null, null);
|
||||
return getSongs(cursor);
|
||||
}
|
||||
|
||||
public static ArrayList<Song> getSongs(final Context context, final String query) {
|
||||
Cursor cursor = makeSongCursor(context, MediaStore.Audio.AudioColumns.TITLE + " LIKE ?", new String[]{"%" + query + "%"});
|
||||
return getSongs(cursor);
|
||||
}
|
||||
|
||||
public static Song getSong(final Context context, final int queryId) {
|
||||
Cursor cursor = makeSongCursor(context, MediaStore.Audio.AudioColumns._ID + "=?", new String[]{String.valueOf(queryId)});
|
||||
return getSong(cursor);
|
||||
}
|
||||
|
||||
public static ArrayList<Song> getSongs(final Cursor cursor) {
|
||||
ArrayList<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final int id = cursor.getInt(0);
|
||||
final String songName = cursor.getString(1);
|
||||
final int id = cursor.getInt(0);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
|
|
@ -35,19 +48,35 @@ public class SongLoader {
|
|||
songs.add(song);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static Cursor makeSongCursor(final Context context) {
|
||||
return makeSongCursor(context, MediaStore.Audio.AudioColumns.IS_MUSIC + "=?", new String[]{"1"});
|
||||
public static Song getSong(Cursor cursor) {
|
||||
Song song = new Song();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String songName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return song;
|
||||
}
|
||||
|
||||
public static Cursor makeSongCursor(final Context context, final String selection, final String[] values) {
|
||||
String finalSelection = BASE_SELECTION;
|
||||
if (selection != null) {
|
||||
finalSelection += " AND " + selection;
|
||||
String baseSelection = BASE_SELECTION;
|
||||
if (selection != null && !selection.trim().equals("")) {
|
||||
baseSelection += " AND " + selection;
|
||||
}
|
||||
|
||||
return context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
|
||||
|
|
@ -68,50 +97,6 @@ public class SongLoader {
|
|||
MediaStore.Audio.AudioColumns.ARTIST_ID,
|
||||
/* 7 */
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ID
|
||||
}, finalSelection, values, PreferenceUtils.getInstance(context).getSongSortOrder());
|
||||
}
|
||||
|
||||
public static List<Song> getSongs(final Context context, final String query) {
|
||||
Cursor cursor = makeSongCursor(context, MediaStore.Audio.AudioColumns.TITLE + " LIKE ?", new String[]{"%" + query + "%"});
|
||||
List<Song> songs = new ArrayList<>();
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
final String songName = cursor.getString(1);
|
||||
final int id = cursor.getInt(0);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
|
||||
final Song song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
songs.add(song);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
return songs;
|
||||
}
|
||||
|
||||
public static Song getSong(final Context context, final int queryId) {
|
||||
Cursor cursor = makeSongCursor(context, MediaStore.Audio.AudioColumns._ID + "=?", new String[]{String.valueOf(queryId)});
|
||||
Song song = null;
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int id = cursor.getInt(0);
|
||||
final String songName = cursor.getString(1);
|
||||
final String artist = cursor.getString(2);
|
||||
final String album = cursor.getString(3);
|
||||
final long duration = cursor.getLong(4);
|
||||
final int trackNumber = cursor.getInt(5);
|
||||
final int artistId = cursor.getInt(6);
|
||||
final int albumId = cursor.getInt(7);
|
||||
song = new Song(id, albumId, artistId, songName, artist, album, duration, trackNumber);
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
return song;
|
||||
}, baseSelection, values, PreferenceUtils.getInstance(context).getSongSortOrder());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package com.kabouzeid.gramophone.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
|
|
@ -12,10 +10,13 @@ import android.content.Intent;
|
|||
import android.content.IntentFilter;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.RemoteControlClient;
|
||||
import android.media.audiofx.AudioEffect;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
|
|
@ -24,16 +25,12 @@ import android.os.Message;
|
|||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.kabouzeid.gramophone.R;
|
||||
import com.kabouzeid.gramophone.appwidget.MusicPlayerWidget;
|
||||
import com.kabouzeid.gramophone.helper.MediaSessionHelper;
|
||||
import com.kabouzeid.gramophone.helper.PlayingNotificationHelper;
|
||||
import com.kabouzeid.gramophone.helper.ShuffleHelper;
|
||||
import com.kabouzeid.gramophone.misc.AppKeys;
|
||||
|
|
@ -85,6 +82,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
public static final int REPEAT_MODE_NONE = 0;
|
||||
public static final int REPEAT_MODE_ALL = 1;
|
||||
public static final int REPEAT_MODE_THIS = 2;
|
||||
|
||||
private static final String TAG = MusicService.class.getSimpleName();
|
||||
private final IBinder musicBind = new MusicBinder();
|
||||
|
||||
|
|
@ -100,14 +98,14 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
private boolean thingsRegistered;
|
||||
private boolean saveQueuesAgain;
|
||||
private boolean isSavingQueues;
|
||||
private PlayingNotificationHelper playingNotificationHelper;
|
||||
private AudioManager audioManager;
|
||||
private MediaSessionCompat mediaSession;
|
||||
private RemoteControlClient remoteControlClient;
|
||||
private PowerManager.WakeLock wakeLock;
|
||||
private String currentAlbumArtUri;
|
||||
private MusicPlayerHandler playerHandler;
|
||||
private boolean fadingDown = false;
|
||||
private HandlerThread handlerThread;
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
|
|
@ -132,6 +130,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
isPlayerPrepared = false;
|
||||
playingQueue = new ArrayList<>();
|
||||
originalPlayingQueue = new ArrayList<>();
|
||||
playingNotificationHelper = new PlayingNotificationHelper(this);
|
||||
|
||||
shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(AppKeys.SP_SHUFFLE_MODE, 0);
|
||||
repeatMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(AppKeys.SP_REPEAT_MODE, 0);
|
||||
|
|
@ -145,56 +144,16 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
handlerThread.start();
|
||||
playerHandler = new MusicPlayerHandler(this, handlerThread.getLooper());
|
||||
|
||||
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
|
||||
setUpMediaSession();
|
||||
|
||||
registerEverything();
|
||||
}
|
||||
|
||||
private void setUpMediaSession() {
|
||||
mediaSession = new MediaSessionCompat(this, "Phonograph", new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class), getMediaButtonIntent());
|
||||
mediaSession.setCallback(new MediaSessionCompat.Callback() {
|
||||
@Override
|
||||
public void onPause() {
|
||||
pausePlaying(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlay() {
|
||||
resumePlaying(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSeekTo(long pos) {
|
||||
//TODO
|
||||
//seek(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToNext() {
|
||||
playNextSong(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSkipToPrevious() {
|
||||
playPreviousSong(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
stopPlaying();
|
||||
}
|
||||
});
|
||||
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
|
||||
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
}
|
||||
|
||||
private void registerEverything() {
|
||||
if (!thingsRegistered) {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
||||
registerReceiver(becomingNoisyReceiver, intentFilter);
|
||||
getAudioManager().registerMediaButtonEventReceiver(new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class));
|
||||
initRemoteControlClient();
|
||||
thingsRegistered = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -206,6 +165,16 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
return audioManager;
|
||||
}
|
||||
|
||||
private void initRemoteControlClient() {
|
||||
remoteControlClient = new RemoteControlClient(getMediaButtonIntent());
|
||||
remoteControlClient.setTransportControlFlags(
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_PLAY |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_NEXT |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
|
||||
getAudioManager().registerRemoteControlClient(remoteControlClient);
|
||||
}
|
||||
|
||||
private PendingIntent getMediaButtonIntent() {
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
mediaButtonIntent.setComponent(new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class));
|
||||
|
|
@ -259,7 +228,11 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
unregisterEverything();
|
||||
|
||||
playerHandler.removeCallbacksAndMessages(null);
|
||||
handlerThread.quitSafely();
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
handlerThread.quitSafely();
|
||||
} else {
|
||||
handlerThread.quit();
|
||||
}
|
||||
|
||||
killEverythingAndReleaseResources();
|
||||
}
|
||||
|
|
@ -279,6 +252,8 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
private void unregisterEverything() {
|
||||
if (thingsRegistered) {
|
||||
unregisterReceiver(becomingNoisyReceiver);
|
||||
getAudioManager().unregisterRemoteControlClient(remoteControlClient);
|
||||
getAudioManager().unregisterMediaButtonEventReceiver(new ComponentName(getApplicationContext(), MediaButtonIntentReceiver.class));
|
||||
getAudioManager().abandonAudioFocus(audioFocusListener);
|
||||
thingsRegistered = false;
|
||||
}
|
||||
|
|
@ -286,7 +261,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
|
||||
private void killEverythingAndReleaseResources() {
|
||||
stopPlaying();
|
||||
stopForeground(true);
|
||||
playingNotificationHelper.killNotification();
|
||||
savePosition();
|
||||
saveQueues();
|
||||
stopSelf();
|
||||
|
|
@ -300,8 +275,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
player.release();
|
||||
player = null;
|
||||
}
|
||||
mediaSession.setActive(false);
|
||||
mediaSession.release();
|
||||
notifyChange(PLAYSTATE_CHANGED);
|
||||
}
|
||||
|
||||
|
|
@ -309,11 +282,11 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
return isPlaying(false);
|
||||
}
|
||||
|
||||
private boolean isPlaying(boolean alsoIfIsFadingDown) {
|
||||
if (!alsoIfIsFadingDown)
|
||||
return player != null && isPlayerPrepared && player.isPlaying() && !fadingDown;
|
||||
else
|
||||
private boolean isPlaying(boolean doNotConsiderFadingDown) {
|
||||
if (doNotConsiderFadingDown)
|
||||
return player != null && isPlayerPrepared && player.isPlaying();
|
||||
else
|
||||
return player != null && isPlayerPrepared && player.isPlaying() && !fadingDown;
|
||||
}
|
||||
|
||||
public void saveQueues() {
|
||||
|
|
@ -411,32 +384,14 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
return (getAudioManager().requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
|
||||
}
|
||||
|
||||
private void updateMediaSession(final String what) {
|
||||
private void updateRemoteControlClient() {
|
||||
final Song song = playingQueue.get(getPosition());
|
||||
|
||||
int playState = isPlaying()
|
||||
? PlaybackStateCompat.STATE_PLAYING
|
||||
: PlaybackStateCompat.STATE_PAUSED;
|
||||
|
||||
if (what.equals(PLAYSTATE_CHANGED) || what.equals(POSITION_IN_SONG_CHANGED)) {
|
||||
MediaSessionHelper.applyState(mediaSession, new PlaybackStateCompat.Builder()
|
||||
.setActions(getAvailablePlaybackStateActions())
|
||||
.setState(playState, player != null ? getSongProgressMillis() : 0, 1.0f).build());
|
||||
} else if (what.equals(META_CHANGED)) {
|
||||
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.artistName)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.albumName)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, song.duration)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, getPosition() + 1)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, getPlayingQueue().size())
|
||||
.build());
|
||||
|
||||
MediaSessionHelper.applyState(mediaSession, new PlaybackStateCompat.Builder()
|
||||
.setActions(getAvailablePlaybackStateActions())
|
||||
.setState(playState, player != null ? getSongProgressMillis() : 0, 1.0f).build());
|
||||
}
|
||||
|
||||
remoteControlClient
|
||||
.editMetadata(false)
|
||||
.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, song.artistName)
|
||||
.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, song.title)
|
||||
.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, song.duration)
|
||||
.apply();
|
||||
currentAlbumArtUri = MusicUtil.getAlbumArtUri(song.albumId).toString();
|
||||
ImageLoader.getInstance().displayImage(currentAlbumArtUri, new NonViewAware(new ImageSize(-1, -1), ViewScaleType.CROP), new SimpleImageLoadingListener() {
|
||||
@Override
|
||||
|
|
@ -451,7 +406,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
config = Bitmap.Config.ARGB_8888;
|
||||
}
|
||||
albumArt = albumArt.copy(config, false);
|
||||
updateMediaSessionBitmap(albumArt.copy(albumArt.getConfig(), true));
|
||||
updateRemoteControlClientBitmap(albumArt.copy(albumArt.getConfig(), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -459,38 +414,16 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
if (currentAlbumArtUri.equals(imageUri))
|
||||
updateMediaSessionBitmap(null);
|
||||
updateRemoteControlClientBitmap(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateMediaSessionBitmap(final Bitmap albumArt) {
|
||||
MediaMetadataCompat current = mediaSession.getController().getMetadata();
|
||||
if (current == null) current = new MediaMetadataCompat.Builder().build();
|
||||
|
||||
mediaSession.setMetadata(new MediaMetadataCompat.Builder(current)
|
||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArt)
|
||||
.build());
|
||||
}
|
||||
|
||||
private long getAvailablePlaybackStateActions() {
|
||||
if (getPlayingQueue() == null || getPlayingQueue().isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long actions = PlaybackStateCompat.ACTION_PLAY_PAUSE;
|
||||
if (isPlaying()) {
|
||||
actions |= PlaybackStateCompat.ACTION_PAUSE;
|
||||
} else {
|
||||
actions |= PlaybackStateCompat.ACTION_PLAY;
|
||||
}
|
||||
if (getPosition() > 0) {
|
||||
actions |= PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
|
||||
}
|
||||
if (getPosition() < getPlayingQueue().size() - 1) {
|
||||
actions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
|
||||
}
|
||||
return actions;
|
||||
private void updateRemoteControlClientBitmap(final Bitmap albumArt) {
|
||||
remoteControlClient
|
||||
.editMetadata(false)
|
||||
.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, albumArt)
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void setUpMediaPlayerIfNeeded() {
|
||||
|
|
@ -507,14 +440,7 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
}
|
||||
|
||||
private void updateNotification() {
|
||||
final boolean isPlaying = isPlaying();
|
||||
Notification notification = PlayingNotificationHelper.buildNotification(this, mediaSession.getSessionToken(), getPlayingQueue().get(getPosition()), isPlaying);
|
||||
if (isPlaying)
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
else {
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
stopForeground(false);
|
||||
}
|
||||
playingNotificationHelper.buildNotification(playingQueue.get(position), isPlaying());
|
||||
}
|
||||
|
||||
private void updateWidgets() {
|
||||
|
|
@ -742,7 +668,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
}
|
||||
|
||||
public void resumePlaying(boolean forceNoFading) {
|
||||
mediaSession.setActive(true);
|
||||
if (!forceNoFading && PreferenceUtils.getInstance(this).fadePlayPauseAndInterruptions()) {
|
||||
playerHandler.removeMessages(FADEDOWNANDPAUSE);
|
||||
playerHandler.sendEmptyMessage(FADEUPANDRESUME);
|
||||
|
|
@ -824,7 +749,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
|
||||
public void seekTo(int millis) {
|
||||
player.seekTo(millis);
|
||||
notifyChange(POSITION_IN_SONG_CHANGED);
|
||||
}
|
||||
|
||||
public boolean isPlayerPrepared() {
|
||||
|
|
@ -883,11 +807,6 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
}
|
||||
|
||||
private void notifyChange(final String what) {
|
||||
updateMediaSession(what);
|
||||
if (what.equals(POSITION_IN_SONG_CHANGED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Intent internalIntent = new Intent(what);
|
||||
final int position = getPosition();
|
||||
if (position >= 0 && !playingQueue.isEmpty()) {
|
||||
|
|
@ -907,11 +826,13 @@ public class MusicService extends Service implements MediaPlayer.OnPreparedListe
|
|||
|
||||
if (what.equals(PLAYSTATE_CHANGED)) {
|
||||
final boolean isPlaying = isPlaying();
|
||||
updateNotification();
|
||||
playingNotificationHelper.updatePlayState(isPlaying);
|
||||
MusicPlayerWidget.updateWidgetsPlayState(this, isPlaying);
|
||||
remoteControlClient.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
|
||||
} else if (what.equals(META_CHANGED)) {
|
||||
updateNotification();
|
||||
updateWidgets();
|
||||
updateRemoteControlClient();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import com.kabouzeid.gramophone.adapter.PagerAdapter;
|
|||
import com.kabouzeid.gramophone.dialogs.AboutDialog;
|
||||
import com.kabouzeid.gramophone.dialogs.CreatePlaylistDialog;
|
||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||
import com.kabouzeid.gramophone.helper.SearchQueryHelper;
|
||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
|
||||
import com.kabouzeid.gramophone.loader.AlbumSongLoader;
|
||||
|
|
@ -405,6 +406,10 @@ public class MainActivity extends AbsFabActivity
|
|||
String mimeType = intent.getType();
|
||||
boolean handled = false;
|
||||
|
||||
if (intent.getAction() != null
|
||||
&& intent.getAction().equals(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH)) {
|
||||
MusicPlayerRemote.openQueue(SearchQueryHelper.getSongs(this, intent.getExtras()), 0, true);
|
||||
}
|
||||
if (uri != null && uri.toString().length() > 0) {
|
||||
MusicPlayerRemote.playFile(uri);
|
||||
handled = true;
|
||||
|
|
|
|||
129
app/src/main/res/layout/notification_controller.xml
Normal file
129
app/src/main/res/layout/notification_controller.xml
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?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
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:internal="http://schemas.android.com/apk/prv/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:background="@color/default_notification_color"
|
||||
android:orientation="horizontal"
|
||||
internal:layout_maxHeight="64dp"
|
||||
internal:layout_minHeight="64dp"
|
||||
tools:ignore="DisableBaselineAlignment">
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:internal="http://schemas.android.com/apk/prv/res/android"
|
||||
android:id="@+id/icon_group"
|
||||
android:layout_width="@dimen/notification_large_icon_width"
|
||||
android:layout_height="@dimen/notification_large_icon_height"
|
||||
android:layout_weight="0">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:scaleType="centerInside"
|
||||
tools:ignore="ContentDescription" />
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_vertical"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="@dimen/notification_large_icon_height"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="bottom"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/Theme.MaterialMusic.Notification.Title"
|
||||
android:textColor="@color/abc_primary_text_material_dark"
|
||||
tools:ignore="NestedWeights" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/Theme.MaterialMusic.Notification"
|
||||
android:textColor="@color/abc_secondary_text_material_dark" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<!-- media buttons will be added here -->
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_prev"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_skip_previous_white_36dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_play_pause"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_play_arrow_white_36dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_next"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_skip_next_white_36dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
153
app/src/main/res/layout/notification_controller_big.xml
Normal file
153
app/src/main/res/layout/notification_controller_big.xml
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<?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
|
||||
-->
|
||||
|
||||
<!-- Layout to be used with only max 3 actions. It has a much larger picture at the left side-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="128dp"
|
||||
android:background="@color/default_notification_color">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="128dp"
|
||||
android:scaleType="centerCrop"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_quit"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:src="@drawable/ic_close_white_24dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_toLeftOf="@id/action_quit"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:layout_toStartOf="@id/action_quit"
|
||||
android:minHeight="@dimen/notification_large_icon_height"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/Theme.MaterialMusic.Notification.Title"
|
||||
android:textColor="@color/abc_primary_text_material_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="-1dp"
|
||||
android:layout_marginTop="-1dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/Theme.MaterialMusic.Notification"
|
||||
android:textColor="@color/abc_secondary_text_material_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/Theme.MaterialMusic.Notification"
|
||||
android:textColor="@color/abc_secondary_text_material_dark" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<!-- media buttons will be added here -->
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_prev"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_skip_previous_white_36dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_play_pause"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_play_arrow_white_36dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/action_next"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_skip_next_white_36dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_above="@id/media_actions"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:background="@drawable/notification_template_divider_media"
|
||||
tools:ignore="ContentDescription" />
|
||||
</RelativeLayout>
|
||||
|
|
@ -24,6 +24,8 @@
|
|||
<color name="sliding_tabs_activated">#FFFFFF</color>
|
||||
<color name="sliding_tabs_deactivated">#99FFFFFF</color>
|
||||
|
||||
<color name="default_notification_color">@color/grey_800</color>
|
||||
|
||||
<!--notification-->
|
||||
<drawable name="notification_template_divider_media">#29ffffff</drawable>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue