diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000..59587ff Binary files /dev/null and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/modules.xml b/.idea/modules.xml index 0c96608..c51a432 100755 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,7 +3,6 @@ - diff --git a/README.md b/README.md index 0832f52..ca7c973 100755 --- a/README.md +++ b/README.md @@ -32,14 +32,13 @@ Used to quickly search for songs, artists or albums in the library. 2 search mo ## Releases -[Latest Release (v 1.2)](https://github.com/Valou3433/blade-player/releases/download/v1.2/blade-1.2.apk) +[Latest Release (v 1.3)](https://github.com/Valou3433/blade-player/releases/download/v1.3/blade-1.3.apk) -[Mirror](http://valou3433.fr/blade/blade-1.2.apk) +[Mirror](http://valou3433.fr/blade/blade-1.3.apk) -- added ability to edit local songs metadata -- added themes (for now only blade, nightly and green) -- added image generation for local playlists, ... -- fixed bugs +- added song linkmanager +- added animation and red theme +- fixed bugs on search, mediasession, tag editor
Link to all [Blade-Player Releases](https://github.com/Valou3433/blade-player/releases) diff --git a/app/app.iml b/app/app.iml index c3f46c7..541f529 100755 --- a/app/app.iml +++ b/app/app.iml @@ -84,20 +84,36 @@ + + + + + + + + + + + + + + + + @@ -150,5 +166,6 @@ +
\ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 90a7a51..12f423b 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "v.blade" minSdkVersion 16 targetSdkVersion 27 - versionCode 11 - versionName "1.2" + versionCode 12 + versionName "1.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } compileOptions { diff --git a/app/release/blade-1.3.apk b/app/release/blade-1.3.apk new file mode 100644 index 0000000..86ffc27 Binary files /dev/null and b/app/release/blade-1.3.apk differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index caaa6a7..51d4f27 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -89,12 +89,21 @@ android:name="android.support.PARENT_ACTIVITY" android:value="v.blade.ui.MainActivity"/> - - + + + + + diff --git a/app/src/main/java/v/blade/BladeApplication.java b/app/src/main/java/v/blade/BladeApplication.java index c68f6c7..0671ecb 100644 --- a/app/src/main/java/v/blade/BladeApplication.java +++ b/app/src/main/java/v/blade/BladeApplication.java @@ -46,6 +46,8 @@ else if(theme.equalsIgnoreCase("blade")) ThemesActivity.setThemeToBlade(); else if(theme.equalsIgnoreCase("green")) ThemesActivity.setThemeToGreen(); + else if(theme.equalsIgnoreCase("red")) + ThemesActivity.setThemeToRed(); } } } diff --git a/app/src/main/java/v/blade/library/LibraryObject.java b/app/src/main/java/v/blade/library/LibraryObject.java index 48a3836..f63e64a 100755 --- a/app/src/main/java/v/blade/library/LibraryObject.java +++ b/app/src/main/java/v/blade/library/LibraryObject.java @@ -19,6 +19,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.net.Uri; import java.io.Serializable; @@ -52,6 +53,7 @@ public void setArt(String path, Bitmap miniatureArt) public boolean getArtLoading() {return this.artLoad;} public Bitmap getArtMiniature() {return miniatureArt;} public Bitmap getArt() {return BitmapFactory.decodeFile(artPath);} + public Uri getArtUri() {return artPath == null ? null : Uri.parse(artPath);} public boolean hasArt() {return hasArt;} public String getType() {return this.getClass().getSimpleName();} diff --git a/app/src/main/java/v/blade/library/LibraryService.java b/app/src/main/java/v/blade/library/LibraryService.java index c422935..42c9088 100755 --- a/app/src/main/java/v/blade/library/LibraryService.java +++ b/app/src/main/java/v/blade/library/LibraryService.java @@ -29,6 +29,7 @@ public class LibraryService public static boolean SAVE_PLAYLISTS_TO_LIBRARY; public static boolean REGISTER_SONGS_BETTER_SOURCES; public static Uri TREE_URI; + public static boolean ENABLE_SONG_CHANGE_ANIM; /* library */ private static final List artists = Collections.synchronizedList(new ArrayList()); @@ -43,7 +44,7 @@ public class LibraryService static HashMap> songsByName = new HashMap<>(); //song linkss - private static final HashMap> songLinks = new HashMap<>(); + public static final HashMap> songLinks = new HashMap<>(); /* list callbacks */ public interface UserLibraryCallback{void onLibraryChange();} @@ -157,6 +158,8 @@ public static void configureLibrary(Context appContext) String treeUri = generalPrefs.getString("sdcard_uri", null); if(treeUri != null) TREE_URI = Uri.parse(treeUri); + ENABLE_SONG_CHANGE_ANIM = generalPrefs.getBoolean("anim_0", true); + SAVE_PLAYLISTS_TO_LIBRARY = generalPrefs.getBoolean("save_playlist_to_library", false); REGISTER_SONGS_BETTER_SOURCES = generalPrefs.getBoolean("register_better_sources", true); @@ -319,7 +322,7 @@ public static void linkSong(Song source, Song destination, boolean save) { if(src != null) { - destination.getSources().addSource(src); + destination.getSources().addSource(src); //addSource is checking for double-add unregisterSong(source, src); } } @@ -347,27 +350,32 @@ public static void linkSong(Song source, Song destination, boolean save) list.add(source); //save songlinks to cache (by rewriting hashmap) - if(save) + if(save) writeLinks(); + } + + /* + * Rewrite song links on disk + */ + public static void writeLinks() + { + try { - try + songLinksFile.createNewFile(); + BufferedWriter writer = new BufferedWriter(new FileWriter(songLinksFile)); + for(Song s : songLinks.keySet()) { - songLinksFile.createNewFile(); - BufferedWriter writer = new BufferedWriter(new FileWriter(songLinksFile)); - for(Song s : songLinks.keySet()) - { - List links = songLinks.get(s); - writer.write(s.getArtist() + CACHE_SEPARATOR + s.getAlbum() + CACHE_SEPARATOR + s.getTitle() + CACHE_SEPARATOR + - links.size() + CACHE_SEPARATOR); - for(Song linked : links) - writer.write(linked.getArtist() + CACHE_SEPARATOR + linked.getAlbum() + CACHE_SEPARATOR + linked.getTitle() + CACHE_SEPARATOR); - writer.newLine(); - } - writer.close(); - } - catch (IOException e) - { - e.printStackTrace(); + List links = songLinks.get(s); + writer.write(s.getArtist() + CACHE_SEPARATOR + s.getAlbum() + CACHE_SEPARATOR + s.getTitle() + CACHE_SEPARATOR + + links.size() + CACHE_SEPARATOR); + for(Song linked : links) + writer.write(linked.getArtist() + CACHE_SEPARATOR + linked.getAlbum() + CACHE_SEPARATOR + linked.getTitle() + CACHE_SEPARATOR); + writer.newLine(); } + writer.close(); + } + catch (IOException e) + { + e.printStackTrace(); } } @@ -437,27 +445,31 @@ private static void registerSongBetterSources() //search all songs on best source ArrayList bestSourceSongs = new ArrayList<>(); - for(Song s : songs) + synchronized (songs) { - if(s.getSources().getSourceByPriority(0).getSource() != bestSource && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) + for(Song s : songs) { - //query bestSource for this song - try + if(s.getSources().getSourceByPriority(0).getSource() != bestSource && s.getSources().getSourceByPriority(0).getSource() != Source.SOURCE_LOCAL_LIB) { - if(bestSource.searchForSong(s)) + //query bestSource for this song + try + { + if(bestSource.searchForSong(s)) + { + bestSourceSongs.add(s); + } + addedSongs++; + if(addedSongs >= BETTER_SOURCES_MAX) break; + } + catch (Exception error) { - bestSourceSongs.add(s); + //TODO : handle error + continue; } - addedSongs++; - if(addedSongs >= BETTER_SOURCES_MAX) break; - } - catch (Exception error) - { - //TODO : handle error - continue; } } } + //also search for songs in playlist if(!SAVE_PLAYLISTS_TO_LIBRARY) { @@ -512,7 +524,7 @@ private static void registerSongLinks() while (reader.ready()) { String[] line = reader.readLine().split(CACHE_SEPARATOR); - System.out.println(Arrays.toString(line)); + //System.out.println(Arrays.toString(line)); Song song = getSongHandle(line[2], line[1], line[0], 0, null, 0, 0); if(song == null) continue; int size = Integer.parseInt(line[3]); @@ -527,6 +539,7 @@ private static void registerSongLinks() } catch (IOException e) { + System.err.println("registerSongLinks() encoutered IOException"); e.printStackTrace(); } } @@ -720,9 +733,13 @@ static Song getSongHandle(String name, String album, String artist, long duratio if(alb.getName().equalsIgnoreCase(album) && alb.getArtist().getName().equalsIgnoreCase(songArtist.getName())) songAlbum = alb; } - if(songAlbum == null) for(Album alb : albumHandles) - if(alb.getName().equalsIgnoreCase(album) && alb.getArtist().getName().equalsIgnoreCase(songArtist.getName())) - songAlbum = alb; + if(songAlbum == null) + synchronized (albumHandles) + { + for (Album alb : albumHandles) + if (alb.getName().equalsIgnoreCase(album) && alb.getArtist().getName().equalsIgnoreCase(songArtist.getName())) + songAlbum = alb; + } if(songAlbum == null) {songAlbum = new Album(album, songArtist); songAlbum.setHandled(true); albumHandles.add(songAlbum);} songAlbum.getSources().addSource(source); diff --git a/app/src/main/java/v/blade/library/Source.java b/app/src/main/java/v/blade/library/Source.java index 9c03886..94dce4a 100755 --- a/app/src/main/java/v/blade/library/Source.java +++ b/app/src/main/java/v/blade/library/Source.java @@ -35,7 +35,6 @@ import javax.net.ssl.HttpsURLConnection; import java.io.*; -import java.lang.Error; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; @@ -148,7 +147,7 @@ public void playSong(Song song, PlayerCallback callback) if(local == null) {if(callback != null) callback.onFailure(player); return;} if(mediaPlayer == null) {if(callback != null) callback.onFailure(player); return;} - if(song.getFormat().equals("audio/x-ms-wma")) + if(song.getFormat() != null && song.getFormat().equals("audio/x-ms-wma")) { Toast.makeText(LibraryService.appContext, LibraryService.appContext.getString(R.string.format_unsupported), Toast.LENGTH_SHORT).show(); callback.onFailure(player); @@ -676,7 +675,7 @@ public void seekTo(int msec) @Override public int getCurrentPosition() { - return spotifyPlayer == null ? 0 : (int) spotifyPlayer.getPlaybackState().positionMs; + return spotifyPlayer == null ? 0 : (spotifyPlayer.getPlaybackState() == null ? 0 : (int) spotifyPlayer.getPlaybackState().positionMs); } }; @@ -794,42 +793,45 @@ public void registerCachedSongs() spr.close(); //spotify playlists - for(File f : spotifyPlaylistsCache.listFiles()) + if(spotifyPlaylistsCache.exists()) { - ArrayList thisList = new ArrayList<>(); - BufferedReader sppr = new BufferedReader(new FileReader(f)); - String id = sppr.readLine(); - boolean isMine = Boolean.parseBoolean(sppr.readLine()); - String owner = null; String ownerID = null; - if(!isMine) {owner = sppr.readLine(); ownerID = sppr.readLine();} - boolean isCollab = Boolean.parseBoolean(sppr.readLine()); - while(sppr.ready()) + for(File f : spotifyPlaylistsCache.listFiles()) { - String[] tp = sppr.readLine().split(CACHE_SEPARATOR); - Song song = LibraryService.SAVE_PLAYLISTS_TO_LIBRARY ? - LibraryService.registerSong(tp[2], tp[1], Integer.parseInt(tp[4]), 0, - Long.parseLong(tp[5]), tp[0], new SongSources.SongSource(tp[6], SOURCE_SPOTIFY)) - : LibraryService.getSongHandle(tp[0], tp[1], tp[2], Long.parseLong(tp[5]), - new SongSources.SongSource(tp[6], SOURCE_SPOTIFY), Integer.parseInt(tp[4]), 0); - song.setFormat(tp[3]); - thisList.add(song); - - if(!song.getAlbum().hasArt() && !song.getAlbum().getArtLoading()) + ArrayList thisList = new ArrayList<>(); + BufferedReader sppr = new BufferedReader(new FileReader(f)); + String id = sppr.readLine(); + boolean isMine = Boolean.parseBoolean(sppr.readLine()); + String owner = null; String ownerID = null; + if(!isMine) {owner = sppr.readLine(); ownerID = sppr.readLine();} + boolean isCollab = Boolean.parseBoolean(sppr.readLine()); + while(sppr.ready()) { - //the image is supposed to be cached locally, so no need to provide URL - spotifyCachedToLoadArt.add(song.getAlbum()); - song.getAlbum().setArtLoading(); + String[] tp = sppr.readLine().split(CACHE_SEPARATOR); + Song song = LibraryService.SAVE_PLAYLISTS_TO_LIBRARY ? + LibraryService.registerSong(tp[2], tp[1], Integer.parseInt(tp[4]), 0, + Long.parseLong(tp[5]), tp[0], new SongSources.SongSource(tp[6], SOURCE_SPOTIFY)) + : LibraryService.getSongHandle(tp[0], tp[1], tp[2], Long.parseLong(tp[5]), + new SongSources.SongSource(tp[6], SOURCE_SPOTIFY), Integer.parseInt(tp[4]), 0); + song.setFormat(tp[3]); + thisList.add(song); + + if(!song.getAlbum().hasArt() && !song.getAlbum().getArtLoading()) + { + //the image is supposed to be cached locally, so no need to provide URL + spotifyCachedToLoadArt.add(song.getAlbum()); + song.getAlbum().setArtLoading(); + } } + sppr.close(); + + Playlist p = new Playlist(f.getName(), thisList); + if(!isMine) p.setOwner(owner, ownerID); + if(isCollab) p.setCollaborative(); + spotifyCachedToLoadArt.add(p); + p.setArtLoading(); + p.getSources().addSource(new SongSources.SongSource(id, SOURCE_SPOTIFY)); + LibraryService.getPlaylists().add(p); } - sppr.close(); - - Playlist p = new Playlist(f.getName(), thisList); - if(!isMine) p.setOwner(owner, ownerID); - if(isCollab) p.setCollaborative(); - spotifyCachedToLoadArt.add(p); - p.setArtLoading(); - p.getSources().addSource(new SongSources.SongSource(id, SOURCE_SPOTIFY)); - LibraryService.getPlaylists().add(p); } } } @@ -1082,19 +1084,21 @@ public List query(String query) Song song = LibraryService.getSongHandle(t.name, t.album.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); tr.add(song); + //System.out.println("[SART] Song : " + song.getName() + " - " + song.getAlbum() + " - " + song.getArtist() + " , hasArt = " + song.getAlbum().hasArt() + " art = " + song.getAlbum().getArtUri()); if(!song.getAlbum().hasArt()) { if(t.album.images.get(0) != null) urls.put(song.getAlbum(), t.album.images.get(0).url); + //System.out.println("[SART] Album " + song.getAlbum() + " (artist = " + song.getArtist() + ") : img = " + t.album.images.get(0).url); } } for(kaaes.spotify.webapi.android.models.AlbumSimple a : albums.albums.items) { Album album = null; Pager albumTracks = spotifyApi.getService().getAlbumTracks(a.id); - for(Track t : tracks.tracks.items) + for(Track t : albumTracks.items) { - Song currentSong = LibraryService.getSongHandle(t.name, t.album.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); + Song currentSong = LibraryService.getSongHandle(t.name, a.name, t.artists.get(0).name, t.duration_ms, new SongSources.SongSource(t.id, SOURCE_SPOTIFY), t.track_number, 0); if(album == null) album = currentSong.getAlbum(); } @@ -1131,6 +1135,7 @@ public void run() for(Album a : urls.keySet()) { + System.out.println("[SART] Loading albumArt for " + a.getName() + " - " + a.getArtist().getName() + " ; img = " + urls.get(a)); LibraryService.loadArt(a, urls.get(a), false); } } diff --git a/app/src/main/java/v/blade/player/PlayerMediaPlayer.java b/app/src/main/java/v/blade/player/PlayerMediaPlayer.java index 1c53b9e..8151cbe 100755 --- a/app/src/main/java/v/blade/player/PlayerMediaPlayer.java +++ b/app/src/main/java/v/blade/player/PlayerMediaPlayer.java @@ -59,7 +59,8 @@ public void onPlaybackError(SourcePlayer player, String errMsg) { if(currentActivePlayer == player) { - Toast.makeText(context, context.getString(R.string.playback_error) + " : " + errMsg, Toast.LENGTH_SHORT).show(); + if(context != null) Toast.makeText(context, context.getString(R.string.playback_error) + " : " + errMsg, Toast.LENGTH_SHORT).show(); + currentState = PLAYER_STATE_PAUSED; listener.onStateChange(); } @@ -139,6 +140,8 @@ public void play() { if(requestAudioFocus()) { + if(currentActivePlayer == null) return; + currentActivePlayer.play(new SourcePlayer.PlayerCallback() { @Override public void onSucess(SourcePlayer player) @@ -159,10 +162,9 @@ public void onSucess(SourcePlayer player) @Override public void onFailure(SourcePlayer player) { - Toast.makeText(context, context.getString(R.string.playback_error), Toast.LENGTH_SHORT).show(); - if(player == currentActivePlayer) { + Toast.makeText(context, context.getString(R.string.playback_error), Toast.LENGTH_SHORT).show(); currentState = PLAYER_STATE_PAUSED; listener.onStateChange(); } @@ -202,7 +204,11 @@ public void stop() } public void seekTo(int msec) { - if(currentActivePlayer != null) currentActivePlayer.seekTo(msec); + if(currentActivePlayer != null) + { + currentActivePlayer.seekTo(msec); + listener.onStateChange(); //update mediasession position + } } public int getCurrentPosition() { @@ -234,11 +240,14 @@ public void playSong(final Song song) //oreo+ : we need to show notification as soon as first 'playSong()' is called (service start) if(!notificationShown) {listener.onStateChange(); notificationShown = true;} + if(song == null) return; currentSong = song; if(currentActivePlayer != null && isPlaying()) currentActivePlayer.pause(null); /* select appropriate mediaplayer and start playback */ + if(song.getSources().getSourceByPriority(0) == null) + {currentState = PLAYER_STATE_PAUSED; listener.onStateChange();} currentActivePlayer = song.getSources().getSourceByPriority(0).getSource().getPlayer(); if(requestAudioFocus()) @@ -264,10 +273,9 @@ public void onSucess(SourcePlayer player) @Override public void onFailure(SourcePlayer player) { - Toast.makeText(context, context.getString(R.string.playback_error), Toast.LENGTH_SHORT).show(); - if(currentActivePlayer == player) { + Toast.makeText(context, context.getString(R.string.playback_error), Toast.LENGTH_SHORT).show(); currentState = PLAYER_STATE_PAUSED; listener.onStateChange(); } @@ -291,7 +299,9 @@ public PlaybackStateCompat getPlaybackState() long actions = PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID | PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH | PlaybackStateCompat.ACTION_SKIP_TO_NEXT - | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; + | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS + | PlaybackStateCompat.ACTION_SET_REPEAT_MODE + | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE; int playbackState = 0; switch(currentState) diff --git a/app/src/main/java/v/blade/player/PlayerNotification.java b/app/src/main/java/v/blade/player/PlayerNotification.java index 0485cd0..2560de8 100755 --- a/app/src/main/java/v/blade/player/PlayerNotification.java +++ b/app/src/main/java/v/blade/player/PlayerNotification.java @@ -23,6 +23,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.BitmapFactory; import android.os.Build; import android.support.annotation.RequiresApi; import android.support.v4.app.NotificationCompat; @@ -102,7 +103,7 @@ private NotificationCompat.Builder buildNotification(int playerState, MediaSessi .setColor(ContextCompat.getColor(mService, ThemesActivity.currentColorPrimary)) .setSmallIcon(R.drawable.app_icon_notif) //icon that will be displayed in status bar .setContentIntent(contentIntent) //intent that will be sent on notification click - .setLargeIcon(mService.getCurrentArt()) + .setLargeIcon(mService.getCurrentArt() == null ? BitmapFactory.decodeResource(mService.getResources(), R.drawable.ic_albums) : mService.getCurrentArt()) .setContentTitle(playing.getTitle()) .setContentText(playing.getArtist().getName() + " - " + playing.getAlbum().getName()) .setDeleteIntent(null) //intent on notification slide diff --git a/app/src/main/java/v/blade/player/PlayerService.java b/app/src/main/java/v/blade/player/PlayerService.java index 0e8f35f..b4a2eec 100755 --- a/app/src/main/java/v/blade/player/PlayerService.java +++ b/app/src/main/java/v/blade/player/PlayerService.java @@ -69,28 +69,45 @@ else if((repeatMode == PlaybackStateCompat.REPEAT_MODE_ALL) || (currentPosition else mPlayer.setPlaylistEnded(); } - /* send current playbackstate to mediasession */ + /* send current state to mediasession */ + + //send PlaybackState mSession.setPlaybackState(mPlayer.getPlaybackState()); - currentArt = getCurrentPlaylist().get(currentPosition).getAlbum().getArt(); + //build and send current media informations MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); + Song currentSong = getCurrentSong(); + currentArt = currentSong.getAlbum().getArt(); builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, currentArt); - builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentPlaylist.get(currentPosition).getArtist().getName()); - builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentPlaylist.get(currentPosition).getAlbum().getName()); - builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentPlaylist.get(currentPosition).getTitle()); + builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, currentArt); + builder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, currentSong.getAlbum().getArtUri() == null ? null : currentSong.getAlbum().getArtUri().toString()); + builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, currentSong.getAlbum().getArtUri() == null ? null : currentSong.getAlbum().getArtUri().toString()); + builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentSong.getArtist().getName()); + builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentSong.getAlbum().getName()); + builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentSong.getTitle()); + builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, currentSong.getTitle()); + builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, currentSong.getArtist().getName() + " - " + currentSong.getAlbum().getName()); + builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, mPlayer.getDuration()); MediaMetadataCompat metadata = builder.build(); mSession.setMetadata(metadata); + //send shuffle/repeat infos + mSession.setShuffleMode(shuffleMode ? PlaybackStateCompat.SHUFFLE_MODE_NONE : PlaybackStateCompat.SHUFFLE_MODE_ALL); + mSession.setRepeatMode(repeatMode); + + //set mediasession active if it was not + if(!mSession.isActive()) mSession.setActive(true); + /* update notification */ if(mNotification == null) { - mNotification = mNotificationManager.getNotification(getCurrentSong(), mPlayer.getCurrentState(), mSession.getSessionToken()); + mNotification = mNotificationManager.getNotification(currentSong, mPlayer.getCurrentState(), mSession.getSessionToken()); startForeground(PlayerNotification.NOTIFICATION_ID, mNotification); } else { stopForeground(false); - mNotification = mNotificationManager.getNotification(getCurrentSong(), mPlayer.getCurrentState(), mSession.getSessionToken()); + mNotification = mNotificationManager.getNotification(currentSong, mPlayer.getCurrentState(), mSession.getSessionToken()); mNotificationManager.getNotificationManager().notify(PlayerNotification.NOTIFICATION_ID, mNotification); } } @@ -164,6 +181,7 @@ public void onRewind() @Override public void onStop() { + //TODO : find a way to stop correctly (on intent reception, doesnt really stop) mPlayer.stop(); mSession.setActive(false); diff --git a/app/src/main/java/v/blade/ui/MainActivity.java b/app/src/main/java/v/blade/ui/MainActivity.java index ef10ff0..d394b6c 100755 --- a/app/src/main/java/v/blade/ui/MainActivity.java +++ b/app/src/main/java/v/blade/ui/MainActivity.java @@ -373,7 +373,7 @@ else if(currentContext == CONTEXT_SONGS && fromPlaylists) if(currentContext != CONTEXT_SONGS && currentContext != CONTEXT_SEARCH) popupMenu.getMenu().findItem(R.id.action_link_to).setVisible(false); - if(object.getSources() == null || object.getSources().getLocal() == null) + if(object.getSources() == null || object.getSources().getLocal() == null || object instanceof Playlist) popupMenu.getMenu().findItem(R.id.action_tag_edit).setVisible(false); popupMenu.show(); @@ -423,6 +423,7 @@ public void onClick(View v) @Override public void onClick(View v) { + if(musicPlayer == null) return; if(musicPlayer.isPlaying()) PlayerConnection.musicController.getTransportControls().pause(); else PlayerConnection.musicController.getTransportControls().play(); } @@ -627,7 +628,7 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) searchView.setQueryHint(getString(R.string.search_web)); // Set to empty activity fromPlaylists = false; currentObject = null; backBundle = null; back2Bundle = null; - setContentToSearch(new ArrayList()); + setContentToSearch(null); break; case R.id.nav_artists: @@ -772,8 +773,14 @@ private void setContentToSearch(ArrayList searchResult) currentObject = null; fromPlaylists = false; this.setTitle(getResources().getString(R.string.action_search)); currentContext = CONTEXT_SEARCH; + + if(searchResult == null) searchResult = new ArrayList<>(); + else if(searchResult.isEmpty()) + Toast.makeText(this, R.string.no_results_found, Toast.LENGTH_SHORT).show(); + LibraryObjectAdapter adapter = new LibraryObjectAdapter(this, searchResult); adapter.registerMoreClickListener(mainListViewMoreListener); + mainListView.setAdapter(adapter); } diff --git a/app/src/main/java/v/blade/ui/PlayActivity.java b/app/src/main/java/v/blade/ui/PlayActivity.java index 934789d..e346441 100755 --- a/app/src/main/java/v/blade/ui/PlayActivity.java +++ b/app/src/main/java/v/blade/ui/PlayActivity.java @@ -18,6 +18,8 @@ package v.blade.ui; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Handler; import android.support.v4.content.ContextCompat; @@ -28,6 +30,8 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; import android.widget.*; import com.mobeta.android.dslv.DragSortController; import com.mobeta.android.dslv.DragSortListView; @@ -49,6 +53,8 @@ public class PlayActivity extends AppCompatActivity boolean isDisplayingAlbumArt = true; /* activity components */ private ImageView albumView; + private Bitmap lastAlbumBitmap = null; + private Bitmap nullBitmap = null; private TextView songTitle; private TextView songArtistAlbum; private TextView playlistPosition; @@ -138,10 +144,6 @@ else if(touchDeltaX <= -DELTA_X_MIN) onNextClicked(v); } } - case MotionEvent.ACTION_MOVE: - { - //TODO : animate during touch event - } } return true; } @@ -243,6 +245,8 @@ protected void onCreate(Bundle savedInstanceState) playlistView.setDropListener(playlistDropListener); albumView.setOnTouchListener(albumDragListener); + nullBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_albums); + LibraryService.configureLibrary(getApplicationContext()); if(!PlayerConnection.init(new PlayerConnection.Callback() { @@ -332,8 +336,37 @@ private void refreshState(PlaybackStateCompat state) //set album view / playlistView if(currentSong == null) return; - if(currentSong.getAlbum().hasArt()) albumView.setImageBitmap(musicPlayer.getCurrentArt()); - else albumView.setImageResource(R.drawable.ic_albums); + + Bitmap currentAlbumBitmap = musicPlayer.getCurrentArt() == null ? nullBitmap : musicPlayer.getCurrentArt(); + if(lastAlbumBitmap == null | (!LibraryService.ENABLE_SONG_CHANGE_ANIM)) + { + albumView.setImageBitmap(currentAlbumBitmap); + lastAlbumBitmap = currentAlbumBitmap; + } + else if(!lastAlbumBitmap.sameAs(currentAlbumBitmap)) + { + final Animation anim_out = AnimationUtils.loadAnimation(PlayActivity.this, android.R.anim.fade_out); + final Animation anim_in = AnimationUtils.loadAnimation(PlayActivity.this, android.R.anim.fade_in); + anim_out.setDuration(150); + anim_in.setDuration(300); + anim_out.setAnimationListener(new Animation.AnimationListener() + { + @Override public void onAnimationStart(Animation animation) {} + @Override public void onAnimationRepeat(Animation animation) {} + @Override public void onAnimationEnd(Animation animation) + { + albumView.setImageBitmap(currentAlbumBitmap); + lastAlbumBitmap = currentAlbumBitmap; + anim_in.setAnimationListener(new Animation.AnimationListener() { + @Override public void onAnimationStart(Animation animation) {} + @Override public void onAnimationRepeat(Animation animation) {} + @Override public void onAnimationEnd(Animation animation) {} + }); + albumView.startAnimation(anim_in); + } + }); + albumView.startAnimation(anim_out); + } if(playlistAdapter == null) { diff --git a/app/src/main/java/v/blade/ui/TagEditorActivity.java b/app/src/main/java/v/blade/ui/TagEditorActivity.java index 5796db4..e3da9b2 100644 --- a/app/src/main/java/v/blade/ui/TagEditorActivity.java +++ b/app/src/main/java/v/blade/ui/TagEditorActivity.java @@ -3,13 +3,13 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.provider.MediaStore; import android.support.v4.content.ContextCompat; import android.support.v4.provider.DocumentFile; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.EditText; +import android.widget.ImageView; import android.widget.Toast; import org.jaudiotagger.audio.AudioFile; import org.jaudiotagger.audio.AudioFileIO; @@ -22,12 +22,12 @@ import java.io.File; import java.util.ArrayList; -import java.util.Arrays; public class TagEditorActivity extends AppCompatActivity { private LibraryObject currentObject; EditText nameEdit, albumEdit, artistEdit, yearEdit, trackEdit; + ImageView imageEdit; @Override protected void onCreate(Bundle savedInstanceState) @@ -48,6 +48,7 @@ protected void onCreate(Bundle savedInstanceState) artistEdit = findViewById(R.id.artist_edit); yearEdit = findViewById(R.id.year_edit); trackEdit = findViewById(R.id.track_edit); + imageEdit = findViewById(R.id.image_edit); //get current object and fill details currentObject = MainActivity.selectedObject; @@ -62,6 +63,10 @@ protected void onCreate(Bundle savedInstanceState) artistEdit.setText(((Song) currentObject).getArtist().getName()); trackEdit.setText(((Song) currentObject).getTrackNumber() == 0 ? "" : "" + ((Song) currentObject).getTrackNumber()); yearEdit.setText(((Song) currentObject).getYear() == 0 ? "" : "" + ((Song) currentObject).getYear()); + imageEdit.setImageBitmap(((Song) currentObject).getAlbum().getArt()); + + if(((Song) currentObject).getAlbum().hasArt()) imageEdit.setImageBitmap(((Song) currentObject).getAlbum().getArt()); + else imageEdit.setImageResource(R.drawable.ic_albums); } else if(currentObject instanceof Album) { @@ -69,6 +74,9 @@ else if(currentObject instanceof Album) albumEdit.setText(currentObject.getName()); artistEdit.setText(((Album) currentObject).getArtist().getName()); trackEdit.setEnabled(false); + + if(((Album) currentObject).hasArt()) imageEdit.setImageBitmap(((Album) currentObject).getArt()); + else imageEdit.setImageResource(R.drawable.ic_albums); } else if(currentObject instanceof Artist) { @@ -77,6 +85,7 @@ else if(currentObject instanceof Artist) artistEdit.setText(currentObject.getName()); trackEdit.setEnabled(false); yearEdit.setEnabled(false); + imageEdit.setEnabled(false); } //set theme @@ -122,14 +131,61 @@ else if(currentObject instanceof Artist) for(Song currentSong : songs) { File basicFile = new File(currentSong.getPath()); - AudioFile currentFile = AudioFileIO.read(basicFile); + + String[] splitted = basicFile.getAbsolutePath().split("/"); + String[] splittedTreeUri = LibraryService.TREE_URI.getPath().split("/"); + + int index = -1; + for(int i = 0; i < splitted.length; i++) + { + if(splittedTreeUri[2].substring(0, splittedTreeUri[2].length()-1).equals(splitted[i])) + { + index = i; + break; + } + } + + AudioFile currentFile = null; + + if(index != -1) + { + //file is on SD card, lets retrieve path + DocumentFile f = DocumentFile.fromTreeUri(this, LibraryService.TREE_URI); + for(int i = index+1; i < splitted.length; i++) f = f.findFile(splitted[i]); + if(!f.canWrite()) + { + Toast.makeText(this, R.string.give_perm_ext, Toast.LENGTH_LONG).show(); + return; + } + + //OutputStream os = getContentResolver().openOutputStream(f.getUri()); + //InputStream is = getContentResolver().openInputStream(f.getUri()); + Toast.makeText(this, "Edition on SD Card is not supported for now", Toast.LENGTH_LONG).show(); + } + else + { + //file is on local storage, direct access + currentFile = AudioFileIO.read(basicFile); + } + + boolean nameEditE = nameEdit.isEnabled() && !nameEdit.getText().toString().equals(""); + boolean albumEditE = albumEdit.isEnabled() && !albumEdit.getText().toString().equals(""); + boolean artistEditE = artistEdit.isEnabled() && !artistEdit.getText().toString().equals(""); + boolean yearEditE = yearEdit.isEnabled() && !yearEdit.getText().toString().equals(""); + boolean trackEditE = trackEdit.isEnabled() && !trackEdit.getText().toString().equals(""); Tag currentTag = currentFile.getTagOrCreateAndSetDefault(); - if(nameEdit.isEnabled()) currentTag.setField(FieldKey.TITLE, nameEdit.getText().toString()); - if(albumEdit.isEnabled()) currentTag.setField(FieldKey.ALBUM, albumEdit.getText().toString()); - if(artistEdit.isEnabled()) currentTag.setField(FieldKey.ARTIST, artistEdit.getText().toString()); - if(yearEdit.isEnabled()) currentTag.setField(FieldKey.YEAR, yearEdit.getText().toString()); - if(trackEdit.isEnabled()) currentTag.setField(FieldKey.TRACK, trackEdit.getText().toString()); + if(nameEditE) + currentTag.setField(FieldKey.TITLE, nameEdit.getText().toString()); + if(albumEditE) + currentTag.setField(FieldKey.ALBUM, albumEdit.getText().toString()); + if(artistEditE) + currentTag.setField(FieldKey.ARTIST, artistEdit.getText().toString()); + if(yearEditE) + currentTag.setField(FieldKey.YEAR, yearEdit.getText().toString()); + if(trackEditE) + currentTag.setField(FieldKey.TRACK, trackEdit.getText().toString()); + currentFile.commit(); //actualize contentprovider @@ -138,18 +194,21 @@ else if(currentObject instanceof Artist) //actualize song in library SongSources.SongSource localOld = currentSong.getSources().getLocal(); LibraryService.unregisterSong(currentSong, localOld); - LibraryService.registerSong(artistEdit.isEnabled() ? artistEdit.getText().toString() : currentSong.getArtist().getName(), - albumEdit.isEnabled() ? albumEdit.getText().toString() : currentSong.getAlbum().getName(), - trackEdit.isEnabled() ? Integer.parseInt(trackEdit.getText().toString()) : currentSong.getTrackNumber(), - yearEdit.isEnabled() ? Integer.parseInt(yearEdit.getText().toString()) : currentSong.getYear(), + Song newSong = LibraryService.registerSong(artistEditE ? artistEdit.getText().toString() : currentSong.getArtist().getName(), + albumEditE ? albumEdit.getText().toString() : currentSong.getAlbum().getName(), + trackEditE ? Integer.parseInt(trackEdit.getText().toString()) : currentSong.getTrackNumber(), + yearEditE ? Integer.parseInt(yearEdit.getText().toString()) : currentSong.getYear(), currentSong.getDuration(), - nameEdit.isEnabled() ? nameEdit.getText().toString() : currentSong.getName(), + nameEditE ? nameEdit.getText().toString() : currentSong.getName(), localOld); + newSong.setFormat(currentSong.getFormat()); + newSong.setPath(currentSong.getPath()); } MainActivity.selectedObject = null; Intent intent = new Intent(TagEditorActivity.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); } catch (Exception e) diff --git a/app/src/main/java/v/blade/ui/settings/LinkManagerActivity.java b/app/src/main/java/v/blade/ui/settings/LinkManagerActivity.java new file mode 100644 index 0000000..63585a7 --- /dev/null +++ b/app/src/main/java/v/blade/ui/settings/LinkManagerActivity.java @@ -0,0 +1,151 @@ +package v.blade.ui.settings; + +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.*; +import v.blade.R; +import v.blade.library.LibraryService; +import v.blade.library.Song; + +import java.util.List; + +public class LinkManagerActivity extends AppCompatActivity +{ + ListView linkList; + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + setTheme(ThemesActivity.currentAppTheme); + + setContentView(R.layout.activity_link_manager); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + linkList = findViewById(R.id.link_list); + linkList.setBackgroundColor(ContextCompat.getColor(this, ThemesActivity.currentColorBackground)); + linkList.setAdapter(new BaseAdapter() + { + class ViewHolder + { + TextView originalSongTitle; + TextView originalSongInfo; + ImageView image; + ListView links; + } + + @Override + public int getCount() {return LibraryService.songLinks.size();} + @Override + public Object getItem(int position) {return position;} + @Override + public long getItemId(int position) {return position;} + + @Override + public View getView(int position, View convertView, ViewGroup parent) + { + Song currentSong = (Song) LibraryService.songLinks.keySet().toArray()[position]; + + ViewHolder viewHolder; + + if(convertView == null) + { + viewHolder = new ViewHolder(); + + //map to song layout + convertView = LayoutInflater.from(LinkManagerActivity.this).inflate(R.layout.linkmanager_list_layout, parent, false); + + //get title and subtitle views + viewHolder.originalSongTitle = convertView.findViewById(R.id.element_title); + viewHolder.originalSongInfo = convertView.findViewById(R.id.element_subtitle); + viewHolder.image = convertView.findViewById(R.id.element_image); + viewHolder.links = convertView.findViewById(R.id.element_list); + + convertView.setTag(viewHolder); + } + else viewHolder = (ViewHolder) convertView.getTag(); + + viewHolder.image.setImageBitmap(currentSong.getAlbum().getArtMiniature()); + viewHolder.originalSongTitle.setText(currentSong.getTitle()); + viewHolder.originalSongInfo.setText(currentSong.getAlbum().getName() + " - " + currentSong.getArtist().getName()); + List linkedSongs = LibraryService.songLinks.get(currentSong); + viewHolder.links.setAdapter(new BaseAdapter() + { + class ViewHolder + { + ImageView image; + TextView songTitle; + TextView songInfo; + ImageView more; + } + + @Override + public int getCount() {return linkedSongs.size();} + @Override + public Object getItem(int i) {return linkedSongs.get(i);} + @Override + public long getItemId(int i) {return i;} + + @Override + public View getView(int position, View convertView, ViewGroup viewGroup) + { + Song currentSong1 = linkedSongs.get(position); + + ViewHolder viewHolder = new ViewHolder(); + + if(convertView == null) + { + viewHolder = new ViewHolder(); + + //map to song layout + convertView = LayoutInflater.from(LinkManagerActivity.this).inflate(R.layout.list_layout, parent, false); + + //get title and subtitle views + viewHolder.songTitle = convertView.findViewById(R.id.element_title); + viewHolder.songInfo = convertView.findViewById(R.id.element_subtitle); + viewHolder.image = convertView.findViewById(R.id.element_image); + viewHolder.more = convertView.findViewById(R.id.element_more); + + viewHolder.more.setImageResource(R.drawable.ic_cancel_black); + + viewHolder.image.setVisibility(View.GONE); + viewHolder.image.setEnabled(false); + convertView.setTag(viewHolder); + } + else viewHolder = (ViewHolder) convertView.getTag(); + + viewHolder.songTitle.setText(currentSong1.getTitle()); + viewHolder.songInfo.setText(currentSong1.getAlbum().getName() + " - " + currentSong1.getArtist().getName()); + + viewHolder.more.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View view) + { + LibraryService.songLinks.get(currentSong).remove(currentSong1); + notifyDataSetChanged(); + LibraryService.writeLinks(); + + //put message to user to notify him we need resync + Toast.makeText(LinkManagerActivity.this, getText(R.string.pls_resync), Toast.LENGTH_SHORT).show(); + } + }); + + return convertView; + } + }); + + return convertView; + } + }); + } +} diff --git a/app/src/main/java/v/blade/ui/settings/SettingsActivity.java b/app/src/main/java/v/blade/ui/settings/SettingsActivity.java index 437c29f..af06909 100755 --- a/app/src/main/java/v/blade/ui/settings/SettingsActivity.java +++ b/app/src/main/java/v/blade/ui/settings/SettingsActivity.java @@ -58,6 +58,13 @@ public final void onActivityResult(final int requestCode, final int resultCode, // Get Uri from Storage Access Framework. Uri treeUri = resultData.getData(); + if(!treeUri.toString().endsWith("%3A")) + { + //show the user that we are not happy + Toast.makeText(this, R.string.please_sd_root, Toast.LENGTH_LONG).show(); + return; + } + // Persist URI in shared preference so that you can use it later. SharedPreferences sharedPreferences = getSharedPreferences(PREFERENCES_GENERAL_FILE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); @@ -104,6 +111,11 @@ else if(preference.getKey().equals("themes")) Intent intent = new Intent(getActivity(), ThemesActivity.class); startActivity(intent); } + else if(preference.getKey().equals("link_manager")) + { + Intent intent = new Intent(getActivity(), LinkManagerActivity.class); + startActivity(intent); + } else if(preference.getKey().equals("sd_perm")) { if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) @@ -131,6 +143,11 @@ else if(preference.getKey().equals("register_better_sources")) LibraryService.REGISTER_SONGS_BETTER_SOURCES = generalPrefs.getBoolean("register_better_sources", true); Toast.makeText(getActivity(), getText(R.string.pls_resync), Toast.LENGTH_SHORT).show(); } + else if(preference.getKey().equals("anim_0")) + { + SharedPreferences generalPrefs = getActivity().getSharedPreferences(SettingsActivity.PREFERENCES_GENERAL_FILE_NAME, Context.MODE_PRIVATE); + LibraryService.ENABLE_SONG_CHANGE_ANIM = generalPrefs.getBoolean("anim_0", true); + } return super.onPreferenceTreeClick(preference); } diff --git a/app/src/main/java/v/blade/ui/settings/ThemesActivity.java b/app/src/main/java/v/blade/ui/settings/ThemesActivity.java index bd4b95c..2a837a8 100644 --- a/app/src/main/java/v/blade/ui/settings/ThemesActivity.java +++ b/app/src/main/java/v/blade/ui/settings/ThemesActivity.java @@ -51,7 +51,7 @@ class ViewHolder ImageView more; } @Override - public int getCount() {return 3;} + public int getCount() {return 4;} @Override public Object getItem(int position) {return null;} @Override @@ -108,6 +108,15 @@ public View getView(int position, View convertView, ViewGroup parent) mViewHolder.desc.setText(getText(R.string.theme_green_desc)); break; } + case 3: + { + //red theme + mViewHolder.img.setImageResource(0); + mViewHolder.img.setBackgroundColor(ContextCompat.getColor(ThemesActivity.this, R.color.redColorPrimary)); + mViewHolder.title.setText("Red"); + mViewHolder.desc.setText(getText(R.string.theme_red_desc)); + break; + } } mViewHolder.more.setImageResource(0); @@ -159,6 +168,19 @@ public void onItemClick(AdapterView parent, View view, int position, long id) editor.putString("theme", "green"); editor.apply(); + break; + } + case 3: + { + //blade red + if(currentColorPrimary == R.color.redColorPrimary) return; + setThemeToRed(); + + SharedPreferences pref = getSharedPreferences(SettingsActivity.PREFERENCES_GENERAL_FILE_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = pref.edit(); + editor.putString("theme", "red"); + editor.apply(); + break; } } @@ -209,4 +231,16 @@ public static void setThemeToGreen() currentAppThemeWithActionBar = R.style.Green_AppTheme; currentControlTheme = R.style.Theme_ControlTheme; } + public static void setThemeToRed() + { + currentColorPrimary = R.color.redColorPrimary; + currentColorPrimaryDark = R.color.redColorPrimaryDark; + currentColorPrimaryLight = R.color.redColorPrimaryLight; + currentColorAccent = R.color.redColorAccent; + currentColorPrimaryLighter = R.color.redColorPrimaryLighter; + currentColorBackground = R.color.redColorBackground; + currentAppTheme = R.style.Red_AppTheme_NoActionBar; + currentAppThemeWithActionBar = R.style.Red_AppTheme; + currentControlTheme = R.style.Theme_ControlTheme; + } } diff --git a/app/src/main/res/drawable-hdpi/ic_cancel_black.png b/app/src/main/res/drawable-hdpi/ic_cancel_black.png new file mode 100644 index 0000000..dbf21d4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_cancel_black.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_cancel_black.png b/app/src/main/res/drawable-mdpi/ic_cancel_black.png new file mode 100644 index 0000000..b6dcafd Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_cancel_black.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_cancel_black.png b/app/src/main/res/drawable-xhdpi/ic_cancel_black.png new file mode 100644 index 0000000..2608842 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_cancel_black.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_cancel_black.png b/app/src/main/res/drawable-xxhdpi/ic_cancel_black.png new file mode 100644 index 0000000..e3d4605 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_cancel_black.png differ diff --git a/app/src/main/res/layout/activity_link_manager.xml b/app/src/main/res/layout/activity_link_manager.xml new file mode 100644 index 0000000..17aa438 --- /dev/null +++ b/app/src/main/res/layout/activity_link_manager.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_tag_editor.xml b/app/src/main/res/layout/activity_tag_editor.xml index 3b36898..513d27b 100644 --- a/app/src/main/res/layout/activity_tag_editor.xml +++ b/app/src/main/res/layout/activity_tag_editor.xml @@ -15,6 +15,10 @@ android:id="@+id/include"/> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 99fde6a..14797af 100755 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -76,7 +76,7 @@ Could not save tags Please give permission to access SD Card (in settings) SD Card Permission - Select folder that Blade will be able to access (SD Card root or your music folder) + Select folder that Blade will be able to access (SD Card root) This permission is only needed on Android KitKat (19) or more. Permission granted Themes @@ -85,4 +85,9 @@ The default theme of Blade Nightly The default theme of Blade release A clean green theme + A clean red theme + Please select the SD Card root folder ! + Enable animation on song change + Song links manager + No results found \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5a72a9a..8934d54 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -76,7 +76,7 @@ Impossible de sauvegarder les tags Donnez la permission d\'accéder à la Carte SD (dans les paramètres) Permission Carte SD - Sélectionner le dossier auquel Blade pourra accéder (la racine de la carte SD ou votre dossier de musiques) + Sélectionner le dossier auquel Blade pourra accéder (la racine de la carte SD) La permission d\'accéder à la carte SD est uniquement nécessaire sur Android KitKat (19) ou plus. Permission obtenue Thèmes @@ -85,4 +85,9 @@ Le thème par défaut de Blade Nightly Le thème par défaut de Blade Release Un thème simple et vert + Un thème simple et rouge + S\'il vous plait sélectionnez la racine de la carte SD ! + Activer l\'animation de changement de son + Gérer les liens entre titres + Aucun résultat \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2bf91df..9c753b6 100755 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -20,4 +20,11 @@ #ffffff #c8e6c9 #e8f5e9 + + #d32f2f + #b71c1c + #f44336 + #ffffff + #ef9a9a + #fbe9e7 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5e2fa84..8622050 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -86,8 +86,7 @@ Could not save tags Please give permission to access SD Card (in settings) SD Card Permission - Select folder that Blade will be able to access (SD Card root or your music folder) - + Select folder that Blade will be able to access (SD Card root) This permission is only needed on Android KitKat (19) or more. Permission granted Themes @@ -96,4 +95,9 @@ The default theme of Blade Nightly The default theme of Blade release A clean green theme + A clean red theme + Please select the SD Card root folder ! + Enable animation on song change + Song links manager + No results found \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 863e7ed..b9f2ddc 100755 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -42,6 +42,17 @@ false true + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index cbf13b9..f2d696f 100755 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -8,7 +8,11 @@ android:summary="@string/register_better_sources_expl" android:key="register_better_sources" android:defaultValue="true"/> + + diff --git a/libraries/drag-sort-listview b/libraries/drag-sort-listview index 9327c45..36fdc99 160000 --- a/libraries/drag-sort-listview +++ b/libraries/drag-sort-listview @@ -1 +1 @@ -Subproject commit 9327c4543cc8d6ed759070b27626df5f94b5cd4b +Subproject commit 36fdc99762698787372ed187cb35abbcc4f43a3b