Search in sources :

Example 21 with Song

use of com.simplecity.amp_library.model.Song in project Shuttle by timusus.

the class ShuttleUtilsTest method testIncrementPlayCount.

@Test
public void testIncrementPlayCount() throws Exception {
    Context mockContext = mock(Context.class);
    ContentResolver mockContentResolver = mock(ContentResolver.class);
    Song fakeSong = new Song(mock(Cursor.class));
    fakeSong.id = 100L;
    fakeSong.playCount = 50;
    // getPlayCount has extra logic to conduct SQL Queries, which would be a pain to mock in a test
    // so, we can use a Spy here in order to use both fake data (above) plus control return behaviors (below)
    Song spySong = spy(fakeSong);
    doReturn(50).when(spySong).getPlayCount(any(Context.class));
    when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
    // Setup to perform the method call on a song with no play counts
    when(mockContentResolver.update(any(Uri.class), any(ContentValues.class), anyString(), any(String[].class))).thenReturn(0);
    // Call the method and capture the ContentValues object
    ArgumentCaptor<ContentValues> contentValuesCaptor = new ArgumentCaptor<>();
    ShuttleUtils.incrementPlayCount(mockContext, spySong);
    verify(mockContentResolver).update(any(Uri.class), contentValuesCaptor.capture(), anyString(), any(String[].class));
    // Check that the values being updated or inserted match the actual song
    ContentValues values = contentValuesCaptor.getValue();
    assertThat(values.get("_id")).isEqualTo(100L);
    assertThat(values.get("play_count")).isEqualTo(51);
    verify(mockContentResolver).insert(any(Uri.class), eq(values));
    // Next, test what happens with a song that already had play counts (should not cause an insert operation)
    reset(mockContentResolver);
    when(mockContentResolver.update(any(Uri.class), contentValuesCaptor.capture(), anyString(), any(String[].class))).thenReturn(1);
    ShuttleUtils.incrementPlayCount(mockContext, spySong);
    verify(mockContentResolver, never()).insert(any(Uri.class), any(ContentValues.class));
}
Also used : Context(android.content.Context) ContentValues(android.content.ContentValues) Song(com.simplecity.amp_library.model.Song) ArgumentCaptor(org.mockito.ArgumentCaptor) Cursor(android.database.Cursor) Uri(android.net.Uri) ContentResolver(android.content.ContentResolver) Test(org.junit.Test)

Example 22 with Song

use of com.simplecity.amp_library.model.Song in project Shuttle by timusus.

the class OperatorsUnitTest method testSongsToAlbums.

@Test
public void testSongsToAlbums() throws Exception {
    // When a user doesn't have album tags for a particular album, Android ends up using the
    // folder name as the album name. So if there's a bunch of songs in a folder, and none of the
    // songs have correct album tags, all the songs will have the same album id.
    // This test ensures that the songsToAlbums operator recognises that these songs don't share
    // a common albumArtist name (remember, the albumArtistName falls back to artistName if not present)
    // and as such, the songs belong to separate albums.
    // Conditions:
    // 1. The album name & album ID is the same for all songs.
    // 2. The album artist differs for some songs.
    List<Song> songs = new ArrayList<>();
    for (int i = 0; i < 15; i++) {
        Song song = new Song(mock(Cursor.class));
        song.id = i;
        song.name = "Song " + i;
        song.albumId = 0;
        song.albumName = "Music";
        song.albumArtistName = "Artist 1";
        songs.add(song);
    }
    for (int i = 0; i < 15; i++) {
        Song song = new Song(mock(Cursor.class));
        song.id = i + 14;
        song.name = "Song " + i;
        song.albumId = 0;
        song.albumName = "Music";
        song.albumArtistName = "Artist 2";
        songs.add(song);
    }
    assertThat(Operators.songsToAlbums(songs).size()).isEqualTo(2);
    // Test to ensure that only one album is generated under the following conditions:
    // 1. The album name & album ID is the same for all songs.
    // 2. The album artist is the same for all songs.
    // 3. The artist differs for some songs.
    songs = new ArrayList<>();
    for (int i = 0; i < 15; i++) {
        Song song = new Song(mock(Cursor.class));
        song.id = i;
        song.name = "Song " + i;
        song.albumId = 0;
        song.albumName = "Music";
        song.artistId = 0;
        song.artistName = "Artist 1";
        song.albumArtistName = "Album Artist 1";
        songs.add(song);
    }
    for (int i = 0; i < 15; i++) {
        Song song = new Song(mock(Cursor.class));
        song.id = i + 14;
        song.name = "Song " + i;
        song.albumId = 0;
        song.albumName = "Music";
        song.artistId = 1;
        song.artistName = "Artist 2";
        song.albumArtistName = "Album Artist 1";
        songs.add(song);
    }
    assertThat(Operators.songsToAlbums(songs).size()).isEqualTo(1);
}
Also used : Song(com.simplecity.amp_library.model.Song) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) Test(org.junit.Test)

Example 23 with Song

use of com.simplecity.amp_library.model.Song in project Shuttle by timusus.

the class SearchUtilsUnitTest method testJaroWinklerObject.

@Test
public void testJaroWinklerObject() throws Exception {
    //These are kind of vague tests, just ensuring that various JaroWinkler search distance
    //queries fall roughly in the expected range..
    //Todo:
    //I'm not really sure if this is the correct use of unit tests. This particular set is less about
    //testing whether the code is sound and working as expected, and more about testing whether the logic
    //behind that code gives us the results we want..
    double SCORE_THRESHOLD = 0.70;
    //We expect this to do well, as 'words' is one of the words in the song name. (Score should be 1.0)
    Song song = new Song(mock(Cursor.class));
    song.name = "The lots of words song";
    SearchUtils.JaroWinklerObject jaroWinklerObject = new SearchUtils.JaroWinklerObject<>(song, "words", song.name);
    assertThat(jaroWinklerObject.score).isGreaterThan(SCORE_THRESHOLD);
    //This was a user-submitted example of a previously failing search - before adjusting the Jaro-Winkler
    //to split the song/album/artist name at whitespace, and return the best score for each substring.
    song = new Song(mock(Cursor.class));
    song.artistName = "Aby Wolf";
    jaroWinklerObject = new SearchUtils.JaroWinklerObject<>(song, "aby wolf", song.artistName);
    assertThat(jaroWinklerObject.score).isGreaterThan(SCORE_THRESHOLD);
    //'Baby worm' isn't similar enough to 'aby wolf' to pass our threshold.
    song = new Song(mock(Cursor.class));
    song.artistName = "Bad Worms";
    jaroWinklerObject = new SearchUtils.JaroWinklerObject<>(song, "aby wolf", song.artistName);
    assertThat(jaroWinklerObject.score).isLessThan(SCORE_THRESHOLD);
}
Also used : Song(com.simplecity.amp_library.model.Song) Cursor(android.database.Cursor) Test(org.junit.Test)

Example 24 with Song

use of com.simplecity.amp_library.model.Song in project Shuttle by timusus.

the class PlaylistUtils method addToFavorites.

/**
     * Add a song to the favourites playlist
     */
public static void addToFavorites(final Context context) {
    Song song = MusicUtils.getSong();
    if (song == null) {
        return;
    }
    Observable.fromCallable(Playlist::favoritesPlaylist).flatMap(playlist -> playlist.getSongsObservable(context).flatMap(new Func1<List<Song>, Observable<Playlist>>() {

        @Override
        public Observable<Playlist> call(List<Song> songs) {
            Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlist.id);
            ContentValues values = new ContentValues();
            values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, song.id);
            values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, songs.size() + 1);
            context.getContentResolver().insert(uri, values);
            return Observable.just(playlist);
        }
    })).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(playlist -> {
        Toast.makeText(context, context.getResources().getString(R.string.song_to_favourites, song.name), Toast.LENGTH_SHORT).show();
        LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(MusicService.InternalIntents.FAVORITE_CHANGED));
    });
}
Also used : R(com.simplecity.amp_library.R) Spannable(android.text.Spannable) Uri(android.net.Uri) AndroidSchedulers(rx.android.schedulers.AndroidSchedulers) LocalBroadcastManager(android.support.v4.content.LocalBroadcastManager) PlayCountTable(com.simplecity.amp_library.sql.providers.PlayCountTable) Song(com.simplecity.amp_library.model.Song) CheckBox(android.widget.CheckBox) ContentResolver(android.content.ContentResolver) MediaStore(android.provider.MediaStore) Schedulers(rx.schedulers.Schedulers) View(android.view.View) Log(android.util.Log) Playlist(com.simplecity.amp_library.model.Playlist) BaseColumns(android.provider.BaseColumns) SubMenu(android.view.SubMenu) Query(com.simplecity.amp_library.model.Query) List(java.util.List) TextView(android.widget.TextView) ContentValues(android.content.ContentValues) MaterialDialog(com.afollestad.materialdialogs.MaterialDialog) SqlUtils(com.simplecity.amp_library.sql.SqlUtils) TextWatcher(android.text.TextWatcher) FileType(com.simplecity.amp_library.interfaces.FileType) Context(android.content.Context) Stream(com.annimon.stream.Stream) Environment(android.os.Environment) Dialog(android.app.Dialog) Intent(android.content.Intent) NonNull(android.support.annotation.NonNull) Editable(android.text.Editable) Observable(rx.Observable) SpannableStringBuilder(android.text.SpannableStringBuilder) Func1(rx.functions.Func1) Toast(android.widget.Toast) BaseFileObject(com.simplecity.amp_library.model.BaseFileObject) Cursor(android.database.Cursor) SqlBriteUtils(com.simplecity.amp_library.sql.sqlbrite.SqlBriteUtils) Collectors(com.annimon.stream.Collectors) LayoutInflater(android.view.LayoutInflater) StyleSpan(android.text.style.StyleSpan) FileWriter(java.io.FileWriter) ProgressDialog(android.app.ProgressDialog) TextUtils(android.text.TextUtils) DialogAction(com.afollestad.materialdialogs.DialogAction) IOException(java.io.IOException) WorkerThread(android.support.annotation.WorkerThread) File(java.io.File) ShuttleApplication(com.simplecity.amp_library.ShuttleApplication) MusicService(com.simplecity.amp_library.playback.MusicService) Crashlytics(com.crashlytics.android.Crashlytics) CustomEditText(com.simplecity.amp_library.ui.views.CustomEditText) EditText(android.widget.EditText) ContentUris(android.content.ContentUris) ContentValues(android.content.ContentValues) Song(com.simplecity.amp_library.model.Song) Playlist(com.simplecity.amp_library.model.Playlist) List(java.util.List) Intent(android.content.Intent) Uri(android.net.Uri) Observable(rx.Observable)

Example 25 with Song

use of com.simplecity.amp_library.model.Song in project Shuttle by timusus.

the class PlaylistUtils method addToPlaylist.

/**
     * Method addToPlaylist.
     *
     * @param playlist Playlist
     * @param songs    List<Song>
     * @return boolean true if the playlist addition was successful
     */
public static void addToPlaylist(Context context, Playlist playlist, List<Song> songs) {
    if (playlist == null || songs == null || songs.isEmpty()) {
        return;
    }
    playlist.getSongsObservable(context).observeOn(AndroidSchedulers.mainThread()).subscribe(existingSongs -> {
        if (!SettingsManager.getInstance().ignoreDuplicates()) {
            List<Song> duplicates = Stream.of(existingSongs).filter(songs::contains).distinct().collect(Collectors.toList());
            if (!duplicates.isEmpty()) {
                View customView = LayoutInflater.from(context).inflate(R.layout.dialog_playlist_duplicates, null);
                TextView messageText = (TextView) customView.findViewById(R.id.textView);
                CheckBox applyToAll = (CheckBox) customView.findViewById(R.id.applyToAll);
                CheckBox alwaysAdd = (CheckBox) customView.findViewById(R.id.alwaysAdd);
                if (duplicates.size() <= 1) {
                    applyToAll.setVisibility(View.GONE);
                    applyToAll.setChecked(false);
                }
                messageText.setText(getPlaylistRemoveString(context, duplicates.get(0)));
                applyToAll.setText(getApplyCheckboxString(context, duplicates.size()));
                DialogUtils.getBuilder(context).title(R.string.dialog_title_playlist_duplicates).customView(customView, false).positiveText(R.string.dialog_button_playlist_duplicate_add).autoDismiss(false).onPositive((dialog, which) -> {
                    if (duplicates.size() != 1 && !applyToAll.isChecked()) {
                        duplicates.remove(0);
                        messageText.setText(getPlaylistRemoveString(context, duplicates.get(0)));
                        applyToAll.setText(getApplyCheckboxString(context, duplicates.size()));
                    } else {
                        insertPlaylistItems(context, playlist, songs, existingSongs.size());
                        SettingsManager.getInstance().setIgnoreDuplicates(alwaysAdd.isChecked());
                        dialog.dismiss();
                    }
                }).negativeText(R.string.dialog_button_playlist_duplicate_skip).onNegative((dialog, which) -> {
                    if (duplicates.size() != 1 && !applyToAll.isChecked()) {
                        songs.remove(duplicates.remove(0));
                        messageText.setText(getPlaylistRemoveString(context, duplicates.get(0)));
                        applyToAll.setText(getApplyCheckboxString(context, duplicates.size()));
                    } else {
                        Stream.of(duplicates).filter(songs::contains).forEach(songs::remove);
                        insertPlaylistItems(context, playlist, songs, existingSongs.size());
                        SettingsManager.getInstance().setIgnoreDuplicates(alwaysAdd.isChecked());
                        dialog.dismiss();
                    }
                }).show();
            } else {
                insertPlaylistItems(context, playlist, songs, existingSongs.size());
            }
        } else {
            insertPlaylistItems(context, playlist, songs, existingSongs.size());
        }
    });
}
Also used : R(com.simplecity.amp_library.R) Spannable(android.text.Spannable) Uri(android.net.Uri) AndroidSchedulers(rx.android.schedulers.AndroidSchedulers) LocalBroadcastManager(android.support.v4.content.LocalBroadcastManager) PlayCountTable(com.simplecity.amp_library.sql.providers.PlayCountTable) Song(com.simplecity.amp_library.model.Song) CheckBox(android.widget.CheckBox) ContentResolver(android.content.ContentResolver) MediaStore(android.provider.MediaStore) Schedulers(rx.schedulers.Schedulers) View(android.view.View) Log(android.util.Log) Playlist(com.simplecity.amp_library.model.Playlist) BaseColumns(android.provider.BaseColumns) SubMenu(android.view.SubMenu) Query(com.simplecity.amp_library.model.Query) List(java.util.List) TextView(android.widget.TextView) ContentValues(android.content.ContentValues) MaterialDialog(com.afollestad.materialdialogs.MaterialDialog) SqlUtils(com.simplecity.amp_library.sql.SqlUtils) TextWatcher(android.text.TextWatcher) FileType(com.simplecity.amp_library.interfaces.FileType) Context(android.content.Context) Stream(com.annimon.stream.Stream) Environment(android.os.Environment) Dialog(android.app.Dialog) Intent(android.content.Intent) NonNull(android.support.annotation.NonNull) Editable(android.text.Editable) Observable(rx.Observable) SpannableStringBuilder(android.text.SpannableStringBuilder) Func1(rx.functions.Func1) Toast(android.widget.Toast) BaseFileObject(com.simplecity.amp_library.model.BaseFileObject) Cursor(android.database.Cursor) SqlBriteUtils(com.simplecity.amp_library.sql.sqlbrite.SqlBriteUtils) Collectors(com.annimon.stream.Collectors) LayoutInflater(android.view.LayoutInflater) StyleSpan(android.text.style.StyleSpan) FileWriter(java.io.FileWriter) ProgressDialog(android.app.ProgressDialog) TextUtils(android.text.TextUtils) DialogAction(com.afollestad.materialdialogs.DialogAction) IOException(java.io.IOException) WorkerThread(android.support.annotation.WorkerThread) File(java.io.File) ShuttleApplication(com.simplecity.amp_library.ShuttleApplication) MusicService(com.simplecity.amp_library.playback.MusicService) Crashlytics(com.crashlytics.android.Crashlytics) CustomEditText(com.simplecity.amp_library.ui.views.CustomEditText) EditText(android.widget.EditText) ContentUris(android.content.ContentUris) Song(com.simplecity.amp_library.model.Song) CheckBox(android.widget.CheckBox) TextView(android.widget.TextView) View(android.view.View) TextView(android.widget.TextView)

Aggregations

Song (com.simplecity.amp_library.model.Song)26 List (java.util.List)14 Intent (android.content.Intent)13 Stream (com.annimon.stream.Stream)12 Album (com.simplecity.amp_library.model.Album)12 Context (android.content.Context)11 ArrayList (java.util.ArrayList)11 Observable (rx.Observable)11 Toast (android.widget.Toast)10 Collectors (com.annimon.stream.Collectors)10 R (com.simplecity.amp_library.R)10 AndroidSchedulers (rx.android.schedulers.AndroidSchedulers)10 Schedulers (rx.schedulers.Schedulers)10 Uri (android.net.Uri)9 AlbumArtist (com.simplecity.amp_library.model.AlbumArtist)9 Playlist (com.simplecity.amp_library.model.Playlist)9 ContentUris (android.content.ContentUris)7 MediaStore (android.provider.MediaStore)7 View (android.view.View)7 Glide (com.bumptech.glide.Glide)7