Folders and files got all menu items now. Added play next option for all bulk actions.
This commit is contained in:
parent
9c84d59d89
commit
da7867c32d
19 changed files with 416 additions and 284 deletions
|
|
@ -16,11 +16,10 @@ import com.kabouzeid.appthemehelper.util.ATHUtil;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
||||||
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
||||||
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
|
||||||
import com.kabouzeid.gramophone.dialogs.ClearSmartPlaylistDialog;
|
import com.kabouzeid.gramophone.dialogs.ClearSmartPlaylistDialog;
|
||||||
import com.kabouzeid.gramophone.dialogs.DeletePlaylistDialog;
|
import com.kabouzeid.gramophone.dialogs.DeletePlaylistDialog;
|
||||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
|
||||||
import com.kabouzeid.gramophone.helper.menu.PlaylistMenuHelper;
|
import com.kabouzeid.gramophone.helper.menu.PlaylistMenuHelper;
|
||||||
|
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
|
||||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.loader.PlaylistSongLoader;
|
import com.kabouzeid.gramophone.loader.PlaylistSongLoader;
|
||||||
import com.kabouzeid.gramophone.model.Playlist;
|
import com.kabouzeid.gramophone.model.Playlist;
|
||||||
|
|
@ -148,11 +147,8 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter<PlaylistAdapter.ViewH
|
||||||
DeletePlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST");
|
DeletePlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case R.id.action_add_to_playlist:
|
default:
|
||||||
AddToPlaylistDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId());
|
||||||
break;
|
|
||||||
case R.id.action_add_to_current_playing:
|
|
||||||
MusicPlayerRemote.enqueue(getSongList(selection));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,17 +152,7 @@ public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewH
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(MenuItem menuItem, ArrayList<File> selection) {
|
protected void onMultipleItemAction(MenuItem menuItem, ArrayList<File> selection) {
|
||||||
if (callbacks == null) return;
|
if (callbacks == null) return;
|
||||||
switch (menuItem.getItemId()) {
|
callbacks.onMultipleItemAction(menuItem, selection);
|
||||||
case R.id.action_add_to_current_playing:
|
|
||||||
callbacks.onAddToCurrentPlaying(selection);
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_playlist:
|
|
||||||
callbacks.onAddToPlaylist(selection);
|
|
||||||
break;
|
|
||||||
case R.id.action_delete_from_device:
|
|
||||||
callbacks.onDeleteFromDevice(selection);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
@ -218,10 +208,6 @@ public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewH
|
||||||
|
|
||||||
void onFileMenuClicked(File file, View view);
|
void onFileMenuClicked(File file, View view);
|
||||||
|
|
||||||
void onAddToPlaylist(ArrayList<File> files);
|
void onMultipleItemAction(MenuItem item, ArrayList<File> files);
|
||||||
|
|
||||||
void onAddToCurrentPlaying(ArrayList<File> files);
|
|
||||||
|
|
||||||
void onDeleteFromDevice(ArrayList<File> files);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,11 +17,9 @@ import com.kabouzeid.appthemehelper.util.MaterialValueHelper;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
||||||
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
||||||
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
|
||||||
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
|
||||||
import com.kabouzeid.gramophone.glide.PhonographColoredTarget;
|
import com.kabouzeid.gramophone.glide.PhonographColoredTarget;
|
||||||
import com.kabouzeid.gramophone.glide.SongGlideRequest;
|
import com.kabouzeid.gramophone.glide.SongGlideRequest;
|
||||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
|
||||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.model.Album;
|
import com.kabouzeid.gramophone.model.Album;
|
||||||
import com.kabouzeid.gramophone.model.Song;
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
|
|
@ -173,17 +171,7 @@ public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Album> selection) {
|
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Album> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId());
|
||||||
case R.id.action_delete_from_device:
|
|
||||||
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_playlist:
|
|
||||||
AddToPlaylistDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_current_playing:
|
|
||||||
MusicPlayerRemote.enqueue(getSongList(selection));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,11 @@ import com.kabouzeid.appthemehelper.util.MaterialValueHelper;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
||||||
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
||||||
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
|
||||||
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
|
||||||
import com.kabouzeid.gramophone.glide.PhonographColoredTarget;
|
import com.kabouzeid.gramophone.glide.PhonographColoredTarget;
|
||||||
import com.kabouzeid.gramophone.glide.artistimage.ArtistImage;
|
import com.kabouzeid.gramophone.glide.artistimage.ArtistImage;
|
||||||
import com.kabouzeid.gramophone.glide.palette.BitmapPaletteTranscoder;
|
import com.kabouzeid.gramophone.glide.palette.BitmapPaletteTranscoder;
|
||||||
import com.kabouzeid.gramophone.glide.palette.BitmapPaletteWrapper;
|
import com.kabouzeid.gramophone.glide.palette.BitmapPaletteWrapper;
|
||||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
|
||||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.loader.ArtistSongLoader;
|
import com.kabouzeid.gramophone.loader.ArtistSongLoader;
|
||||||
import com.kabouzeid.gramophone.model.Artist;
|
import com.kabouzeid.gramophone.model.Artist;
|
||||||
|
|
@ -175,24 +173,14 @@ public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolde
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Artist> selection) {
|
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Artist> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId());
|
||||||
case R.id.action_delete_from_device:
|
|
||||||
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_playlist:
|
|
||||||
AddToPlaylistDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_current_playing:
|
|
||||||
MusicPlayerRemote.enqueue(getSongList(selection));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private ArrayList<Song> getSongList(@NonNull List<Artist> artists) {
|
private ArrayList<Song> getSongList(@NonNull List<Artist> artists) {
|
||||||
final ArrayList<Song> songs = new ArrayList<>();
|
final ArrayList<Song> songs = new ArrayList<>();
|
||||||
for (Artist artist : artists) {
|
for (Artist artist : artists) {
|
||||||
songs.addAll(ArtistSongLoader.getArtistSongList(activity, artist.id));
|
songs.addAll(ArtistSongLoader.getArtistSongList(activity, artist.id)); // maybe async in future?
|
||||||
}
|
}
|
||||||
return songs;
|
return songs;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,10 @@ import android.widget.TextView;
|
||||||
import com.afollestad.materialcab.MaterialCab;
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
|
||||||
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
|
||||||
import com.kabouzeid.gramophone.glide.SongGlideRequest;
|
import com.kabouzeid.gramophone.glide.SongGlideRequest;
|
||||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||||
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
|
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
|
||||||
|
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
|
||||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.model.Song;
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
import com.kabouzeid.gramophone.util.NavigationUtil;
|
import com.kabouzeid.gramophone.util.NavigationUtil;
|
||||||
|
|
@ -137,17 +136,7 @@ public class ArtistSongAdapter extends ArrayAdapter<Song> implements MaterialCab
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
private void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
SongsMenuHelper.handleMenuClick(activity, selection, menuItem.getItemId());
|
||||||
case R.id.action_delete_from_device:
|
|
||||||
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_playlist:
|
|
||||||
AddToPlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_current_playing:
|
|
||||||
MusicPlayerRemote.enqueue(selection);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void toggleChecked(Song song) {
|
protected void toggleChecked(Song song) {
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,11 @@ import com.kabouzeid.appthemehelper.util.MaterialValueHelper;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
||||||
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
||||||
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
|
||||||
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
|
||||||
import com.kabouzeid.gramophone.glide.PhonographColoredTarget;
|
import com.kabouzeid.gramophone.glide.PhonographColoredTarget;
|
||||||
import com.kabouzeid.gramophone.glide.SongGlideRequest;
|
import com.kabouzeid.gramophone.glide.SongGlideRequest;
|
||||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||||
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
|
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
|
||||||
|
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
|
||||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.model.Song;
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
import com.kabouzeid.gramophone.util.MusicUtil;
|
import com.kabouzeid.gramophone.util.MusicUtil;
|
||||||
|
|
@ -173,17 +172,7 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
SongsMenuHelper.handleMenuClick(activity, selection, menuItem.getItemId());
|
||||||
case R.id.action_delete_from_device:
|
|
||||||
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_playlist:
|
|
||||||
AddToPlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
|
||||||
break;
|
|
||||||
case R.id.action_add_to_current_playing:
|
|
||||||
MusicPlayerRemote.enqueue(selection);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,20 @@ public class MusicPlayerRemote {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean playNext(@NonNull ArrayList<Song> songs) {
|
||||||
|
if (musicService != null) {
|
||||||
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
musicService.addSongs(getPosition() + 1, songs);
|
||||||
|
} else {
|
||||||
|
openQueue(songs, 0, false);
|
||||||
|
}
|
||||||
|
final String toast = songs.size() == 1 ? musicService.getResources().getString(R.string.added_title_to_playing_queue) : musicService.getResources().getString(R.string.added_x_titles_to_playing_queue, songs.size());
|
||||||
|
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean enqueue(Song song) {
|
public static boolean enqueue(Song song) {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
if (getPlayingQueue().size() > 0) {
|
if (getPlayingQueue().size() > 0) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.kabouzeid.gramophone.helper.menu;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
@ -25,8 +26,8 @@ import com.kabouzeid.gramophone.util.NavigationUtil;
|
||||||
public class SongMenuHelper {
|
public class SongMenuHelper {
|
||||||
public static final int MENU_RES = R.menu.menu_item_song;
|
public static final int MENU_RES = R.menu.menu_item_song;
|
||||||
|
|
||||||
public static boolean handleMenuClick(@NonNull AppCompatActivity activity, @NonNull Song song, @NonNull MenuItem item) {
|
public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull Song song, int menuItemId) {
|
||||||
switch (item.getItemId()) {
|
switch (menuItemId) {
|
||||||
case R.id.action_set_as_ringtone:
|
case R.id.action_set_as_ringtone:
|
||||||
MusicUtil.setRingtone(activity, song.id);
|
MusicUtil.setRingtone(activity, song.id);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -86,7 +87,7 @@ public class SongMenuHelper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
return handleMenuClick(activity, getSong(), item);
|
return handleMenuClick(activity, getSong(), item.getItemId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Song getSong();
|
public abstract Song getSong();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.kabouzeid.gramophone.helper.menu;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
|
||||||
|
import com.kabouzeid.gramophone.R;
|
||||||
|
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
||||||
|
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
||||||
|
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||||
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
|
*/
|
||||||
|
public class SongsMenuHelper {
|
||||||
|
public static boolean handleMenuClick(@NonNull FragmentActivity activity, @NonNull ArrayList<Song> songs, int menuItemId) {
|
||||||
|
switch (menuItemId) {
|
||||||
|
case R.id.action_play_next:
|
||||||
|
MusicPlayerRemote.playNext(songs);
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
MusicPlayerRemote.enqueue(songs);
|
||||||
|
return true;
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
AddToPlaylistDialog.create(songs).show(activity.getSupportFragmentManager(), "ADD_PLAYLIST");
|
||||||
|
return true;
|
||||||
|
case R.id.action_delete_from_device:
|
||||||
|
DeleteSongsDialog.create(songs).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -653,6 +653,12 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
|
||||||
notifyChange(QUEUE_CHANGED);
|
notifyChange(QUEUE_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSongs(int position, List<Song> songs) {
|
||||||
|
playingQueue.addAll(position, songs);
|
||||||
|
originalPlayingQueue.addAll(position, songs);
|
||||||
|
notifyChange(QUEUE_CHANGED);
|
||||||
|
}
|
||||||
|
|
||||||
public void addSongs(List<Song> songs) {
|
public void addSongs(List<Song> songs) {
|
||||||
playingQueue.addAll(songs);
|
playingQueue.addAll(songs);
|
||||||
originalPlayingQueue.addAll(songs);
|
originalPlayingQueue.addAll(songs);
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ import com.kabouzeid.appthemehelper.common.ATHToolbarActivity;
|
||||||
import com.kabouzeid.appthemehelper.util.ToolbarContentTintHelper;
|
import com.kabouzeid.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.adapter.SongFileAdapter;
|
import com.kabouzeid.gramophone.adapter.SongFileAdapter;
|
||||||
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
|
||||||
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
|
||||||
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||||
|
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
|
||||||
|
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
|
||||||
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader;
|
import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader;
|
||||||
import com.kabouzeid.gramophone.model.Song;
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
|
|
@ -75,11 +75,6 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
|
|
||||||
private static final int LOADER_ID = 1;
|
private static final int LOADER_ID = 1;
|
||||||
|
|
||||||
private static final int ADD_TO_PLAYLIST = 0;
|
|
||||||
private static final int ADD_TO_CURRENT_PLAYING = 1;
|
|
||||||
private static final int DELETE = 2;
|
|
||||||
private static final int PLAY = 3;
|
|
||||||
|
|
||||||
protected static final String PATH = "path";
|
protected static final String PATH = "path";
|
||||||
protected static final String CRUMBS = "crumbs";
|
protected static final String CRUMBS = "crumbs";
|
||||||
|
|
||||||
|
|
@ -301,20 +296,58 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
setCrumb(new BreadCrumbLayout.Crumb(file), true);
|
setCrumb(new BreadCrumbLayout.Crumb(file), true);
|
||||||
} else {
|
} else {
|
||||||
List<File> files = new LinkedList<>();
|
|
||||||
files.add(file.getParentFile());
|
|
||||||
|
|
||||||
FileFilter fileFilter = new FileFilter() {
|
FileFilter fileFilter = new FileFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File pathname) {
|
public boolean accept(File pathname) {
|
||||||
return !pathname.isDirectory() && getFileFilter().accept(pathname);
|
return !pathname.isDirectory() && getFileFilter().accept(pathname);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
new ListSongsAsyncTask(getActivity(), file, new ListSongsAsyncTask.OnSongsListedCallback() {
|
||||||
new ListSongsAsyncTask(this, PLAY, file).execute(new ListSongsAsyncTask.LoadingInfo(files, fileFilter, getFileComparator()));
|
@Override
|
||||||
|
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
|
||||||
|
File file = (File) extra;
|
||||||
|
int startIndex = -1;
|
||||||
|
for (int i = 0; i < songs.size(); i++) {
|
||||||
|
if (file.getPath().equals(songs.get(i).data)) { // path is already canonical here
|
||||||
|
startIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (startIndex > -1) {
|
||||||
|
MusicPlayerRemote.openQueue(songs, startIndex, true);
|
||||||
|
} else {
|
||||||
|
final File finalFile = file;
|
||||||
|
Snackbar.make(coordinatorLayout, Html.fromHtml(String.format(getString(R.string.not_listed_in_media_store), file.getName())), Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.action_scan, new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
new ListPathsAsyncTask(getActivity(), new ListPathsAsyncTask.OnPathsListedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPathsListed(@Nullable String[] paths) {
|
||||||
|
scanPaths(paths);
|
||||||
|
}
|
||||||
|
}).execute(new ListPathsAsyncTask.LoadingInfo(finalFile, getFileFilter()));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setActionTextColor(ThemeStore.accentColor(getActivity()))
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file.getParentFile()), fileFilter, getFileComparator()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMultipleItemAction(MenuItem item, ArrayList<File> files) {
|
||||||
|
final int itemId = item.getItemId();
|
||||||
|
new ListSongsAsyncTask(getActivity(), null, new ListSongsAsyncTask.OnSongsListedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
|
||||||
|
SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId);
|
||||||
|
}
|
||||||
|
}).execute(new ListSongsAsyncTask.LoadingInfo(files, getFileFilter(), getFileComparator()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileMenuClicked(final File file, View view) {
|
public void onFileMenuClicked(final File file, View view) {
|
||||||
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
|
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
|
||||||
|
|
@ -323,12 +356,30 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
final int itemId = item.getItemId();
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.action_play_next:
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
case R.id.action_delete_from_device:
|
||||||
|
new ListSongsAsyncTask(getActivity(), null, new ListSongsAsyncTask.OnSongsListedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
|
||||||
|
SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId);
|
||||||
|
}
|
||||||
|
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), getFileFilter(), getFileComparator()));
|
||||||
|
return true;
|
||||||
case R.id.action_set_as_start_directory:
|
case R.id.action_set_as_start_directory:
|
||||||
PreferenceUtil.getInstance(getActivity()).setStartDirectory(file);
|
PreferenceUtil.getInstance(getActivity()).setStartDirectory(file);
|
||||||
|
Toast.makeText(getActivity(), String.format(getString(R.string.new_start_directory), file.getPath()), Toast.LENGTH_SHORT).show();
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_scan:
|
case R.id.action_scan:
|
||||||
scan(file);
|
new ListPathsAsyncTask(getActivity(), new ListPathsAsyncTask.OnPathsListedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPathsListed(@Nullable String[] paths) {
|
||||||
|
scanPaths(paths);
|
||||||
|
}
|
||||||
|
}).execute(new ListPathsAsyncTask.LoadingInfo(file, getFileFilter()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -339,9 +390,32 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onMenuItemClick(MenuItem item) {
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
final int itemId = item.getItemId();
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.action_play_next:
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
case R.id.action_go_to_album:
|
||||||
|
case R.id.action_go_to_artist:
|
||||||
|
case R.id.action_share:
|
||||||
|
case R.id.action_tag_editor:
|
||||||
|
case R.id.action_details:
|
||||||
|
case R.id.action_set_as_ringtone:
|
||||||
|
case R.id.action_delete_from_device:
|
||||||
|
new ListSongsAsyncTask(getActivity(), null, new ListSongsAsyncTask.OnSongsListedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
|
||||||
|
SongMenuHelper.handleMenuClick(getActivity(), songs.get(0), itemId);
|
||||||
|
}
|
||||||
|
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), getFileFilter(), getFileComparator()));
|
||||||
|
return true;
|
||||||
case R.id.action_scan:
|
case R.id.action_scan:
|
||||||
scan(file);
|
new ListPathsAsyncTask(getActivity(), new ListPathsAsyncTask.OnPathsListedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPathsListed(@Nullable String[] paths) {
|
||||||
|
scanPaths(paths);
|
||||||
|
}
|
||||||
|
}).execute(new ListPathsAsyncTask.LoadingInfo(file, getFileFilter()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -351,23 +425,10 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
popupMenu.show();
|
popupMenu.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scan(File file) {
|
private ArrayList<File> toList(File file) {
|
||||||
new ScanFilesAsyncTask(this, file).execute(getFileFilter());
|
ArrayList<File> files = new ArrayList<>(1);
|
||||||
}
|
files.add(file);
|
||||||
|
return files;
|
||||||
@Override
|
|
||||||
public void onAddToPlaylist(ArrayList<File> files) {
|
|
||||||
new ListSongsAsyncTask(this, ADD_TO_PLAYLIST, null).execute(new ListSongsAsyncTask.LoadingInfo(files, getFileFilter(), getFileComparator()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAddToCurrentPlaying(ArrayList<File> files) {
|
|
||||||
new ListSongsAsyncTask(this, ADD_TO_CURRENT_PLAYING, null).execute(new ListSongsAsyncTask.LoadingInfo(files, getFileFilter(), getFileComparator()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDeleteFromDevice(ArrayList<File> files) {
|
|
||||||
new ListSongsAsyncTask(this, DELETE, null).execute(new ListSongsAsyncTask.LoadingInfo(files, getFileFilter(), getFileComparator()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Comparator<File> fileComparator = new Comparator<File>() {
|
Comparator<File> fileComparator = new Comparator<File>() {
|
||||||
|
|
@ -419,6 +480,60 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scanPaths(@Nullable String[] toBeScanned) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
if (toBeScanned == null || toBeScanned.length < 1) {
|
||||||
|
Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
MediaScannerConnection.scanFile(getActivity().getApplicationContext(), toBeScanned, null, new UpdateToastCompletionListener(getActivity(), toBeScanned));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UpdateToastCompletionListener implements MediaScannerConnection.OnScanCompletedListener {
|
||||||
|
int scanned = 0;
|
||||||
|
int failed = 0;
|
||||||
|
|
||||||
|
private final String[] toBeScanned;
|
||||||
|
|
||||||
|
private final String scannedFiles;
|
||||||
|
private final String couldNotScanFiles;
|
||||||
|
|
||||||
|
private final WeakReference<Toast> toastWeakReference;
|
||||||
|
private final WeakReference<Activity> activityWeakReference;
|
||||||
|
|
||||||
|
@SuppressLint("ShowToast")
|
||||||
|
public UpdateToastCompletionListener(Activity activity, String[] toBeScanned) {
|
||||||
|
this.toBeScanned = toBeScanned;
|
||||||
|
scannedFiles = activity.getString(R.string.scanned_files);
|
||||||
|
couldNotScanFiles = activity.getString(R.string.could_not_scan_files);
|
||||||
|
toastWeakReference = new WeakReference<>(Toast.makeText(activity, "", Toast.LENGTH_SHORT));
|
||||||
|
activityWeakReference = new WeakReference<>(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanCompleted(final String path, final Uri uri) {
|
||||||
|
Activity activity = activityWeakReference.get();
|
||||||
|
if (activity != null) {
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast toast = toastWeakReference.get();
|
||||||
|
if (toast != null) {
|
||||||
|
if (uri == null) {
|
||||||
|
failed++;
|
||||||
|
} else {
|
||||||
|
scanned++;
|
||||||
|
}
|
||||||
|
String text = " " + String.format(scannedFiles, scanned, toBeScanned.length) + (failed > 0 ? " " + String.format(couldNotScanFiles, failed) : "");
|
||||||
|
toast.setText(text);
|
||||||
|
toast.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateAdapter(@NonNull List<File> files) {
|
private void updateAdapter(@NonNull List<File> files) {
|
||||||
adapter.swapDataSet(files);
|
adapter.swapDataSet(files);
|
||||||
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
|
||||||
|
|
@ -470,60 +585,23 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSongsListed(int requestCode, ArrayList<Song> songs, Object extra) {
|
|
||||||
switch (requestCode) {
|
|
||||||
case ADD_TO_PLAYLIST:
|
|
||||||
AddToPlaylistDialog.create(songs).show(getFragmentManager(), "ADD_PLAYLIST");
|
|
||||||
break;
|
|
||||||
case ADD_TO_CURRENT_PLAYING:
|
|
||||||
MusicPlayerRemote.enqueue(songs);
|
|
||||||
break;
|
|
||||||
case DELETE:
|
|
||||||
DeleteSongsDialog.create(songs).show(getFragmentManager(), "DELETE_SONGS");
|
|
||||||
break;
|
|
||||||
case PLAY:
|
|
||||||
File file = (File) extra;
|
|
||||||
int startIndex = -1;
|
|
||||||
for (int i = 0; i < songs.size(); i++) {
|
|
||||||
if (file.getPath().equals(songs.get(i).data)) { // path is already canonical here
|
|
||||||
startIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (startIndex > -1) {
|
|
||||||
MusicPlayerRemote.openQueue(songs, startIndex, true);
|
|
||||||
} else {
|
|
||||||
final File finalFile = file;
|
|
||||||
Snackbar.make(coordinatorLayout, Html.fromHtml(String.format(getString(R.string.not_listed_in_media_store), file.getName())), Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(R.string.action_scan, new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
scan(finalFile);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setActionTextColor(ThemeStore.accentColor(getActivity()))
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ListSongsAsyncTask extends DialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> {
|
private static class ListSongsAsyncTask extends DialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> {
|
||||||
private WeakReference<FoldersFragment> fragmentWeakReference;
|
private WeakReference<Context> contextWeakReference;
|
||||||
private final int requestCode;
|
private WeakReference<OnSongsListedCallback> callbackWeakReference;
|
||||||
private final Object extra;
|
private final Object extra;
|
||||||
|
|
||||||
public ListSongsAsyncTask(FoldersFragment foldersFragment, int requestCode, Object extra) {
|
public ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
|
||||||
super(foldersFragment.getActivity(), R.string.listing_files);
|
super(context, R.string.listing_files);
|
||||||
this.requestCode = requestCode;
|
|
||||||
this.extra = extra;
|
this.extra = extra;
|
||||||
fragmentWeakReference = new WeakReference<>(foldersFragment);
|
contextWeakReference = new WeakReference<>(context);
|
||||||
|
callbackWeakReference = new WeakReference<>(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
checkFragmentReference();
|
checkCallbackReference();
|
||||||
|
checkContextReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -532,14 +610,16 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
LoadingInfo info = params[0];
|
LoadingInfo info = params[0];
|
||||||
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
|
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
|
||||||
|
|
||||||
if (isCancelled() || checkFragmentReference() == null) return null;
|
if (isCancelled() || checkContextReference() == null || checkCallbackReference() == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
Collections.sort(files, info.fileComparator);
|
Collections.sort(files, info.fileComparator);
|
||||||
|
|
||||||
FoldersFragment fragment = checkFragmentReference();
|
Context context = checkContextReference();
|
||||||
if (isCancelled() || fragment == null) return null;
|
if (isCancelled() || context == null || checkCallbackReference() == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
return FileUtil.matchFilesWithMediaStore(fragment.getActivity(), files);
|
return FileUtil.matchFilesWithMediaStore(context, files);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
cancel(false);
|
cancel(false);
|
||||||
|
|
@ -550,9 +630,25 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(ArrayList<Song> songs) {
|
protected void onPostExecute(ArrayList<Song> songs) {
|
||||||
super.onPostExecute(songs);
|
super.onPostExecute(songs);
|
||||||
FoldersFragment fragment = checkFragmentReference();
|
OnSongsListedCallback callback = checkCallbackReference();
|
||||||
if (!songs.isEmpty() && fragment != null)
|
if (songs != null && callback != null && !songs.isEmpty())
|
||||||
fragment.onSongsListed(requestCode, songs, extra);
|
callback.onSongsListed(songs, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context checkContextReference() {
|
||||||
|
Context context = contextWeakReference.get();
|
||||||
|
if (context == null) {
|
||||||
|
cancel(false);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OnSongsListedCallback checkCallbackReference() {
|
||||||
|
OnSongsListedCallback callback = callbackWeakReference.get();
|
||||||
|
if (callback == null) {
|
||||||
|
cancel(false);
|
||||||
|
}
|
||||||
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LoadingInfo {
|
public static class LoadingInfo {
|
||||||
|
|
@ -567,118 +663,57 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public interface OnSongsListedCallback {
|
||||||
* @return true if the task was canceled
|
void onSongsListed(@NonNull ArrayList<Song> songs, Object extra);
|
||||||
*/
|
|
||||||
private FoldersFragment checkFragmentReference() {
|
|
||||||
FoldersFragment fragment = fragmentWeakReference.get();
|
|
||||||
if (fragment == null) {
|
|
||||||
cancel(false);
|
|
||||||
}
|
|
||||||
return fragment;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onScanPaths(final String[] toBeScanned) {
|
private static class ListPathsAsyncTask extends DialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> {
|
||||||
if (getActivity() == null) return;
|
private WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference;
|
||||||
if (toBeScanned == null || toBeScanned.length < 1) {
|
|
||||||
Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
MediaScannerConnection.scanFile(getActivity().getApplicationContext(), toBeScanned, null, new UpdateToastCompletionListener(getActivity(), toBeScanned));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class UpdateToastCompletionListener implements MediaScannerConnection.OnScanCompletedListener {
|
public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) {
|
||||||
int scanned = 0;
|
super(context, R.string.listing_files);
|
||||||
int failed = 0;
|
onPathsListedCallbackWeakReference = new WeakReference<>(callback);
|
||||||
|
|
||||||
private final String[] toBeScanned;
|
|
||||||
|
|
||||||
private final String scannedFiles;
|
|
||||||
private final String couldNotScanFiles;
|
|
||||||
|
|
||||||
private final WeakReference<Toast> toastWeakReference;
|
|
||||||
private final WeakReference<Activity> activityWeakReference;
|
|
||||||
|
|
||||||
@SuppressLint("ShowToast")
|
|
||||||
public UpdateToastCompletionListener(Activity activity, String[] toBeScanned) {
|
|
||||||
this.toBeScanned = toBeScanned;
|
|
||||||
scannedFiles = activity.getString(R.string.scanned_files);
|
|
||||||
couldNotScanFiles = activity.getString(R.string.could_not_scan_files);
|
|
||||||
toastWeakReference = new WeakReference<>(Toast.makeText(activity, "", Toast.LENGTH_SHORT));
|
|
||||||
activityWeakReference = new WeakReference<>(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onScanCompleted(final String path, final Uri uri) {
|
|
||||||
Activity activity = activityWeakReference.get();
|
|
||||||
if (activity != null) {
|
|
||||||
activity.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast toast = toastWeakReference.get();
|
|
||||||
if (toast != null) {
|
|
||||||
if (uri == null) {
|
|
||||||
failed++;
|
|
||||||
} else {
|
|
||||||
scanned++;
|
|
||||||
}
|
|
||||||
String text = " " + String.format(scannedFiles, scanned, toBeScanned.length) + (failed > 0 ? " " + String.format(couldNotScanFiles, failed) : "");
|
|
||||||
toast.setText(text);
|
|
||||||
toast.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ScanFilesAsyncTask extends DialogAsyncTask<FileFilter, String, String[]> {
|
|
||||||
private final File file;
|
|
||||||
private WeakReference<FoldersFragment> fragmentWeakReference;
|
|
||||||
|
|
||||||
public ScanFilesAsyncTask(FoldersFragment foldersFragment, File file) {
|
|
||||||
super(foldersFragment.getActivity(), R.string.listing_files);
|
|
||||||
this.file = file;
|
|
||||||
fragmentWeakReference = new WeakReference<>(foldersFragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
super.onPreExecute();
|
super.onPreExecute();
|
||||||
checkFragmentReference();
|
checkCallbackReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] doInBackground(FileFilter... params) {
|
protected String[] doInBackground(LoadingInfo... params) {
|
||||||
try {
|
try {
|
||||||
if (isCancelled() || checkFragmentReference() == null) return null;
|
if (isCancelled() || checkCallbackReference() == null) return null;
|
||||||
|
|
||||||
final String[] toBeScanned;
|
LoadingInfo info = params[0];
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
final String[] paths;
|
||||||
List<File> files = FileUtil.listFilesDeep(file, params[0]);
|
|
||||||
|
|
||||||
if (isCancelled() || checkFragmentReference() == null) return null;
|
if (info.file.isDirectory()) {
|
||||||
|
List<File> files = FileUtil.listFilesDeep(info.file, info.fileFilter);
|
||||||
|
|
||||||
toBeScanned = new String[files.size()];
|
if (isCancelled() || checkCallbackReference() == null) return null;
|
||||||
|
|
||||||
|
paths = new String[files.size()];
|
||||||
for (int i = 0; i < files.size(); i++) {
|
for (int i = 0; i < files.size(); i++) {
|
||||||
File f = files.get(i);
|
File f = files.get(i);
|
||||||
try {
|
try {
|
||||||
toBeScanned[i] = f.getCanonicalPath(); // canonical path is important here because we want to compare the path with the media store entry later
|
paths[i] = f.getCanonicalPath(); // canonical path is important here because we want to compare the path with the media store entry later
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
toBeScanned[i] = f.getPath();
|
paths[i] = f.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCancelled() || checkFragmentReference() == null) return toBeScanned;
|
if (isCancelled() || checkCallbackReference() == null) return paths;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toBeScanned = new String[1];
|
paths = new String[1];
|
||||||
toBeScanned[0] = file.getPath();
|
paths[0] = info.file.getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
return toBeScanned;
|
return paths;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
cancel(false);
|
cancel(false);
|
||||||
|
|
@ -687,20 +722,34 @@ public class FoldersFragment extends AbsMainActivityFragment implements MainActi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(String[] toBeScanned) {
|
protected void onPostExecute(String[] paths) {
|
||||||
super.onPostExecute(toBeScanned);
|
super.onPostExecute(paths);
|
||||||
FoldersFragment fragment = checkFragmentReference();
|
OnPathsListedCallback callback = checkCallbackReference();
|
||||||
if (fragment != null) {
|
if (callback != null) {
|
||||||
fragment.onScanPaths(toBeScanned);
|
callback.onPathsListed(paths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FoldersFragment checkFragmentReference() {
|
private OnPathsListedCallback checkCallbackReference() {
|
||||||
FoldersFragment fragment = fragmentWeakReference.get();
|
OnPathsListedCallback callback = onPathsListedCallbackWeakReference.get();
|
||||||
if (fragment == null) {
|
if (callback == null) {
|
||||||
cancel(false);
|
cancel(false);
|
||||||
}
|
}
|
||||||
return fragment;
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LoadingInfo {
|
||||||
|
public final File file;
|
||||||
|
public final FileFilter fileFilter;
|
||||||
|
|
||||||
|
public LoadingInfo(File file, FileFilter fileFilter) {
|
||||||
|
this.file = file;
|
||||||
|
this.fileFilter = fileFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnPathsListedCallback {
|
||||||
|
void onPathsListed(@Nullable String[] paths);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
app/src/main/res/drawable/ic_redo_white_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_redo_white_24dp.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z" />
|
||||||
|
</vector>
|
||||||
|
|
@ -3,15 +3,21 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_playlist"
|
android:id="@+id/action_play_next"
|
||||||
android:icon="@drawable/ic_playlist_add_white_24dp"
|
android:icon="@drawable/ic_redo_white_24dp"
|
||||||
android:title="@string/action_add_to_playlist"
|
android:title="@string/action_play_next"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_current_playing"
|
android:id="@+id/action_add_to_current_playing"
|
||||||
android:icon="@drawable/ic_library_add_white_24dp"
|
android:icon="@drawable/ic_library_add_white_24dp"
|
||||||
android:title="@string/action_add_to_playing_queue"
|
android:title="@string/action_add_to_playing_queue"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:icon="@drawable/ic_playlist_add_white_24dp"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/action_play_next"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_current_playing"
|
||||||
|
android:title="@string/action_add_to_playing_queue"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_set_as_start_directory"
|
android:id="@+id/action_set_as_start_directory"
|
||||||
android:title="@string/action_set_as_start_directory"
|
android:title="@string/action_set_as_start_directory"
|
||||||
|
|
@ -9,4 +21,8 @@
|
||||||
android:id="@+id/action_scan"
|
android:id="@+id/action_scan"
|
||||||
android:title="@string/action_scan"
|
android:title="@string/action_scan"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete_from_device"
|
||||||
|
android:title="@string/action_delete_from_device"
|
||||||
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -1,8 +1,48 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_play_next"
|
||||||
|
android:title="@string/action_play_next"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_current_playing"
|
||||||
|
android:title="@string/action_add_to_playing_queue"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_go_to_album"
|
||||||
|
android:title="@string/action_go_to_album"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_go_to_artist"
|
||||||
|
android:title="@string/action_go_to_artist"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_share"
|
||||||
|
android:title="@string/action_share"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_tag_editor"
|
||||||
|
android:title="@string/action_tag_editor"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_details"
|
||||||
|
android:title="@string/action_details"
|
||||||
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_scan"
|
android:id="@+id/action_scan"
|
||||||
android:title="@string/action_scan"
|
android:title="@string/action_scan"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_set_as_ringtone"
|
||||||
|
android:title="@string/action_set_as_ringtone"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete_from_device"
|
||||||
|
android:title="@string/action_delete_from_device"
|
||||||
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -3,21 +3,27 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_playlist"
|
android:id="@+id/action_play_next"
|
||||||
android:icon="@drawable/ic_playlist_add_white_24dp"
|
android:icon="@drawable/ic_redo_white_24dp"
|
||||||
android:title="@string/action_add_to_playlist"
|
android:title="@string/action_play_next"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_current_playing"
|
android:id="@+id/action_add_to_current_playing"
|
||||||
android:icon="@drawable/ic_library_add_white_24dp"
|
android:icon="@drawable/ic_library_add_white_24dp"
|
||||||
android:title="@string/action_add_to_playing_queue"
|
android:title="@string/action_add_to_playing_queue"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:icon="@drawable/ic_playlist_add_white_24dp"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_from_device"
|
android:id="@+id/action_delete_from_device"
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
android:title="@string/action_delete_from_device"
|
android:title="@string/action_delete_from_device"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -1,23 +1,29 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_playlist"
|
android:id="@+id/action_play_next"
|
||||||
android:icon="@drawable/ic_playlist_add_white_24dp"
|
android:icon="@drawable/ic_redo_white_24dp"
|
||||||
android:title="@string/action_add_to_playlist"
|
android:title="@string/action_play_next"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_current_playing"
|
android:id="@+id/action_add_to_current_playing"
|
||||||
android:icon="@drawable/ic_library_add_white_24dp"
|
android:icon="@drawable/ic_library_add_white_24dp"
|
||||||
android:title="@string/action_add_to_playing_queue"
|
android:title="@string/action_add_to_playing_queue"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:icon="@drawable/ic_playlist_add_white_24dp"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_playlist"
|
android:id="@+id/action_delete_playlist"
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
android:title="@string/delete_playlists_title"
|
android:title="@string/delete_playlists_title"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -1,23 +1,29 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_playlist"
|
android:id="@+id/action_play_next"
|
||||||
android:icon="@drawable/ic_playlist_add_white_24dp"
|
android:icon="@drawable/ic_redo_white_24dp"
|
||||||
android:title="@string/action_add_to_playlist"
|
android:title="@string/action_play_next"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_add_to_current_playing"
|
android:id="@+id/action_add_to_current_playing"
|
||||||
android:icon="@drawable/ic_library_add_white_24dp"
|
android:icon="@drawable/ic_library_add_white_24dp"
|
||||||
android:title="@string/action_add_to_playing_queue"
|
android:title="@string/action_add_to_playing_queue"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add_to_playlist"
|
||||||
|
android:icon="@drawable/ic_playlist_add_white_24dp"
|
||||||
|
android:title="@string/action_add_to_playlist"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_remove_from_playlist"
|
android:id="@+id/action_remove_from_playlist"
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
android:title="@string/action_remove_from_playlist"
|
android:title="@string/action_remove_from_playlist"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -245,4 +245,5 @@
|
||||||
<string name="scanned_files">Scanned %1$d of %2$d files.</string>
|
<string name="scanned_files">Scanned %1$d of %2$d files.</string>
|
||||||
<string name="could_not_scan_files">Could not scan %d files.</string>
|
<string name="could_not_scan_files">Could not scan %d files.</string>
|
||||||
<string name="listing_files">Listing files</string>
|
<string name="listing_files">Listing files</string>
|
||||||
|
<string name="new_start_directory">%s is the new start directory.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue