Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #530 Added ability to exclude songs from being included in shuffle random mode #563

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions res/values/translatable.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ THE SOFTWARE.
<string name="play_or_enqueue">Enqueue if playing; Play if paused</string>
<string name="open">Open</string>
<string name="go_home">Home directory</string>
<string name="exclude_from_shuffle">Exclude from shuffle</string>
<string name="include_in_shuffle">Include in shuffle</string>

<plurals name="playing">
<item quantity="one">1 track playing.</item>
Expand Down
103 changes: 99 additions & 4 deletions src/ch/blinkenlights/android/medialibrary/MediaLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@

package ch.blinkenlights.android.medialibrary;

import android.content.Context;
import android.content.ContentValues;
import android.database.Cursor;
import android.content.Context;
import android.database.ContentObserver;
import android.provider.MediaStore;
import android.database.Cursor;
import android.os.Environment;
import android.provider.MediaStore;

import java.util.ArrayList;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import ch.blinkenlights.android.vanilla.MediaUtils;
import ch.blinkenlights.android.vanilla.QueryTask;

public class MediaLibrary {

Expand Down Expand Up @@ -404,6 +408,93 @@ public static void movePlaylistItem(Context context, long from, long to) {
notifyObserver();
}

/**
* Sets the no shuffle flag for all songs with the deignated ids
* @param context caller context
* @param ids the songs ids to set
*/
public static void addToNoShuffle(Context context, List<Long> ids) {
updateNoShuffleColumn(context, ids, true);
}

/**
* Clears the no shuffle flag for all songs with the deignated ids
* @param context caller context
* @param ids the songs ids to set
*/
public static void removeFromNoShuffle(Context context, List<Long> ids) {
updateNoShuffleColumn(context, ids, false);
}

/**
* Perorms an update operation on the song table setting or clearing the no shuffle column
* @param context caller context
* @param ids the ids of thr song rows to update
* @param isNoShuffle true if the no shuffle flag is to be set, false otherwise
*/
private static void updateNoShuffleColumn(Context context, List<Long> ids, boolean isNoShuffle) {
if(null != ids && !ids.isEmpty()) {
ContentValues contentValues = new ContentValues();
contentValues.put(SongColumns.NO_SHUFFLE, isNoShuffle ? 1 : 0);
String idList = buildIdListString(ids);
getBackend(context).update(TABLE_SONGS, contentValues, SongColumns._ID + " IN " + idList, null);
MediaUtils.onMediaChange();
}
}

/**
* Returns a string fo the for (id, id2, id3...)
* @param ids List of ids to process
* @return a string fo the for (id, id2, id3...)
*/
private static String buildIdListString(List<Long> ids) {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (int i = 0; i < ids.size(); i++) {
sb.append(String.valueOf(ids.get(i)));
if (i < ids.size() - 1) {
sb.append(",");
}
}
sb.append(")");
return sb.toString();
}

/**
* Tests if the any of the songs ids in the list have the no shuffle flag set
* @param context the caller context
* @param ids the song ids to test
* @return true id any of the song ids have the no shuffle flag set, false otherwise
*/
public static boolean isNoShuffle(Context context, List<Long> ids) {
String idList = buildIdListString(ids);
Cursor cursor = null;
cursor = getBackend(context).query(true, TABLE_SONGS, new String[] {SongColumns._ID},
SongColumns._ID + " IN " + idList + " AND " + SongColumns.NO_SHUFFLE + " = 1", null,
null, null, null, null);
return cursor != null && cursor.getCount() > 0;
}

/**
* Returns a list of song ids that have the no shuffle flag set
* @param context the caller context
* @return a list of song ids that have the no shuffle flag set
*/
public static List<Long> getNoShuffleSongIDs(Context context) {
List<Long> noShuffleIDs = new ArrayList<>();
QueryTask query = new QueryTask(TABLE_SONGS, new String[]{SongColumns._ID}, SongColumns.NO_SHUFFLE + " = 1" , null, null);
Cursor cursor = query.runQuery(context);
if (cursor != null && cursor.getCount() > 0) {
int noShuffleCount = cursor.getCount();
for (int i = 0; i != noShuffleCount; ++i) {
if (cursor.moveToNext())
noShuffleIDs.add(cursor.getLong(0));
}
cursor.close();
}
return noShuffleIDs;
}

/**
* Returns the number of songs in the music library
*
Expand Down Expand Up @@ -516,6 +607,10 @@ public interface SongColumns {
* The mtime of this item
*/
String MTIME = "mtime";
/**
* Flag to indicate no shuffle
*/
String NO_SHUFFLE = "no_shuffle";
}

// Columns of Album entries
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@

package ch.blinkenlights.android.medialibrary;

import android.content.Context;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.Cursor;
import android.util.Log;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -35,7 +36,7 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
/**
* The database version we are using
*/
private static final int DATABASE_VERSION = 20170217;
private static final int DATABASE_VERSION = 20170311;
/**
* on-disk file to store the database
*/
Expand Down
11 changes: 11 additions & 0 deletions src/ch/blinkenlights/android/medialibrary/MediaSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ public class MediaSchema {
+" LEFT JOIN "+MediaLibrary.TABLE_CONTRIBUTORS+" AS _artist ON _artist."+MediaLibrary.ContributorColumns._ID+" = "+MediaLibrary.TABLE_CONTRIBUTORS_SONGS+"."+MediaLibrary.ContributorSongColumns._CONTRIBUTOR_ID
+" ;";

/**
* Add no shuffle column to songs table
*/
private static final String DATABASE_SONGS_ADD_NO_SHUFFLE_COLUMN = "ALTER TABLE " + MediaLibrary.TABLE_SONGS + " ADD COLUMN " + MediaLibrary.SongColumns.NO_SHUFFLE + " INTEGER";


/**
* Creates a new database schema on dbh
*
Expand All @@ -264,6 +270,7 @@ public static void createDatabaseSchema(SQLiteDatabase dbh) {
dbh.execSQL(VIEW_CREATE_COMPOSERS);
dbh.execSQL(VIEW_CREATE_PLAYLIST_SONGS);
dbh.execSQL(DATABASE_CREATE_PREFERENCES);
dbh.execSQL(DATABASE_SONGS_ADD_NO_SHUFFLE_COLUMN);
}

/**
Expand Down Expand Up @@ -302,6 +309,10 @@ public static void upgradeDatabaseSchema(SQLiteDatabase dbh, int oldVersion) {
dbh.execSQL(VIEW_CREATE_SONGS_ALBUMS_ARTISTS_HUGE);
}

if(oldVersion < 20170311) {
dbh.execSQL(DATABASE_SONGS_ADD_NO_SHUFFLE_COLUMN);
}

}

/**
Expand Down
62 changes: 58 additions & 4 deletions src/ch/blinkenlights/android/vanilla/LibraryActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

package ch.blinkenlights.android.vanilla;

import ch.blinkenlights.android.medialibrary.MediaLibrary;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
Expand All @@ -50,12 +48,16 @@
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.SearchView;
import android.widget.TextView;

import junit.framework.Assert;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import junit.framework.Assert;
import ch.blinkenlights.android.medialibrary.MediaLibrary;

/**
* The library activity where songs to play can be selected from the library.
Expand Down Expand Up @@ -405,6 +407,45 @@ private void expand(Intent intent)
mViewPager.setCurrentItem(tab);
}

/**
* Sets the no shuffle flag for all songs designated by the given intent
* @param intent row data for the parent directory or file
* @return List of song ids processed
*/
private List<Long> handleExcludeFromShuffle(Intent intent) {
List<Long> songIDs = getSongIds(intent);
MediaLibrary.addToNoShuffle(this, songIDs);
return songIDs;
}

/**
* Clears the no shuffle flag for all songs designated by the given intent
* @param intent row data for the parent directory or file
* @return List of song ids processed
*/
private List<Long> handleIncludeInShuffle(Intent intent) {
List<Long> songIDs = getSongIds(intent);
MediaLibrary.removeFromNoShuffle(this, songIDs);
return songIDs;
}

/**
* Returns a list of the songs ids specified by the given intent
* @param intent row data for the parent directory or file
* @return List of song ids processed
*/
private List<Long> getSongIds(Intent intent) {
QueryTask query = buildQueryFromIntent(intent, true, null);
List<Long> ids = new ArrayList<>();
Cursor cursor = query.runQuery(this);
if (cursor != null) {
while (cursor.moveToNext()) {
ids.add(cursor.getLong(0));
}
}
return ids;
}

/**
* Open the playback activity and close any activities above it in the
* stack.
Expand Down Expand Up @@ -583,6 +624,8 @@ private void setLimiter(int limiterType, String selection)
private static final int CTX_MENU_MORE_FROM_ARTIST = 9;
private static final int CTX_MENU_OPEN_EXTERNAL = 10;
private static final int CTX_MENU_PLUGINS = 11;
private static final int CTX_MENU_EXCLUDE_FROM_SHUFFLE = 12;
private static final int CTX_MENU_INCLUDE_IN_SHUFFLE = 13;

/**
* Creates a context menu for an adapter row.
Expand Down Expand Up @@ -623,6 +666,11 @@ public void onCreateContextMenu(ContextMenu menu, Intent rowData)
menu.add(0, CTX_MENU_PLUGINS, 1, R.string.plugins).setIntent(rowData); // last in order
}
menu.addSubMenu(0, CTX_MENU_ADD_TO_PLAYLIST, 0, R.string.add_to_playlist).getItem().setIntent(rowData);
if(type == MediaUtils.TYPE_FILE) {
boolean isNoShuffle = MediaLibrary.isNoShuffle(this, getSongIds(rowData));
menu.add(0, isNoShuffle ? CTX_MENU_INCLUDE_IN_SHUFFLE : CTX_MENU_EXCLUDE_FROM_SHUFFLE, 0,
isNoShuffle ? R.string.include_in_shuffle : R.string.exclude_from_shuffle).setIntent(rowData);
}
menu.add(0, CTX_MENU_DELETE, 0, R.string.delete).setIntent(rowData);
}
}
Expand Down Expand Up @@ -727,6 +775,12 @@ public void onClick(DialogInterface dialog, int id) {
PlaylistDialog plDialog = PlaylistDialog.newInstance(this, intent, (id == LibraryAdapter.HEADER_ID ? (MediaAdapter)mCurrentAdapter : null));
plDialog.show(getFragmentManager(), "PlaylistDialog");
break;
case CTX_MENU_EXCLUDE_FROM_SHUFFLE:
handleExcludeFromShuffle(intent);
break;
case CTX_MENU_INCLUDE_IN_SHUFFLE:
handleIncludeInShuffle(intent);
break;
default:
return super.onContextItemSelected(item);
}
Expand Down
31 changes: 13 additions & 18 deletions src/ch/blinkenlights/android/vanilla/MediaUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@

package ch.blinkenlights.android.vanilla;

import ch.blinkenlights.android.medialibrary.MediaLibrary;
import ch.blinkenlights.android.medialibrary.MediaMetadataExtractor;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.widget.Toast;

import java.io.File;
import java.util.ArrayList;
Expand All @@ -33,21 +40,8 @@
import java.util.Map;
import java.util.Random;

import android.util.Log;

import junit.framework.Assert;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.database.MatrixCursor;
import android.util.Log;
import android.widget.Toast;
import ch.blinkenlights.android.medialibrary.MediaLibrary;
import ch.blinkenlights.android.medialibrary.MediaMetadataExtractor;


/**
Expand Down Expand Up @@ -342,7 +336,8 @@ public static boolean isSongAvailable(Context context) {
* @param context The Context to use
*/
private static long[] queryAllSongs(Context context) {
QueryTask query = new QueryTask(MediaLibrary.TABLE_SONGS, Song.EMPTY_PROJECTION, null, null, null);
QueryTask query = new QueryTask(MediaLibrary.TABLE_SONGS, Song.EMPTY_PROJECTION,
MediaLibrary.SongColumns.NO_SHUFFLE + " != 1 OR " + MediaLibrary.SongColumns.NO_SHUFFLE + " ISNULL", null, null);
Cursor cursor = query.runQuery(context);
if (cursor == null || cursor.getCount() == 0) {
sSongCount = 0;
Expand Down
7 changes: 3 additions & 4 deletions src/ch/blinkenlights/android/vanilla/PlaybackService.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@

package ch.blinkenlights.android.vanilla;

import ch.blinkenlights.android.medialibrary.MediaLibrary;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
Expand Down Expand Up @@ -56,19 +54,20 @@
import android.os.Process;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.Toast;
import java.lang.Math;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import ch.blinkenlights.android.medialibrary.MediaLibrary;


/**
* Handles music playback and pretty much all the other work.
Expand Down
Loading