Java tutorial
/* This file is part of the Android Clementine Remote. * Copyright (C) 2013, Andreas Muttscheller <asfa194@gmail.com> * * 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 de.qspool.clementineremote.ui.fragments; import com.afollestad.materialdialogs.MaterialDialog; import android.annotation.SuppressLint; import android.app.Fragment; import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; import android.os.Message; import android.preference.PreferenceManager; import android.support.v4.content.ContextCompat; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SearchView; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.LinkedList; import de.qspool.clementineremote.App; import de.qspool.clementineremote.R; import de.qspool.clementineremote.SharedPreferencesKeys; import de.qspool.clementineremote.backend.ClementineLibraryDownloader; import de.qspool.clementineremote.backend.database.SongSelectItem; import de.qspool.clementineremote.backend.downloader.DownloadManager; import de.qspool.clementineremote.backend.elements.DownloaderResult; import de.qspool.clementineremote.backend.elements.DownloaderResult.DownloadResult; import de.qspool.clementineremote.backend.library.LibraryDatabaseHelper; import de.qspool.clementineremote.backend.library.LibraryQuery; import de.qspool.clementineremote.backend.listener.OnLibraryDownloadListener; import de.qspool.clementineremote.backend.listener.OnSongSelectFinishedListener; import de.qspool.clementineremote.backend.pb.ClementineMessage; import de.qspool.clementineremote.backend.pb.ClementineMessageFactory; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.MsgType; import de.qspool.clementineremote.backend.player.MySong; import de.qspool.clementineremote.ui.adapter.DynamicSongQueryAdapter; import de.qspool.clementineremote.ui.interfaces.BackPressHandleable; import de.qspool.clementineremote.ui.interfaces.RemoteDataReceiver; import de.qspool.clementineremote.utils.Utilities; public class LibraryFragment extends Fragment implements BackPressHandleable, RemoteDataReceiver, SwipeRefreshLayout.OnRefreshListener, SharedPreferences.OnSharedPreferenceChangeListener { private ActionBar mActionBar; private SwipeRefreshLayout mSwipeRefreshLayout; private SwipeRefreshLayout mEmptyLibrary; private ListView mList; private LinkedList<DynamicSongQueryAdapter> mAdapters = new LinkedList<>(); private TextView mLibraryEmptyText; private MaterialDialog mProgressDialog; private String mLastFilter = ""; private ClementineLibraryDownloader mClementineLibraryDownloader; private int mLibraryQueriesDone; private int mLibraryLevels; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Get the actionbar mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); setHasOptionsMenu(true); } @Override public void onResume() { super.onResume(); // Check if we are still connected if (App.ClementineConnection == null || App.Clementine == null || !App.ClementineConnection.isConnected()) { return; } setActionBarTitle(); if (mClementineLibraryDownloader != null) { createDownloadProgressDialog(); mClementineLibraryDownloader.addOnLibraryDownloadListener(mOnLibraryDownloadListener); } } @Override public void onPause() { super.onPause(); if (mClementineLibraryDownloader != null) { mClementineLibraryDownloader.removeOnLibraryDownloadListener(mOnLibraryDownloadListener); mProgressDialog.dismiss(); } } @SuppressLint({ "InlinedApi", "NewApi" }) @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_library, container, false); mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.library_refresh_layout); mEmptyLibrary = (SwipeRefreshLayout) view.findViewById(R.id.library_refresh_empty_layout); mSwipeRefreshLayout.setOnRefreshListener(this); mEmptyLibrary.setOnRefreshListener(this); mSwipeRefreshLayout.setColorSchemeResources(R.color.orange); mEmptyLibrary.setColorSchemeResources(R.color.orange); mList = (ListView) view.findViewById(R.id.library); mLibraryEmptyText = (TextView) mEmptyLibrary.findViewById(R.id.library_empty_txt); mList.setOnItemClickListener(oiclLibraryClick); mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); mList.setMultiChoiceModeListener(new MultiChoiceModeListener() { @Override public boolean onActionItemClicked(ActionMode mode, android.view.MenuItem item) { SparseBooleanArray checkedPositions = mList.getCheckedItemPositions(); final LinkedList<SongSelectItem> selectedItems = new LinkedList<>(); final LinkedList<String> urls = new LinkedList<>(); mLibraryQueriesDone = 0; for (int i = 0; i < checkedPositions.size(); ++i) { int position = checkedPositions.keyAt(i); if (checkedPositions.valueAt(i)) { selectedItems.add(mAdapters.getLast().getItem(position)); } } for (SongSelectItem libraryItem : selectedItems) { OnSongSelectFinishedListener listener; switch (item.getItemId()) { case R.id.library_context_add: listener = new OnSongSelectFinishedListener() { @Override public void OnSongSelectFinished(LinkedList<SongSelectItem> l) { addSongsToPlaylist(l); } }; break; case R.id.library_context_download: listener = new OnSongSelectFinishedListener() { @Override public void OnSongSelectFinished(LinkedList<SongSelectItem> l) { for (SongSelectItem libItem : l) { urls.add(libItem.getUrl()); } mLibraryQueriesDone++; // Have we got all queries? if (mLibraryQueriesDone == selectedItems.size() && !urls.isEmpty()) { DownloadManager.getInstance() .addJob(ClementineMessageFactory.buildDownloadSongsMessage( ClementineRemoteProtocolBuffer.DownloadItem.Urls, urls)); } } }; break; default: return false; } queryLibraryItems(libraryItem, listener); } mode.finish(); return true; } @Override public boolean onCreateActionMode(ActionMode mode, android.view.Menu menu) { android.view.MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.library_context_menu, menu); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getActivity().getWindow() .setStatusBarColor(ContextCompat.getColor(getActivity(), R.color.grey_cab_status)); } return true; } @Override public boolean onPrepareActionMode(ActionMode mode, android.view.Menu menu) { return false; } @Override public void onDestroyActionMode(ActionMode mode) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getActivity().getWindow() .setStatusBarColor(ContextCompat.getColor(getActivity(), R.color.actionbar_dark)); } } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { } }); createRootAdapter(); showList(); mActionBar.setTitle(""); mActionBar.setSubtitle("/"); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); prefs.registerOnSharedPreferenceChangeListener(this); setHasOptionsMenu(true); return view; } @Override public void onDestroyView() { super.onDestroyView(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); prefs.unregisterOnSharedPreferenceChangeListener(this); mAdapters.clear(); } private OnLibraryDownloadListener mOnLibraryDownloadListener = new OnLibraryDownloadListener() { @Override public void OnLibraryDownloadFinished(DownloaderResult result) { mProgressDialog.dismiss(); mClementineLibraryDownloader = null; mSwipeRefreshLayout.setRefreshing(false); mEmptyLibrary.setRefreshing(false); if (result.getResult() == DownloadResult.SUCCESSFUL) { LibraryQuery libraryQuery = new LibraryQuery(getActivity()); libraryQuery.openDatabase(); libraryQuery.setLevel(0); DynamicSongQueryAdapter a = new DynamicSongQueryAdapter(getActivity(), libraryQuery); mAdapters.add(a); showList(); } else { Utilities.ShowMessageDialog(getActivity(), R.string.library_download_error, result.getMessageStringId()); } } @Override public void OnProgressUpdate(long progress, int total) { mProgressDialog.setProgress((int) progress); mProgressDialog.setMaxProgress(total); } @Override public void OnOptimizeLibrary() { mProgressDialog.dismiss(); mProgressDialog = new MaterialDialog.Builder(getActivity()).title(R.string.library_please_wait) .content(R.string.library_optimize).cancelable(false).progress(true, -1).show(); } }; private void createDownloadProgressDialog() { mProgressDialog = new MaterialDialog.Builder(getActivity()).title(R.string.library_please_wait) .content(R.string.library_download).cancelable(false).progress(false, 0).show(); } private void createRootAdapter() { // Check if we have the correct Clementine library. Otherwise delete it LibraryDatabaseHelper libraryDatabaseHelper = new LibraryDatabaseHelper(); libraryDatabaseHelper.removeDatabaseIfFromOtherClementine(); LibraryQuery libraryQuery = new LibraryQuery(getActivity()); mLibraryLevels = libraryQuery.getMaxLevels(); // Create the adapter if (mClementineLibraryDownloader == null && libraryDatabaseHelper.databaseExists()) { libraryQuery.openDatabase(); libraryQuery.setLevel(0); DynamicSongQueryAdapter a = new DynamicSongQueryAdapter(getActivity(), libraryQuery); mAdapters.add(a); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); inflater.inflate(R.menu.library_menu, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public void onPrepareOptionsMenu(Menu menu) { // Create a listener for search change SearchView searchView = (SearchView) menu.findItem(R.id.library_menu_search).getActionView(); final SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String newText) { // Set the filter text as the fragments might not yet // created. Only the left and right fragment from the // currently active is created (onCreate() called). // Therefore the other adapters are not yet created, // onCreate filters for this string given in setFilterText() if (!mAdapters.isEmpty()) { mAdapters.getLast().getFilter().filter(newText); mLastFilter = newText; mLibraryEmptyText.setText(R.string.library_no_search_results); } return true; } @Override public boolean onQueryTextSubmit(String query) { // Do something if (!mAdapters.isEmpty()) { mAdapters.getLast().getFilter().filter(query); mLastFilter = query; } return true; } }; searchView.setOnQueryTextListener(queryTextListener); searchView.setQueryHint(getString(R.string.playlist_search_hint)); EditText searchText = (EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text); searchText.setHintTextColor(ContextCompat.getColor(getActivity(), R.color.searchview_edittext_hint)); super.onPrepareOptionsMenu(menu); } private void queryLibraryItems(SongSelectItem libraryItem, OnSongSelectFinishedListener onSongSelectFinishedListener) { if (libraryItem.getLevel() == mLibraryLevels - 1) { LinkedList<SongSelectItem> result = new LinkedList<>(); result.add(libraryItem); onSongSelectFinishedListener.OnSongSelectFinished(result); } else { LibraryQuery libraryQuery = new LibraryQuery(getActivity()); libraryQuery.openDatabase(); libraryQuery.setLevel(mLibraryLevels - 1); libraryQuery.setSelection(libraryItem.getSelection()); libraryQuery.addOnLibrarySelectFinishedListener(onSongSelectFinishedListener); libraryQuery.selectDataAsync(); } } private void setActionBarTitle() { MySong currentSong = App.Clementine.getCurrentSong(); if (currentSong == null) { mActionBar.setTitle(getString(R.string.player_nosong)); } else { mActionBar.setTitle(currentSong.getArtist() + " / " + currentSong.getTitle()); } } @Override public void onViewCreated(final View view, final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mList.setFastScrollEnabled(true); mList.setTextFilterEnabled(true); mList.setSelector(new ColorDrawable(ContextCompat.getColor(getActivity(), android.R.color.transparent))); mList.setOnItemClickListener(oiclLibraryClick); mList.setDivider(null); mList.setDividerHeight(0); mList.setEmptyView(mEmptyLibrary); } @Override public void MessageFromClementine(ClementineMessage clementineMessage) { switch (clementineMessage.getMessageType()) { case CURRENT_METAINFO: setActionBarTitle(); break; default: break; } } @Override public boolean onBackPressed() { // When we have only one item left, just use the normal back behavior if (mAdapters.size() <= 1) { return false; } // Remove the last element and show the new list mAdapters.removeLast(); showList(); return true; } /** * Show the last element in the list of adapters */ private void showList() { if (mAdapters.isEmpty() || mAdapters.getLast().isEmpty()) { mList.setEmptyView(mEmptyLibrary); } else { DynamicSongQueryAdapter adapter = mAdapters.getLast(); adapter.getFilter().filter(mLastFilter); mList.setAdapter(adapter); if (adapter.isEmpty()) { mActionBar.setSubtitle("/ "); } else { SongSelectItem item = adapter.getItem(0); StringBuilder sb = new StringBuilder(); sb.append("/ "); for (int i = 0; i < mAdapters.size() - 1; i++) { sb.append(item.getSelection()[i]); if (i < mAdapters.size() - 2) sb.append(" / "); } mActionBar.setSubtitle(sb.toString()); } } } private void addSongsToPlaylist(LinkedList<SongSelectItem> l) { Message msg = Message.obtain(); LinkedList<String> urls = new LinkedList<>(); for (SongSelectItem item : l) { urls.add(item.getUrl()); } msg.obj = ClementineMessageFactory.buildInsertUrl(App.Clementine.getPlaylistManager().getActivePlaylistId(), urls); App.ClementineConnection.mHandler.sendMessage(msg); String text = getActivity().getResources().getQuantityString(R.plurals.songs_added, urls.size(), urls.size()); Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show(); } private OnItemClickListener oiclLibraryClick = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { SongSelectItem item = mAdapters.getLast().getItem(position); if (item.getLevel() == mLibraryLevels - 1) { Message msg = Message.obtain(); LinkedList<String> urls = new LinkedList<>(); urls.add(item.getUrl()); msg.obj = ClementineMessageFactory .buildInsertUrl(App.Clementine.getPlaylistManager().getActivePlaylistId(), urls); App.ClementineConnection.mHandler.sendMessage(msg); String text = getActivity().getResources().getQuantityString(R.plurals.songs_added, 1, 1); Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show(); } else { LibraryQuery libraryQuery = new LibraryQuery(getActivity()); libraryQuery.openDatabase(); libraryQuery.setLevel(mAdapters.size()); libraryQuery.setSelection(item.getSelection()); mAdapters.add(new DynamicSongQueryAdapter(getActivity(), libraryQuery)); showList(); } } }; @Override public void onRefresh() { mAdapters.clear(); showList(); mClementineLibraryDownloader = new ClementineLibraryDownloader(getActivity()); mClementineLibraryDownloader.addOnLibraryDownloadListener(mOnLibraryDownloadListener); mClementineLibraryDownloader.startDownload(ClementineMessage.getMessage(MsgType.GET_LIBRARY)); createDownloadProgressDialog(); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals(SharedPreferencesKeys.SP_LIBRARY_GROUPING) || key.equals(SharedPreferencesKeys.SP_LIBRARY_SORTING)) { mAdapters.clear(); createRootAdapter(); showList(); } } }