Recently played is now history. Songs are always added to the history but their play count will be only increased if they were played at least half. This makes the top tracks much more accurate.
This commit is contained in:
parent
29f91b6969
commit
cf4ed6d5c0
10 changed files with 163 additions and 31 deletions
|
|
@ -29,6 +29,13 @@
|
|||
|
||||
<ol>
|
||||
<li><b>NEW:</b> Added a drag view to playlist songs for easier rearrangement.</li>
|
||||
<li><b>IMPROVEMENT:</b> Replaced recently played with history. Songs are always added to the
|
||||
history
|
||||
once they are started but their
|
||||
play count will be only increased if they were played at least half. This makes the top
|
||||
tracks playlist much more accurate.
|
||||
</li>
|
||||
<li><b>IMPROVEMENT:</b> Synced translations.</li>
|
||||
<li><b>FIX:</b> Cab (contextual action bar) content not visible with light primary colors.</li>
|
||||
</ol>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
package com.kabouzeid.gramophone.helper;
|
||||
|
||||
/**
|
||||
* Simple thread safe stop watch.
|
||||
*
|
||||
* @author Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
public class StopWatch {
|
||||
|
||||
/**
|
||||
* The time the stop watch was last started.
|
||||
*/
|
||||
private long startTime;
|
||||
|
||||
/**
|
||||
* The time elapsed before the current {@link #startTime}.
|
||||
*/
|
||||
private long previousElapsedTime;
|
||||
|
||||
/**
|
||||
* Whether the stop watch is currently running or not.
|
||||
*/
|
||||
private boolean isRunning;
|
||||
|
||||
/**
|
||||
* Starts or continues the stop watch.
|
||||
*
|
||||
* @see #pause()
|
||||
* @see #reset()
|
||||
*/
|
||||
public void start() {
|
||||
synchronized (this) {
|
||||
startTime = System.currentTimeMillis();
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the stop watch. It can be continued later from {@link #start()}.
|
||||
*
|
||||
* @see #start()
|
||||
* @see #reset()
|
||||
*/
|
||||
public void pause() {
|
||||
synchronized (this) {
|
||||
previousElapsedTime += System.currentTimeMillis() - startTime;
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and resets the stop watch to zero milliseconds.
|
||||
*
|
||||
* @see #start()
|
||||
* @see #pause()
|
||||
*/
|
||||
public void reset() {
|
||||
synchronized (this) {
|
||||
startTime = 0;
|
||||
previousElapsedTime = 0;
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the total elapsed time in milliseconds
|
||||
*/
|
||||
public final long getElapsedTime() {
|
||||
synchronized (this) {
|
||||
long currentElapsedTime = 0;
|
||||
if (isRunning) {
|
||||
currentElapsedTime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
return previousElapsedTime + currentElapsedTime;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%d millis", getElapsedTime());
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import android.support.annotation.NonNull;
|
|||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.kabouzeid.gramophone.model.Song;
|
||||
import com.kabouzeid.gramophone.provider.RecentlyPlayedStore;
|
||||
import com.kabouzeid.gramophone.provider.HistoryStore;
|
||||
import com.kabouzeid.gramophone.provider.SongPlayCountStore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -50,7 +50,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
ArrayList<Long> missingIds = retCursor.getMissingIds();
|
||||
if (missingIds != null && missingIds.size() > 0) {
|
||||
for (long id : missingIds) {
|
||||
RecentlyPlayedStore.getInstance(context).removeSongId(id);
|
||||
HistoryStore.getInstance(context).removeSongId(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,11 +76,11 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
@Nullable
|
||||
private static SortedCursor makeRecentTracksCursorImpl(@NonNull final Context context) {
|
||||
// first get the top results ids from the internal database
|
||||
Cursor songs = RecentlyPlayedStore.getInstance(context).queryRecentIds();
|
||||
Cursor songs = HistoryStore.getInstance(context).queryRecentIds();
|
||||
|
||||
try {
|
||||
return makeSortedCursor(context, songs,
|
||||
songs.getColumnIndex(RecentlyPlayedStore.RecentStoreColumns.ID));
|
||||
songs.getColumnIndex(HistoryStore.RecentStoreColumns.ID));
|
||||
} finally {
|
||||
if (songs != null) {
|
||||
songs.close();
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@ import android.support.annotation.NonNull;
|
|||
import com.kabouzeid.gramophone.R;
|
||||
import com.kabouzeid.gramophone.loader.TopAndRecentlyPlayedTracksLoader;
|
||||
import com.kabouzeid.gramophone.model.Song;
|
||||
import com.kabouzeid.gramophone.provider.RecentlyPlayedStore;
|
||||
import com.kabouzeid.gramophone.provider.HistoryStore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
public class RecentlyPlayedPlaylist extends AbsSmartPlaylist {
|
||||
public class HistoryPlaylist extends AbsSmartPlaylist {
|
||||
|
||||
public RecentlyPlayedPlaylist(@NonNull Context context) {
|
||||
super(context.getString(R.string.recently_played), R.drawable.ic_access_time_white_24dp);
|
||||
public HistoryPlaylist(@NonNull Context context) {
|
||||
super(context.getString(R.string.history), R.drawable.ic_access_time_white_24dp);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
|
@ -27,6 +27,6 @@ public class RecentlyPlayedPlaylist extends AbsSmartPlaylist {
|
|||
|
||||
@Override
|
||||
public void clear(@NonNull Context context) {
|
||||
RecentlyPlayedStore.getInstance(context).clear();
|
||||
HistoryStore.getInstance(context).clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -24,15 +24,15 @@ import android.database.sqlite.SQLiteOpenHelper;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
public class RecentlyPlayedStore extends SQLiteOpenHelper {
|
||||
public class HistoryStore extends SQLiteOpenHelper {
|
||||
private static final int MAX_ITEMS_IN_DB = 100;
|
||||
|
||||
public static final String DATABASE_NAME = "recently_played.db";
|
||||
public static final String DATABASE_NAME = "history.db";
|
||||
private static final int VERSION = 1;
|
||||
@Nullable
|
||||
private static RecentlyPlayedStore sInstance = null;
|
||||
private static HistoryStore sInstance = null;
|
||||
|
||||
public RecentlyPlayedStore(final Context context) {
|
||||
public HistoryStore(final Context context) {
|
||||
super(context, DATABASE_NAME, null, VERSION);
|
||||
}
|
||||
|
||||
|
|
@ -56,18 +56,23 @@ public class RecentlyPlayedStore extends SQLiteOpenHelper {
|
|||
}
|
||||
|
||||
@NonNull
|
||||
public static synchronized RecentlyPlayedStore getInstance(@NonNull final Context context) {
|
||||
public static synchronized HistoryStore getInstance(@NonNull final Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new RecentlyPlayedStore(context.getApplicationContext());
|
||||
sInstance = new HistoryStore(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void addSongId(final long songId) {
|
||||
if (songId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final SQLiteDatabase database = getWritableDatabase();
|
||||
database.beginTransaction();
|
||||
|
||||
try {
|
||||
// remove previous entries
|
||||
removeSongId(songId);
|
||||
|
||||
// add the entry
|
||||
|
|
@ -35,7 +35,7 @@ public class SongPlayCountStore extends SQLiteOpenHelper {
|
|||
private static SongPlayCountStore sInstance = null;
|
||||
|
||||
public static final String DATABASE_NAME = "song_play_count.db";
|
||||
private static final int VERSION = 1;
|
||||
private static final int VERSION = 2;
|
||||
|
||||
// interpolator curve applied for measuring the curve
|
||||
@NonNull
|
||||
|
|
@ -129,8 +129,8 @@ public class SongPlayCountStore extends SQLiteOpenHelper {
|
|||
*
|
||||
* @param songId The song id to increase the play count
|
||||
*/
|
||||
public void bumpSongCount(final long songId) {
|
||||
if (songId < 0) {
|
||||
public void bumpPlayCount(final long songId) {
|
||||
if (songId == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,10 @@ import com.kabouzeid.gramophone.R;
|
|||
import com.kabouzeid.gramophone.appwidget.WidgetMedium;
|
||||
import com.kabouzeid.gramophone.helper.PlayingNotificationHelper;
|
||||
import com.kabouzeid.gramophone.helper.ShuffleHelper;
|
||||
import com.kabouzeid.gramophone.helper.StopWatch;
|
||||
import com.kabouzeid.gramophone.model.Song;
|
||||
import com.kabouzeid.gramophone.provider.HistoryStore;
|
||||
import com.kabouzeid.gramophone.provider.MusicPlaybackQueueStore;
|
||||
import com.kabouzeid.gramophone.provider.RecentlyPlayedStore;
|
||||
import com.kabouzeid.gramophone.provider.SongPlayCountStore;
|
||||
import com.kabouzeid.gramophone.util.MusicUtil;
|
||||
import com.kabouzeid.gramophone.util.PreferenceUtil;
|
||||
|
|
@ -101,8 +102,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
private final IBinder musicBind = new MusicBinder();
|
||||
|
||||
private MultiPlayer player;
|
||||
private ArrayList<Song> playingQueue;
|
||||
private ArrayList<Song> originalPlayingQueue;
|
||||
private ArrayList<Song> playingQueue = new ArrayList<>();
|
||||
private ArrayList<Song> originalPlayingQueue = new ArrayList<>();
|
||||
private int position = -1;
|
||||
private int nextPosition = -1;
|
||||
private int shuffleMode;
|
||||
|
|
@ -124,8 +125,9 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
private QueueSaveHandler queueSaveHandler;
|
||||
private HandlerThread musicPlayerHandlerThread;
|
||||
private HandlerThread queueSaveHandlerThread;
|
||||
private RecentlyPlayedStore recentlyPlayedStore;
|
||||
private HistoryStore historyStore;
|
||||
private SongPlayCountStore songPlayCountStore;
|
||||
private SongPlayCountHelper songPlayCountHelper = new SongPlayCountHelper();
|
||||
private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, @NonNull Intent intent) {
|
||||
|
|
@ -145,12 +147,10 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
playingQueue = new ArrayList<>();
|
||||
originalPlayingQueue = new ArrayList<>();
|
||||
|
||||
playingNotificationHelper = new PlayingNotificationHelper(this);
|
||||
|
||||
recentlyPlayedStore = RecentlyPlayedStore.getInstance(this);
|
||||
historyStore = HistoryStore.getInstance(this);
|
||||
songPlayCountStore = SongPlayCountStore.getInstance(this);
|
||||
|
||||
shuffleMode = PreferenceManager.getDefaultSharedPreferences(this).getInt(SAVED_SHUFFLE_MODE, 0);
|
||||
|
|
@ -809,8 +809,8 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
}
|
||||
|
||||
private void notifyChange(@NonNull final String what) {
|
||||
sendChangeIntent(what);
|
||||
handleChange(what);
|
||||
sendChangeIntent(what);
|
||||
}
|
||||
|
||||
private void sendChangeIntent(@NonNull final String what) {
|
||||
|
|
@ -844,6 +844,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
if (!isPlaying && getSongProgressMillis() > 0) {
|
||||
savePositionInTrack();
|
||||
}
|
||||
songPlayCountHelper.notifyPlayStateChanged(isPlaying);
|
||||
break;
|
||||
case META_CHANGED:
|
||||
updateNotification();
|
||||
|
|
@ -852,8 +853,11 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
savePosition();
|
||||
savePositionInTrack();
|
||||
final Song currentSong = getCurrentSong();
|
||||
recentlyPlayedStore.addSongId(currentSong.id);
|
||||
songPlayCountStore.bumpSongCount(currentSong.id);
|
||||
historyStore.addSongId(currentSong.id);
|
||||
if (songPlayCountHelper.shouldBumpPlayCount()) {
|
||||
songPlayCountStore.bumpPlayCount(songPlayCountHelper.getSong().id);
|
||||
}
|
||||
songPlayCountHelper.notifySongChanged(currentSong);
|
||||
break;
|
||||
case QUEUE_CHANGED:
|
||||
saveState();
|
||||
|
|
@ -1057,4 +1061,36 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
|||
sendBroadcast(new Intent(MEDIA_STORE_CHANGED));
|
||||
}
|
||||
}
|
||||
|
||||
private static class SongPlayCountHelper {
|
||||
public static final String TAG = SongPlayCountHelper.class.getSimpleName();
|
||||
|
||||
private StopWatch stopWatch = new StopWatch();
|
||||
private Song song = new Song();
|
||||
|
||||
public Song getSong() {
|
||||
return song;
|
||||
}
|
||||
|
||||
boolean shouldBumpPlayCount() {
|
||||
return song.duration * 0.5d < stopWatch.getElapsedTime();
|
||||
}
|
||||
|
||||
void notifySongChanged(Song song) {
|
||||
synchronized (this) {
|
||||
stopWatch.reset();
|
||||
this.song = song;
|
||||
}
|
||||
}
|
||||
|
||||
void notifyPlayStateChanged(boolean isPlaying) {
|
||||
synchronized (this) {
|
||||
if (isPlaying) {
|
||||
stopWatch.start();
|
||||
} else {
|
||||
stopWatch.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.kabouzeid.gramophone.ui.activities.base;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
|
@ -204,6 +205,7 @@ public abstract class AbsMusicServiceActivity extends AbsBaseActivity implements
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private boolean hasExternalStoragePermission() {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import com.kabouzeid.gramophone.R;
|
|||
import com.kabouzeid.gramophone.adapter.PlaylistAdapter;
|
||||
import com.kabouzeid.gramophone.loader.PlaylistLoader;
|
||||
import com.kabouzeid.gramophone.model.Playlist;
|
||||
import com.kabouzeid.gramophone.model.smartplaylist.HistoryPlaylist;
|
||||
import com.kabouzeid.gramophone.model.smartplaylist.LastAddedPlaylist;
|
||||
import com.kabouzeid.gramophone.model.smartplaylist.MyTopTracksPlaylist;
|
||||
import com.kabouzeid.gramophone.model.smartplaylist.RecentlyPlayedPlaylist;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ public class PlaylistViewFragment extends AbsMainActivityRecyclerViewFragment<Pl
|
|||
ArrayList<Playlist> playlists = new ArrayList<>();
|
||||
|
||||
playlists.add(new LastAddedPlaylist(getActivity()));
|
||||
playlists.add(new RecentlyPlayedPlaylist(getActivity()));
|
||||
playlists.add(new HistoryPlaylist(getActivity()));
|
||||
playlists.add(new MyTopTracksPlaylist(getActivity()));
|
||||
|
||||
playlists.addAll(PlaylistLoader.getAllPlaylists(getActivity()));
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@
|
|||
<string name="rescanning_media">Rescanning media…</string>
|
||||
<string name="favorites">Favorites</string>
|
||||
<string name="last_added">Last added</string>
|
||||
<string name="recently_played">Recently played</string>
|
||||
<string name="history">History</string>
|
||||
<string name="my_top_tracks">My top tracks</string>
|
||||
<string name="remove_cover">Remove cover</string>
|
||||
<string name="download_from_last_fm">Download from Last.fm</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue