diff --git a/FAQ.md b/FAQ.md index 44fe86d..7abed4d 100644 --- a/FAQ.md +++ b/FAQ.md @@ -6,12 +6,7 @@ This frequently asked questions will be continually updated to accommodate new i **What is Blade Music Player?** -Blade is a music player compatible to play music from local library, Spotify, Deezer and SoundCloud. - - -**Where can I find the current playlist?** - -The current playlist can be found as a bar at the bottom of the app. +Blade is a music player for Android that can play music from local library, Spotify, and Deezer. (SoundCloud, and others planned) **How can I navigate between songs, artists and album?** @@ -24,29 +19,29 @@ Swipe the left border of your screen or tap on the three horizontal lines at the To add a song to favourite playlist, tap on the vertical dots on a song then tap on add to playlist, and tap on favorite. -**Do I need Dezzer premium account to use the app?** +**Do I need a Deezer premium account to use the app?** -Yes, if you want to play music from Denver.(without premium account, you get access to just 30 secs preview). +Yes, if you want to play music from Deezer (without premium account, you get access to 30 secs preview only). +**Do I need a Spotify premium account to use the app?** -**Can I exclude folders from my library?** +Yes, if you want to play music from Spotify (without premium account, the playback won't launch). -No, the music library is scanned by the operating system. To exclude folders create a file called ".nomedia". +**Can I exclude folders from my library?** -**What sources does Blade plays from?** - -Blade plays from Spotify, Deedee and SoundCloud. +No, the music library is scanned by the operating system. To exclude folders create a file called ".nomedia". **How do I search for a song or playlist?** -To search for a playlist, the search icon can be found at the top bar of the app. Type the song and search. - +If you want to search your local library for a song, just use the 'search' icon on the top bar while in library. +If you want to search the internet (spotify/deezer) for a song, use the search option on the navigation drawer. -**Do I have to login to my Deezer and Spirit accounts before playing music from them?** +**Do I have to login to my Deezer and Spotify accounts before playing music from them?** -Yes, you must login to your account before you can play musics from them. +Yes, you must login to your account before you can play musics from them. However, know that nobody except the +Spotify/Deezer servers will access your private data (password...), as we use the OAuth2 protocol. **Where can Blade Music Player be downloaded?** diff --git a/app/build.gradle b/app/build.gradle index 9025052..3eca484 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "v.blade" minSdkVersion 16 targetSdkVersion 27 - versionCode 9 - versionName "1.1" + versionCode 10 + versionName "1.1.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } compileOptions { diff --git a/app/release/blade-1.1.apk b/app/release/blade-1.1.1.apk similarity index 77% rename from app/release/blade-1.1.apk rename to app/release/blade-1.1.1.apk index e1106e8..98c0a2b 100644 Binary files a/app/release/blade-1.1.apk and b/app/release/blade-1.1.1.apk differ diff --git a/app/src/main/java/v/blade/library/LibraryService.java b/app/src/main/java/v/blade/library/LibraryService.java index acbe557..bfc2351 100644 --- a/app/src/main/java/v/blade/library/LibraryService.java +++ b/app/src/main/java/v/blade/library/LibraryService.java @@ -6,7 +6,6 @@ import android.graphics.BitmapFactory; import android.os.Looper; import android.util.Log; -import retrofit.RetrofitError; import v.blade.ui.settings.SettingsActivity; import java.io.*; @@ -404,6 +403,7 @@ public void run() for(Source s : Source.SOURCES) s.registerSongs(); + registerSongLinks(); registerSongBetterSources(); sortLibrary(); synchronization = false; @@ -429,132 +429,65 @@ private static void registerSongBetterSources() int addedSongs = 0; - if(bestSource == Source.SOURCE_SPOTIFY) + //search all songs on best source + ArrayList bestSourceSongs = new ArrayList<>(); + for(Song s : songs) { - //search all songs on spotify - ArrayList spotifySongs = new ArrayList<>(); - for(Song s : songs) + if(s.getSources().getSourceByPriority(0).getSource() != bestSource && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) { - if(s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_SPOTIFY && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) - { - //query spotify for this song - try - { - if(Source.SOURCE_SPOTIFY.searchForSong(s)) - { - spotifySongs.add(s); - } - addedSongs++; - if(addedSongs >= BETTER_SOURCES_MAX) break; - } - catch (RetrofitError error) - { - //TODO : handle error - continue; - } - } - } - //also search for songs in playlist - if(!SAVE_PLAYLISTS_TO_LIBRARY) - { - for(Playlist p : playlists) + //query bestSource for this song + try { - for(Song s : p.getContent()) + if(bestSource.searchForSong(s)) { - if(s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_SPOTIFY && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) - { - if(Source.SOURCE_SPOTIFY.searchForSong(s)) - { - spotifySongs.add(s); - } - addedSongs++; - if(addedSongs >= BETTER_SOURCES_MAX) break; - } + bestSourceSongs.add(s); } + addedSongs++; + if(addedSongs >= BETTER_SOURCES_MAX) break; } - } - - //cache theses - try - { - //TODO : for now we keep betterSources forever, find a way to get rid of unused ones - if(betterSourceFile.exists()) betterSourceFile.createNewFile(); - RandomAccessFile randomAccessFile = new RandomAccessFile(betterSourceFile.getAbsolutePath(), "rw"); - randomAccessFile.seek(randomAccessFile.length()); - for(Song song : spotifySongs) + catch (Exception error) { - randomAccessFile.writeUTF(song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR - + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSourceByPriority(0).getId() - + CACHE_SEPARATOR + "\n"); + //TODO : handle error + continue; } - randomAccessFile.close(); } - catch(IOException e) {e.printStackTrace();} - } - else if(bestSource == Source.SOURCE_DEEZER) + //also search for songs in playlist + if(!SAVE_PLAYLISTS_TO_LIBRARY) { - ArrayList deezerSongs = new ArrayList<>(); - - //search all songs on deezer - synchronized (songs) + for(Playlist p : playlists) { - for (Song s : songs) + for(Song s : p.getContent()) { - if (s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_DEEZER && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) + if(s.getSources().getSourceByPriority(0).getSource() != bestSource && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) { - if(Source.SOURCE_DEEZER.searchForSong(s)) + if(bestSource.searchForSong(s)) { - deezerSongs.add(s); + bestSourceSongs.add(s); } addedSongs++; if(addedSongs >= BETTER_SOURCES_MAX) break; } } } - //also search for songs in playlist - if(!SAVE_PLAYLISTS_TO_LIBRARY) - { - synchronized (playlists) - { - for(Playlist p : playlists) - { - synchronized (p.getContent()) - { - for(Song s : p.getContent()) - { - if(s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_DEEZER && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) - { - if(Source.SOURCE_DEEZER.searchForSong(s)) - { - deezerSongs.add(s); - } - addedSongs++; - if(addedSongs >= BETTER_SOURCES_MAX) break; - } - } - } - } - } - } + } - //cache theses - try + //cache theses + try + { + //TODO : for now we keep betterSources forever, find a way to get rid of unused ones + if(betterSourceFile.exists()) betterSourceFile.createNewFile(); + RandomAccessFile randomAccessFile = new RandomAccessFile(betterSourceFile.getAbsolutePath(), "rw"); + randomAccessFile.seek(randomAccessFile.length()); + for(Song song : bestSourceSongs) { - //TODO : for now we keep betterSources forever, find a way to get rid of unused ones - if(betterSourceFile.exists()) betterSourceFile.createNewFile(); - RandomAccessFile randomAccessFile = new RandomAccessFile(betterSourceFile.getAbsolutePath(), "rw"); - randomAccessFile.seek(randomAccessFile.length()); - for(Song song : deezerSongs) - { - randomAccessFile.writeUTF(song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR - + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSourceByPriority(0).getId() - + CACHE_SEPARATOR + "\n"); - } - randomAccessFile.close(); + randomAccessFile.writeUTF(song.getTitle() + CACHE_SEPARATOR + song.getAlbum().getName() + CACHE_SEPARATOR + song.getArtist().getName() + CACHE_SEPARATOR + + song.getFormat() + CACHE_SEPARATOR + song.getTrackNumber() + CACHE_SEPARATOR + song.getDuration() + CACHE_SEPARATOR + song.getSources().getSourceByPriority(0).getId() + + CACHE_SEPARATOR + "\n"); } - catch(IOException e) {e.printStackTrace();} + randomAccessFile.close(); } + catch(IOException e) {e.printStackTrace();} if(currentCallback != null) currentCallback.onLibraryChange(); } @@ -680,8 +613,9 @@ public static ArrayList query(String s) synchronized(playlists) { for(Playlist playlist : playlists) - if(playlist.getName().toLowerCase().contains(q)) - tr.add(playlist); + if(playlist.getName() != null) + if(playlist.getName().toLowerCase().contains(q)) + tr.add(playlist); } return tr; @@ -828,9 +762,16 @@ static void loadArt(LibraryObject obj, String path, boolean local) byte[] buffer = new byte[4096]; while(true) { - int count = nis.read(buffer, 0, 4096); - if(count == -1) break; - fos.write(buffer, 0, count); + try + { + int count = nis.read(buffer, 0, 4096); + if(count == -1) break; + fos.write(buffer, 0, count); + } + catch(InterruptedIOException ex) + { + ex.printStackTrace(); + } } connection.getInputStream().close(); diff --git a/app/src/main/java/v/blade/library/Source.java b/app/src/main/java/v/blade/library/Source.java index 3d69228..0bd3f40 100644 --- a/app/src/main/java/v/blade/library/Source.java +++ b/app/src/main/java/v/blade/library/Source.java @@ -90,7 +90,7 @@ public interface OperationCallback {void onSucess(LibraryObject result); void on public static Source SOURCE_LOCAL_LIB = new Source(R.drawable.ic_local, 0, "Local") { - private LongSparseArray idsorted_albums; + private LongSparseArray idsorted_albums = null; private SourcePlayer player = new SourcePlayer() { @@ -221,7 +221,11 @@ public void registerSongs() /* get content resolver and init temp sorted arrays */ final ContentResolver musicResolver = LibraryService.appContext.getContentResolver(); - idsorted_albums = new LongSparseArray<>(); + boolean loadAlbumArts = true; + if(idsorted_albums != null) //load of album art is not finished but resync was called + loadAlbumArts = false; + + if(loadAlbumArts) idsorted_albums = new LongSparseArray<>(); LongSparseArray idsorted_songs = new LongSparseArray<>(); /* let's get all music files of the user, and register them and their attributes */ @@ -268,7 +272,8 @@ public void registerSongs() s.setFormat(musicCursor.getString(formatColumn)); s.setPath(thisPath); idsorted_songs.put(thisId, s); - if(idsorted_albums.get(albumId) == null) idsorted_albums.put(albumId, s.getAlbum()); + if(loadAlbumArts) + if(idsorted_albums.get(albumId) == null) idsorted_albums.put(albumId, s.getAlbum()); } while (musicCursor.moveToNext()); musicCursor.close(); @@ -939,7 +944,7 @@ public void registerSongs() Playlist list = new Playlist(playlistBase.name, thisList); list.getSources().addSource(new SongSources.SongSource(playlistBase.id, SOURCE_SPOTIFY)); if(playlistBase.collaborative) list.setCollaborative(); - if(!playlistBase.owner.id.equals(mePrivate.id)) list.setOwner(playlistBase.owner.display_name == null ? playlistBase.owner.id : playlistBase.owner.display_name, playlistBase.owner.id); + if(playlistBase.owner != null) if(!playlistBase.owner.id.equals(mePrivate.id)) list.setOwner(playlistBase.owner.display_name == null ? playlistBase.owner.id : playlistBase.owner.display_name, playlistBase.owner.id); spotifyPlaylists.add(list); LibraryService.getPlaylists().add(list); if(LibraryService.currentCallback != null) LibraryService.currentCallback.onLibraryChange(); @@ -971,7 +976,8 @@ public void registerSongs() bw.close(); //playlists - for(File f : spotifyPlaylistsCache.listFiles()) f.delete(); + if(spotifyPlaylistsCache.exists() && spotifyPlaylistsCache.listFiles() != null) + for(File f : spotifyPlaylistsCache.listFiles()) f.delete(); for(Playlist p : spotifyPlaylists) { cachePlaylist(p); diff --git a/app/src/main/java/v/blade/player/PlayerMediaPlayer.java b/app/src/main/java/v/blade/player/PlayerMediaPlayer.java index 1785d66..1c53b9e 100644 --- a/app/src/main/java/v/blade/player/PlayerMediaPlayer.java +++ b/app/src/main/java/v/blade/player/PlayerMediaPlayer.java @@ -217,7 +217,7 @@ public int getDuration() { if(currentActivePlayer == Source.SOURCE_LOCAL_LIB.getPlayer()) return currentActivePlayer.getDuration(); - return ((int) currentSong.getDuration()); + return currentSong == null ? 0 : ((int) currentSong.getDuration()); } public void setVolume(float left, float right) { diff --git a/app/src/main/java/v/blade/player/PlayerService.java b/app/src/main/java/v/blade/player/PlayerService.java index 86606d8..0e8f35f 100644 --- a/app/src/main/java/v/blade/player/PlayerService.java +++ b/app/src/main/java/v/blade/player/PlayerService.java @@ -275,7 +275,7 @@ public void setCurrentPosition(int position) {currentPosition = position; mPlayer.playSong(shuffleMode ? shufflePlaylist.get(currentPosition) : currentPlaylist.get(currentPosition));} public ArrayList getCurrentPlaylist() {return shuffleMode ? shufflePlaylist : currentPlaylist;} public int getCurrentPosition() {return currentPosition;} - public Song getCurrentSong() {return shuffleMode ? shufflePlaylist.get(currentPosition) : currentPlaylist.get(currentPosition);} + public Song getCurrentSong() {return currentPlaylist == null ? null : shuffleMode ? shufflePlaylist.get(currentPosition) : currentPlaylist.get(currentPosition);} public boolean isPlaying() {return mPlayer.isPlaying();} public boolean isShuffleEnabled() {return shuffleMode;} public int getRepeatMode() {return repeatMode;} diff --git a/app/src/main/java/v/blade/ui/MainActivity.java b/app/src/main/java/v/blade/ui/MainActivity.java index b965a57..e5e3bc1 100644 --- a/app/src/main/java/v/blade/ui/MainActivity.java +++ b/app/src/main/java/v/blade/ui/MainActivity.java @@ -876,7 +876,7 @@ static void showAddToPlaylist(Activity context, LibraryObject object) {Playlist p = list.get(i); if(!p.isMine() && !p.isCollaborative()) list.remove(i);} list.add(0, new Playlist(context.getString(R.string.new_playlist), null)); - LibraryObjectAdapter adapter = new LibraryObjectAdapter(context, list); + LibraryObjectAdapter adapter = new LibraryObjectAdapter(context, list, false); adapter.setHideMore(true); AlertDialog.Builder builder = new AlertDialog.Builder(context) .setTitle(context.getString(R.string.add_to_playlist)) diff --git a/app/src/main/java/v/blade/ui/PlayActivity.java b/app/src/main/java/v/blade/ui/PlayActivity.java index 4c765dc..b0fc05c 100644 --- a/app/src/main/java/v/blade/ui/PlayActivity.java +++ b/app/src/main/java/v/blade/ui/PlayActivity.java @@ -322,8 +322,11 @@ private void refreshState(PlaybackStateCompat state) /* button actions */ public void onPlayClicked(View v) { - if(musicPlayer.isPlaying()) PlayerConnection.musicController.getTransportControls().pause(); - else PlayerConnection.musicController.getTransportControls().play(); + if(musicPlayer != null && PlayerConnection.musicController != null) + { + if(musicPlayer.isPlaying()) PlayerConnection.musicController.getTransportControls().pause(); + else PlayerConnection.musicController.getTransportControls().play(); + } } public void onPrevClicked(View v) {