package com.simplecity.amp_library.ui.fragments; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.ActionMode; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; import com.bignerdranch.android.multiselector.ModalMultiSelectorCallback; import com.bignerdranch.android.multiselector.MultiSelector; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import com.simplecity.amp_library.R; import com.simplecity.amp_library.model.Playlist; import com.simplecity.amp_library.model.Song; import com.simplecity.amp_library.playback.MusicService; import com.simplecity.amp_library.sql.databases.BlacklistHelper; import com.simplecity.amp_library.ui.activities.MainActivity; import com.simplecity.amp_library.ui.adapters.QueueAdapter; import com.simplecity.amp_library.ui.adapters.SongAdapter; import com.simplecity.amp_library.ui.modelviews.SongView; import com.simplecity.amp_library.ui.recyclerview.ItemTouchHelperCallback; import com.simplecity.amp_library.ui.recyclerview.ScrollBlockingRecyclerView; import com.simplecity.amp_library.utils.ColorUtils; import com.simplecity.amp_library.utils.DialogUtils; import com.simplecity.amp_library.utils.MenuUtils; import com.simplecity.amp_library.utils.MusicUtils; import com.simplecity.amp_library.utils.PermissionUtils; import com.simplecity.amp_library.utils.PlaylistUtils; import com.simplecity.amp_library.utils.ShuttleUtils; import com.simplecity.amp_library.utils.ThemeUtils; import com.simplecityapps.recyclerview_fastscroll.interfaces.OnFastScrollStateChangeListener; import java.util.List; import rx.Observable; public class QueueFragment extends BaseFragment implements MusicUtils.Defs, SongAdapter.SongListener, RecyclerView.RecyclerListener { private static final String TAG = "QueueFragment"; private SharedPreferences prefs; private View rootView; private ScrollBlockingRecyclerView recyclerView; private ItemTouchHelper itemTouchHelper; QueueAdapter songAdapter; private View header; private TextView lineTwo; MultiSelector multiSelector = new MultiSelector(); ActionMode actionMode; boolean inActionMode = false; private BroadcastReceiver receiver; private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener; private RequestManager requestManager; public static QueueFragment newInstance() { Bundle args = new Bundle(); QueueFragment fragment = new QueueFragment(); fragment.setArguments(args); return fragment; } public QueueFragment() { } @Override public void onAttach(Context context) { super.onAttach(context); if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).toggleQueue(true); } } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); songAdapter = new QueueAdapter(); songAdapter.setListener(this); prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity()); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action != null) { switch (action) { case MusicService.InternalIntents.META_CHANGED: updateTrackInfo(); songAdapter.notifyDataSetChanged(); break; case MusicService.InternalIntents.QUEUE_CHANGED: if (intent.getBooleanExtra(MusicService.FROM_USER, false)) { } else { refreshAdapterItems(); } break; case MusicService.InternalIntents.SHUFFLE_CHANGED: refreshAdapterItems(); break; case MusicService.InternalIntents.PLAY_STATE_CHANGED: case MusicService.InternalIntents.SERVICE_CONNECTED: updateTrackInfo(); if (songAdapter != null) { songAdapter.notifyDataSetChanged(); } break; } } } }; sharedPreferenceChangeListener = (sharedPreferences, key) -> { if (key.equals("pref_theme_highlight_color") || key.equals("pref_theme_accent_color") || key.equals("pref_theme_white_accent")) { themeUIComponents(); } }; prefs.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); if (requestManager == null) { requestManager = Glide.with(this); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_queue, container, false); header = rootView.findViewById(R.id.header); if (ShuttleUtils.isLandscape() || ShuttleUtils.isTablet()) { header.setVisibility(View.GONE); } else { header.setVisibility(View.VISIBLE); } lineTwo = (TextView) rootView.findViewById(R.id.line2); recyclerView = (ScrollBlockingRecyclerView) rootView.findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setRecyclerListener(this); recyclerView.setAdapter(songAdapter); recyclerView.setStateChangeListener(new OnFastScrollStateChangeListener() { @Override public void onFastScrollStart() { recyclerView.setBlockScroll(false); } @Override public void onFastScrollStop() { recyclerView.setBlockScroll(true); } }); itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback((fromPosition, toPosition) -> { songAdapter.moveItem(fromPosition, toPosition); }, (fromPosition, toPosition) -> { MusicUtils.moveQueueItem(fromPosition, toPosition); recyclerView.setBlockScroll(true); }, () -> { //We've finished our drag event. Allow the sliding up panel to intercept touch events recyclerView.setBlockScroll(true); })); itemTouchHelper.attachToRecyclerView(recyclerView); themeUIComponents(); updateTrackInfo(); return rootView; } @Override public void onPause() { if (receiver != null) { getActivity().unregisterReceiver(receiver); } if (getParentFragment() != null && getParentFragment() instanceof PlayerFragment) { ((PlayerFragment) getParentFragment()).setDragView(null); } if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setScrollableView(null); } super.onPause(); } @Override public void onResume() { super.onResume(); songAdapter.notifyItemRangeChanged(0, songAdapter.getItemCount()); IntentFilter filter = new IntentFilter(); filter.addAction(MusicService.InternalIntents.META_CHANGED); filter.addAction(MusicService.InternalIntents.QUEUE_CHANGED); filter.addAction(MusicService.InternalIntents.SHUFFLE_CHANGED); filter.addAction(MusicService.InternalIntents.PLAY_STATE_CHANGED); filter.addAction(MusicService.InternalIntents.SERVICE_CONNECTED); getActivity().registerReceiver(receiver, filter); updateTrackInfo(); if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setScrollableView(recyclerView); } if (getParentFragment() != null && getParentFragment() instanceof PlayerFragment) { ((PlayerFragment) getParentFragment()).setDragView(rootView); } refreshAdapterItems(); } @Override public void onDestroy() { prefs.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); super.onDestroy(); } void refreshAdapterItems() { PermissionUtils.RequestStoragePermissions(() -> { if (getActivity() != null && isAdded()) { int count = songAdapter.items.size(); songAdapter.items.clear(); songAdapter.notifyItemRangeRemoved(0, count); songAdapter.items.addAll(Stream.of(MusicUtils.getQueue()) .map(song -> { SongView songView = new SongView(song, multiSelector, requestManager); songView.setShowAlbumArt(true); songView.setEditable(true); return songView; }).collect(Collectors.toList())); songAdapter.notifyItemRangeInserted(0, songAdapter.items.size()); scrollToCurrentItem(); } }); } void updateTrackInfo() { if (lineTwo != null) { String trackName = MusicUtils.getSongName(); String artistName = MusicUtils.getAlbumArtistName(); if (trackName != null) { lineTwo.setText(MusicUtils.getSongName()); if (artistName != null) { lineTwo.setText(String.format("%s | %s", trackName, artistName)); } } } } private void themeUIComponents() { ThemeUtils.themeRecyclerView(recyclerView); recyclerView.setThumbColor(ColorUtils.getAccentColor()); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { ThemeUtils.themeRecyclerView(recyclerView); super.onScrollStateChanged(recyclerView, newState); } }); if (header != null) { header.setBackgroundColor(ColorUtils.getPrimaryColor()); } } @Override public void onItemClick(View v, int position, Song song) { if (inActionMode) { multiSelector.setSelected(position, songAdapter.getItemId(position), !multiSelector.isSelected(position, songAdapter.getItemId(position))); if (multiSelector.getSelectedPositions().size() == 0) { if (actionMode != null) { actionMode.finish(); } } } else { MusicUtils.setQueuePosition(position); songAdapter.notifyDataSetChanged(); } } @Override public void onOverflowClick(View v, int position, Song song) { PopupMenu menu = new PopupMenu(QueueFragment.this.getActivity(), v); MenuUtils.addQueueMenuOptions(getActivity(), menu); MenuUtils.addClickHandler((AppCompatActivity) getActivity(), menu, song, item -> { switch (item.getItemId()) { case REMOVE: { MusicUtils.removeFromQueue(song, true); songAdapter.removeItem(position); return true; } case BLACKLIST: { BlacklistHelper.addToBlacklist(song); MusicUtils.removeFromQueue(song, true); songAdapter.removeItem(position); return true; } } return false; }); menu.show(); } @Override public void onLongClick(View v, int position, Song song) { if (inActionMode) { return; } if (multiSelector.getSelectedPositions().size() == 0) { actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback); inActionMode = true; } multiSelector.setSelected(position, songAdapter.getItemId(position), !multiSelector.isSelected(position, songAdapter.getItemId(position))); } @Override public void onShuffleClick() { //Nothing to do } @Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { if (itemTouchHelper != null) { //We've started a drag event, so don't allow the SlidingUpPanel to intercept touch events recyclerView.setBlockScroll(false); itemTouchHelper.startDrag(viewHolder); } } @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { if (holder.getAdapterPosition() != -1) { songAdapter.items.get(holder.getAdapterPosition()).recycle(holder); } } public void scrollToCurrentItem() { recyclerView.scrollToPosition(MusicUtils.getQueuePosition()); } private ActionMode.Callback mActionModeCallback = new ModalMultiSelectorCallback(multiSelector) { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { ThemeUtils.themeContextualActionBar(getActivity()); inActionMode = true; MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.context_menu_queue, menu); SubMenu sub = menu.getItem(0).getSubMenu(); PlaylistUtils.makePlaylistMenu(QueueFragment.this.getActivity(), sub, SONG_FRAGMENT_GROUP_ID); return true; } private List<Song> getCheckedSongs() { return Stream.of(multiSelector.getSelectedPositions()) .map(i -> songAdapter.getSong(i)) .collect(Collectors.toList()); } @Override public boolean onActionItemClicked(final ActionMode mode, MenuItem item) { final List<Song> checkedSongs = getCheckedSongs(); if (checkedSongs == null || checkedSongs.size() == 0) { return true; } switch (item.getItemId()) { case NEW_PLAYLIST: PlaylistUtils.createPlaylistDialog(getActivity(), checkedSongs); break; case PLAYLIST_SELECTED: Playlist playlist = (Playlist) item.getIntent().getSerializableExtra(ShuttleUtils.ARG_PLAYLIST); PlaylistUtils.addToPlaylist(getContext(), playlist, checkedSongs); break; case R.id.delete: new DialogUtils.DeleteDialogBuilder() .context(getContext()) .singleMessageId(R.string.delete_song_desc) .multipleMessage(R.string.delete_song_desc_multiple) .itemNames(Stream.of(checkedSongs) .map(song -> song.name) .collect(Collectors.toList())) .songsToDelete(Observable.just(checkedSongs)) .build() .show(); mode.finish(); break; } return true; } @Override public void onDestroyActionMode(ActionMode actionMode) { super.onDestroyActionMode(actionMode); inActionMode = false; QueueFragment.this.actionMode = null; multiSelector.clearSelections(); } }; @Override protected String screenName() { return TAG; } }