Initial commit retro music app

This commit is contained in:
h4h13 2018-07-27 18:37:33 +05:30
parent ab332473bc
commit fe890632fd
932 changed files with 83126 additions and 0 deletions

View file

@ -0,0 +1,211 @@
/*
* Copyright (C) 2007 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.
*/
// Modified for Phonograph by Karim Abou Zeid (kabouzeid).
package code.name.monkey.retromusic.service;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import android.view.KeyEvent;
import code.name.monkey.retromusic.BuildConfig;
import static code.name.monkey.retromusic.Constants.ACTION_PAUSE;
import static code.name.monkey.retromusic.Constants.ACTION_PLAY;
import static code.name.monkey.retromusic.Constants.ACTION_REWIND;
import static code.name.monkey.retromusic.Constants.ACTION_SKIP;
import static code.name.monkey.retromusic.Constants.ACTION_STOP;
import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE;
/**
* Used to control headset playback.
* Single press: pause/resume
* Double press: next track
* Triple press: previous track
*/
public class MediaButtonIntentReceiver extends BroadcastReceiver {
public static final String TAG = MediaButtonIntentReceiver.class.getSimpleName();
private static final boolean DEBUG = BuildConfig.DEBUG;
private static final int MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2;
private static final int DOUBLE_CLICK = 400;
private static WakeLock mWakeLock = null;
private static int mClickCounter = 0;
private static long mLastClickTime = 0;
@SuppressLint("HandlerLeak") // false alarm, handler is already static
private static Handler mHandler = new Handler() {
@Override
public void handleMessage(final Message msg) {
switch (msg.what) {
case MSG_HEADSET_DOUBLE_CLICK_TIMEOUT:
final int clickCount = msg.arg1;
final String command;
if (DEBUG) Log.v(TAG, "Handling headset click, count = " + clickCount);
switch (clickCount) {
case 1:
command = ACTION_TOGGLE_PAUSE;
break;
case 2:
command = ACTION_SKIP;
break;
case 3:
command = ACTION_REWIND;
break;
default:
command = null;
break;
}
if (command != null) {
final Context context = (Context) msg.obj;
startService(context, command);
}
break;
}
releaseWakeLockIfHandlerIdle();
}
};
public static boolean handleIntent(final Context context, final Intent intent) {
final String intentAction = intent.getAction();
if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event == null) {
return false;
}
final int keycode = event.getKeyCode();
final int action = event.getAction();
final long eventTime = event.getEventTime() != 0 ?
event.getEventTime() : System.currentTimeMillis();
String command = null;
switch (keycode) {
case KeyEvent.KEYCODE_MEDIA_STOP:
command = ACTION_STOP;
break;
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
command = ACTION_TOGGLE_PAUSE;
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
command = ACTION_SKIP;
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
command = ACTION_REWIND;
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
command = ACTION_PAUSE;
break;
case KeyEvent.KEYCODE_MEDIA_PLAY:
command = ACTION_PLAY;
break;
}
if (command != null) {
if (action == KeyEvent.ACTION_DOWN) {
if (event.getRepeatCount() == 0) {
// Only consider the first event in a sequence, not the repeat events,
// so that we don't trigger in cases where the first event went to
// a different app (e.g. when the user ends a phone call by
// long pressing the headset button)
// The service may or may not be running, but we need to send it
// a command.
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
if (eventTime - mLastClickTime >= DOUBLE_CLICK) {
mClickCounter = 0;
}
mClickCounter++;
if (DEBUG) Log.v(TAG, "Got headset click, count = " + mClickCounter);
mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT);
Message msg = mHandler.obtainMessage(
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context);
long delay = mClickCounter < 3 ? DOUBLE_CLICK : 0;
if (mClickCounter >= 3) {
mClickCounter = 0;
}
mLastClickTime = eventTime;
acquireWakeLockAndSendMessage(context, msg, delay);
} else {
startService(context, command);
}
return true;
}
}
}
}
return false;
}
private static void startService(Context context, String command) {
final Intent intent = new Intent(context, MusicService.class);
intent.setAction(command);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
private static void acquireWakeLockAndSendMessage(Context context, Message msg, long delay) {
if (mWakeLock == null) {
Context appContext = context.getApplicationContext();
PowerManager pm = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
if (pm != null) {
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"RetroMusicApp:Wakelock headset button");
}
mWakeLock.setReferenceCounted(false);
}
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what);
// Make sure we don't indefinitely hold the wake lock under any circumstances
mWakeLock.acquire(10000);
mHandler.sendMessageDelayed(msg, delay);
}
private static void releaseWakeLockIfHandlerIdle() {
if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) {
if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock");
return;
}
if (mWakeLock != null) {
if (DEBUG) Log.v(TAG, "Releasing wake lock");
mWakeLock.release();
mWakeLock = null;
}
}
@Override
public void onReceive(final Context context, final Intent intent) {
if (DEBUG) Log.v(TAG, "Received intent: " + intent);
if (handleIntent(context, intent) && isOrderedBroadcast()) {
abortBroadcast();
}
}
}

View file

@ -0,0 +1,334 @@
package code.name.monkey.retromusic.service;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.audiofx.AudioEffect;
import android.net.Uri;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.service.playback.Playback;
import code.name.monkey.retromusic.util.PreferenceUtil;
/**
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
*/
public class MultiPlayer implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
public static final String TAG = MultiPlayer.class.getSimpleName();
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
private MediaPlayer mNextMediaPlayer;
private Context context;
@Nullable
private Playback.PlaybackCallbacks callbacks;
private boolean mIsInitialized = false;
/**
* Constructor of <code>MultiPlayer</code>
*/
MultiPlayer(final Context context) {
this.context = context;
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
}
/**
* @param path The path of the file, or the http/rtsp URL of the stream
* you want to play
* @return True if the <code>player</code> has been prepared and is
* ready to play, false otherwise
*/
@Override
public boolean setDataSource(@NonNull final String path) {
mIsInitialized = false;
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
if (mIsInitialized) {
setNextDataSource(null);
}
return mIsInitialized;
}
/**
* @param player The {@link MediaPlayer} to use
* @param path The path of the file, or the http/rtsp URL of the stream
* you want to play
* @return True if the <code>player</code> has been prepared and is
* ready to play, false otherwise
*/
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
if (context == null) {
return false;
}
try {
player.reset();
player.setOnPreparedListener(null);
if (path.startsWith("content://")) {
player.setDataSource(context, Uri.parse(path));
} else {
player.setDataSource(path);
}
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.prepare();
} catch (Exception e) {
return false;
}
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
context.sendBroadcast(intent);
return true;
}
/**
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
*
* @param path The path of the file, or the http/rtsp URL of the stream
* you want to play
*/
@Override
public void setNextDataSource(@Nullable final String path) {
if (context == null) {
return;
}
try {
mCurrentMediaPlayer.setNextMediaPlayer(null);
} catch (IllegalArgumentException e) {
Log.i(TAG, "Next media player is current one, continuing");
} catch (IllegalStateException e) {
Log.e(TAG, "Media player not initialized!");
return;
}
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
if (path == null) {
return;
}
if (PreferenceUtil.getInstance(context).gaplessPlayback()) {
mNextMediaPlayer = new MediaPlayer();
mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
if (setDataSourceImpl(mNextMediaPlayer, path)) {
try {
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e);
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
} else {
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
mNextMediaPlayer = null;
}
}
}
}
/**
* Sets the callbacks
*
* @param callbacks The callbacks to use
*/
@Override
public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) {
this.callbacks = callbacks;
}
/**
* @return True if the player is ready to go, false otherwise
*/
@Override
public boolean isInitialized() {
return mIsInitialized;
}
/**
* Starts or resumes playback.
*/
@Override
public boolean start() {
try {
mCurrentMediaPlayer.start();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Resets the MediaPlayer to its uninitialized state.
*/
@Override
public void stop() {
mCurrentMediaPlayer.reset();
mIsInitialized = false;
}
/**
* Releases resources associated with this MediaPlayer object.
*/
@Override
public void release() {
stop();
mCurrentMediaPlayer.release();
if (mNextMediaPlayer != null) {
mNextMediaPlayer.release();
}
}
/**
* Pauses playback. Call start() to resume.
*/
@Override
public boolean pause() {
try {
mCurrentMediaPlayer.pause();
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Checks whether the MultiPlayer is playing.
*/
@Override
public boolean isPlaying() {
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
}
/**
* Gets the duration of the file.
*
* @return The duration in milliseconds
*/
@Override
public int duration() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getDuration();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @return The current position in milliseconds
*/
@Override
public int position() {
if (!mIsInitialized) {
return -1;
}
try {
return mCurrentMediaPlayer.getCurrentPosition();
} catch (IllegalStateException e) {
return -1;
}
}
/**
* Gets the current playback position.
*
* @param whereto The offset in milliseconds from the start to seek to
* @return The offset in milliseconds from the start to seek to
*/
@Override
public int seek(final int whereto) {
try {
mCurrentMediaPlayer.seekTo(whereto);
return whereto;
} catch (IllegalStateException e) {
return -1;
}
}
@Override
public boolean setVolume(final float vol) {
try {
mCurrentMediaPlayer.setVolume(vol, vol);
return true;
} catch (IllegalStateException e) {
return false;
}
}
/**
* Sets the audio session ID.
*
* @param sessionId The audio session ID
*/
@Override
public boolean setAudioSessionId(final int sessionId) {
try {
mCurrentMediaPlayer.setAudioSessionId(sessionId);
return true;
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
return false;
}
}
/**
* Returns the audio session ID.
*
* @return The current audio session ID.
*/
@Override
public int getAudioSessionId() {
return mCurrentMediaPlayer.getAudioSessionId();
}
/**
* {@inheritDoc}
*/
@Override
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = new MediaPlayer();
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
if (context != null) {
Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void onCompletion(final MediaPlayer mp) {
if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) {
mIsInitialized = false;
mCurrentMediaPlayer.release();
mCurrentMediaPlayer = mNextMediaPlayer;
mIsInitialized = true;
mNextMediaPlayer = null;
if (callbacks != null)
callbacks.onTrackWentToNext();
} else {
if (callbacks != null)
callbacks.onTrackEnded();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,316 @@
/*
* Copyright (C) 2015 Naman Dwivedi
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.service;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.media.MediaDescription;
import android.media.browse.MediaBrowser;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.service.media.MediaBrowserService;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.util.RetroUtil;
/**
* @author Hemanth S (h4h13).
*/
@TargetApi(21)
public class WearBrowserService extends MediaBrowserService {
public static final String MEDIA_ID_ROOT = "__ROOT__";
public static final int TYPE_ARTIST = 0;
public static final int TYPE_ALBUM = 1;
public static final int TYPE_SONG = 2;
public static final int TYPE_PLAYLIST = 3;
public static final int TYPE_ARTIST_SONG_ALBUMS = 4;
public static final int TYPE_ALBUM_SONGS = 5;
public static final int TYPE_ARTIST_ALL_SONGS = 6;
public static final int TYPE_PLAYLIST_ALL_SONGS = 7;
public static WearBrowserService sInstance;
MediaSession mSession;
private Context mContext;
private boolean mServiceStarted;
public static WearBrowserService getInstance() {
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
mContext = this;
mSession = new MediaSession(this, "WearBrowserService");
setSessionToken(mSession.getSessionToken());
mSession.setCallback(new MediaSessionCallback());
mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
}
@Override
public int onStartCommand(Intent startIntent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
mServiceStarted = false;
mSession.release();
}
@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String s, int i, @Nullable Bundle bundle) {
return new BrowserRoot(MEDIA_ID_ROOT, null);
}
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowser.MediaItem>> result) {
result.detach();
loadChildren(parentId, result);
}
private void setSessionActive() {
if (!mServiceStarted) {
startService(new Intent(getApplicationContext(), WearBrowserService.class));
mServiceStarted = true;
}
if (!mSession.isActive()) {
mSession.setActive(true);
}
}
private void setSessionInactive() {
if (mServiceStarted) {
stopSelf();
mServiceStarted = false;
}
if (mSession.isActive()) {
mSession.setActive(false);
}
}
private void fillMediaItems(List<MediaBrowser.MediaItem> mediaItems, String mediaId,
String title, Uri icon, String subTitle, int playableOrBrowsable) {
mediaItems.add(new MediaBrowser.MediaItem(
new MediaDescription.Builder()
.setMediaId(mediaId)
.setTitle(title)
.setIconUri(icon)
.setSubtitle(subTitle)
.build(), playableOrBrowsable
));
}
private void addMediaRoots(List<MediaBrowser.MediaItem> mMediaRoot) {
mMediaRoot.add(new MediaBrowser.MediaItem(
new MediaDescription.Builder()
.setMediaId(Integer.toString(TYPE_ARTIST))
.setTitle(getString(R.string.artists))
.setIconUri(Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"))
.setSubtitle(getString(R.string.artists))
.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
));
mMediaRoot.add(new MediaBrowser.MediaItem(
new MediaDescription.Builder()
.setMediaId(Integer.toString(TYPE_ALBUM))
.setTitle(getString(R.string.albums))
.setIconUri(Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"))
.setSubtitle(getString(R.string.albums))
.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
));
mMediaRoot.add(new MediaBrowser.MediaItem(
new MediaDescription.Builder()
.setMediaId(Integer.toString(TYPE_SONG))
.setTitle(getString(R.string.songs))
.setIconUri(Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"))
.setSubtitle(getString(R.string.songs))
.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
));
mMediaRoot.add(new MediaBrowser.MediaItem(
new MediaDescription.Builder()
.setMediaId(Integer.toString(TYPE_PLAYLIST))
.setTitle(getString(R.string.playlists))
.setIconUri(Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"))
.setSubtitle(getString(R.string.playlists))
.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
));
}
private void loadChildren(final String parentId, final Result<List<MediaBrowser.MediaItem>> result) {
final List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>();
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(final Void... unused) {
if (parentId.equals(MEDIA_ID_ROOT)) {
addMediaRoots(mediaItems);
} else {
switch (Integer.parseInt(Character.toString(parentId.charAt(0)))) {
/*case TYPE_ARTIST:
List<Artist> artistList = ArtistLoader.getAllArtists(mContext);
for (Artist artist : artistList) {
String albumNmber = TimberUtils.makeLabel(mContext, R.plurals.Nalbums, artist.albumCount);
String songCount = TimberUtils.makeLabel(mContext, R.plurals.Nsongs, artist.songCount);
fillMediaItems(mediaItems, Integer.toString(TYPE_ARTIST_SONG_ALBUMS) + Long.toString(artist.id), artist.name, Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"), TimberUtils.makeCombinedString(mContext, albumNmber, songCount), MediaBrowser.MediaItem.FLAG_BROWSABLE);
}
break;
case TYPE_ALBUM:
List<Album> albumList = AlbumLoader.getAllAlbums(mContext);
for (Album album : albumList) {
fillMediaItems(mediaItems, Integer.toString(TYPE_ALBUM_SONGS) + Long.toString(album.id), album.title, TimberUtils.getAlbumArtUri(album.id), album.artistName, MediaBrowser.MediaItem.FLAG_BROWSABLE);
}
break;*/
case TYPE_SONG:
List<Song> songList = SongLoader.getAllSongs(mContext).blockingFirst();
for (Song song : songList) {
fillMediaItems(mediaItems, String.valueOf(song.id), song.title, RetroUtil.getAlbumArtUri(song.albumId), song.artistName, MediaBrowser.MediaItem.FLAG_PLAYABLE);
}
break;
/* case TYPE_ALBUM_SONGS:
List<Song> albumSongList = AlbumSongLoader.getSongsForAlbum(mContext, Long.parseLong(parentId.substring(1)));
for (Song song : albumSongList) {
fillMediaItems(mediaItems, String.valueOf(song.id), song.title, TimberUtils.getAlbumArtUri(song.albumId), song.artistName, MediaBrowser.MediaItem.FLAG_PLAYABLE);
}
break;
case TYPE_ARTIST_SONG_ALBUMS:
fillMediaItems(mediaItems, Integer.toString(TYPE_ARTIST_ALL_SONGS) + Long.parseLong(parentId.substring(1)), "All songs", Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"), "All songs by artist", MediaBrowser.MediaItem.FLAG_BROWSABLE);
List<Album> artistAlbums = ArtistAlbumLoader.getAlbumsForArtist(mContext, Long.parseLong(parentId.substring(1)));
for (Album album : artistAlbums) {
String songCount = TimberUtils.makeLabel(mContext, R.plurals.Nsongs, album.songCount);
fillMediaItems(mediaItems, Integer.toString(TYPE_ALBUM_SONGS) + Long.toString(album.id), album.title, TimberUtils.getAlbumArtUri(album.id), songCount, MediaBrowser.MediaItem.FLAG_BROWSABLE);
}
break;
case TYPE_ARTIST_ALL_SONGS:
List<Song> artistSongs = ArtistSongLoader.getSongsForArtist(mContext, Long.parseLong(parentId.substring(1)));
for (Song song : artistSongs) {
fillMediaItems(mediaItems, String.valueOf(song.id), song.title, TimberUtils.getAlbumArtUri(song.albumId), song.albumName, MediaBrowser.MediaItem.FLAG_PLAYABLE);
}
break;
case TYPE_PLAYLIST:
List<Playlist> playlistList = PlaylistLoader.getPlaylists(mContext, false);
for (Playlist playlist : playlistList) {
String songCount = TimberUtils.makeLabel(mContext, R.plurals.Nsongs, playlist.songCount);
fillMediaItems(mediaItems, Integer.toString(TYPE_PLAYLIST_ALL_SONGS) + Long.toString(playlist.id), playlist.name,
Uri.parse("android.resource://" +
"naman14.timber/drawable/ic_empty_music2"), songCount, MediaBrowser.MediaItem.FLAG_BROWSABLE);
}
break;
case TYPE_PLAYLIST_ALL_SONGS:
List<Song> playlistSongs = PlaylistSongLoader.getSongsInPlaylist(mContext, Long.parseLong(parentId.substring(1)));
for (Song song : playlistSongs) {
fillMediaItems(mediaItems, String.valueOf(song.id), song.title, TimberUtils.getAlbumArtUri(song.albumId), song.albumName, MediaBrowser.MediaItem.FLAG_PLAYABLE);
}
break;*/
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
result.sendResult(mediaItems);
}
}.execute();
}
private final class MediaSessionCallback extends MediaSession.Callback {
@Override
public void onPlay() {
setSessionActive();
}
@Override
public void onSeekTo(long position) {
}
@Override
public void onPlayFromMediaId(final String mediaId, Bundle extras) {
long songId = Long.parseLong(mediaId);
setSessionActive();
ArrayList<Song> songs = new ArrayList<>();
songs.add(SongLoader.getSong(mContext, Integer.parseInt(mediaId)).blockingFirst());
MusicPlayerRemote.openQueue(songs, 0, true);
}
@Override
public void onPause() {
}
@Override
public void onStop() {
setSessionInactive();
}
@Override
public void onSkipToNext() {
}
@Override
public void onSkipToPrevious() {
}
@Override
public void onFastForward() {
}
@Override
public void onRewind() {
}
@Override
public void onCustomAction(String action, Bundle extras) {
}
}
}

View file

@ -0,0 +1,186 @@
package code.name.monkey.retromusic.service.daydream;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.service.dreams.DreamService;
import android.support.transition.TransitionManager;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import code.name.monkey.retromusic.loaders.SongLoader;
import code.name.monkey.retromusic.model.Song;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget;
import code.name.monkey.retromusic.glide.SongGlideRequest;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* Created by hemanths on 25/09/17.
*/
public class RetroMusicAlbums extends DreamService {
@BindView(R.id.recycler_view)
RecyclerView mRecyclerView;
@BindView(R.id.title)
TextView mTitle;
@BindView(R.id.text)
TextView mText;
@BindView(R.id.title_container)
ViewGroup mViewGroup;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
String artist = intent.getStringExtra("artist");
String track = intent.getStringExtra("track");
if (mViewGroup != null) {
mViewGroup.setVisibility(View.VISIBLE);
TransitionManager.beginDelayedTransition(mViewGroup);
if (mTitle != null) {
mTitle.setText(track);
}
if (mText != null) {
mText.setText(artist);
}
}
}
};
private Unbinder unbinder;
private CompositeDisposable mDisposable;
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
View view = LayoutInflater.from(this).inflate(R.layout.dream_service, null);
setContentView(view);
unbinder = ButterKnife.bind(this, view);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
GridLayoutManager layoutManager = new GridLayoutManager(this, 4, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(layoutManager);
mDisposable.add(getPlayingQueue()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.flatMap((Function<ArrayList<Song>, ObservableSource<ArrayList<Song>>>) songs -> Observable.create(e -> {
if (songs.isEmpty()) {
e.onNext(SongLoader.getAllSongs(RetroMusicAlbums.this).blockingFirst());
} else {
e.onNext(songs);
}
e.onComplete();
}))
.subscribe(songs -> {
if (songs.size() > 0) {
ImagesAdapter imagesAdapter = new ImagesAdapter(songs);
mRecyclerView.setAdapter(imagesAdapter);
}
}));
}
@Override
public void onCreate() {
super.onCreate();
setInteractive(true);
setFullscreen(true);
mDisposable = new CompositeDisposable();
IntentFilter iF = new IntentFilter();
iF.addAction("com.android.music.musicservicecommand");
iF.addAction("com.android.music.metachanged");
registerReceiver(mBroadcastReceiver, iF);
}
@Override
public void onDestroy() {
super.onDestroy();
unbinder.unbind();
mDisposable.clear();
unregisterReceiver(mBroadcastReceiver);
}
private Observable<ArrayList<Song>> getPlayingQueue() {
return Observable.just(MusicPlayerRemote.getPlayingQueue());
}
class ImagesAdapter extends RecyclerView.Adapter<ImagesAdapter.ViewHolder> {
private final ArrayList<Song> list;
public ImagesAdapter(ArrayList<Song> songs) {
this.list = songs;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(getApplicationContext())
.inflate(R.layout.image, viewGroup, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int i) {
Song song = list.get(i);
SongGlideRequest.Builder.from(Glide.with(getApplicationContext()), song)
.checkIgnoreMediaStore(getApplicationContext())
.generatePalette(getApplicationContext()).build()
.override(400, 400)
.into(new RetroMusicColoredTarget(holder.image) {
@Override
public void onColorReady(int color) {
}
});
}
@Override
public int getItemCount() {
return list.size();
}
class ViewHolder extends MediaEntryViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
@Override
public void onClick(View v) {
super.onClick(v);
MusicPlayerRemote.openQueue(list, getAdapterPosition(), true);
}
}
}
}

View file

@ -0,0 +1,80 @@
package code.name.monkey.retromusic.service.notification;
import static android.content.Context.NOTIFICATION_SERVICE;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
import android.support.annotation.RequiresApi;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.service.MusicService;
public abstract class PlayingNotification {
static final String NOTIFICATION_CHANNEL_ID = "playing_notification";
private static final int NOTIFICATION_ID = 1;
private static final int NOTIFY_MODE_FOREGROUND = 1;
private static final int NOTIFY_MODE_BACKGROUND = 0;
protected MusicService service;
boolean stopped;
private int notifyMode = NOTIFY_MODE_BACKGROUND;
private NotificationManager notificationManager;
public synchronized void init(MusicService service) {
this.service = service;
notificationManager = (NotificationManager) service.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
}
public abstract void update();
public synchronized void stop() {
stopped = true;
service.stopForeground(true);
notificationManager.cancel(NOTIFICATION_ID);
}
void updateNotifyModeAndPostNotification(Notification notification) {
int newNotifyMode;
if (service.isPlaying()) {
newNotifyMode = NOTIFY_MODE_FOREGROUND;
} else {
newNotifyMode = NOTIFY_MODE_BACKGROUND;
}
if (notifyMode != newNotifyMode && newNotifyMode == NOTIFY_MODE_BACKGROUND) {
service.stopForeground(false);
}
if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
service.startForeground(NOTIFICATION_ID, notification);
} else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) {
notificationManager.notify(NOTIFICATION_ID, notification);
}
notifyMode = newNotifyMode;
}
@RequiresApi(26)
private void createNotificationChannel() {
NotificationChannel notificationChannel = notificationManager
.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
if (notificationChannel == null) {
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
service.getString(R.string.playing_notification_name),
NotificationManager.IMPORTANCE_LOW);
notificationChannel
.setDescription(service.getString(R.string.playing_notification_description));
notificationChannel.enableLights(false);
notificationChannel.enableVibration(false);
notificationChannel.setShowBadge(false);
notificationManager.createNotificationChannel(notificationChannel);
}
}
}

View file

@ -0,0 +1,225 @@
package code.name.monkey.retromusic.service.notification;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.view.View;
import android.widget.RemoteViews;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.Target;
import code.name.monkey.appthemehelper.util.ColorUtil;
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.glide.SongGlideRequest;
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.service.MusicService;
import code.name.monkey.retromusic.ui.activities.MainActivity;
import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroColorUtil;
import code.name.monkey.retromusic.util.RetroUtil;
import static code.name.monkey.retromusic.Constants.ACTION_QUIT;
import static code.name.monkey.retromusic.Constants.ACTION_REWIND;
import static code.name.monkey.retromusic.Constants.ACTION_SKIP;
import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE;
import static code.name.monkey.retromusic.util.RetroUtil.createBitmap;
public class PlayingNotificationImpl extends PlayingNotification {
private Target<BitmapPaletteWrapper> target;
@Override
public synchronized void update() {
stopped = false;
final Song song = service.getCurrentSong();
final boolean isPlaying = service.isPlaying();
final RemoteViews notificationLayout = new RemoteViews(service.getPackageName(),
R.layout.notification);
final RemoteViews notificationLayoutBig = new RemoteViews(service.getPackageName(),
R.layout.notification_big);
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE);
} else {
notificationLayout.setViewVisibility(R.id.media_titles, View.VISIBLE);
notificationLayout.setTextViewText(R.id.title, song.title);
notificationLayout.setTextViewText(R.id.text, song.artistName);
}
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils
.isEmpty(song.albumName)) {
notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE);
} else {
notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE);
notificationLayoutBig.setTextViewText(R.id.title, song.title);
notificationLayoutBig.setTextViewText(R.id.text, song.artistName);
notificationLayoutBig.setTextViewText(R.id.text2, song.albumName);
}
linkButtons(notificationLayout, notificationLayoutBig);
Intent action = new Intent(service, MainActivity.class);
action.putExtra("expand", true);
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
final PendingIntent clickIntent = PendingIntent
.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT);
final PendingIntent deleteIntent = buildPendingIntent(service, ACTION_QUIT, null);
final Notification notification = new NotificationCompat.Builder(service,
NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(clickIntent)
.setDeleteIntent(deleteIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContent(notificationLayout)
.setCustomBigContentView(notificationLayoutBig)
.setOngoing(isPlaying)
.build();
final int bigNotificationImageSize = service.getResources()
.getDimensionPixelSize(R.dimen.notification_big_image_size);
service.runOnUiThread(new Runnable() {
@Override
public void run() {
if (target != null) {
Glide.clear(target);
}
target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service)
.generatePalette(service).build()
.into(new SimpleTarget<BitmapPaletteWrapper>(bigNotificationImageSize,
bigNotificationImageSize) {
@Override
public void onResourceReady(BitmapPaletteWrapper resource,
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
update(resource.getBitmap(),
PreferenceUtil.getInstance(service).isDominantColor() ?
RetroColorUtil.getDominantColor(resource.getBitmap(), Color.TRANSPARENT) :
RetroColorUtil.getColor(resource.getPalette(), Color.TRANSPARENT));
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable);
update(null, Color.WHITE);
}
private void update(@Nullable Bitmap bitmap, int bgColor) {
if (bitmap != null) {
notificationLayout.setImageViewBitmap(R.id.image, bitmap);
notificationLayoutBig.setImageViewBitmap(R.id.image, bitmap);
} else {
notificationLayout.setImageViewResource(R.id.image, R.drawable.default_album_art);
notificationLayoutBig
.setImageViewResource(R.id.image, R.drawable.default_album_art);
}
if (!PreferenceUtil.getInstance(service).coloredNotification()) {
bgColor = Color.WHITE;
}
setBackgroundColor(bgColor);
setNotificationContent(ColorUtil.isColorLight(bgColor));
if (stopped) {
return; // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(notification);
}
private void setBackgroundColor(int color) {
notificationLayout.setInt(R.id.root, "setBackgroundColor", color);
notificationLayoutBig.setInt(R.id.root, "setBackgroundColor", color);
}
private void setNotificationContent(boolean dark) {
int primary = MaterialValueHelper.getPrimaryTextColor(service, dark);
int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark);
Bitmap close = createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, primary),
1.5f);
Bitmap prev = createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
primary), 1.5f);
Bitmap next = createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
primary), 1.5f);
Bitmap playPause = createBitmap(RetroUtil.getTintedVectorDrawable(service,
isPlaying ? R.drawable.ic_pause_white_24dp
: R.drawable.ic_play_arrow_white_24dp, primary), 1.5f);
notificationLayout.setTextColor(R.id.title, primary);
notificationLayout.setTextColor(R.id.text, secondary);
notificationLayout.setImageViewBitmap(R.id.action_prev, prev);
notificationLayout.setImageViewBitmap(R.id.action_next, next);
notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause);
notificationLayoutBig.setTextColor(R.id.title, primary);
notificationLayoutBig.setTextColor(R.id.text, secondary);
notificationLayoutBig.setTextColor(R.id.text2, secondary);
notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close);
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev);
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next);
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause);
}
});
}
});
}
private void linkButtons(final RemoteViews notificationLayout,
final RemoteViews notificationLayoutBig) {
PendingIntent pendingIntent;
final ComponentName serviceName = new ComponentName(service, MusicService.class);
// Previous track
pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent);
notificationLayoutBig.setOnClickPendingIntent(R.id.action_prev, pendingIntent);
// Play and pause
pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent);
notificationLayoutBig.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent);
// Next track
pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent);
notificationLayoutBig.setOnClickPendingIntent(R.id.action_next, pendingIntent);
// Close
pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName);
notificationLayoutBig.setOnClickPendingIntent(R.id.action_quit, pendingIntent);
}
private PendingIntent buildPendingIntent(Context context, final String action,
final ComponentName serviceName) {
Intent intent = new Intent(action);
intent.setComponent(serviceName);
return PendingIntent.getService(context, 0, intent, 0);
}
}

View file

@ -0,0 +1,146 @@
package code.name.monkey.retromusic.service.notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.media.app.NotificationCompat.MediaStyle;
import android.text.Html;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import code.name.monkey.retromusic.Constants;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.RetroApplication;
import code.name.monkey.retromusic.glide.SongGlideRequest;
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.service.MusicService;
import code.name.monkey.retromusic.ui.activities.MainActivity;
import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroColorUtil;
import static code.name.monkey.retromusic.Constants.ACTION_QUIT;
import static code.name.monkey.retromusic.Constants.ACTION_REWIND;
import static code.name.monkey.retromusic.Constants.ACTION_SKIP;
import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE;
public class PlayingNotificationImpl24 extends PlayingNotification {
@Override
public synchronized void update() {
stopped = false;
final Song song = service.getCurrentSong();
final boolean isPlaying = service.isPlaying();
final int playButtonResId = isPlaying ? R.drawable.ic_pause_white_24dp :
R.drawable.ic_play_arrow_white_24dp;
Intent action = new Intent(service, MainActivity.class);
action.putExtra("expand", true);
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
final PendingIntent clickIntent = PendingIntent
.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT);
final ComponentName serviceName = new ComponentName(service, MusicService.class);
Intent intent = new Intent(Constants.ACTION_QUIT);
intent.setComponent(serviceName);
final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
final int bigNotificationImageSize = service.getResources()
.getDimensionPixelSize(R.dimen.notification_big_image_size);
service.runOnUiThread(() -> SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service)
.generatePalette(service).build()
.into(new SimpleTarget<BitmapPaletteWrapper>(bigNotificationImageSize,
bigNotificationImageSize) {
@Override
public void onResourceReady(BitmapPaletteWrapper resource,
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
update(resource.getBitmap(),
PreferenceUtil.getInstance(RetroApplication.getInstance()).isDominantColor() ?
RetroColorUtil.getDominantColor(resource.getBitmap(), Color.TRANSPARENT) :
RetroColorUtil.getColor(resource.getPalette(), Color.TRANSPARENT));
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
update(null, Color.TRANSPARENT);
}
void update(Bitmap bitmap, int color) {
if (bitmap == null) {
bitmap = BitmapFactory
.decodeResource(service.getResources(), R.drawable.default_album_art);
}
NotificationCompat.Action playPauseAction = new NotificationCompat.Action(
playButtonResId,
service.getString(R.string.action_play_pause),
retrievePlaybackAction(ACTION_TOGGLE_PAUSE));
NotificationCompat.Action closeAction = new NotificationCompat.Action(
R.drawable.ic_close_white_24dp,
service.getString(R.string.close_notification),
retrievePlaybackAction(ACTION_QUIT));
NotificationCompat.Action previousAction = new NotificationCompat.Action(
R.drawable.ic_skip_previous_white_24dp,
service.getString(R.string.action_previous),
retrievePlaybackAction(ACTION_REWIND));
NotificationCompat.Action nextAction = new NotificationCompat.Action(
R.drawable.ic_skip_next_white_24dp,
service.getString(R.string.action_next),
retrievePlaybackAction(ACTION_SKIP));
NotificationCompat.Builder builder = new NotificationCompat.Builder(service,
NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(bitmap)
.setContentIntent(clickIntent)
.setDeleteIntent(deleteIntent)
.setContentTitle(Html.fromHtml("<b>" + song.title + "</b>"))
.setContentText(song.artistName)
.setSubText(Html.fromHtml("<b>" + song.albumName + "</b>"))
.setOngoing(isPlaying)
.setShowWhen(false)
.addAction(previousAction)
.addAction(playPauseAction)
.addAction(nextAction)
.addAction(closeAction);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setStyle(new MediaStyle()
.setMediaSession(service.getMediaSession().getSessionToken())
.setShowActionsInCompactView(0, 1, 2, 3, 4))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O && PreferenceUtil
.getInstance(service).coloredNotification()) {
builder.setColor(color);
}
}
if (stopped) {
return; // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(builder.build());
}
}));
}
private PendingIntent retrievePlaybackAction(final String action) {
final ComponentName serviceName = new ComponentName(service, MusicService.class);
Intent intent = new Intent(action);
intent.setComponent(serviceName);
return PendingIntent.getService(service, 0, intent, 0);
}
}

View file

@ -0,0 +1,244 @@
package code.name.monkey.retromusic.service.notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.widget.RemoteViews;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.Target;
import code.name.monkey.appthemehelper.util.ColorUtil;
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.glide.SongGlideRequest;
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
import code.name.monkey.retromusic.model.Song;
import code.name.monkey.retromusic.service.MusicService;
import code.name.monkey.retromusic.ui.activities.MainActivity;
import code.name.monkey.retromusic.util.PreferenceUtil;
import code.name.monkey.retromusic.util.RetroUtil;
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor;
import static code.name.monkey.retromusic.Constants.ACTION_QUIT;
import static code.name.monkey.retromusic.Constants.ACTION_REWIND;
import static code.name.monkey.retromusic.Constants.ACTION_SKIP;
import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE;
import static code.name.monkey.retromusic.util.RetroUtil.createBitmap;
/**
* @author Hemanth S (h4h13).
*/
public class PlayingNotificationOreo extends PlayingNotification {
private Target<BitmapPaletteWrapper> target;
private RemoteViews getCombinedRemoteViews(boolean collapsed, Song song) {
RemoteViews remoteViews = new RemoteViews(service.getPackageName(),
collapsed ? R.layout.layout_notification_collapsed : R.layout.layout_notification_expanded);
remoteViews.setTextViewText(R.id.appName, service.getString(R.string.app_name) + "" + song.albumName);
remoteViews.setTextViewText(R.id.title, song.title);
remoteViews.setTextViewText(R.id.subtitle, song.artistName);
TypedArray typedArray = service
.obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground});
int selectableItemBackground = typedArray.getResourceId(0, 0);
typedArray.recycle();
remoteViews.setInt(R.id.content, "setBackgroundResource", selectableItemBackground);
linkButtons(remoteViews);
//setNotificationContent(remoteViews, ColorUtil.isColorLight(backgroundColor));
return remoteViews;
}
@Override
public void update() {
stopped = false;
final Song song = service.getCurrentSong();
final boolean isPlaying = service.isPlaying();
final RemoteViews notificationLayout = getCombinedRemoteViews(true, song);
final RemoteViews notificationLayoutBig = getCombinedRemoteViews(false, song);
Intent action = new Intent(service, MainActivity.class);
action.putExtra("expand", true);
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
final PendingIntent clickIntent = PendingIntent
.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT);
final PendingIntent deleteIntent = buildPendingIntent(service, ACTION_QUIT, null);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(service,
NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentIntent(clickIntent)
.setDeleteIntent(deleteIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setCustomContentView(notificationLayout)
.setCustomBigContentView(notificationLayoutBig)
.setOngoing(isPlaying);
final int bigNotificationImageSize = service.getResources()
.getDimensionPixelSize(R.dimen.notification_big_image_size);
service.runOnUiThread(new Runnable() {
@Override
public void run() {
if (target != null) {
Glide.clear(target);
}
target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service)
.generatePalette(service).build()
.into(new SimpleTarget<BitmapPaletteWrapper>(bigNotificationImageSize,
bigNotificationImageSize) {
@Override
public void onResourceReady(BitmapPaletteWrapper resource,
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
MediaNotificationProcessor mediaNotificationProcessor = new MediaNotificationProcessor(service, service, new MediaNotificationProcessor.onColorThing() {
@Override
public void bothColor(int i, int i2) {
update(resource.getBitmap(), i, i2);
}
});
mediaNotificationProcessor.processNotification(resource.getBitmap());
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable);
update(null, Color.WHITE, Color.BLACK);
}
private void update(@Nullable Bitmap bitmap, int bgColor, int textColor) {
if (bitmap != null) {
notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap);
notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap);
} else {
notificationLayout
.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art);
notificationLayoutBig
.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art);
}
if (!PreferenceUtil.getInstance(service).coloredNotification()) {
bgColor = Color.WHITE;
}
setBackgroundColor(bgColor);
setNotificationContent(ColorUtil.isColorLight(bgColor));
if (stopped) {
return; // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(builder.build());
}
private void setBackgroundColor(int color) {
notificationLayout.setInt(R.id.image, "setBackgroundColor", color);
notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color);
notificationLayout.setInt(R.id.foregroundImage, "setColorFilter", color);
notificationLayoutBig.setInt(R.id.foregroundImage, "setColorFilter", color);
}
private void setNotificationContent(boolean dark) {
int primary = MaterialValueHelper.getPrimaryTextColor(service, dark);
int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark);
Bitmap close = createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, primary),
1.3f);
Bitmap prev = createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
primary), 1.3f);
Bitmap next = createBitmap(
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
primary), 1.3f);
Bitmap playPause = createBitmap(RetroUtil.getTintedVectorDrawable(service,
isPlaying ? R.drawable.ic_pause_white_24dp
: R.drawable.ic_play_arrow_white_24dp, primary), 1.3f);
notificationLayout.setTextColor(R.id.title, primary);
notificationLayout.setTextColor(R.id.subtitle, secondary);
notificationLayout.setTextColor(R.id.appName, secondary);
notificationLayout.setImageViewBitmap(R.id.action_prev, prev);
notificationLayout.setImageViewBitmap(R.id.action_next, next);
notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause);
notificationLayoutBig.setTextColor(R.id.title, primary);
notificationLayoutBig.setTextColor(R.id.subtitle, secondary);
notificationLayoutBig.setTextColor(R.id.appName, secondary);
notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close);
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev);
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next);
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause);
notificationLayout.setImageViewBitmap(R.id.smallIcon,
createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary), 0.6f));
notificationLayoutBig.setImageViewBitmap(R.id.smallIcon,
createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary), 0.6f));
notificationLayout.setInt(R.id.arrow, "setColorFilter", secondary);
notificationLayoutBig.setInt(R.id.arrow, "setColorFilter", secondary);
}
});
}
});
if (stopped) {
return; // notification has been stopped before loading was finished
}
updateNotifyModeAndPostNotification(builder.build());
}
private PendingIntent buildPendingIntent(Context context, final String action,
final ComponentName serviceName) {
Intent intent = new Intent(action);
intent.setComponent(serviceName);
return PendingIntent.getService(context, 0, intent, 0);
}
private void linkButtons(final RemoteViews notificationLayout) {
PendingIntent pendingIntent;
final ComponentName serviceName = new ComponentName(service, MusicService.class);
// Previous track
pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent);
// Play and pause
pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent);
// Next track
pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent);
// Close
pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName);
notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent);
}
}

View file

@ -0,0 +1,43 @@
package code.name.monkey.retromusic.service.playback;
import android.support.annotation.Nullable;
public interface Playback {
boolean setDataSource(String path);
void setNextDataSource(@Nullable String path);
void setCallbacks(PlaybackCallbacks callbacks);
boolean isInitialized();
boolean start();
void stop();
void release();
boolean pause();
boolean isPlaying();
int duration();
int position();
int seek(int whereto);
boolean setVolume(float vol);
boolean setAudioSessionId(int sessionId);
int getAudioSessionId();
interface PlaybackCallbacks {
void onTrackWentToNext();
void onTrackEnded();
}
}