diff --git a/app/src/main/java/com/kabouzeid/gramophone/App.java b/app/src/main/java/com/kabouzeid/gramophone/App.java index e5e7613b..b9e14502 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/App.java +++ b/app/src/main/java/com/kabouzeid/gramophone/App.java @@ -3,15 +3,12 @@ package com.kabouzeid.gramophone; import android.app.Application; import com.crashlytics.android.Crashlytics; -import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.utils.L; import com.squareup.otto.Bus; import com.squareup.otto.ThreadEnforcer; -import org.jaudiotagger.tag.TagOptionSingleton; - import io.fabric.sdk.android.Fabric; /** @@ -24,14 +21,11 @@ public class App extends Application { @Override public void onCreate() { super.onCreate(); - if (!BuildConfig.DEBUG) Fabric.with(this, new Crashlytics()); - MusicPlayerRemote.startAndBindService(this); + if (!BuildConfig.DEBUG) Fabric.with(this, new Crashlytics()); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).build(); ImageLoader.getInstance().init(config); L.writeLogs(false); // turns off UILs annoying LogCat output - - TagOptionSingleton.getInstance().isAndroid(); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/appwidget/WidgetMedium.java b/app/src/main/java/com/kabouzeid/gramophone/appwidget/WidgetMedium.java index 8eb625bb..fdb4d19b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/appwidget/WidgetMedium.java +++ b/app/src/main/java/com/kabouzeid/gramophone/appwidget/WidgetMedium.java @@ -14,7 +14,7 @@ import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; -import com.kabouzeid.gramophone.ui.activities.MusicControllerActivity; +import com.kabouzeid.gramophone.ui.activities.MainActivity; import com.kabouzeid.gramophone.util.MusicUtil; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.FailReason; @@ -117,7 +117,7 @@ public class WidgetMedium extends AppWidgetProvider { final ComponentName serviceName = new ComponentName(context, MusicService.class); switch (which) { case 0: - action = new Intent(context, MusicControllerActivity.class); + action = new Intent(context, MainActivity.class); pendingIntent = PendingIntent.getActivity(context, 0, action, 0); return pendingIntent; case 1: diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java index 60e50f10..79cca3b8 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/MusicPlayerRemote.java @@ -1,7 +1,9 @@ package com.kabouzeid.gramophone.helper; +import android.app.Activity; import android.content.ComponentName; import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; import android.content.ServiceConnection; import android.media.audiofx.AudioEffect; @@ -15,6 +17,7 @@ import com.kabouzeid.gramophone.service.MusicService; import java.util.ArrayList; import java.util.Random; +import java.util.WeakHashMap; /** * @author Karim Abou Zeid (kabouzeid) @@ -23,28 +26,75 @@ public class MusicPlayerRemote { public static final String TAG = MusicPlayerRemote.class.getSimpleName(); - public static final String SERVICE_BOUND = "com.kabouzeid.gramophone.SERVICE_BOUND"; + public static MusicService musicService; - private static MusicService musicService; + private static final WeakHashMap mConnectionMap = new WeakHashMap<>(); - private static final ServiceConnection musicConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - MusicService.MusicBinder binder = (MusicService.MusicBinder) service; - musicService = binder.getService(); - musicService.sendBroadcast(new Intent(SERVICE_BOUND)); + public static ServiceToken bindToService(final Context context, + final ServiceConnection callback) { + Activity realActivity = ((Activity) context).getParent(); + if (realActivity == null) { + realActivity = (Activity) context; } - @Override - public void onServiceDisconnected(ComponentName name) { + final ContextWrapper contextWrapper = new ContextWrapper(realActivity); + contextWrapper.startService(new Intent(contextWrapper, MusicService.class)); + + final ServiceBinder binder = new ServiceBinder(callback); + + if (contextWrapper.bindService(new Intent().setClass(contextWrapper, MusicService.class), binder, Context.BIND_AUTO_CREATE)) { + mConnectionMap.put(contextWrapper, binder); + return new ServiceToken(contextWrapper); + } + return null; + } + + public static void unbindFromService(final ServiceToken token) { + if (token == null) { + return; + } + final ContextWrapper mContextWrapper = token.mWrappedContext; + final ServiceBinder mBinder = mConnectionMap.remove(mContextWrapper); + if (mBinder == null) { + return; + } + mContextWrapper.unbindService(mBinder); + if (mConnectionMap.isEmpty()) { musicService = null; } - }; + } - public static void startAndBindService(final Context context) { - Intent musicServiceIntent = new Intent(context, MusicService.class); - context.bindService(musicServiceIntent, musicConnection, Context.BIND_AUTO_CREATE); - context.startService(musicServiceIntent); + public static final class ServiceBinder implements ServiceConnection { + private final ServiceConnection mCallback; + + public ServiceBinder(final ServiceConnection callback) { + mCallback = callback; + } + + @Override + public void onServiceConnected(final ComponentName className, final IBinder service) { + MusicService.MusicBinder binder = (MusicService.MusicBinder) service; + musicService = binder.getService(); + if (mCallback != null) { + mCallback.onServiceConnected(className, service); + } + } + + @Override + public void onServiceDisconnected(final ComponentName className) { + if (mCallback != null) { + mCallback.onServiceDisconnected(className); + } + musicService = null; + } + } + + public static final class ServiceToken { + public ContextWrapper mWrappedContext; + + public ServiceToken(final ContextWrapper context) { + mWrappedContext = context; + } } public static boolean isServiceConnected() { diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java b/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java index 9844a111..0db5b85a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java +++ b/app/src/main/java/com/kabouzeid/gramophone/helper/PlayingNotificationHelper.java @@ -7,7 +7,6 @@ package com.kabouzeid.gramophone.helper; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.TaskStackBuilder; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -22,7 +21,7 @@ import android.widget.RemoteViews; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.service.MusicService; -import com.kabouzeid.gramophone.ui.activities.MusicControllerActivity; +import com.kabouzeid.gramophone.ui.activities.MainActivity; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.PreferenceUtils; import com.nostra13.universalimageloader.core.ImageLoader; @@ -122,11 +121,7 @@ public class PlayingNotificationHelper { } 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); + return PendingIntent.getActivity(service, 0, new Intent(service, MainActivity.class), 0); } private void setUpExpandedPlaybackActions() { @@ -263,7 +258,7 @@ public class PlayingNotificationHelper { this.isPlaying = isPlaying; if (notification == null) { - return; + updateNotification(); } if (notificationLayout != null) { notificationLayout.setImageViewResource(R.id.action_play_pause, diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java index 62da89d1..b17d057b 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java @@ -51,6 +51,8 @@ import java.util.List; * @author Karim Abou Zeid (kabouzeid), Andrew Neal */ public class MusicService extends Service { + public static final String TAG = MusicService.class.getSimpleName(); + public static final String PHONOGRAPH_PACKAGE_NAME = "com.kabouzeid.gramophone"; public static final String MUSIC_PACKAGE_NAME = "com.android.music"; @@ -114,6 +116,7 @@ public class MusicService extends Service { private RecentlyPlayedStore recentlyPlayedStore; private SongPlayCountStore songPlayCountStore; private boolean notNotifiedMetaChangedForCurrentTrack; + private boolean isServiceInUse; private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { @Override @@ -250,8 +253,7 @@ public class MusicService extends Service { stop(); break; case ACTION_QUIT: - quit(); - break; + return quit(); } } } @@ -267,9 +269,24 @@ public class MusicService extends Service { @Override public IBinder onBind(Intent intent) { + isServiceInUse = true; return musicBind; } + @Override + public void onRebind(Intent intent) { + isServiceInUse = true; + } + + @Override + public boolean onUnbind(Intent intent) { + isServiceInUse = false; + if (!isPlaying()) { + stopSelf(); + } + return true; + } + private void unregisterReceiversAndRemoteControlClient() { if (receiversAndRemoteControlClientRegistered) { unregisterReceiver(becomingNoisyReceiver); @@ -282,12 +299,18 @@ public class MusicService extends Service { } } - private void quit() { + private int quit() { unregisterReceiversAndRemoteControlClient(); - closeAudioEffectSession(); - stop(); + pause(); playingNotificationHelper.killNotification(); - stopSelf(); + + if (isServiceInUse) { + return START_STICKY; + } else { + closeAudioEffectSession(); + stopSelf(); + return START_NOT_STICKY; + } } private void releaseResources() { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java index e7c378e7..0390555e 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/AlbumDetailActivity.java @@ -111,16 +111,7 @@ public class AlbumDetailActivity extends AbsFabActivity implements PaletteColorH setNavigationBarColor(DialogUtils.resolveColor(this, R.attr.default_bar_color)); } - Bundle intentExtras = getIntent().getExtras(); - int albumId = -1; - if (intentExtras != null) { - albumId = intentExtras.getInt(EXTRA_ALBUM_ID); - } - album = AlbumLoader.getAlbum(this, albumId); - if (album.id == -1) { - finish(); - } - + getAlbumFromIntentExtras(); setUpObservableListViewParams(); setUpToolBar(); setUpViews(); @@ -180,6 +171,15 @@ public class AlbumDetailActivity extends AbsFabActivity implements PaletteColorH } }; + private void getAlbumFromIntentExtras() { + Bundle intentExtras = getIntent().getExtras(); + final int albumId = intentExtras.getInt(EXTRA_ALBUM_ID); + album = AlbumLoader.getAlbum(this, albumId); + if (album.id == -1) { + finish(); + } + } + @Override public String getTag() { return TAG; diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java index 4e0dedc4..d0d9811a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java @@ -135,7 +135,7 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor lastFMRestClient = new LastFMRestClient(this); - getIntentExtras(); + getArtistFromIntentExtras(); initViews(); setUpObservableListViewParams(); setUpViews(); @@ -430,11 +430,11 @@ public class ArtistDetailActivity extends AbsFabActivity implements PaletteColor notifyTaskColorChange(toolbarColor); } - private void getIntentExtras() { + private void getArtistFromIntentExtras() { Bundle intentExtras = getIntent().getExtras(); final int artistId = intentExtras.getInt(EXTRA_ARTIST_ID); artist = ArtistLoader.getArtist(this, artistId); - if (artist == null) { + if (artist.id == -1) { finish(); } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsFabActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsFabActivity.java index 32fb94f9..7f2eac98 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsFabActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsFabActivity.java @@ -27,7 +27,7 @@ import butterknife.Optional; /** * @author Karim Abou Zeid (kabouzeid) */ -public abstract class AbsFabActivity extends AbsPlaybackStatusActivity { +public abstract class AbsFabActivity extends AbsPlaybackControlActivity { public static final String TAG = AbsFabActivity.class.getSimpleName(); @Optional diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsPlaybackStatusActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsPlaybackControlActivity.java similarity index 68% rename from app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsPlaybackStatusActivity.java rename to app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsPlaybackControlActivity.java index be27a341..e2433341 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsPlaybackStatusActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsPlaybackControlActivity.java @@ -1,9 +1,13 @@ package com.kabouzeid.gramophone.ui.activities.base; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; import com.kabouzeid.gramophone.service.MusicService; @@ -13,7 +17,8 @@ import java.lang.ref.WeakReference; /** * @author Karim Abou Zeid (kabouzeid) */ -public abstract class AbsPlaybackStatusActivity extends AbsBaseActivity { +public abstract class AbsPlaybackControlActivity extends AbsBaseActivity { + private MusicPlayerRemote.ServiceToken serviceToken; private PlaybackStatusReceiver playbackStatusReceiver; public void onPlayingMetaChanged() { @@ -36,23 +41,32 @@ public abstract class AbsPlaybackStatusActivity extends AbsBaseActivity { } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + serviceToken = MusicPlayerRemote.bindToService(this, new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + AbsPlaybackControlActivity.this.onServiceConnected(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + + } + }); + playbackStatusReceiver = new PlaybackStatusReceiver(this); + } + @Override protected void onStart() { super.onStart(); - playbackStatusReceiver = new PlaybackStatusReceiver(this); - - // ensures that onServiceConnected() is called even if the service is already connected and wont sent the Intent again. - if (MusicPlayerRemote.isServiceConnected()) { - onServiceConnected(); - } - final IntentFilter filter = new IntentFilter(); filter.addAction(MusicService.PLAY_STATE_CHANGED); filter.addAction(MusicService.SHUFFLE_MODE_CHANGED); filter.addAction(MusicService.REPEAT_MODE_CHANGED); filter.addAction(MusicService.META_CHANGED); - filter.addAction(MusicPlayerRemote.SERVICE_BOUND); registerReceiver(playbackStatusReceiver, filter); } @@ -66,11 +80,17 @@ public abstract class AbsPlaybackStatusActivity extends AbsBaseActivity { } } + @Override + protected void onDestroy() { + super.onDestroy(); + MusicPlayerRemote.unbindFromService(serviceToken); + } + private static final class PlaybackStatusReceiver extends BroadcastReceiver { - private final WeakReference reference; + private final WeakReference reference; - public PlaybackStatusReceiver(final AbsPlaybackStatusActivity activity) { + public PlaybackStatusReceiver(final AbsPlaybackControlActivity activity) { reference = new WeakReference<>(activity); } @@ -90,9 +110,6 @@ public abstract class AbsPlaybackStatusActivity extends AbsBaseActivity { case MusicService.SHUFFLE_MODE_CHANGED: reference.get().onShuffleModeChanged(); break; - case MusicPlayerRemote.SERVICE_BOUND: - reference.get().onServiceConnected(); - break; } } } diff --git a/app/src/main/res/drawable-xhdpi/default_album_art.png b/app/src/main/res/drawable-xhdpi/default_album_art.png new file mode 100644 index 00000000..1647dde0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/default_album_art.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/default_album_art.png b/app/src/main/res/drawable-xxxhdpi/default_album_art.png deleted file mode 100644 index 9f6c170c..00000000 Binary files a/app/src/main/res/drawable-xxxhdpi/default_album_art.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/default_album_art_dark.png b/app/src/main/res/drawable-xxxhdpi/default_album_art_dark.png deleted file mode 100644 index 1119de70..00000000 Binary files a/app/src/main/res/drawable-xxxhdpi/default_album_art_dark.png and /dev/null differ