Java tutorial
/* * Copyright (C) 2010-2014 The MPDroid Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.audiokernel.euphonyrmt.fragments; import com.audiokernel.euphonyrmt.MPDApplication; import com.audiokernel.euphonyrmt.R; import com.audiokernel.euphonyrmt.adapters.ArrayIndexerAdapter; import com.audiokernel.euphonyrmt.helpers.MPDAsyncHelper.AsyncExecListener; import com.audiokernel.euphonyrmt.library.SimpleLibraryActivity; import com.audiokernel.euphonyrmt.tools.Tools; import org.a0z.mpd.MPDCommand; import org.a0z.mpd.MPDStatus; import org.a0z.mpd.exception.MPDException; import org.a0z.mpd.item.AbstractAlbum; import org.a0z.mpd.item.AbstractMusic; import org.a0z.mpd.item.Album; import org.a0z.mpd.item.Artist; import org.a0z.mpd.item.Item; import org.a0z.mpd.item.Music; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.support.annotation.StringRes; import android.support.v4.app.Fragment; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.EditText; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; import java.io.IOException; import java.util.List; import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh; import uk.co.senab.actionbarpulltorefresh.library.PullToRefreshLayout; import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener; public abstract class BrowseFragment extends Fragment implements OnMenuItemClickListener, AsyncExecListener, OnItemClickListener, OnRefreshListener { public static final int ADD = 0; public static final int ADD_PLAY = 2; public static final int ADD_REPLACE = 1; public static final int ADD_REPLACE_PLAY = 4; public static final int ADD_TO_PLAYLIST = 3; public static final int GOTO_ARTIST = 5; public static final int MAIN = 0; public static final int PLAYLIST = 3; public static final int POPUP_COVER_BLACKLIST = 10; public static final int POPUP_COVER_SELECTIVE_CLEAN = 11; private static final int MIN_ITEMS_BEFORE_FASTSCROLL = 50; private static final String TAG = "BrowseFragment"; protected final MPDApplication mApp = MPDApplication.getInstance(); final String mContext; final int mIrAdd; final int mIrAdded; protected List<? extends Item> mItems = null; protected int mJobID = -1; protected AbsListView mList; protected TextView mLoadingTextView; protected View mLoadingView; protected View mNoResultView; protected PullToRefreshLayout mPullToRefreshLayout; private boolean mFirstUpdateDone = false; protected BrowseFragment(@StringRes final int rAdd, @StringRes final int rAdded, final String pContext) { super(); mIrAdd = rAdd; mIrAdded = rAdded; mContext = pContext; setHasOptionsMenu(false); } protected abstract void add(Item item, boolean replace, boolean play); protected abstract void add(Item item, String playlist); private void addAndReplace(final MenuItem item) { final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); mApp.oMPDAsyncHelper.execAsync(new Runnable() { @Override public void run() { boolean replace = false; boolean play = false; switch (item.getGroupId()) { case ADD_REPLACE_PLAY: replace = true; play = true; break; case ADD_REPLACE: replace = true; break; case ADD_PLAY: final MPDStatus status = mApp.oMPDAsyncHelper.oMPD.getStatus(); /** * Let the user know if we're not going to play the added music. */ if (status.isRandom() && status.isState(MPDStatus.STATE_PLAYING)) { Tools.notifyUser(R.string.notPlayingInRandomMode); } else { play = true; } break; default: break; } add(mItems.get((int) info.id), replace, play); } }); } private void addToPlaylist(final MenuItem item) { final EditText input = new EditText(getActivity()); final int id = item.getOrder(); if (item.getItemId() == 0) { new AlertDialog.Builder(getActivity()).setTitle(R.string.playlistName) .setMessage(R.string.newPlaylistPrompt).setView(input) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { final String name = input.getText().toString().trim(); if (!name.isEmpty()) { mApp.oMPDAsyncHelper.execAsync(new Runnable() { @Override public void run() { add(mItems.get(id), name); } }); } } }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { // Do nothing. } }).show(); } else { add(mItems.get(id), item.getTitle().toString()); } } @Override public void asyncExecSucceeded(final int jobID) { if (mJobID == jobID) { updateFromItems(); } } protected void asyncUpdate() { } // Override if you want setEmptyView to be called on the list even if you have a header protected boolean forceEmptyView() { return false; } protected ListAdapter getCustomListAdapter() { return new ArrayIndexerAdapter(getActivity(), R.layout.simple_list_item_1, mItems); } /* * Override this to display a custom loading text */ @StringRes public int getLoadingText() { return R.string.loading; } /** * Should return the minimum number of songs in the queue before the * fastscroll thumb is shown */ protected int getMinimumItemsCountBeforeFastscroll() { return MIN_ITEMS_BEFORE_FASTSCROLL; } /* * Override this to display a custom activity title */ public String getTitle() { return ""; } @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final ActionBarActivity activity = (ActionBarActivity) getActivity(); if (activity != null) { final ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } } } @Override public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenu.ContextMenuInfo menuInfo) { final AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; final int index = (int) info.id; if (index >= 0 && mItems.size() > index) { menu.setHeaderTitle(mItems.get((int) info.id).toString()); // If in simple mode, show "Play" (add, replace & play), "Add to queue" and "Add to playlist" if (mApp.isInSimpleMode()) { final MenuItem playItem = menu.add(ADD_REPLACE_PLAY, ADD_REPLACE_PLAY, 0, R.string.play); playItem.setOnMenuItemClickListener(this); final MenuItem addItem = menu.add(ADD, ADD, 0, R.string.addToQueue); addItem.setOnMenuItemClickListener(this); } else { final MenuItem addItem = menu.add(ADD, ADD, 0, mIrAdd); addItem.setOnMenuItemClickListener(this); final MenuItem addAndReplaceItem = menu.add(ADD_REPLACE, ADD_REPLACE, 0, R.string.addAndReplace); addAndReplaceItem.setOnMenuItemClickListener(this); final MenuItem addAndReplacePlayItem = menu.add(ADD_REPLACE_PLAY, ADD_REPLACE_PLAY, 0, R.string.addAndReplacePlay); addAndReplacePlayItem.setOnMenuItemClickListener(this); final MenuItem addAndPlayItem = menu.add(ADD_PLAY, ADD_PLAY, 0, R.string.addAndPlay); addAndPlayItem.setOnMenuItemClickListener(this); } if (R.string.addPlaylist != mIrAdd && R.string.addStream != mIrAdd && mApp.oMPDAsyncHelper.oMPD.isCommandAvailable(MPDCommand.MPD_CMD_LISTPLAYLISTS)) { int id = 0; final SubMenu playlistMenu = menu.addSubMenu(R.string.addToPlaylist); MenuItem item = playlistMenu.add(ADD_TO_PLAYLIST, id++, (int) info.id, R.string.newPlaylist); item.setOnMenuItemClickListener(this); try { final List<Item> playlists = mApp.oMPDAsyncHelper.oMPD.getPlaylists(); if (null != playlists) { for (final Item pl : playlists) { item = playlistMenu.add(ADD_TO_PLAYLIST, id++, (int) info.id, pl.getName()); item.setOnMenuItemClickListener(this); } } } catch (final IOException | MPDException e) { Log.e(TAG, "Failed to parse playlists.", e); } } final MenuItem gotoArtistItem = menu.add(GOTO_ARTIST, GOTO_ARTIST, 0, R.string.goToArtist); gotoArtistItem.setOnMenuItemClickListener(this); } } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.browse, container, false); mList = (AbsListView) view.findViewById(R.id.list); registerForContextMenu(mList); mList.setOnItemClickListener(this); mLoadingView = view.findViewById(R.id.loadingLayout); mLoadingTextView = (TextView) view.findViewById(R.id.loadingText); mNoResultView = view.findViewById(R.id.noResultLayout); mLoadingTextView.setText(getLoadingText()); mPullToRefreshLayout = (PullToRefreshLayout) view.findViewById(R.id.pullToRefresh); return view; } @Override public void onDestroy() { try { mApp.oMPDAsyncHelper.removeAsyncExecListener(this); } catch (final Exception e) { Log.e(TAG, "Error while destroying BrowseFragment", e); } super.onDestroy(); } @Override public void onDestroyView() { // help out the GC; imitated from ListFragment source mLoadingView = null; mLoadingTextView = null; mNoResultView = null; super.onDestroyView(); } @Override public boolean onMenuItemClick(final MenuItem item) { switch (item.getGroupId()) { case ADD_REPLACE_PLAY: case ADD_REPLACE: case ADD: case ADD_PLAY: addAndReplace(item); break; case ADD_TO_PLAYLIST: addToPlaylist(item); break; case GOTO_ARTIST: final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); final Object selectedItem = mItems.get((int) info.id); final Intent intent = new Intent(getActivity(), SimpleLibraryActivity.class); Artist artist = null; if (selectedItem instanceof Album) { artist = ((AbstractAlbum) selectedItem).getArtist(); } else if (selectedItem instanceof Artist) { artist = (Artist) selectedItem; } else if (selectedItem instanceof Music) { artist = new Artist(((AbstractMusic) selectedItem).getAlbumArtistOrArtist()); } if (artist != null) { intent.putExtra("artist", artist); startActivityForResult(intent, -1); } break; default: final String name = item.getTitle().toString(); final int id = item.getOrder(); mApp.oMPDAsyncHelper.execAsync(new Runnable() { @Override public void run() { add(mItems.get(id), name); } }); break; } return false; } @Override public void onRefreshStarted(final View view) { mPullToRefreshLayout.setRefreshComplete(); updateList(); } @Override public void onStart() { super.onStart(); mApp.setActivity(getActivity()); if (!mFirstUpdateDone) { mFirstUpdateDone = true; updateList(); } } @Override public void onStop() { mApp.unsetActivity(getActivity()); super.onStop(); } @Override public void onViewCreated(final View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (mItems != null) { mList.setAdapter(getCustomListAdapter()); } refreshFastScrollStyle(); if (mPullToRefreshLayout != null) { ActionBarPullToRefresh.from(getActivity()).allChildrenArePullable().listener(this) .setup(mPullToRefreshLayout); } } /** * This method is used for the fastcroll visibility decision.<br/> * Don't override this if you want to change the fastscroll style, override * {@link #refreshFastScrollStyle(boolean)} instead. */ protected void refreshFastScrollStyle() { refreshFastScrollStyle(mItems != null && mItems.size() >= getMinimumItemsCountBeforeFastscroll()); } /** * This is required because setting the fast scroll prior to KitKat was * important because of a bug. This bug has since been corrected, but the * opposite order is now required or the fast scroll will not show. * * @param shouldShowFastScroll If the fast scroll should be shown or not */ protected void refreshFastScrollStyle(final boolean shouldShowFastScroll) { if (shouldShowFastScroll) { refreshFastScrollStyle(View.SCROLLBARS_INSIDE_INSET, true); } else { refreshFastScrollStyle(View.SCROLLBARS_INSIDE_OVERLAY, false); } } /** * This is a helper method to workaround shortcomings of the fast scroll API. * * @param scrollbarStyle The {@code View} scrollbar style. * @param isAlwaysVisible The visibility of the scrollbar. */ final void refreshFastScrollStyle(final int scrollbarStyle, final boolean isAlwaysVisible) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mList.setFastScrollAlwaysVisible(isAlwaysVisible); mList.setScrollBarStyle(scrollbarStyle); } else { mList.setScrollBarStyle(scrollbarStyle); mList.setFastScrollAlwaysVisible(isAlwaysVisible); } } public void scrollToTop() { try { mList.setSelection(-1); } catch (final Exception e) { // What if the list is empty or some other bug ? I don't want any // crashes because of that } } public void setActivityTitle(final CharSequence title) { getActivity().setTitle(title); } /** * Update the view from the items list if items is set. */ public void updateFromItems() { if (getView() == null) { // The view has been destroyed, bail. return; } if (mPullToRefreshLayout != null) { mPullToRefreshLayout.setEnabled(true); } if (mItems != null) { mList.setAdapter(getCustomListAdapter()); } try { if (forceEmptyView() || mList instanceof ListView && ((ListView) mList).getHeaderViewsCount() == 0) { mList.setEmptyView(mNoResultView); } else { if (mItems == null || mItems.isEmpty()) { mNoResultView.setVisibility(View.VISIBLE); } } } catch (final Exception e) { Log.e(TAG, "Exception.", e); } mLoadingView.setVisibility(View.GONE); refreshFastScrollStyle(); } public void updateList() { mList.setAdapter(null); mNoResultView.setVisibility(View.GONE); mLoadingView.setVisibility(View.VISIBLE); if (mPullToRefreshLayout != null) { mPullToRefreshLayout.setEnabled(false); } // Loading Artists asynchronous... mApp.oMPDAsyncHelper.addAsyncExecListener(this); mJobID = mApp.oMPDAsyncHelper.execAsync(new Runnable() { @Override public void run() { asyncUpdate(); } }); } }