Java tutorial
/* * Created by Angel Leon (@gubatron), Alden Torres (aldenml) * Copyright (c) 2011-2016, FrostWire(R). All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.andrew.apollo.ui.fragments.profile; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.view.*; import android.widget.*; import com.andrew.apollo.MusicStateListener; import com.andrew.apollo.adapters.ApolloFragmentAdapter; import com.andrew.apollo.loaders.PlaylistLoader; import com.andrew.apollo.menu.CreateNewPlaylist; import com.andrew.apollo.menu.DeleteDialog; import com.andrew.apollo.menu.FragmentMenuItems; import com.andrew.apollo.model.*; import com.andrew.apollo.provider.FavoritesStore; import com.andrew.apollo.provider.RecentStore; import com.andrew.apollo.recycler.RecycleHolder; import com.andrew.apollo.ui.activities.BaseActivity; import com.andrew.apollo.utils.ApolloUtils; import com.andrew.apollo.utils.MusicUtils; import com.andrew.apollo.utils.NavUtils; import com.andrew.apollo.utils.PreferenceUtils; import com.andrew.apollo.widgets.ProfileTabCarousel; import com.andrew.apollo.widgets.VerticalScrollListener; import com.devspark.appmsg.AppMsg; import com.frostwire.android.R; import com.frostwire.logging.Logger; import com.viewpagerindicator.TitlePageIndicator; import java.util.List; /** * Created by gubatron on 1/26/16 on a plane. * * @author gubatron * @author aldenml */ public abstract class ApolloFragment<T extends ApolloFragmentAdapter<I>, I> extends Fragment implements LoaderManager.LoaderCallbacks<List<I>>, AdapterView.OnItemClickListener, MusicStateListener { @SuppressWarnings("unused") private static Logger LOGGER = Logger.getLogger(ApolloFragment.class); private final int GROUP_ID; /** * LoaderCallbacks identifier */ private final int LOADER_ID; /** * The list view */ protected ListView mListView; /** * The grid view */ protected GridView mGridView; /** * The adapter for the list */ protected T mAdapter; /** * Represents a song/album/ */ protected I mItem; /** * Song list. The playlist's, the album's, the artist's discography available. */ private long[] mSongList; /** * Id of a context menu item */ private long mSelectedId; /** * The Id of the playlist the song belongs to */ long mPlaylistId; /** * Song, album, and artist name used in the context menu */ private String mSongName, mAlbumName, mArtistName; /** * Profile header */ ProfileTabCarousel mProfileTabCarousel; protected ViewGroup mRootView; private volatile long lastRestartLoader; protected abstract T createAdapter(); protected abstract String getLayoutTypeName(); public abstract void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id); protected ApolloFragment(int groupId, int loaderId) { this.GROUP_ID = groupId; this.LOADER_ID = loaderId; } @SuppressWarnings("deprecation") @Override public void onAttach(final Activity activity) { super.onAttach(activity); mProfileTabCarousel = (ProfileTabCarousel) activity.findViewById(R.id.activity_profile_base_tab_carousel); if (activity instanceof BaseActivity) { // Register the music status listener ((BaseActivity) activity).setMusicStateListenerListener(this); } } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { // The View for the fragment's UI if (isSimpleLayout()) { mRootView = (ViewGroup) inflater.inflate(R.layout.list_base, null); initListView(); } else { // this inflate here is crashing. mRootView = (ViewGroup) inflater.inflate(R.layout.grid_base, null); initGridView(); } return mRootView; } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create the adapter mAdapter = createAdapter(); } public T getAdapter() { return mAdapter; } @Override public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); // Get the position of the selected item final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; int mSelectedPosition = info.position - mAdapter.getOffset(); // Create a new song mItem = mAdapter.getItem(mSelectedPosition); // TODO: Remove these mutable properties, parametrize the onMenuEvent handlers. if (mItem instanceof Song) { Song mSong = (Song) mItem; mSelectedId = mSong.mSongId; mSongName = mSong.mSongName; mAlbumName = mSong.mAlbumName; mArtistName = mSong.mArtistName; mSongList = null; } else if (mItem instanceof Album) { Album mAlbum = (Album) mItem; mSelectedId = mAlbum.mAlbumId; mSongName = null; mAlbumName = mAlbum.mAlbumName; mArtistName = mAlbum.mArtistName; mSongList = MusicUtils.getSongListForAlbum(getActivity(), mAlbum.mAlbumId); } else if (mItem instanceof Artist) { Artist mArtist = (Artist) mItem; mSelectedId = mArtist.mArtistId; mSongName = null; mArtistName = mArtist.mArtistName; mSongList = MusicUtils.getSongListForArtist(getActivity(), mArtist.mArtistId); } else if (mItem instanceof Genre) { Genre mGenre = (Genre) mItem; mSelectedId = mGenre.mGenreId; mSongList = MusicUtils.getSongListForGenre(getActivity(), mGenre.mGenreId); } else if (mItem instanceof Playlist) { Playlist mPlaylist = (Playlist) mItem; mSelectedId = mPlaylist.mPlaylistId; if (mSelectedId == PlaylistLoader.FAVORITE_PLAYLIST_ID) { mSongList = MusicUtils.getSongListForFavorites(getActivity()); } else if (mSelectedId == PlaylistLoader.LAST_ADDED_PLAYLIST_ID) { mSongList = MusicUtils.getSongListForLastAdded(getActivity()); } else { mSongList = MusicUtils.getSongListForPlaylist(getActivity(), mPlaylist.mPlaylistId); } } // Play the selected songs menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE, getString(R.string.context_menu_play_selection)); // Play the next song menu.add(GROUP_ID, FragmentMenuItems.PLAY_NEXT, Menu.NONE, getString(R.string.context_menu_play_next)); // Add the song/album to the queue menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE, getString(R.string.add_to_queue)); // Add the song to favorite's playlist menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_FAVORITES, Menu.NONE, R.string.add_to_favorites); // Add the song/album to a playlist final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST, Menu.NONE, R.string.add_to_playlist); MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu, true); if (mItem instanceof Song) { menu.add(GROUP_ID, FragmentMenuItems.USE_AS_RINGTONE, Menu.NONE, getString(R.string.context_menu_use_as_ringtone)); } // More by artist menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE, getString(R.string.context_menu_more_by_artist)); // Delete the album menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE, getString(R.string.context_menu_delete)); } @Override public boolean onContextItemSelected(final android.view.MenuItem item) { if (item.getGroupId() == GROUP_ID) { final long[] songList = mSongList != null ? mSongList : new long[] { mSelectedId }; switch (item.getItemId()) { case FragmentMenuItems.PLAY_SELECTION: MusicUtils.playAll(songList, 0, false); return true; case FragmentMenuItems.PLAY_NEXT: MusicUtils.playNext(songList); return true; case FragmentMenuItems.ADD_TO_QUEUE: MusicUtils.addToQueue(getActivity(), songList); return true; case FragmentMenuItems.ADD_TO_FAVORITES: onAddToFavorites(); return true; case FragmentMenuItems.REMOVE_FROM_FAVORITES: onRemoveFromFavorites(); return true; case FragmentMenuItems.NEW_PLAYLIST: CreateNewPlaylist.getInstance(songList).show(getFragmentManager(), "CreatePlaylist"); return true; case FragmentMenuItems.PLAYLIST_SELECTED: final long playlistId = item.getIntent().getLongExtra("playlist", 0); MusicUtils.addToPlaylist(getActivity(), songList, playlistId); refresh(); return true; case FragmentMenuItems.USE_AS_RINGTONE: MusicUtils.setRingtone(getActivity(), mSelectedId); return true; case FragmentMenuItems.DELETE: return onDelete(songList); case FragmentMenuItems.MORE_BY_ARTIST: long artistId = MusicUtils.getIdForArtist(getContext(), mArtistName); long[] tracks = MusicUtils.getSongListForArtist(getActivity(), artistId); NavUtils.openArtistProfile(getActivity(), mArtistName, tracks); return true; case FragmentMenuItems.REMOVE_FROM_PLAYLIST: return onRemoveFromPlaylist(); case FragmentMenuItems.REMOVE_FROM_RECENT: return onRemoveFromRecent(); default: break; } } return super.onContextItemSelected(item); } private boolean onRemoveFromRecent() { RecentStore.getInstance(getActivity()).removeItem(mSelectedId); MusicUtils.refresh(); restartLoader(true); return true; } private boolean onDelete(long[] songList) { if (songList == null || songList.length == 0) { return false; } String title = getResources().getString(R.string.unknown); if (mItem instanceof Song) { title = ((Song) mItem).mSongName; } else if (mItem instanceof Album) { title = ((Album) mItem).mAlbumName; } else if (mItem instanceof Artist) { title = ((Artist) mItem).mArtistName; } DeleteDialog.newInstance(title, songList, null) .setOnDeleteCallback(new DeleteDialog.DeleteDialogCallback() { @Override public void onDelete(long[] id) { restartLoader(true); refresh(); } }).show(getFragmentManager(), "DeleteDialog"); return true; } private boolean onRemoveFromPlaylist() { mAdapter.remove(mItem); mAdapter.notifyDataSetChanged(); if (mItem instanceof Song) { Song song = (Song) mItem; MusicUtils.removeFromPlaylist(getActivity(), song.mSongId, mPlaylistId); restartLoader(true); return true; } return false; } private void onAddToFavorites() { if (mSongList != null) { int added = 0; for (Long songId : mSongList) { try { final Song song = MusicUtils.getSong(getActivity(), songId); if (song != null) { FavoritesStore.getInstance(getActivity()).addSongId(songId, song.mSongName, song.mAlbumName, song.mArtistName); added++; } } catch (Throwable ignored) { ignored.printStackTrace(); } } if (added > 0) { final String message = getResources().getQuantityString(R.plurals.NNNtrackstoplaylist, added, added); AppMsg.makeText(getActivity(), message, AppMsg.STYLE_CONFIRM).show(); } } else if (mSelectedId != -1) { FavoritesStore.getInstance(getActivity()).addSongId(mSelectedId, mSongName, mAlbumName, mArtistName); } } private void onRemoveFromFavorites() { mAdapter.remove(mItem); mAdapter.notifyDataSetChanged(); FavoritesStore.getInstance(getActivity()).removeItem(mSelectedId); refresh(); } /** * {@inheritDoc} */ @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Enable the options menu setHasOptionsMenu(true); initLoader(); } @Override public void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); outState.putAll(getArguments() != null ? getArguments() : new Bundle()); } /** * {@inheritDoc} */ @Override public void onLoadFinished(final Loader<List<I>> loader, final List<I> data) { // Check for any errors if (data == null || data.isEmpty()) { mAdapter.unload(); mAdapter.notifyDataSetChanged(); // Set the empty text final TextView empty = (TextView) mRootView.findViewById(R.id.empty); if (empty != null) { empty.setText(getString(R.string.empty_music)); if (isSimpleLayout()) { mListView.setEmptyView(empty); } else { mGridView.setEmptyView(empty); } } return; } if (mAdapter == null) { mAdapter = createAdapter(); if (isSimpleLayout()) { mListView.setAdapter(mAdapter); } else { mGridView.setAdapter(mAdapter); } } else { mAdapter.unload(); } // Start fresh if (mAdapter != null) { mAdapter.setDataList(data); mAdapter.setNotifyOnChange(false); for (final I item : data) { mAdapter.add(item); } mAdapter.setNotifyOnChange(true); if (mAdapter instanceof ApolloFragmentAdapter.Cacheable) { ((ApolloFragmentAdapter.Cacheable) mAdapter).buildCache(); } mAdapter.notifyDataSetChanged(); } } @Override public void onLoaderReset(final Loader<List<I>> loader) { // Clear the data in the adapter if (mAdapter != null) { mAdapter.unload(); } } /** * Restarts the loader. * (Don't do so until 10 seconds later if you refreshed already) */ public void refresh() { // Scroll to the stop of the list before restarting the loader. // Otherwise, if the user has scrolled enough to move the header, it // becomes misplaced and needs to be reset. if (mListView != null) { mListView.setSelection(0); } else if (mGridView != null) { mGridView.setSelection(0); } if (mAdapter != null) { mAdapter.clear(); } restartLoader(); // this won't be executed if it was recently called. no risk of endless loop. if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } } @Override public void onPause() { super.onPause(); if (mAdapter != null) { mAdapter.flush(); } } public void restartLoader() { restartLoader(false); } public boolean restartLoader(boolean force) { if (force || (System.currentTimeMillis() - lastRestartLoader) >= 5000) { lastRestartLoader = System.currentTimeMillis(); try { getLoaderManager().restartLoader(LOADER_ID, getArguments(), this); return true; } catch (Throwable t) { return false; } } return false; } private void initLoader() { final Intent intent = getActivity().getIntent(); if (intent != null && intent.getExtras() != null) { getLoaderManager().initLoader(LOADER_ID, intent.getExtras(), this); } else { restartLoader(true); } } public void onMetaChanged() { restartLoader(); } protected boolean isSimpleLayout() { //boolean r = PreferenceUtils.getInstance(getActivity()).isSimpleLayout(getLayoutTypeName()); return true; } protected boolean isDetailedLayout() { return PreferenceUtils.getInstance(getActivity()).isDetailedLayout(getLayoutTypeName()); } /** * Sets up the grid view */ private void initGridView() { if (mRootView == null) { throw new RuntimeException("initGridView(): mRootView == null"); } // Initialize the grid mGridView = (GridView) mRootView.findViewById(R.id.grid_base); if (mGridView != null && mAdapter != null) { // Set the data behind the grid mGridView.setAdapter(mAdapter); // Set up the helpers initAbsListView(mGridView); } if (ApolloUtils.isLandscape(getActivity())) { if (isDetailedLayout()) { if (mAdapter != null) { mAdapter.setLoadExtraData(true); } mGridView.setNumColumns(2); } else { mGridView.setNumColumns(4); } } else { if (isDetailedLayout()) { if (mAdapter != null) { mAdapter.setLoadExtraData(true); } mGridView.setNumColumns(1); } else { mGridView.setNumColumns(2); } } } /** * Sets up various helpers for both the list and grid * * @param list The list or grid */ private void initAbsListView(final AbsListView list) { // Release any references to the recycled Views list.setRecyclerListener(new RecycleHolder()); // Listen for ContextMenus to be created list.setOnCreateContextMenuListener(this); // Show the albums and songs from the selected artist list.setOnItemClickListener(this); // To help make scrolling smooth if (mProfileTabCarousel != null) { list.setOnScrollListener(new VerticalScrollListener(null, mProfileTabCarousel, 0)); // Remove the scrollbars and padding for the fast scroll list.setVerticalScrollBarEnabled(false); list.setFastScrollEnabled(false); list.setPadding(0, 0, 0, 0); } } /** * Sets up the list view */ protected void initListView() { if (mRootView == null) { throw new RuntimeException("initListView(): mRootView == null"); } // Initialize the grid mListView = (ListView) mRootView.findViewById(R.id.list_base); // Set the data behind the list if (mAdapter != null) { mListView.setAdapter(mAdapter); } // Set up the helpers initAbsListView(mListView); } /** * Pause disk cache access to ensure smoother scrolling */ final VerticalScrollListener.ScrollableHeader mScrollableHeader = new VerticalScrollListener.ScrollableHeader() { @Override public void onScrollStateChanged(final AbsListView view, final int scrollState) { // Pause disk cache access to ensure smoother scrolling if (mAdapter == null) { return; } mAdapter.setPauseDiskCache(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); } }; /** * @return The position of an item in the list or grid based on the id of * the currently playing album. */ private int getItemPositionByAlbum() { final long albumId = MusicUtils.getCurrentAlbumId(); if (mAdapter == null) { return 0; } for (int i = 0; i < mAdapter.getCount(); i++) { if (((Album) mAdapter.getItem(i)).mAlbumId == albumId) { return i; } } return 0; } /** * Scrolls the list to the currently playing album when the user touches the * header in the {@link TitlePageIndicator}. */ public void scrollToCurrentAlbum() { final int currentAlbumPosition = getItemPositionByAlbum(); if (currentAlbumPosition != 0) { if (isSimpleLayout()) { mListView.setSelection(currentAlbumPosition); } else { mGridView.setSelection(currentAlbumPosition); } } } /** * Scrolls the list to the currently playing song when the user touches the * header in the {@link TitlePageIndicator}. */ public void scrollToCurrentSong() { final int currentSongPosition = getItemPositionBySong(); if (currentSongPosition != 0) { mListView.setSelection(currentSongPosition); } } /** * @return The position of an item in the list based on the name of the * currently playing song. */ private int getItemPositionBySong() { final long trackId = MusicUtils.getCurrentAudioId(); if (mAdapter == null) { return 0; } for (int i = 0; i < mAdapter.getCount(); i++) { if (((Song) mAdapter.getItem(i)).mSongId == trackId) { return i; } } return 0; } }