Save the current queue async in an SQL database with an handler running on a background thread (issue #43). Also saving position inside the track now. Fixed a bug where the lockscreen and widget were not updated correctly when pressing play after opening the app for the first time after it was closed before.
This commit is contained in:
parent
2c125ab325
commit
fc2726bf42
5 changed files with 285 additions and 103 deletions
|
|
@ -4,22 +4,11 @@ package com.kabouzeid.gramophone.misc;
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public final class AppKeys {
|
public final class AppKeys {
|
||||||
public static final String CL_CURRENT_ACTIVITY = "Current activity";
|
|
||||||
|
|
||||||
public static final String SP_USER_LEARNED_DRAWER = "com.kabouzeid.gramophone.NAVIGATION_DRAWER_LEARNED";
|
|
||||||
public static final String SP_SHUFFLE_MODE = "com.kabouzeid.gramophone.SHUFFLE_MODE";
|
public static final String SP_SHUFFLE_MODE = "com.kabouzeid.gramophone.SHUFFLE_MODE";
|
||||||
public static final String SP_REPEAT_MODE = "com.kabouzeid.gramophone.REPEAT_MODE";
|
public static final String SP_REPEAT_MODE = "com.kabouzeid.gramophone.REPEAT_MODE";
|
||||||
|
|
||||||
public static final String IS_ORIGINAL_PLAYING_QUEUE = "com.kabouzeid.gramophone.ORIGINAL_PLAYING_QUEUE";
|
|
||||||
public static final String IS_PLAYING_QUEUE = "com.kabouzeid.gramophone.PLAYING_QUEUE";
|
|
||||||
public static final String IS_POSITION_IN_QUEUE = "com.kabouzeid.gramophone.POSITION_IN_QUEUE";
|
|
||||||
// public static final String IS_ARTIST_JSON_INFO_CACHE = "com.kabouzeid.gramophone.ARTIST_JSON_INFO_CACHE";
|
|
||||||
|
|
||||||
public static final String E_ALBUM = "com.kabouzeid.gramophone.ALBUM";
|
public static final String E_ALBUM = "com.kabouzeid.gramophone.ALBUM";
|
||||||
public static final String E_ARTIST = "com.kabouzeid.gramophone.ARTIST";
|
public static final String E_ARTIST = "com.kabouzeid.gramophone.ARTIST";
|
||||||
// public static final String E_SONG = "com.kabouzeid.gramophone.SONG";
|
|
||||||
public static final String E_PLAYLIST = "com.kabouzeid.gramophone.PLAYLIST";
|
|
||||||
// public static final String E_TAG_EDIT_MODE = "com.kabouzeid.gramophone.TAG_EDIT_MODE";
|
|
||||||
public static final String E_ID = "com.kabouzeid.gramophone.ID";
|
public static final String E_ID = "com.kabouzeid.gramophone.ID";
|
||||||
public static final String E_PALETTE = "com.kabouzeid.gramophone.PALETTE";
|
public static final String E_PALETTE = "com.kabouzeid.gramophone.PALETTE";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The CyanogenMod Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.kabouzeid.gramophone.provider;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import com.kabouzeid.gramophone.loader.SongLoader;
|
||||||
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andrew Neal, modified for Phonograph by Karim Abou Zeid
|
||||||
|
* <p/>
|
||||||
|
* This keeps track of the music playback and history state of the playback service
|
||||||
|
*/
|
||||||
|
public class MusicPlaybackQueueStore extends SQLiteOpenHelper {
|
||||||
|
private static MusicPlaybackQueueStore sInstance = null;
|
||||||
|
public static final String DATABASE_NAME = "music_playback_state.db";
|
||||||
|
public static final String PLAYING_QUEUE_TABLE_NAME = "playing_queue";
|
||||||
|
public static final String ORIGINAL_PLAYING_QUEUE_TABLE_NAME = "original_playing_queue";
|
||||||
|
private static final int VERSION = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of <code>MusicPlaybackState</code>
|
||||||
|
*
|
||||||
|
* @param context The {@link Context} to use
|
||||||
|
*/
|
||||||
|
public MusicPlaybackQueueStore(final Context context) {
|
||||||
|
super(context, DATABASE_NAME, null, VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(final SQLiteDatabase db) {
|
||||||
|
createTable(db, PLAYING_QUEUE_TABLE_NAME);
|
||||||
|
createTable(db, ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTable(final SQLiteDatabase db, final String tableName) {
|
||||||
|
//noinspection StringBufferReplaceableByString
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("CREATE TABLE IF NOT EXISTS ");
|
||||||
|
builder.append(tableName);
|
||||||
|
builder.append("(");
|
||||||
|
|
||||||
|
builder.append(BaseColumns._ID);
|
||||||
|
builder.append(" INT NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.TITLE);
|
||||||
|
builder.append(" STRING NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.ARTIST);
|
||||||
|
builder.append(" STRING NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.ALBUM);
|
||||||
|
builder.append(" STRING NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.DURATION);
|
||||||
|
builder.append(" LONG NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.TRACK);
|
||||||
|
builder.append(" INT NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.ARTIST_ID);
|
||||||
|
builder.append(" INT NOT NULL,");
|
||||||
|
|
||||||
|
builder.append(MediaStore.Audio.AudioColumns.ALBUM_ID);
|
||||||
|
builder.append(" INT NOT NULL);");
|
||||||
|
|
||||||
|
db.execSQL(builder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
|
||||||
|
// not necessary yet
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
// If we ever have downgrade, drop the table to be safe
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + PLAYING_QUEUE_TABLE_NAME);
|
||||||
|
db.execSQL("DROP TABLE IF EXISTS " + ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
|
||||||
|
onCreate(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context The {@link Context} to use
|
||||||
|
* @return A new instance of this class.
|
||||||
|
*/
|
||||||
|
public static synchronized MusicPlaybackQueueStore getInstance(final Context context) {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new MusicPlaybackQueueStore(context.getApplicationContext());
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void saveQueues(final ArrayList<Song> playingQueue, final ArrayList<Song> originalPlayingQueue) {
|
||||||
|
saveQueue(PLAYING_QUEUE_TABLE_NAME, playingQueue);
|
||||||
|
saveQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME, originalPlayingQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the existing database and saves the queue into the db so that when the
|
||||||
|
* app is restarted, the tracks you were listening to is restored
|
||||||
|
*
|
||||||
|
* @param queue the queue to save
|
||||||
|
*/
|
||||||
|
private synchronized void saveQueue(final String tableName, final ArrayList<Song> queue) {
|
||||||
|
final SQLiteDatabase database = getWritableDatabase();
|
||||||
|
database.beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
database.delete(tableName, null, null);
|
||||||
|
database.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
database.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int NUM_PROCESS = 20;
|
||||||
|
int position = 0;
|
||||||
|
while (position < queue.size()) {
|
||||||
|
database.beginTransaction();
|
||||||
|
try {
|
||||||
|
for (int i = position; i < queue.size() && i < position + NUM_PROCESS; i++) {
|
||||||
|
Song song = queue.get(i);
|
||||||
|
ContentValues values = new ContentValues(4);
|
||||||
|
|
||||||
|
values.put(BaseColumns._ID, song.id);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.TITLE, song.title);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.ARTIST, song.artistName);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.ALBUM, song.albumName);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.DURATION, song.duration);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.TRACK, song.trackNumber);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.ARTIST_ID, song.artistId);
|
||||||
|
values.put(MediaStore.Audio.AudioColumns.ALBUM_ID, song.albumId);
|
||||||
|
|
||||||
|
database.insert(tableName, null, values);
|
||||||
|
}
|
||||||
|
database.setTransactionSuccessful();
|
||||||
|
} finally {
|
||||||
|
database.endTransaction();
|
||||||
|
position += NUM_PROCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Song> getSavedPlayingQueue() {
|
||||||
|
return getQueue(PLAYING_QUEUE_TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Song> getSavedOriginalPlayingQueue() {
|
||||||
|
return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Song> getQueue(final String tableName) {
|
||||||
|
Cursor cursor = getReadableDatabase().query(tableName, null,
|
||||||
|
null, null, null, null, null);
|
||||||
|
return SongLoader.getSongs(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,6 @@ import android.os.Message;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
|
@ -33,9 +32,9 @@ import com.kabouzeid.gramophone.helper.PlayingNotificationHelper;
|
||||||
import com.kabouzeid.gramophone.helper.ShuffleHelper;
|
import com.kabouzeid.gramophone.helper.ShuffleHelper;
|
||||||
import com.kabouzeid.gramophone.misc.AppKeys;
|
import com.kabouzeid.gramophone.misc.AppKeys;
|
||||||
import com.kabouzeid.gramophone.model.Song;
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
|
import com.kabouzeid.gramophone.provider.MusicPlaybackQueueStore;
|
||||||
import com.kabouzeid.gramophone.provider.RecentlyPlayedStore;
|
import com.kabouzeid.gramophone.provider.RecentlyPlayedStore;
|
||||||
import com.kabouzeid.gramophone.provider.SongPlayCountStore;
|
import com.kabouzeid.gramophone.provider.SongPlayCountStore;
|
||||||
import com.kabouzeid.gramophone.util.InternalStorageUtil;
|
|
||||||
import com.kabouzeid.gramophone.util.MusicUtil;
|
import com.kabouzeid.gramophone.util.MusicUtil;
|
||||||
import com.kabouzeid.gramophone.util.PreferenceUtils;
|
import com.kabouzeid.gramophone.util.PreferenceUtils;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
|
@ -45,7 +44,6 @@ import com.nostra13.universalimageloader.core.assist.ViewScaleType;
|
||||||
import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
|
import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
|
||||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -66,13 +64,16 @@ public class MusicService extends Service {
|
||||||
public static final String ACTION_QUIT = "com.kabouzeid.gramophone.action.QUIT";
|
public static final String ACTION_QUIT = "com.kabouzeid.gramophone.action.QUIT";
|
||||||
|
|
||||||
public static final String META_CHANGED = "com.kabouzeid.gramophone.metachanged";
|
public static final String META_CHANGED = "com.kabouzeid.gramophone.metachanged";
|
||||||
public static final String PLAYSTATE_CHANGED = "com.kabouzeid.gramophone.playstatechanged";
|
public static final String PLAY_STATE_CHANGED = "com.kabouzeid.gramophone.playstatechanged";
|
||||||
public static final String REPEATMODE_CHANGED = "com.kabouzeid.gramophone.repeatmodechanged";
|
public static final String REPEAT_MODE_CHANGED = "com.kabouzeid.gramophone.repeatmodechanged";
|
||||||
public static final String SHUFFLEMODE_CHANGED = "com.kabouzeid.gramophone.shufflemodechanged";
|
public static final String SHUFFLE_MODE_CHANGED = "com.kabouzeid.gramophone.shufflemodechanged";
|
||||||
|
|
||||||
public static final String SETTING_GAPLESS_PLAYBACK_CHANGED = "com.kabouzeid.gramophone.SETTING_GAPLESS_PLAYBACK_CHANGED";
|
public static final String SETTING_GAPLESS_PLAYBACK_CHANGED = "com.kabouzeid.gramophone.SETTING_GAPLESS_PLAYBACK_CHANGED";
|
||||||
public static final String SETTING_GAPLESS_PLAYBACK_CHANGED_VALUE_EXTRA = "com.kabouzeid.gramophone.SETTING_GAPLESS_PLAYBACK_CHANGED_VALUE_EXTRA";
|
public static final String SETTING_GAPLESS_PLAYBACK_CHANGED_VALUE_EXTRA = "com.kabouzeid.gramophone.SETTING_GAPLESS_PLAYBACK_CHANGED_VALUE_EXTRA";
|
||||||
|
|
||||||
|
public static final String SAVED_POSITION = "SAVED_POSITION";
|
||||||
|
public static final String SAVED_POSITION_IN_TRACK = "SAVED_POSITION_IN_TRACK";
|
||||||
|
|
||||||
private static final int FOCUS_CHANGE = 5;
|
private static final int FOCUS_CHANGE = 5;
|
||||||
private static final int DUCK = 6;
|
private static final int DUCK = 6;
|
||||||
private static final int UNDUCK = 7;
|
private static final int UNDUCK = 7;
|
||||||
|
|
@ -82,6 +83,7 @@ public class MusicService extends Service {
|
||||||
public static final int TRACK_ENDED = 11;
|
public static final int TRACK_ENDED = 11;
|
||||||
public static final int TRACK_WENT_TO_NEXT = 12;
|
public static final int TRACK_WENT_TO_NEXT = 12;
|
||||||
public static final int PLAY_SONG = 13;
|
public static final int PLAY_SONG = 13;
|
||||||
|
public static final int SAVE_QUEUES = 14;
|
||||||
|
|
||||||
public static final int SHUFFLE_MODE_NONE = 0;
|
public static final int SHUFFLE_MODE_NONE = 0;
|
||||||
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
public static final int SHUFFLE_MODE_SHUFFLE = 1;
|
||||||
|
|
@ -89,7 +91,6 @@ public class MusicService extends Service {
|
||||||
public static final int REPEAT_MODE_ALL = 1;
|
public static final int REPEAT_MODE_ALL = 1;
|
||||||
public static final int REPEAT_MODE_THIS = 2;
|
public static final int REPEAT_MODE_THIS = 2;
|
||||||
|
|
||||||
private static final String TAG = MusicService.class.getSimpleName();
|
|
||||||
private final IBinder musicBind = new MusicBinder();
|
private final IBinder musicBind = new MusicBinder();
|
||||||
|
|
||||||
private MultiPlayer player;
|
private MultiPlayer player;
|
||||||
|
|
@ -101,8 +102,6 @@ public class MusicService extends Service {
|
||||||
private int repeatMode;
|
private int repeatMode;
|
||||||
private boolean pausedByTransientLossOfFocus;
|
private boolean pausedByTransientLossOfFocus;
|
||||||
private boolean receiversAndRemoteControlClientRegistered;
|
private boolean receiversAndRemoteControlClientRegistered;
|
||||||
private boolean saveQueuesAgain;
|
|
||||||
private boolean isSavingQueues;
|
|
||||||
private PlayingNotificationHelper playingNotificationHelper;
|
private PlayingNotificationHelper playingNotificationHelper;
|
||||||
private AudioManager audioManager;
|
private AudioManager audioManager;
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
|
@ -110,10 +109,13 @@ public class MusicService extends Service {
|
||||||
private PowerManager.WakeLock wakeLock;
|
private PowerManager.WakeLock wakeLock;
|
||||||
private String currentAlbumArtUri;
|
private String currentAlbumArtUri;
|
||||||
private MusicPlayerHandler playerHandler;
|
private MusicPlayerHandler playerHandler;
|
||||||
|
private QueueSaveHandler queueSaveHandler;
|
||||||
private boolean isFadingDown = false;
|
private boolean isFadingDown = false;
|
||||||
private HandlerThread handlerThread;
|
private HandlerThread musicPlayerHandlerThread;
|
||||||
|
private HandlerThread queueSaveHandlerThread;
|
||||||
private RecentlyPlayedStore recentlyPlayedStore;
|
private RecentlyPlayedStore recentlyPlayedStore;
|
||||||
private SongPlayCountStore songPlayCountStore;
|
private SongPlayCountStore songPlayCountStore;
|
||||||
|
private boolean notNotifiedMetaChangedForCurrentTrack;
|
||||||
|
|
||||||
private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -159,10 +161,15 @@ public class MusicService extends Service {
|
||||||
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
|
||||||
wakeLock.setReferenceCounted(false);
|
wakeLock.setReferenceCounted(false);
|
||||||
|
|
||||||
handlerThread = new HandlerThread("MusicPlayerHandler",
|
musicPlayerHandlerThread = new HandlerThread("MusicPlayerHandler",
|
||||||
Process.THREAD_PRIORITY_BACKGROUND);
|
Process.THREAD_PRIORITY_BACKGROUND);
|
||||||
handlerThread.start();
|
musicPlayerHandlerThread.start();
|
||||||
playerHandler = new MusicPlayerHandler(this, handlerThread.getLooper());
|
playerHandler = new MusicPlayerHandler(this, musicPlayerHandlerThread.getLooper());
|
||||||
|
|
||||||
|
queueSaveHandlerThread = new HandlerThread("QueueSaveHandler",
|
||||||
|
Process.THREAD_PRIORITY_BACKGROUND);
|
||||||
|
queueSaveHandlerThread.start();
|
||||||
|
queueSaveHandler = new QueueSaveHandler(this, queueSaveHandlerThread.getLooper());
|
||||||
|
|
||||||
player = new MultiPlayer(this);
|
player = new MultiPlayer(this);
|
||||||
player.setHandler(playerHandler);
|
player.setHandler(playerHandler);
|
||||||
|
|
@ -236,7 +243,7 @@ public class MusicService extends Service {
|
||||||
stop();
|
stop();
|
||||||
break;
|
break;
|
||||||
case ACTION_QUIT:
|
case ACTION_QUIT:
|
||||||
saveAndQuit();
|
quit();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +253,7 @@ public class MusicService extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
saveAndQuit();
|
quit();
|
||||||
releaseResources();
|
releaseResources();
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
}
|
}
|
||||||
|
|
@ -268,22 +275,26 @@ public class MusicService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveAndQuit() {
|
private void quit() {
|
||||||
unregisterReceiversAndRemoteControlClient();
|
unregisterReceiversAndRemoteControlClient();
|
||||||
closeAudioEffectSession();
|
closeAudioEffectSession();
|
||||||
stop();
|
stop();
|
||||||
playingNotificationHelper.killNotification();
|
playingNotificationHelper.killNotification();
|
||||||
savePosition();
|
|
||||||
saveQueuesImpl();
|
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseResources() {
|
private void releaseResources() {
|
||||||
playerHandler.removeCallbacksAndMessages(null);
|
playerHandler.removeCallbacksAndMessages(null);
|
||||||
if (Build.VERSION.SDK_INT >= 18) {
|
if (Build.VERSION.SDK_INT >= 18) {
|
||||||
handlerThread.quitSafely();
|
musicPlayerHandlerThread.quitSafely();
|
||||||
} else {
|
} else {
|
||||||
handlerThread.quit();
|
musicPlayerHandlerThread.quit();
|
||||||
|
}
|
||||||
|
queueSaveHandler.removeCallbacksAndMessages(null);
|
||||||
|
if (Build.VERSION.SDK_INT >= 18) {
|
||||||
|
queueSaveHandlerThread.quitSafely();
|
||||||
|
} else {
|
||||||
|
queueSaveHandlerThread.quit();
|
||||||
}
|
}
|
||||||
player.release();
|
player.release();
|
||||||
player = null;
|
player = null;
|
||||||
|
|
@ -291,8 +302,9 @@ public class MusicService extends Service {
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
pausedByTransientLossOfFocus = false;
|
pausedByTransientLossOfFocus = false;
|
||||||
|
savePositionInTrack();
|
||||||
player.stop();
|
player.stop();
|
||||||
notifyChange(PLAYSTATE_CHANGED);
|
notifyChange(PLAY_STATE_CHANGED);
|
||||||
getAudioManager().abandonAudioFocus(audioFocusListener);
|
getAudioManager().abandonAudioFocus(audioFocusListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,26 +312,27 @@ public class MusicService extends Service {
|
||||||
return player.isPlaying() && !isFadingDown;
|
return player.isPlaying() && !isFadingDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveQueuesImpl() {
|
public void saveState() {
|
||||||
try {
|
saveQueues();
|
||||||
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_PLAYING_QUEUE, playingQueue);
|
savePosition();
|
||||||
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_ORIGINAL_PLAYING_QUEUE, originalPlayingQueue);
|
savePositionInTrack();
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "error while saving music service queue state", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void savePosition() {
|
private void saveQueues() {
|
||||||
new Thread(new Runnable() {
|
queueSaveHandler.removeMessages(SAVE_QUEUES);
|
||||||
@Override
|
queueSaveHandler.sendEmptyMessage(SAVE_QUEUES);
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
InternalStorageUtil.writeObject(MusicService.this, AppKeys.IS_POSITION_IN_QUEUE, getPosition());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "error while saving music service position state", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveQueuesImpl() {
|
||||||
|
MusicPlaybackQueueStore.getInstance(this).saveQueues(playingQueue, originalPlayingQueue);
|
||||||
}
|
}
|
||||||
}).start();
|
|
||||||
|
private void savePosition() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION, getPosition()).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePositionInTrack() {
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).edit().putInt(SAVED_POSITION_IN_TRACK, getSongProgressMillis()).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPosition() {
|
public int getPosition() {
|
||||||
|
|
@ -340,6 +353,7 @@ public class MusicService extends Service {
|
||||||
boolean prepared = openCurrent();
|
boolean prepared = openCurrent();
|
||||||
if (prepared) prepareNext();
|
if (prepared) prepareNext();
|
||||||
notifyChange(META_CHANGED);
|
notifyChange(META_CHANGED);
|
||||||
|
notNotifiedMetaChangedForCurrentTrack = false;
|
||||||
return prepared;
|
return prepared;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -494,7 +508,7 @@ public class MusicService extends Service {
|
||||||
.putInt(AppKeys.SP_REPEAT_MODE, repeatMode)
|
.putInt(AppKeys.SP_REPEAT_MODE, repeatMode)
|
||||||
.apply();
|
.apply();
|
||||||
prepareNext();
|
prepareNext();
|
||||||
notifyChange(REPEATMODE_CHANGED);
|
notifyChange(REPEAT_MODE_CHANGED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -516,45 +530,23 @@ public class MusicService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveState() {
|
|
||||||
saveQueuesAsync();
|
|
||||||
savePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveQueuesAsync() {
|
|
||||||
if (isSavingQueues) {
|
|
||||||
saveQueuesAgain = true;
|
|
||||||
} else {
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
isSavingQueues = true;
|
|
||||||
do {
|
|
||||||
saveQueuesAgain = false;
|
|
||||||
saveQueuesImpl();
|
|
||||||
} while (saveQueuesAgain);
|
|
||||||
isSavingQueues = false;
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreQueueAndPosition() {
|
private void restoreQueueAndPosition() {
|
||||||
try {
|
ArrayList<Song> restoredQueue = MusicPlaybackQueueStore.getInstance(this).getSavedPlayingQueue();
|
||||||
@SuppressWarnings("unchecked")
|
ArrayList<Song> restoredOriginalQueue = MusicPlaybackQueueStore.getInstance(this).getSavedOriginalPlayingQueue();
|
||||||
ArrayList<Song> restoredQueue = (ArrayList<Song>) InternalStorageUtil.readObject(this, AppKeys.IS_PLAYING_QUEUE);
|
int restoredPosition = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION, -1);
|
||||||
@SuppressWarnings("unchecked")
|
int restoredPositionInTrack = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_POSITION_IN_TRACK, -1);
|
||||||
ArrayList<Song> restoredOriginalQueue = (ArrayList<Song>) InternalStorageUtil.readObject(this, AppKeys.IS_ORIGINAL_PLAYING_QUEUE);
|
|
||||||
int restoredPosition = (int) InternalStorageUtil.readObject(this, AppKeys.IS_POSITION_IN_QUEUE);
|
|
||||||
|
|
||||||
|
if (restoredQueue != null && restoredOriginalQueue != null && restoredPosition != -1) {
|
||||||
this.originalPlayingQueue = restoredOriginalQueue;
|
this.originalPlayingQueue = restoredOriginalQueue;
|
||||||
this.playingQueue = restoredQueue;
|
this.playingQueue = restoredQueue;
|
||||||
|
|
||||||
setPosition(restoredPosition);
|
setPosition(restoredPosition);
|
||||||
openCurrent();
|
openCurrent();
|
||||||
prepareNext();
|
prepareNext();
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
if (restoredPositionInTrack > 0) seek(restoredPositionInTrack);
|
||||||
|
|
||||||
|
notNotifiedMetaChangedForCurrentTrack = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -570,12 +562,6 @@ public class MusicService extends Service {
|
||||||
saveState();
|
saveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void addSongs(int position, List<Song> songs) {
|
|
||||||
// playingQueue.addAll(position, songs);
|
|
||||||
// originalPlayingQueue.addAll(position, songs);
|
|
||||||
// saveState();
|
|
||||||
// }
|
|
||||||
|
|
||||||
public void addSongs(List<Song> songs) {
|
public void addSongs(List<Song> songs) {
|
||||||
playingQueue.addAll(songs);
|
playingQueue.addAll(songs);
|
||||||
originalPlayingQueue.addAll(songs);
|
originalPlayingQueue.addAll(songs);
|
||||||
|
|
@ -649,7 +635,7 @@ public class MusicService extends Service {
|
||||||
playerHandler.removeMessages(FADE_UP_AND_RESUME);
|
playerHandler.removeMessages(FADE_UP_AND_RESUME);
|
||||||
if (player.isPlaying()) {
|
if (player.isPlaying()) {
|
||||||
player.pause();
|
player.pause();
|
||||||
notifyChange(PLAYSTATE_CHANGED);
|
notifyChange(PLAY_STATE_CHANGED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -676,7 +662,11 @@ public class MusicService extends Service {
|
||||||
} else {
|
} else {
|
||||||
registerReceiversAndRemoteControlClient();
|
registerReceiversAndRemoteControlClient();
|
||||||
player.start();
|
player.start();
|
||||||
notifyChange(PLAYSTATE_CHANGED);
|
notifyChange(PLAY_STATE_CHANGED);
|
||||||
|
if (notNotifiedMetaChangedForCurrentTrack) {
|
||||||
|
notifyChange(META_CHANGED);
|
||||||
|
notNotifiedMetaChangedForCurrentTrack = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -734,6 +724,7 @@ public class MusicService extends Service {
|
||||||
|
|
||||||
public void seek(int millis) {
|
public void seek(int millis) {
|
||||||
player.seek(millis);
|
player.seek(millis);
|
||||||
|
savePositionInTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cycleRepeatMode() {
|
public void cycleRepeatMode() {
|
||||||
|
|
@ -786,7 +777,7 @@ public class MusicService extends Service {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
prepareNext();
|
prepareNext();
|
||||||
notifyChange(SHUFFLEMODE_CHANGED);
|
notifyChange(SHUFFLE_MODE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyChange(final String what) {
|
private void notifyChange(final String what) {
|
||||||
|
|
@ -807,16 +798,21 @@ public class MusicService extends Service {
|
||||||
publicMusicIntent.setAction(what.replace(PHONOGRAPH_PACKAGE_NAME, MUSIC_PACKAGE_NAME));
|
publicMusicIntent.setAction(what.replace(PHONOGRAPH_PACKAGE_NAME, MUSIC_PACKAGE_NAME));
|
||||||
sendStickyBroadcast(publicMusicIntent);
|
sendStickyBroadcast(publicMusicIntent);
|
||||||
|
|
||||||
if (what.equals(PLAYSTATE_CHANGED)) {
|
if (what.equals(PLAY_STATE_CHANGED)) {
|
||||||
final boolean isPlaying = isPlayingAndNotFadingDown();
|
final boolean isPlaying = isPlayingAndNotFadingDown();
|
||||||
playingNotificationHelper.updatePlayState(isPlaying);
|
playingNotificationHelper.updatePlayState(isPlaying);
|
||||||
MusicPlayerWidget.updateWidgetsPlayState(this, isPlaying);
|
MusicPlayerWidget.updateWidgetsPlayState(this, isPlaying);
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
remoteControlClient.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
|
remoteControlClient.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
|
||||||
|
if (!isPlaying && getSongProgressMillis() > 0) {
|
||||||
|
savePositionInTrack();
|
||||||
|
}
|
||||||
} else if (what.equals(META_CHANGED)) {
|
} else if (what.equals(META_CHANGED)) {
|
||||||
updateNotification();
|
updateNotification();
|
||||||
updateWidgets();
|
updateWidgets();
|
||||||
updateRemoteControlClient();
|
updateRemoteControlClient();
|
||||||
|
savePosition();
|
||||||
|
savePositionInTrack();
|
||||||
recentlyPlayedStore.addSongId(currentSong.id);
|
recentlyPlayedStore.addSongId(currentSong.id);
|
||||||
songPlayCountStore.bumpSongCount(currentSong.id);
|
songPlayCountStore.bumpSongCount(currentSong.id);
|
||||||
}
|
}
|
||||||
|
|
@ -850,6 +846,25 @@ public class MusicService extends Service {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class QueueSaveHandler extends Handler {
|
||||||
|
private final WeakReference<MusicService> mService;
|
||||||
|
|
||||||
|
public QueueSaveHandler(final MusicService service, final Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
mService = new WeakReference<>(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
final MusicService service = mService.get();
|
||||||
|
switch (msg.what) {
|
||||||
|
case SAVE_QUEUES:
|
||||||
|
service.saveQueuesImpl();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class MusicPlayerHandler extends Handler {
|
private static final class MusicPlayerHandler extends Handler {
|
||||||
private final WeakReference<MusicService> mService;
|
private final WeakReference<MusicService> mService;
|
||||||
private float currentDuckVolume = 1.0f;
|
private float currentDuckVolume = 1.0f;
|
||||||
|
|
@ -891,7 +906,7 @@ public class MusicService extends Service {
|
||||||
case FADE_DOWN_AND_PAUSE:
|
case FADE_DOWN_AND_PAUSE:
|
||||||
if (!service.isFadingDown) {
|
if (!service.isFadingDown) {
|
||||||
service.isFadingDown = true;
|
service.isFadingDown = true;
|
||||||
service.notifyChange(PLAYSTATE_CHANGED);
|
service.notifyChange(PLAY_STATE_CHANGED);
|
||||||
}
|
}
|
||||||
currentPlayPauseFadeVolume -= .125f;
|
currentPlayPauseFadeVolume -= .125f;
|
||||||
if (currentPlayPauseFadeVolume > 0f) {
|
if (currentPlayPauseFadeVolume > 0f) {
|
||||||
|
|
@ -907,7 +922,7 @@ public class MusicService extends Service {
|
||||||
case FADE_UP_AND_RESUME:
|
case FADE_UP_AND_RESUME:
|
||||||
if (service.isFadingDown) {
|
if (service.isFadingDown) {
|
||||||
service.isFadingDown = false;
|
service.isFadingDown = false;
|
||||||
service.notifyChange(PLAYSTATE_CHANGED);
|
service.notifyChange(PLAY_STATE_CHANGED);
|
||||||
}
|
}
|
||||||
service.playImpl();
|
service.playImpl();
|
||||||
currentPlayPauseFadeVolume += .125f;
|
currentPlayPauseFadeVolume += .125f;
|
||||||
|
|
@ -935,7 +950,7 @@ public class MusicService extends Service {
|
||||||
|
|
||||||
case TRACK_ENDED:
|
case TRACK_ENDED:
|
||||||
if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
|
if (service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) {
|
||||||
service.notifyChange(PLAYSTATE_CHANGED);
|
service.notifyChange(PLAY_STATE_CHANGED);
|
||||||
service.seek(0);
|
service.seek(0);
|
||||||
} else {
|
} else {
|
||||||
service.playNextSong(false);
|
service.playNextSong(false);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import com.crashlytics.android.Crashlytics;
|
||||||
import com.kabouzeid.gramophone.App;
|
import com.kabouzeid.gramophone.App;
|
||||||
import com.kabouzeid.gramophone.BuildConfig;
|
import com.kabouzeid.gramophone.BuildConfig;
|
||||||
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
|
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
|
||||||
import com.kabouzeid.gramophone.misc.AppKeys;
|
|
||||||
import com.kabouzeid.gramophone.model.UIPreferenceChangedEvent;
|
import com.kabouzeid.gramophone.model.UIPreferenceChangedEvent;
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
|
@ -26,7 +25,7 @@ public abstract class AbsBaseActivity extends AbsThemeActivity implements KabVie
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
if (!BuildConfig.DEBUG) Crashlytics.setString(AppKeys.CL_CURRENT_ACTIVITY, getTag());
|
if (!BuildConfig.DEBUG) Crashlytics.setString("Current activity", getTag());
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
try {
|
try {
|
||||||
App.bus.register(uiPreferenceChangeListener);
|
App.bus.register(uiPreferenceChangeListener);
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,9 @@ public abstract class AbsPlaybackStatusActivity extends AbsBaseActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
final IntentFilter filter = new IntentFilter();
|
final IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(MusicService.PLAYSTATE_CHANGED);
|
filter.addAction(MusicService.PLAY_STATE_CHANGED);
|
||||||
filter.addAction(MusicService.SHUFFLEMODE_CHANGED);
|
filter.addAction(MusicService.SHUFFLE_MODE_CHANGED);
|
||||||
filter.addAction(MusicService.REPEATMODE_CHANGED);
|
filter.addAction(MusicService.REPEAT_MODE_CHANGED);
|
||||||
filter.addAction(MusicService.META_CHANGED);
|
filter.addAction(MusicService.META_CHANGED);
|
||||||
filter.addAction(MusicPlayerRemote.SERVICE_BOUND);
|
filter.addAction(MusicPlayerRemote.SERVICE_BOUND);
|
||||||
|
|
||||||
|
|
@ -81,13 +81,13 @@ public abstract class AbsPlaybackStatusActivity extends AbsBaseActivity {
|
||||||
case MusicService.META_CHANGED:
|
case MusicService.META_CHANGED:
|
||||||
reference.get().onPlayingMetaChanged();
|
reference.get().onPlayingMetaChanged();
|
||||||
break;
|
break;
|
||||||
case MusicService.PLAYSTATE_CHANGED:
|
case MusicService.PLAY_STATE_CHANGED:
|
||||||
reference.get().onPlayStateChanged();
|
reference.get().onPlayStateChanged();
|
||||||
break;
|
break;
|
||||||
case MusicService.REPEATMODE_CHANGED:
|
case MusicService.REPEAT_MODE_CHANGED:
|
||||||
reference.get().onRepeatModeChanged();
|
reference.get().onRepeatModeChanged();
|
||||||
break;
|
break;
|
||||||
case MusicService.SHUFFLEMODE_CHANGED:
|
case MusicService.SHUFFLE_MODE_CHANGED:
|
||||||
reference.get().onShuffleModeChanged();
|
reference.get().onShuffleModeChanged();
|
||||||
break;
|
break;
|
||||||
case MusicPlayerRemote.SERVICE_BOUND:
|
case MusicPlayerRemote.SERVICE_BOUND:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue