diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDApplicationBase.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDApplicationBase.java index 59b0c9fb47..cba7b37f07 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDApplicationBase.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/MPDApplicationBase.java @@ -20,6 +20,7 @@ import com.anpmech.mpd.subsystem.status.IdleSubsystemMonitor; import com.anpmech.mpd.subsystem.status.StatusChangeListener; import com.anpmech.mpd.subsystem.status.TrackPositionListener; +import com.namelessdev.mpdroid.favorites.Favorites; import com.namelessdev.mpdroid.helpers.CachedMPD; import com.namelessdev.mpdroid.helpers.MPDAsyncHelper; import com.namelessdev.mpdroid.helpers.UpdateTrackInfo; @@ -82,6 +83,8 @@ class MPDApplicationBase extends Application implements private MPD mMPD; + private Favorites mFavorites; + private MPDAsyncHelper mMPDAsyncHelper; private ServiceBinder mServiceBinder; @@ -232,6 +235,10 @@ public MPD getMPD() { return mMPD; } + public Favorites getFavorites(){ + return mFavorites; + } + /** * Called upon receiving messages from any handler, in this case most often the Service. * @@ -395,6 +402,8 @@ public void onCreate() { mMPD = new MPD(); } + mFavorites = new Favorites(this); + mIdleSubsystemMonitor = new IdleSubsystemMonitor(mMPD); } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java index 25ed82ed76..5417e6db35 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/SearchActivity.java @@ -22,6 +22,7 @@ import com.anpmech.mpd.item.Item; import com.anpmech.mpd.item.Music; import com.namelessdev.mpdroid.adapters.SeparatedListAdapter; +import com.namelessdev.mpdroid.favorites.Favorites; import com.namelessdev.mpdroid.helpers.MPDAsyncHelper.AsyncExecListener; import com.namelessdev.mpdroid.library.SimpleLibraryActivity; import com.namelessdev.mpdroid.tools.Tools; @@ -71,6 +72,8 @@ public class SearchActivity extends MPDActivity implements OnMenuItemClickListen public static final int GOTO_ALBUM = 4; + public static final int ADD_TO_FAVORITES = 5; + public static final int MAIN = 0; public static final int PLAYLIST = 3; @@ -374,6 +377,8 @@ public void onCreateContextMenu(final ContextMenu menu, final View v, switch (mPager.getCurrentItem()) { case RESULT_ALBUM: + final MenuItem addToFavoritesIcon = menu.add(Menu.NONE, ADD_TO_FAVORITES, 0, R.string.addToFavorites); + addToFavoritesIcon.setOnMenuItemClickListener(this); final Album album = mAlbumResults.get((int) info.id); menu.setHeaderTitle(album.toString()); setContextForObject(album); @@ -428,7 +433,7 @@ public void onItemClick(final AdapterView parent, final View view, final int intent.putExtra(Artist.EXTRA, (Parcelable) selectedItem); startActivityForResult(intent, -1); } else if (selectedItem instanceof Album) { - final Intent intent = new Intent(this, SimpleLibraryActivity.class); + final Intent intent = new Intent(this, SimpleLibraryActivity.class); intent.putExtra(Album.EXTRA, (Parcelable) selectedItem); startActivityForResult(intent, -1); } @@ -471,7 +476,14 @@ public boolean onMenuItemClick(final MenuItem item) { intent.putExtra(Album.EXTRA, music.getAlbum()); startActivityForResult(intent, -1); } - } else { + } + else if (item.getItemId() == ADD_TO_FAVORITES) { + if (selectedItem instanceof Album){ + Favorites favorites = new Favorites(this); + favorites.addAlbum((Album)selectedItem); + } + } + else{ mApp.getAsyncHelper().execAsync(new Runnable() { @Override public void run() { diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/favorites/Favorites.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/favorites/Favorites.java new file mode 100644 index 0000000000..a42f0c9774 --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/favorites/Favorites.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010-2016 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.namelessdev.mpdroid.favorites; + +import com.anpmech.mpd.item.Album; +import com.anpmech.mpd.item.AlbumBuilder; +import com.anpmech.mpd.item.Artist; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Created by thaag on 2/14/2016. + */ +public class Favorites { + + private final Context mContext; + private Set mAlbums = new HashSet<>(); + + public Favorites(Context context){ + this.mContext = context; + loadFavorites(); + } + + public void addAlbum(final Album newAlbum) { + boolean alreadyInList = false; + for (Album album : mAlbums){ + if (album.equals(newAlbum)){ + alreadyInList = true; + break; + } + } + + if (!alreadyInList){ + mAlbums.add(newAlbum); + } + + saveFavorites(); + } + + public void removeAlbum(final Album remAlbum){ + Iterator iterator = mAlbums.iterator(); + while (iterator.hasNext()){ + Album album = iterator.next(); + if (album.equals(remAlbum)){ + iterator.remove(); + break; + } + } + + saveFavorites(); + } + + public Collection getAlbums() { + loadFavorites(); + return mAlbums; + } + + public void loadFavorites() { + mAlbums.clear(); + + SharedPreferences prefs = mContext.getSharedPreferences("favorites",0); + JSONArray jsonFavorites = null; + try { + jsonFavorites = new JSONArray(prefs.getString("favorites", "{}")); + + } catch (JSONException e) { + e.printStackTrace(); + } + + if (jsonFavorites != null){ + final AlbumBuilder albumBuilder = new AlbumBuilder(); + + + for (int i=0; i> extends Fragment implements public static final int POPUP_COVER_SELECTIVE_CLEAN = 11; + public static final int POPUP_ADD_TO_FAVORITES = 12; + + public static final int POPUP_REMOVE_FROM_FAVORITES = 24; + /** * This is the group number used to enable or disable the playlist add group. */ @@ -495,11 +499,11 @@ public void onCreateContextMenu(final ContextMenu menu, final View v, } } - if (getArtist(item) != null) { - final MenuItem gotoArtistItem = - menu.add(GOTO_ARTIST, GOTO_ARTIST, 0, R.string.goToArtist); - gotoArtistItem.setOnMenuItemClickListener(this); - } + //if (getArtist(item) != null) { + // final MenuItem gotoArtistItem = + // menu.add(GOTO_ARTIST, GOTO_ARTIST, 0, R.string.goToArtist); + // gotoArtistItem.setOnMenuItemClickListener(this); + //} } } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/FavoritesFragment.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/FavoritesFragment.java new file mode 100644 index 0000000000..41a8eff1ca --- /dev/null +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/FavoritesFragment.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2010-2016 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.namelessdev.mpdroid.fragments; + +import com.anpmech.mpd.exception.MPDException; +import com.anpmech.mpd.item.Album; +import com.anpmech.mpd.item.Artist; +import com.anpmech.mpd.item.Genre; +import com.anpmech.mpd.item.PlaylistFile; +import com.namelessdev.mpdroid.R; +import com.namelessdev.mpdroid.adapters.ArrayIndexerAdapter; +import com.namelessdev.mpdroid.cover.CoverAsyncHelper; +import com.namelessdev.mpdroid.cover.CoverManager; +import com.namelessdev.mpdroid.favorites.Favorites; +import com.namelessdev.mpdroid.helpers.AlbumInfo; +import com.namelessdev.mpdroid.library.ILibraryFragmentActivity; +import com.namelessdev.mpdroid.tools.Tools; +import com.namelessdev.mpdroid.views.AlbumGridDataBinder; +import com.namelessdev.mpdroid.views.holders.AlbumViewHolder; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.StringRes; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.ListAdapter; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.io.IOException; +import java.util.Collections; + +public class FavoritesFragment extends BrowseFragment { + + private static final String ALBUM_YEAR_SORT_KEY = "sortAlbumsByYear"; + + private static final String SHOW_ALBUM_TRACK_COUNT_KEY = "showAlbumTrackCount"; + + private static final String TAG = "FavoritesFragment"; + + protected Artist mArtist = null; + + protected ProgressBar mCoverArtProgress; + + protected Genre mGenre = null; + + protected boolean mIsCountDisplayed; + + public FavoritesFragment() { + super(R.string.addAlbum, R.string.albumAdded); + } + + private static void refreshCover(final View view, final AlbumInfo album) { + if (view.getTag() instanceof AlbumViewHolder) { + final AlbumViewHolder albumViewHolder = (AlbumViewHolder) view.getTag(); + final Object tag = albumViewHolder.mAlbumCover.getTag(R.id.CoverAsyncHelper); + + if (tag instanceof CoverAsyncHelper) { + ((CoverAsyncHelper) tag).downloadCover(album, true); + } + } + } + + @Override + protected void add(final Album item, final boolean replace, final boolean play) { + try { + mApp.getMPD().add(item, replace, play); + Tools.notifyUser(mIrAdded, item); + } catch (final IOException | MPDException e) { + Log.e(TAG, "Failed to add.", e); + } + } + + @Override + protected void add(final Album item, final PlaylistFile playlist) { + try { + mApp.getMPD().addToPlaylist(playlist, item); + Tools.notifyUser(mIrAdded, item); + } catch (final IOException | MPDException e) { + Log.e(TAG, "Failed to add.", e); + } + } + + @Override + protected void asyncUpdate() { + final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mApp); + final boolean sortByYear = settings.getBoolean(ALBUM_YEAR_SORT_KEY, false); + + try { + replaceItems(mApp.getFavorites().getAlbums()); + + if (sortByYear) { + Collections.sort(mItems, Album.SORT_BY_DATE); + } else { + Collections.sort(mItems); + } + + if (mGenre != null) { // filter albums not in genre + for (int i = mItems.size() - 1; i >= 0; i--) { + if (!mApp.getMPD().isAlbumInGenre(mItems.get(i), mGenre)) { + mItems.remove(i); + } + } + } + } catch (final IOException | MPDException e) { + Log.e(TAG, "Failed to update.", e); + } + } + + /** + * Uses CoverManager to clean up a cover. + * + * @param item The MenuItem from the user interaction. + * @param isWrongCover True to blacklist the cover, false otherwise. + */ + private void cleanupCover(final MenuItem item, final boolean isWrongCover) { + final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + + final Album album = mItems.get((int) info.id); + final AlbumInfo albumInfo = new AlbumInfo(album); + + if (isWrongCover) { + CoverManager.getInstance() + .markWrongCover(albumInfo); + } else { + CoverManager.getInstance() + .clear(albumInfo); + } + + refreshCover(info.targetView, albumInfo); + updateNowPlayingSmallFragment(albumInfo); + } + + private void removeFromFavorites(final MenuItem item){ + final AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + + Favorites favorites = new Favorites(getActivity()); + favorites.removeAlbum(mItems.get((int) info.id)); + } + + @Override + protected Artist getArtist(final Album item) { + return item.getArtist(); + } + + @Override + protected ListAdapter getCustomListAdapter() { + return new ArrayIndexerAdapter<>(getActivity(), new AlbumGridDataBinder(mArtist), + mItems); + } + + /** + * This method returns the default string resource. + * + * @return The default string resource. + */ + @Override + @StringRes + public int getDefaultTitle() { + return R.string.albums; + } + + @Override + @StringRes + public int getLoadingText() { + return R.string.loadingAlbums; + } + + @Override + public String getTitle() { + final String title; + + if (mArtist == null) { + title = super.getTitle(); + } else { + title = mArtist.toString(); + } + + return title; + } + + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Bundle bundle; + if (savedInstanceState == null) { + bundle = getArguments(); + } else { + bundle = savedInstanceState; + } + + if (bundle != null) { + mArtist = bundle.getParcelable(Artist.EXTRA); + mGenre = bundle.getParcelable(Genre.EXTRA); + } + } + + @Override + public void onCreateContextMenu(final ContextMenu menu, final View v, + final ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + final MenuItem removeFromFavoritesItem = menu.add(POPUP_REMOVE_FROM_FAVORITES, + POPUP_REMOVE_FROM_FAVORITES, 0, R.string.removeFromFavorites); + removeFromFavoritesItem.setOnMenuItemClickListener(this); + final MenuItem otherCoverItem = menu.add(POPUP_COVER_BLACKLIST, + POPUP_COVER_BLACKLIST, 0, R.string.otherCover); + otherCoverItem.setOnMenuItemClickListener(this); + final MenuItem resetCoverItem = menu.add(POPUP_COVER_SELECTIVE_CLEAN, + POPUP_COVER_SELECTIVE_CLEAN, 0, R.string.resetCover); + resetCoverItem.setOnMenuItemClickListener(this); + + } + + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + + final View view = inflater.inflate(R.layout.browsegrid, container, false); + mList = (AbsListView) view.findViewById(R.id.grid); + 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()); + mCoverArtProgress = (ProgressBar) view.findViewById(R.id.albumCoverProgress); + + setupStandardToolbar(view); + + + + return view; + + } + + @Override + public void onItemClick(final AdapterView parent, final View view, final int position, + final long id) { + final Activity activity = getActivity(); + final Bundle bundle = new Bundle(); + final Fragment fragment = Fragment.instantiate(activity, SongsFragment.class.getName(), + bundle); + + bundle.putParcelable(Album.EXTRA, mItems.get(position)); + + // Terribly bugged + /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + final TransitionInflater inflater = TransitionInflater.from(activity); + final ImageView albumCoverView = (ImageView) view.findViewById(R.id.albumCover); + final String transitionName = albumCoverView.getTransitionName(); + final Drawable drawable = albumCoverView.getDrawable(); + + if (drawable instanceof BitmapDrawable) { + bundle.putParcelable(SongsFragment.COVER_THUMBNAIL_BUNDLE_KEY, + ((BitmapDrawable) drawable).getBitmap()); + } + + bundle.putString(SongsFragment.COVER_TRANSITION_NAME_BASE, transitionName); + + ((ILibraryFragmentActivity) activity).pushLibraryFragment( + fragment, "songs", albumCoverView, + albumCoverView.getTransitionName(), + inflater.inflateTransition(R.transition.album_songs_transition)); + } else {*/ + ((ILibraryFragmentActivity) activity).pushLibraryFragment(fragment, "songs"); + //} + } + + @Override + public boolean onMenuItemClick(final MenuItem item) { + boolean result = false; + + switch (item.getGroupId()) { + case POPUP_COVER_BLACKLIST: + cleanupCover(item, true); + break; + case POPUP_COVER_SELECTIVE_CLEAN: + cleanupCover(item, false); + break; + case POPUP_REMOVE_FROM_FAVORITES: + removeFromFavorites(item); + break; + default: + result = super.onMenuItemClick(item); + break; + } + return result; + } + + @Override + public void onResume() { + super.onResume(); + + mIsCountDisplayed = PreferenceManager.getDefaultSharedPreferences(mApp) + .getBoolean(SHOW_ALBUM_TRACK_COUNT_KEY, true); + } + + @Override + public void onSaveInstanceState(final Bundle outState) { + if (mArtist != null) { + outState.putParcelable(Artist.EXTRA, mArtist); + } + + if (mGenre != null) { + outState.putParcelable(Genre.EXTRA, mGenre); + } + super.onSaveInstanceState(outState); + } +} diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/LibraryFragmentBase.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/LibraryFragmentBase.java index 9086751bfe..1801fe9e8e 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/LibraryFragmentBase.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/fragments/LibraryFragmentBase.java @@ -179,6 +179,9 @@ public Fragment getItem(final int position) { case LibraryTabsUtil.TAB_STREAMS: fragment = getFragment(StreamsFragment.class); break; + case LibraryTabsUtil.TAB_FAVORITES: + fragment = getFragment(FavoritesFragment.class); + break; default: throw new IllegalStateException("getItem() called with invalid Item."); } diff --git a/MPDroid/src/main/java/com/namelessdev/mpdroid/tools/LibraryTabsUtil.java b/MPDroid/src/main/java/com/namelessdev/mpdroid/tools/LibraryTabsUtil.java index f030652a1e..248b0504f2 100644 --- a/MPDroid/src/main/java/com/namelessdev/mpdroid/tools/LibraryTabsUtil.java +++ b/MPDroid/src/main/java/com/namelessdev/mpdroid/tools/LibraryTabsUtil.java @@ -41,6 +41,8 @@ public final class LibraryTabsUtil { public static final String TAB_STREAMS = "streams"; + public static final String TAB_FAVORITES = "favorites"; + private static final MPDApplication APP = MPDApplication.getInstance(); private static final String LIBRARY_TABS_DELIMITER = "|"; @@ -50,7 +52,8 @@ public final class LibraryTabsUtil { + LIBRARY_TABS_DELIMITER + TAB_PLAYLISTS + LIBRARY_TABS_DELIMITER + TAB_STREAMS + LIBRARY_TABS_DELIMITER + TAB_FILES - + LIBRARY_TABS_DELIMITER + TAB_GENRES; + + LIBRARY_TABS_DELIMITER + TAB_GENRES + + LIBRARY_TABS_DELIMITER + TAB_FAVORITES; private static final String LIBRARY_TABS_SETTINGS_KEY = "currentLibraryTabs"; @@ -63,6 +66,7 @@ public final class LibraryTabsUtil { TABS.put(TAB_STREAMS, R.string.streams); TABS.put(TAB_FILES, R.string.files); TABS.put(TAB_GENRES, R.string.genres); + TABS.put(TAB_FAVORITES, R.string.favorites); } private LibraryTabsUtil() { diff --git a/MPDroid/src/main/res/values-de/strings.xml b/MPDroid/src/main/res/values-de/strings.xml index 4bdeb1bff3..69fca896dd 100644 --- a/MPDroid/src/main/res/values-de/strings.xml +++ b/MPDroid/src/main/res/values-de/strings.xml @@ -318,5 +318,8 @@ Der Stream-Codec wird nicht unterstützt. Wiedergabeliste mischen Wiedergabeliste wurde zufällig gemischt + Favoriten + Zu Favoriten hinzufügen + Aus Favoriten entfernen diff --git a/MPDroid/src/main/res/values/strings.xml b/MPDroid/src/main/res/values/strings.xml index 5dfbf40200..79e0c77850 100644 --- a/MPDroid/src/main/res/values/strings.xml +++ b/MPDroid/src/main/res/values/strings.xml @@ -73,6 +73,7 @@ Playlists Files Albums + Favorites Settings Next Previous @@ -341,4 +342,6 @@ Comments "Changes occurred, reloading." + Add to favorites + Remove from favorites