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.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.ActionMode; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; 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 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.AdaptableItem; import com.simplecity.amp_library.model.AlbumArtist; import com.simplecity.amp_library.model.Playlist; import com.simplecity.amp_library.model.Song; import com.simplecity.amp_library.ui.adapters.AlbumArtistAdapter; import com.simplecity.amp_library.ui.modelviews.AlbumArtistView; import com.simplecity.amp_library.ui.modelviews.EmptyView; import com.simplecity.amp_library.ui.modelviews.ViewType; import com.simplecity.amp_library.ui.recyclerview.GridDividerDecoration; import com.simplecity.amp_library.utils.ColorUtils; import com.simplecity.amp_library.utils.DataManager; 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.SettingsManager; import com.simplecity.amp_library.utils.ShuttleUtils; import com.simplecity.amp_library.utils.SortManager; import com.simplecity.amp_library.utils.ThemeUtils; import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.Collections; import java.util.List; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; public class AlbumArtistFragment extends BaseFragment implements MusicUtils.Defs, AlbumArtistAdapter.AlbumArtistListener, RecyclerView.RecyclerListener { public interface AlbumArtistClickListener { void onItemClicked(AlbumArtist albumArtist, View transitionView); } private static final String TAG = "AlbumArtistFragment"; private static final String ARG_PAGE_TITLE = "page_title"; private static final int MENU_GRID_SIZE = 100; private static final int MENU_GROUP_GRID = 1; private SharedPreferences sharedPreferences; private AlbumArtistClickListener albumArtistClickListener; private FastScrollRecyclerView recyclerView; private GridLayoutManager layoutManager; AlbumArtistAdapter albumArtistAdapter; MultiSelector multiSelector = new MultiSelector(); ActionMode actionMode; boolean inActionMode = false; private BroadcastReceiver broadcastReceiver; private SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener; private boolean sortOrderChanged = false; private Subscription subscription; private RequestManager requestManager; public static AlbumArtistFragment newInstance(String pageTitle) { Bundle args = new Bundle(); AlbumArtistFragment fragment = new AlbumArtistFragment(); args.putString(ARG_PAGE_TITLE, pageTitle); fragment.setArguments(args); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); albumArtistClickListener = (AlbumArtistClickListener) getActivity(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); albumArtistAdapter = new AlbumArtistAdapter(); albumArtistAdapter.setListener(this); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity()); broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction() != null && intent.getAction().equals("restartLoader")) { refreshAdapterItems(); } } }; onSharedPreferenceChangeListener = (sharedPreferences, key) -> { if (key.equals("pref_theme_highlight_color") || key.equals("pref_theme_accent_color") || key.equals("pref_theme_white_accent")) { themeUIComponents(); } else if (key.equals("artistWhitelist")) { refreshAdapterItems(); } }; sharedPreferences.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener); if (requestManager == null) { requestManager = Glide.with(this); } } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (recyclerView == null) { int spanCount = SettingsManager.getInstance().getArtistColumnCount(getResources()); layoutManager = new GridLayoutManager(getContext(), spanCount); layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { if (albumArtistAdapter.items.get(position) instanceof EmptyView) { return spanCount; } return 1; } }); recyclerView = (FastScrollRecyclerView) inflater.inflate(R.layout.fragment_recycler, container, false); recyclerView.setLayoutManager(layoutManager); recyclerView.addItemDecoration(new GridDividerDecoration(getResources(), 4, true)); recyclerView.setRecyclerListener(this); recyclerView.setAdapter(albumArtistAdapter); actionMode = null; themeUIComponents(); } return recyclerView; } @Override public void onPause() { if (broadcastReceiver != null) { getActivity().unregisterReceiver(broadcastReceiver); } if (subscription != null) { subscription.unsubscribe(); } super.onPause(); } @Override public void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(); filter.addAction("restartLoader"); getActivity().registerReceiver(broadcastReceiver, filter); refreshAdapterItems(); } @Override public void onDestroy() { sharedPreferences.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener); super.onDestroy(); } private void themeUIComponents() { ThemeUtils.themeRecyclerView(recyclerView); recyclerView.setThumbColor(ColorUtils.getAccentColor()); recyclerView.setPopupBgColor(ColorUtils.getAccentColor()); recyclerView.setPopupTextColor(ColorUtils.getAccentColorSensitiveTextColor(getContext())); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { ThemeUtils.themeRecyclerView(recyclerView); super.onScrollStateChanged(recyclerView, newState); } }); } void refreshAdapterItems() { PermissionUtils.RequestStoragePermissions(() -> { if (getActivity() != null && isAdded()) { int artistDisplayType = SettingsManager.getInstance().getArtistDisplayType(); boolean ascending = SortManager.getInstance().getArtistsAscending(); subscription = DataManager.getInstance().getAlbumArtistsRelay() .flatMap(albumArtists -> { //Sort SortManager.getInstance().sortAlbumArtists(albumArtists); //Reverse if required if (!ascending) { Collections.reverse(albumArtists); } return Observable.from(albumArtists) .map(albumArtist -> (AdaptableItem) new AlbumArtistView(albumArtist, artistDisplayType, multiSelector, requestManager)) .toList(); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(items -> { if (items.isEmpty()) { albumArtistAdapter.setEmpty(new EmptyView(R.string.empty_artists)); } else { albumArtistAdapter.setItems(items); } //Move the RV back to the top if we've had a sort order change. if (sortOrderChanged) { recyclerView.scrollToPosition(0); } sortOrderChanged = false; }); } }); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_sort_artists, menu); inflater.inflate(R.menu.menu_view_as, menu); menu.addSubMenu(0, MENU_GRID_SIZE, 0, R.string.menu_grid_size); SubMenu subMenu = menu.findItem(MENU_GRID_SIZE).getSubMenu(); int[] columnRange = getResources().getIntArray(R.array.column_range); for (int i = 0; i < columnRange.length; i++) { subMenu.add(MENU_GROUP_GRID, columnRange[i] + 1000, i, String.valueOf(columnRange[i])); } subMenu.setGroupCheckable(MENU_GROUP_GRID, true, true); } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); //Strip the 'asc' or 'desc' flag, we just want to know the sort type int sortOrder = SortManager.getInstance().getArtistsSortOrder(); switch (sortOrder) { case SortManager.ArtistSort.DEFAULT: MenuItem sortDefault = menu.findItem(R.id.sort_default); if (sortDefault != null) { sortDefault.setChecked(true); } break; case SortManager.ArtistSort.NAME: MenuItem sortArtistName = menu.findItem(R.id.sort_artist_name); if (sortArtistName != null) { sortArtistName.setChecked(true); } break; } MenuItem sortAscending = menu.findItem(R.id.sort_ascending); if (sortAscending != null) { sortAscending.setChecked(SortManager.getInstance().getArtistsAscending()); } int displayType = SettingsManager.getInstance().getArtistDisplayType(); switch (displayType) { case ViewType.ARTIST_LIST: MenuItem viewAsList = menu.findItem(R.id.view_as_list); if (viewAsList != null) { viewAsList.setChecked(true); } break; case ViewType.ARTIST_GRID: MenuItem viewAsGrid = menu.findItem(R.id.view_as_grid); if (viewAsGrid != null) { viewAsGrid.setChecked(true); } break; case ViewType.ARTIST_CARD: MenuItem viewAsGridCard = menu.findItem(R.id.view_as_grid_card); if (viewAsGridCard != null) { viewAsGridCard.setChecked(true); } break; case ViewType.ARTIST_PALETTE: MenuItem viewAsGridPalette = menu.findItem(R.id.view_as_grid_palette); if (viewAsGridPalette != null) { viewAsGridPalette.setChecked(true); } break; } MenuItem gridMenuItem = menu.findItem(MENU_GRID_SIZE); if (displayType == ViewType.ARTIST_LIST) { gridMenuItem.setVisible(false); } else { gridMenuItem.setVisible(true); int columnCount = SettingsManager.getInstance().getArtistColumnCount(getResources()); gridMenuItem.getSubMenu().findItem(columnCount + 1000).setChecked(true); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.sort_default: SortManager.getInstance().setArtistsSortOrder(SortManager.ArtistSort.DEFAULT); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.sort_artist_name: SortManager.getInstance().setArtistsSortOrder(SortManager.ArtistSort.NAME); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.sort_ascending: SortManager.getInstance().setArtistsAscending(!item.isChecked()); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.view_as_list: SettingsManager.getInstance().setArtistDisplayType(ViewType.ARTIST_LIST); layoutManager.setSpanCount(getResources().getInteger(R.integer.list_num_columns)); albumArtistAdapter.updateItemViewType(); albumArtistAdapter.notifyItemRangeChanged(0, albumArtistAdapter.getItemCount()); break; case R.id.view_as_grid: SettingsManager.getInstance().setArtistDisplayType(ViewType.ARTIST_GRID); layoutManager.setSpanCount(SettingsManager.getInstance().getArtistColumnCount(getResources())); albumArtistAdapter.updateItemViewType(); albumArtistAdapter.notifyItemRangeChanged(0, albumArtistAdapter.getItemCount()); break; case R.id.view_as_grid_card: SettingsManager.getInstance().setArtistDisplayType(ViewType.ARTIST_CARD); layoutManager.setSpanCount(SettingsManager.getInstance().getArtistColumnCount(getResources())); albumArtistAdapter.updateItemViewType(); albumArtistAdapter.notifyItemRangeChanged(0, albumArtistAdapter.getItemCount()); break; case R.id.view_as_grid_palette: SettingsManager.getInstance().setArtistDisplayType(ViewType.ARTIST_PALETTE); layoutManager.setSpanCount(SettingsManager.getInstance().getArtistColumnCount(getResources())); albumArtistAdapter.updateItemViewType(); albumArtistAdapter.notifyItemRangeChanged(0, albumArtistAdapter.getItemCount()); break; } if (item.getGroupId() == MENU_GROUP_GRID) { SettingsManager.getInstance().setArtistColumnCount(item.getItemId() - 1000); if (SettingsManager.getInstance().getArtistDisplayType() != ViewType.ARTIST_LIST) { ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(SettingsManager.getInstance().getArtistColumnCount(getResources())); albumArtistAdapter.notifyItemRangeChanged(0, albumArtistAdapter.getItemCount()); } } getActivity().supportInvalidateOptionsMenu(); return super.onOptionsItemSelected(item); } @Override public void onItemClick(View v, int position, AlbumArtist albumArtist) { if (inActionMode) { multiSelector.setSelected(position, albumArtistAdapter.getItemId(position), !multiSelector.isSelected(position, albumArtistAdapter.getItemId(position))); if (multiSelector.getSelectedPositions().size() == 0) { if (actionMode != null) { actionMode.finish(); } } updateActionModeSelectionCount(); } else { albumArtistClickListener.onItemClicked(albumArtist, v.findViewById(R.id.image)); } } @Override public void onOverflowClick(View v, int position, AlbumArtist albumArtist) { PopupMenu menu = new PopupMenu(AlbumArtistFragment.this.getActivity(), v); MenuUtils.addAlbumArtistMenuOptions(getActivity(), menu); MenuUtils.addClickHandler((AppCompatActivity) getActivity(), menu, albumArtist); menu.show(); } @Override public void onLongClick(View v, int position, AlbumArtist albumArtist) { if (inActionMode) { return; } if (multiSelector.getSelectedPositions().size() == 0) { actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback); inActionMode = true; } multiSelector.setSelected(position, albumArtistAdapter.getItemId(position), !multiSelector.isSelected(position, albumArtistAdapter.getItemId(position))); updateActionModeSelectionCount(); } @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { if (holder.getAdapterPosition() != -1) { albumArtistAdapter.items.get(holder.getAdapterPosition()).recycle(holder); } } private void updateActionModeSelectionCount() { if (actionMode != null && multiSelector != null) { actionMode.setTitle(getString(R.string.action_mode_selection_count, multiSelector.getSelectedPositions().size())); } } private ActionMode.Callback mActionModeCallback = new ModalMultiSelectorCallback(multiSelector) { @Override public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { ThemeUtils.themeContextualActionBar(getActivity()); inActionMode = true; MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.context_menu_songs, menu); SubMenu sub = menu.getItem(0).getSubMenu(); PlaylistUtils.makePlaylistMenu(AlbumArtistFragment.this.getActivity(), sub, ARTIST_FRAGMENT_GROUP_ID); return true; } @Override public boolean onActionItemClicked(final ActionMode mode, MenuItem menuItem) { List<AlbumArtist> checkedAlbumArtists = getCheckedAlbumArtists(); if (checkedAlbumArtists == null || checkedAlbumArtists.size() == 0) { return true; } Observable<List<Song>> songsObservable = Observable.defer(() -> Observable.from(checkedAlbumArtists) .flatMap(AlbumArtist::getSongsObservable) .reduce((songs, songs2) -> Stream.concat(Stream.of(songs), Stream.of(songs2)) .collect(Collectors.toList())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) ); switch (menuItem.getItemId()) { case NEW_PLAYLIST: songsObservable.subscribe(songs -> PlaylistUtils.createPlaylistDialog(getActivity(), songs)); return true; case PLAYLIST_SELECTED: songsObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(songs -> { Playlist playlist = (Playlist) menuItem.getIntent().getSerializableExtra(ShuttleUtils.ARG_PLAYLIST); PlaylistUtils.addToPlaylist(getContext(), playlist, songs); }); return true; case R.id.delete: new DialogUtils.DeleteDialogBuilder() .context(getContext()) .singleMessageId(R.string.delete_album_artist_desc) .multipleMessage(R.string.delete_album_artist_desc_multiple) .itemNames(Stream.of(checkedAlbumArtists) .map(albumArtist -> albumArtist.name) .collect(Collectors.toList())) .songsToDelete(songsObservable) .build() .show(); mode.finish(); return true; case R.id.menu_add_to_queue: { songsObservable.subscribe(songs -> MusicUtils.addToQueue(getActivity(), songs)); return true; } } return false; } @Override public void onDestroyActionMode(ActionMode actionMode) { super.onDestroyActionMode(actionMode); inActionMode = false; AlbumArtistFragment.this.actionMode = null; multiSelector.clearSelections(); } }; List<AlbumArtist> getCheckedAlbumArtists() { return Stream.of(multiSelector.getSelectedPositions()) .map(i -> ((AlbumArtistView) albumArtistAdapter.items.get(i)).albumArtist) .collect(Collectors.toList()); } @Override protected String screenName() { return TAG; } }