/* 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();
}
}
}