diff options
author | Michael Bestas <mkbestas@lineageos.org> | 2018-01-22 21:02:42 +0200 |
---|---|---|
committer | Michael Bestas <mkbestas@lineageos.org> | 2018-01-23 19:35:52 +0200 |
commit | a907407e035b52e8dcdd4ac3ae48b533c7942d5a (patch) | |
tree | ef031b8478effd2fd828495832260359486950f4 /src/org/lineageos/eleven/ui/fragments | |
parent | da200d369e4e43f2587273e9dd7af9c91048cf68 (diff) | |
download | android_packages_apps_Eleven-a907407e035b52e8dcdd4ac3ae48b533c7942d5a.tar.gz android_packages_apps_Eleven-a907407e035b52e8dcdd4ac3ae48b533c7942d5a.tar.bz2 android_packages_apps_Eleven-a907407e035b52e8dcdd4ac3ae48b533c7942d5a.zip |
Eleven: rebrand step 1: update paths
Change-Id: Iab35e4024e20c48e7439e78d1c6efe0ef4f730ca
Diffstat (limited to 'src/org/lineageos/eleven/ui/fragments')
21 files changed, 4626 insertions, 0 deletions
diff --git a/src/org/lineageos/eleven/ui/fragments/AlbumDetailFragment.java b/src/org/lineageos/eleven/ui/fragments/AlbumDetailFragment.java new file mode 100644 index 0000000..5aeb70f --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/AlbumDetailFragment.java @@ -0,0 +1,210 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.view.View; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.AlbumDetailSongAdapter; +import com.cyanogenmod.eleven.adapters.DetailSongAdapter; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.cache.ImageFetcher; +import com.cyanogenmod.eleven.model.Album; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.utils.AlbumPopupMenuHelper; +import com.cyanogenmod.eleven.utils.GenreFetcher; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.utils.SongPopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; + +import java.util.List; + +public class AlbumDetailFragment extends DetailFragment implements IChildFragment { + private static final int LOADER_ID = 1; + + private ListView mSongs; + private DetailSongAdapter mSongAdapter; + private TextView mAlbumDuration; + private TextView mGenre; + private ImageView mAlbumArt; + private PopupMenuHelper mSongMenuHelper; + private long mAlbumId; + private String mArtistName; + private String mAlbumName; + private LoadingEmptyContainer mLoadingEmptyContainer; + + @Override + protected int getLayoutToInflate() { + return R.layout.activity_album_detail; + } + + @Override + protected String getTitle() { + return getArguments().getString(Config.ARTIST_NAME); + } + + @Override + protected void onViewCreated() { + super.onViewCreated(); + + Bundle arguments = getArguments(); + String artistName = arguments.getString(Config.ARTIST_NAME); + + setupPopupMenuHelper(); + setupHeader(artistName, arguments); + setupSongList(); + + LoaderManager lm = getLoaderManager(); + lm.initLoader(LOADER_ID, arguments, mSongAdapter); + } + + @Override // DetailFragment + protected PopupMenuHelper createActionMenuHelper() { + return new AlbumPopupMenuHelper(getActivity(), getChildFragmentManager()) { + public Album getAlbum(int position) { + return new Album(mAlbumId, mAlbumName, mArtistName, -1, null); + } + }; + } + + @Override // DetailFragment + protected int getShuffleTitleId() { return R.string.menu_shuffle_album; } + + @Override // DetailFragment + protected void playShuffled() { + MusicUtils.playAlbum(getActivity(), mAlbumId, -1, true); + } + + private void setupHeader(String artist, Bundle arguments) { + mAlbumId = arguments.getLong(Config.ID); + mArtistName = artist; + mAlbumName = arguments.getString(Config.NAME); + String year = arguments.getString(Config.ALBUM_YEAR); + int songCount = arguments.getInt(Config.SONG_COUNT); + + mAlbumArt = (ImageView)mRootView.findViewById(R.id.album_art); + mAlbumArt.setContentDescription(mAlbumName); + ImageFetcher.getInstance(getActivity()).loadAlbumImage(artist, mAlbumName, mAlbumId, mAlbumArt); + + TextView title = (TextView)mRootView.findViewById(R.id.title); + title.setText(mAlbumName); + + setupCountAndYear(mRootView, year, songCount); + + // will be updated once we have song data + mAlbumDuration = (TextView)mRootView.findViewById(R.id.duration); + mGenre = (TextView)mRootView.findViewById(R.id.genre); + } + + private void setupCountAndYear(View root, String year, int songCount) { + TextView songCountAndYear = (TextView)root.findViewById(R.id.song_count_and_year); + if(songCount > 0) { + String countText = getResources(). + getQuantityString(R.plurals.Nsongs, songCount, songCount); + if(year == null) { + songCountAndYear.setText(countText); + } else { + songCountAndYear.setText(getString(R.string.combine_two_strings, countText, year)); + } + } else if(year != null) { + songCountAndYear.setText(year); + } + } + + private void setupPopupMenuHelper() { + mSongMenuHelper = new SongPopupMenuHelper(getActivity(), getChildFragmentManager()) { + @Override + public Song getSong(int position) { + return mSongAdapter.getItem(position); + } + + @Override + protected long getSourceId() { + return mAlbumId; + } + + @Override + protected Config.IdType getSourceType() { + return Config.IdType.Album; + } + }; + } + + private void setupSongList() { + mSongs = (ListView)mRootView.findViewById(R.id.songs); + mSongAdapter = new AlbumDetailSongAdapter(getActivity(), this) { + @Override + protected void onLoading() { + mLoadingEmptyContainer.showLoading(); + } + + @Override + protected void onNoResults() { + getContainingActivity().postRemoveFragment(AlbumDetailFragment.this); + } + }; + mSongAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mSongMenuHelper.showPopupMenu(v, position); + } + }); + mSongs.setAdapter(mSongAdapter); + mSongs.setOnItemClickListener(mSongAdapter); + mLoadingEmptyContainer = + (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + mSongs.setEmptyView(mLoadingEmptyContainer); + } + + /** called back by song loader */ + public void update(List<Song> songs) { + /** compute total run time for album */ + int duration = 0; + for(Song s : songs) { duration += s.mDuration; } + mAlbumDuration.setText(MusicUtils.makeLongTimeString(getActivity(), duration)); + + /** use the first song on the album to get a genre */ + if(!songs.isEmpty()) { + GenreFetcher.fetch(getActivity(), (int) songs.get(0).mSongId, mGenre); + } + } + + @Override + public void restartLoader() { + getLoaderManager().restartLoader(LOADER_ID, getArguments(), mSongAdapter); + ImageFetcher.getInstance(getActivity()).loadAlbumImage(mArtistName, mAlbumName, mAlbumId, + mAlbumArt); + } + + @Override + public void onMetaChanged() { + super.onMetaChanged(); + + mSongAdapter.setCurrentlyPlayingTrack(MusicUtils.getCurrentTrack()); + } + + @Override + public PagerAdapter.MusicFragments getMusicFragmentParent() { + return PagerAdapter.MusicFragments.ALBUM; + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/AlbumFragment.java b/src/org/lineageos/eleven/ui/fragments/AlbumFragment.java new file mode 100644 index 0000000..f645ddd --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/AlbumFragment.java @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.os.Bundle; +import android.os.SystemClock; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.GridView; + +import com.cyanogenmod.eleven.MusicStateListener; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.AlbumAdapter; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.loaders.AlbumLoader; +import com.cyanogenmod.eleven.model.Album; +import com.cyanogenmod.eleven.recycler.RecycleHolder; +import com.cyanogenmod.eleven.sectionadapter.SectionCreator; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.ui.fragments.phone.MusicBrowserFragment; +import com.cyanogenmod.eleven.utils.AlbumPopupMenuHelper; +import com.cyanogenmod.eleven.utils.ApolloUtils; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.NavUtils; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; + +/** + * This class is used to display all of the albums on a user's device. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class AlbumFragment extends MusicBrowserFragment implements + LoaderCallbacks<SectionListContainer<Album>>, OnScrollListener, + OnItemClickListener, MusicStateListener { + + /** + * Grid view column count. ONE - list, TWO - normal grid, FOUR - landscape + */ + private static final int TWO = 2, FOUR = 4; + + /** + * Fragment UI + */ + private ViewGroup mRootView; + + /** + * The adapter for the grid + */ + private AlbumAdapter mAdapter; + + /** + * The grid view + */ + private GridView mGridView; + + /** + * Pop up menu helper + */ + private PopupMenuHelper mPopupMenuHelper; + + /** + * This holds the loading progress bar as well as the no results message + */ + private LoadingEmptyContainer mLoadingEmptyContainer; + + @Override + public int getLoaderId() { + return PagerAdapter.MusicFragments.ALBUM.ordinal(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mPopupMenuHelper = new AlbumPopupMenuHelper(getActivity(), getFragmentManager()) { + public Album getAlbum(int position) { + return mAdapter.getItem(position); + } + }; + + int layout = R.layout.grid_items_normal; + + mAdapter = new AlbumAdapter(getActivity(), layout); + mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mPopupMenuHelper.showPopupMenu(v, position); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + mRootView = (ViewGroup)inflater.inflate(R.layout.grid_base, null); + initGridView(); + + // Register the music status listener + ((BaseActivity)getActivity()).setMusicStateListenerListener(this); + + return mRootView; + } + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // Enable the options menu + setHasOptionsMenu(true); + // Start the loader + initLoader(null, this); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + ((BaseActivity)getActivity()).removeMusicStateListenerListener(this); + } + + + /** + * {@inheritDoc} + */ + @Override + public void onPause() { + super.onPause(); + mAdapter.flush(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onScrollStateChanged(final AbsListView view, final int scrollState) { + // Pause disk cache access to ensure smoother scrolling + if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { + mAdapter.setPauseDiskCache(true); + } else { + mAdapter.setPauseDiskCache(false); + mAdapter.notifyDataSetChanged(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onItemClick(final AdapterView<?> parent, final View view, final int position, + final long id) { + Album album = mAdapter.getItem(position); + NavUtils.openAlbumProfile(getActivity(), album.mAlbumName, album.mArtistName, album.mAlbumId); + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<SectionListContainer<Album>> onCreateLoader(final int id, final Bundle args) { + mLoadingEmptyContainer.showLoading(); + // if we ever decide to add section headers for grid items, we can pass a compartor + // instead of null + return new SectionCreator<Album>(getActivity(), new AlbumLoader(getActivity()), null); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoadFinished(final Loader<SectionListContainer<Album>> loader, + final SectionListContainer<Album> data) { + if (data.mListResults.isEmpty()) { + mAdapter.unload(); + mLoadingEmptyContainer.showNoResults(); + return; + } + + mAdapter.setData(data.mListResults); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoaderReset(final Loader<SectionListContainer<Album>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } + + /** + * 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) { + mGridView.setSelection(currentAlbumPosition); + } + } + + /** + * @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; + } + + int position = mAdapter.getItemPosition(albumId); + + // if for some reason we don't find the item, just jump to the top + if (position < 0) { + return 0; + } + + return position; + } + + /** + * Restarts the loader. + */ + public void refresh() { + // Wait a moment for the preference to change. + SystemClock.sleep(10); + restartLoader(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onScroll(final AbsListView view, final int firstVisibleItem, + final int visibleItemCount, final int totalItemCount) { + // Nothing to do + } + + /** + * {@inheritDoc} + */ + @Override + public void restartLoader() { + // Update the list when the user deletes any items + restartLoader(null, this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onMetaChanged() { + // Nothing to do + } + + @Override + public void onPlaylistChanged() { + // Nothing to do + } + + /** + * 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()); + // Show the albums and songs from the selected artist + list.setOnItemClickListener(this); + // To help make scrolling smooth + list.setOnScrollListener(this); + } + + /** + * Sets up the grid view + */ + private void initGridView() { + int columns = ApolloUtils.isLandscape(getActivity()) ? FOUR : TWO; + mAdapter.setNumColumns(columns); + // Initialize the grid + mGridView = (GridView)mRootView.findViewById(R.id.grid_base); + // Set the data behind the grid + mGridView.setAdapter(mAdapter); + // Set up the helpers + initAbsListView(mGridView); + mGridView.setNumColumns(columns); + + // Show progress bar + mLoadingEmptyContainer = (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + mGridView.setEmptyView(mLoadingEmptyContainer); + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/ArtistDetailFragment.java b/src/org/lineageos/eleven/ui/fragments/ArtistDetailFragment.java new file mode 100644 index 0000000..21bd556 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/ArtistDetailFragment.java @@ -0,0 +1,252 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.ListView; +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.ArtistDetailAlbumAdapter; +import com.cyanogenmod.eleven.adapters.ArtistDetailSongAdapter; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.cache.ImageFetcher; +import com.cyanogenmod.eleven.menu.FragmentMenuItems; +import com.cyanogenmod.eleven.model.Album; +import com.cyanogenmod.eleven.model.Artist; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.utils.AlbumPopupMenuHelper; +import com.cyanogenmod.eleven.utils.ArtistPopupMenuHelper; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.utils.SongPopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; + +import java.util.TreeSet; + +public class ArtistDetailFragment extends FadingBarFragment implements IChildFragment { + private final int ALBUM_LOADER_ID = 0; + private final int SONG_LOADER_ID = 1; + + private long mArtistId; + private String mArtistName; + + private ImageView mHero; + private View mHeader; + + private ListView mSongs; + private ArtistDetailSongAdapter mSongAdapter; + + private RecyclerView mAlbums; + private ArtistDetailAlbumAdapter mAlbumAdapter; + + private PopupMenuHelper mSongPopupMenuHelper; + private PopupMenuHelper mAlbumPopupMenuHelper; + + private LoadingEmptyContainer mLoadingEmptyContainer; + + @Override + protected int getLayoutToInflate() { return R.layout.activity_artist_detail; } + + @Override + protected String getTitle() { + return getArguments().getString(Config.ARTIST_NAME); + } + + protected long getArtistId() { + return getArguments().getLong(Config.ID); + } + + @Override + protected void onViewCreated() { + super.onViewCreated(); + + getContainingActivity().setFragmentPadding(false); + + Bundle arguments = getArguments(); + mArtistName = arguments.getString(Config.ARTIST_NAME); + mArtistId = arguments.getLong(Config.ID); + + setupPopupMenuHelpers(); + setupSongList(); + setupAlbumList(); + setupHero(mArtistName); + + LoaderManager lm = getLoaderManager(); + lm.initLoader(ALBUM_LOADER_ID, arguments, mAlbumAdapter); + lm.initLoader(SONG_LOADER_ID, arguments, mSongAdapter); + } + + @Override // DetailFragment + protected PopupMenuHelper createActionMenuHelper() { + return new ArtistPopupMenuHelper(getActivity(), getChildFragmentManager()) { + public Artist getArtist(int position) { + return new Artist(mArtistId, mArtistName, 0, 0); + } + }; + } + + @Override // DetailFragment + protected int getShuffleTitleId() { return R.string.menu_shuffle_artist; } + + @Override // DetailFragment + protected void playShuffled() { + MusicUtils.playArtist(getActivity(), mArtistId, -1, true); + } + + private void setupHero(String artistName) { + mHero = (ImageView)mHeader.findViewById(R.id.hero); + mHero.setContentDescription(artistName); + // initiate loading the artist image + // since the artist image needs to be scaled to the image view bounds, we need to wait till the first layout + // traversal to be able to get the image view dimensions in the helper method that scales the image + mHero.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mHero.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mHero.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + ImageFetcher.getInstance(getActivity()).loadArtistImage(mArtistName, mHero, true); + } + }); + } + + private void setupAlbumList() { + mAlbums = (RecyclerView) mHeader.findViewById(R.id.albums); + mAlbums.setHasFixedSize(true); + mAlbums.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false)); + mAlbumAdapter = new ArtistDetailAlbumAdapter(getActivity()); + mAlbumAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mAlbumPopupMenuHelper.showPopupMenu(v, position); + } + }); + mAlbums.setAdapter(mAlbumAdapter); + } + + private void setupSongList() { + mSongs = (ListView)mRootView.findViewById(R.id.songs); + mHeader = LayoutInflater.from(getActivity()). + inflate(R.layout.artist_detail_header, mSongs, false); + mSongs.addHeaderView(mHeader); + mSongs.setOnScrollListener(this); + mSongAdapter = new ArtistDetailSongAdapter(getActivity()) { + @Override + protected void onLoading() { + mLoadingEmptyContainer.showLoading(); + } + + @Override + protected void onNoResults() { + // no results - because the user deleted the last item - pop our fragment + // from the stack + getContainingActivity().postRemoveFragment(ArtistDetailFragment.this); + } + }; + mSongAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mSongPopupMenuHelper.showPopupMenu(v, position); + } + }); + mSongs.setAdapter(mSongAdapter); + mSongs.setOnItemClickListener(mSongAdapter); + mLoadingEmptyContainer = + (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + mSongs.setEmptyView(mLoadingEmptyContainer); + } + + private void setupPopupMenuHelpers() { + mSongPopupMenuHelper = new SongPopupMenuHelper(getActivity(), getChildFragmentManager()) { + @Override + public Song getSong(int position) { + return mSongAdapter.getItem(position); + } + + @Override + protected long getSourceId() { + return getArtistId(); + } + + @Override + protected Config.IdType getSourceType() { + return Config.IdType.Artist; + } + + @Override + protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) { + super.updateMenuIds(type, set); + + // since we are already on the artist page, this item doesn't make sense + set.remove(FragmentMenuItems.MORE_BY_ARTIST); + } + }; + + mAlbumPopupMenuHelper = new AlbumPopupMenuHelper(getActivity(), getChildFragmentManager()) { + @Override + public Album getAlbum(int position) { + return mAlbumAdapter.getItem(position); + } + + @Override + protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) { + super.updateMenuIds(type, set); + + // since we are already on the artist page, this item doesn't make sense + set.remove(FragmentMenuItems.MORE_BY_ARTIST); + } + }; + } + + // TODO: change this class to use the same header strategy as PlaylistDetail + protected int getHeaderHeight() { return mHero.getHeight(); } + + protected void setHeaderPosition(float y) { } + + @Override + public void restartLoader() { + Bundle arguments = getArguments(); + LoaderManager lm = getLoaderManager(); + lm.restartLoader(ALBUM_LOADER_ID, arguments, mAlbumAdapter); + lm.restartLoader(SONG_LOADER_ID, arguments, mSongAdapter); + + ImageFetcher.getInstance(getActivity()).loadArtistImage(mArtistName, mHero, true); + } + + @Override + public void onMetaChanged() { + super.onMetaChanged(); + + mSongAdapter.setCurrentlyPlayingTrack(MusicUtils.getCurrentTrack()); + } + + @Override + public PagerAdapter.MusicFragments getMusicFragmentParent() { + return PagerAdapter.MusicFragments.ARTIST; + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/ArtistFragment.java b/src/org/lineageos/eleven/ui/fragments/ArtistFragment.java new file mode 100644 index 0000000..100c55a --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/ArtistFragment.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.os.SystemClock; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; + +import com.cyanogenmod.eleven.MusicStateListener; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.ArtistAdapter; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.loaders.ArtistLoader; +import com.cyanogenmod.eleven.model.Artist; +import com.cyanogenmod.eleven.recycler.RecycleHolder; +import com.cyanogenmod.eleven.sectionadapter.SectionAdapter; +import com.cyanogenmod.eleven.sectionadapter.SectionCreator; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.ui.fragments.phone.MusicBrowserFragment; +import com.cyanogenmod.eleven.utils.ArtistPopupMenuHelper; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.NavUtils; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.utils.SectionCreatorUtils; +import com.cyanogenmod.eleven.utils.SectionCreatorUtils.IItemCompare; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; + +/** + * This class is used to display all of the artists on a user's device. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class ArtistFragment extends MusicBrowserFragment implements + LoaderCallbacks<SectionListContainer<Artist>>, + OnScrollListener, OnItemClickListener, MusicStateListener { + + /** + * Fragment UI + */ + private ViewGroup mRootView; + + /** + * The adapter for the grid + */ + private SectionAdapter<Artist, ArtistAdapter> mAdapter; + + /** + * The list view + */ + private ListView mListView; + + /** + * Pop up menu helper + */ + private PopupMenuHelper mPopupMenuHelper; + + /** + * Loading container and no results container + */ + private LoadingEmptyContainer mLoadingEmptyContainer; + + /** + * Empty constructor as per the {@link Fragment} documentation + */ + public ArtistFragment() { + } + + @Override + public int getLoaderId() { + return PagerAdapter.MusicFragments.ARTIST.ordinal(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mPopupMenuHelper = new ArtistPopupMenuHelper(getActivity(), getFragmentManager()) { + @Override + public Artist getArtist(int position) { + return mAdapter.getTItem(position); + } + }; + + // Create the adapter + final int layout = R.layout.list_item_normal; + ArtistAdapter adapter = new ArtistAdapter(getActivity(), layout); + mAdapter = new SectionAdapter<Artist, ArtistAdapter>(getActivity(), adapter); + mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mPopupMenuHelper.showPopupMenu(v, position); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + // The View for the fragment's UI + mRootView = (ViewGroup)inflater.inflate(R.layout.list_base, null); + initListView(); + + // Register the music status listener + ((BaseActivity)getActivity()).setMusicStateListenerListener(this); + + return mRootView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + ((BaseActivity)getActivity()).removeMusicStateListenerListener(this); + } + + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // Enable the options menu + setHasOptionsMenu(true); + // Start the loader + initLoader(null, this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onPause() { + super.onPause(); + mAdapter.flush(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onScrollStateChanged(final AbsListView view, final int scrollState) { + // Pause disk cache access to ensure smoother scrolling + if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { + mAdapter.getUnderlyingAdapter().setPauseDiskCache(true); + } else { + mAdapter.getUnderlyingAdapter().setPauseDiskCache(false); + mAdapter.notifyDataSetChanged(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onItemClick(final AdapterView<?> parent, final View view, final int position, + final long id) { + Artist artist = mAdapter.getTItem(position); + NavUtils.openArtistProfile(getActivity(), artist.mArtistName); + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<SectionListContainer<Artist>> onCreateLoader(final int id, final Bundle args) { + mLoadingEmptyContainer.showLoading(); + final Context context = getActivity(); + IItemCompare<Artist> comparator = SectionCreatorUtils.createArtistComparison(context); + return new SectionCreator<Artist>(getActivity(), new ArtistLoader(context), comparator); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoadFinished(final Loader<SectionListContainer<Artist>> loader, + final SectionListContainer<Artist> data) { + if (data.mListResults.isEmpty()) { + mAdapter.unload(); + mLoadingEmptyContainer.showNoResults(); + return; + } + + mAdapter.setData(data); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoaderReset(final Loader<SectionListContainer<Artist>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } + + /** + * Scrolls the list to the currently playing artist when the user touches + * the header in the {@link TitlePageIndicator}. + */ + public void scrollToCurrentArtist() { + final int currentArtistPosition = getItemPositionByArtist(); + + if (currentArtistPosition != 0) { + mListView.setSelection(currentArtistPosition); + } + } + + /** + * @return The position of an item in the list or grid based on the name of + * the currently playing artist. + */ + private int getItemPositionByArtist() { + final long artistId = MusicUtils.getCurrentArtistId(); + if (mAdapter == null) { + return 0; + } + + int position = mAdapter.getItemPosition(artistId); + + // if for some reason we don't find the item, just jump to the top + if (position < 0) { + return 0; + } + + return position; + } + + /** + * Restarts the loader. + */ + public void refresh() { + // Wait a moment for the preference to change. + SystemClock.sleep(10); + restartLoader(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onScroll(final AbsListView view, final int firstVisibleItem, + final int visibleItemCount, final int totalItemCount) { + // Nothing to do + } + + /** + * {@inheritDoc} + */ + @Override + public void restartLoader() { + // Update the list when the user deletes any items + restartLoader(null, this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onMetaChanged() { + // Nothing to do + } + + @Override + public void onPlaylistChanged() { + // Nothing to do + } + + /** + * 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()); + // Show the albums and songs from the selected artist + list.setOnItemClickListener(this); + // To help make scrolling smooth + list.setOnScrollListener(this); + } + + /** + * Sets up the list view + */ + private void initListView() { + // Initialize the grid + mListView = (ListView)mRootView.findViewById(R.id.list_base); + // Set the data behind the list + mListView.setAdapter(mAdapter); + // set the loading and empty view container + mLoadingEmptyContainer = (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + mListView.setEmptyView(mLoadingEmptyContainer); + // Set up the helpers + initAbsListView(mListView); + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/AudioPlayerFragment.java b/src/org/lineageos/eleven/ui/fragments/AudioPlayerFragment.java new file mode 100644 index 0000000..58cecd0 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/AudioPlayerFragment.java @@ -0,0 +1,814 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.graphics.Outline; +import android.media.AudioManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.text.Html; +import android.text.Spanned; +import android.text.TextUtils; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.TextView; + +import com.cyanogenmod.eleven.MusicPlaybackService; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.AlbumArtPagerAdapter; +import com.cyanogenmod.eleven.cache.ImageFetcher; +import com.cyanogenmod.eleven.loaders.NowPlayingCursor; +import com.cyanogenmod.eleven.loaders.QueueLoader; +import com.cyanogenmod.eleven.menu.CreateNewPlaylist; +import com.cyanogenmod.eleven.menu.DeleteDialog; +import com.cyanogenmod.eleven.menu.FragmentMenuItems; +import com.cyanogenmod.eleven.ui.activities.HomeActivity; +import com.cyanogenmod.eleven.utils.ApolloUtils; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.NavUtils; +import com.cyanogenmod.eleven.utils.PreferenceUtils; +import com.cyanogenmod.eleven.widgets.BrowseButton; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; +import com.cyanogenmod.eleven.widgets.PlayPauseProgressButton; +import com.cyanogenmod.eleven.widgets.QueueButton; +import com.cyanogenmod.eleven.widgets.RepeatButton; +import com.cyanogenmod.eleven.widgets.RepeatingImageButton; +import com.cyanogenmod.eleven.widgets.ShuffleButton; +import com.cyanogenmod.eleven.widgets.VisualizerView; + +import java.lang.ref.WeakReference; + +import static com.cyanogenmod.eleven.utils.MusicUtils.mService; + +public class AudioPlayerFragment extends Fragment implements ServiceConnection { + private static final String TAG = AudioPlayerFragment.class.getSimpleName(); + + /** + * Used to keep context menu items from bleeding into other fragments + */ + private static final int GROUP_ID = 15; + + // fragment view + private ViewGroup mRootView; + + // Header views + private TextView mSongTitle; + private TextView mArtistName; + + // Playlist Button + private ImageView mAddToPlaylistButton; + + // Menu Button + private ImageView mMenuButton; + + // Message to refresh the time + private static final int REFRESH_TIME = 1; + + // The service token + private MusicUtils.ServiceToken mToken; + + // Play pause and progress button + private PlayPauseProgressButton mPlayPauseProgressButton; + + // Repeat button + private RepeatButton mRepeatButton; + + // Shuffle button + private ShuffleButton mShuffleButton; + + // Previous button + private RepeatingImageButton mPreviousButton; + + // Next button + private RepeatingImageButton mNextButton; + + // Album art ListView + private ViewPager mAlbumArtViewPager; + private LoadingEmptyContainer mQueueEmpty; + + private AlbumArtPagerAdapter mAlbumArtPagerAdapter; + + // Current time + private TextView mCurrentTime; + + // Total time + private TextView mTotalTime; + + // Visualizer View + private VisualizerView mVisualizerView; + + // Broadcast receiver + private PlaybackStatus mPlaybackStatus; + + // Handler used to update the current time + private TimeHandler mTimeHandler; + + // Image cache + private ImageFetcher mImageFetcher; + + // popup menu for pressing the menu icon + private PopupMenu mPopupMenu; + + // Lyrics text view + private TextView mLyricsText; + + private long mSelectedId = -1; + + private boolean mIsPaused = false; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Control the media volume + getActivity().setVolumeControlStream(AudioManager.STREAM_MUSIC); + + // Bind Apollo's service + mToken = MusicUtils.bindToService(getActivity(), this); + + // Initialize the image fetcher/cache + mImageFetcher = ApolloUtils.getImageFetcher(getActivity()); + + // Initialize the handler used to update the current time + mTimeHandler = new TimeHandler(this); + + // Initialize the broadcast receiver + mPlaybackStatus = new PlaybackStatus(this); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + // The View for the fragment's UI + mRootView = (ViewGroup) inflater.inflate(R.layout.activity_player_fragment, null); + + // Header title values + initHeaderBar(); + + initPlaybackControls(); + + mVisualizerView = (VisualizerView) mRootView.findViewById(R.id.visualizerView); + mVisualizerView.initialize(getActivity()); + updateVisualizerPowerSaveMode(); + + mLyricsText = (TextView) mRootView.findViewById(R.id.audio_player_lyrics); + + return mRootView; + } + + /** + * {@inheritDoc} + */ + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + // Set the playback drawables + updatePlaybackControls(); + // Setup the adapter + createAndSetAdapter(); + // Current info + updateNowPlayingInfo(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + + @Override + public void onStart() { + super.onStart(); + + final IntentFilter filter = new IntentFilter(); + // Play and pause changes + filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED); + // Shuffle and repeat changes + filter.addAction(MusicPlaybackService.SHUFFLEMODE_CHANGED); + filter.addAction(MusicPlaybackService.REPEATMODE_CHANGED); + // Track changes + filter.addAction(MusicPlaybackService.META_CHANGED); + // Update a list, probably the playlist fragment's + filter.addAction(MusicPlaybackService.REFRESH); + // Listen to changes to the entire queue + filter.addAction(MusicPlaybackService.QUEUE_CHANGED); + // Listen for lyrics text for the audio track + filter.addAction(MusicPlaybackService.NEW_LYRICS); + // Listen for power save mode changed + filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + // Register the intent filters + getActivity().registerReceiver(mPlaybackStatus, filter); + // Refresh the current time + final long next = refreshCurrentTime(); + queueNextRefresh(next); + + // resumes the update callback for the play pause progress button + mPlayPauseProgressButton.resume(); + } + + @Override + public void onStop() { + super.onStop(); + + // pause the update callback for the play pause progress button + mPlayPauseProgressButton.pause(); + + mImageFetcher.flush(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + mIsPaused = false; + mTimeHandler.removeMessages(REFRESH_TIME); + // Unbind from the service + if (mService != null) { + MusicUtils.unbindFromService(mToken); + mToken = null; + } + + // Unregister the receiver + try { + getActivity().unregisterReceiver(mPlaybackStatus); + } catch (final Throwable e) { + //$FALL-THROUGH$ + } + } + + /** + * Initializes the header bar + */ + private void initHeaderBar() { + View headerBar = mRootView.findViewById(R.id.audio_player_header); + final int bottomActionBarHeight = + getResources().getDimensionPixelSize(R.dimen.bottom_action_bar_height); + + headerBar.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + // since we only want the top and bottom shadows, pad the horizontal width + // to hide the shadows. Can't seem to find a better way to do this + int padWidth = (int)(0.2f * view.getWidth()); + outline.setRect(-padWidth, -bottomActionBarHeight, view.getWidth() + padWidth, + view.getHeight()); + } + }); + + // Title text + mSongTitle = (TextView) mRootView.findViewById(R.id.header_bar_song_title); + mArtistName = (TextView) mRootView.findViewById(R.id.header_bar_artist_title); + + // Buttons + // Search Button + View v = mRootView.findViewById(R.id.header_bar_search_button); + v.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavUtils.openSearch(getActivity(), ""); + } + }); + + // Add to Playlist Button + // Setup the playlist button - add a click listener to show the context + mAddToPlaylistButton = (ImageView) mRootView.findViewById(R.id.header_bar_add_button); + + // Create the context menu when requested + mAddToPlaylistButton.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, menu); + menu.setHeaderTitle(R.string.add_to_playlist); + } + }); + + // add a click listener to show the context + mAddToPlaylistButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // save the current track id + mSelectedId = MusicUtils.getCurrentAudioId(); + mAddToPlaylistButton.showContextMenu(); + } + }); + + // Add the menu button + // menu button + mMenuButton = (ImageView) mRootView.findViewById(R.id.header_bar_menu_button); + mMenuButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showPopupMenu(); + } + }); + } + + /** + * Initializes the items in the now playing screen + */ + private void initPlaybackControls() { + mPlayPauseProgressButton = (PlayPauseProgressButton)mRootView.findViewById(R.id.playPauseProgressButton); + mPlayPauseProgressButton.setDragEnabled(true); + mShuffleButton = (ShuffleButton)mRootView.findViewById(R.id.action_button_shuffle); + mRepeatButton = (RepeatButton)mRootView.findViewById(R.id.action_button_repeat); + mPreviousButton = (RepeatingImageButton)mRootView.findViewById(R.id.action_button_previous); + mNextButton = (RepeatingImageButton)mRootView.findViewById(R.id.action_button_next); + + // Album art view pager + mAlbumArtViewPager = (ViewPager)mRootView.findViewById(R.id.audio_player_album_art_viewpager); + mAlbumArtViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + super.onPageSelected(position); + + int currentPosition = 0; + if (MusicUtils.getShuffleMode() == MusicPlaybackService.SHUFFLE_NONE) { + // if we aren't shuffling, base the position on the queue position + currentPosition = MusicUtils.getQueuePosition(); + } else { + // if we are shuffling, use the history size as the position + currentPosition = MusicUtils.getQueueHistorySize(); + } + + // check if we are going to next or previous track + if (position - currentPosition == 1) { + MusicUtils.asyncNext(getActivity()); + } else if (position - currentPosition == -1) { + MusicUtils.previous(getActivity(), true); + } else if (currentPosition != position) { + Log.w(TAG, "Unexpected page position of " + position + + " when current is: " + currentPosition); + } + } + }); + // view to show in place of album art if queue is empty + mQueueEmpty = (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + setupNoResultsContainer(mQueueEmpty.getNoResultsContainer()); + + // Current time + mCurrentTime = (TextView)mRootView.findViewById(R.id.audio_player_current_time); + // Total time + mTotalTime = (TextView)mRootView.findViewById(R.id.audio_player_total_time); + + // Set the repeat listener for the previous button + mPreviousButton.setRepeatListener(mRewindListener); + // Set the repeat listener for the next button + mNextButton.setRepeatListener(mFastForwardListener); + + mPlayPauseProgressButton.enableAndShow(); + } + + private void setupNoResultsContainer(NoResultsContainer empty) { + int color = getResources().getColor(R.color.no_results_light); + empty.setTextColor(color); + empty.setMainText(R.string.empty_queue_main); + empty.setSecondaryText(R.string.empty_queue_secondary); + } + + /** + * Sets the track name, album name, and album art. + */ + private void updateNowPlayingInfo() { + // Set the track name + mSongTitle.setText(MusicUtils.getTrackName()); + mArtistName.setText(MusicUtils.getArtistName()); + + // Set the total time + String totalTime = MusicUtils.makeShortTimeString(getActivity(), MusicUtils.duration() / 1000); + if (!mTotalTime.getText().equals(totalTime)) { + mTotalTime.setText(totalTime); + } + + if (MusicUtils.getRepeatMode() == MusicPlaybackService.REPEAT_CURRENT) { + // we are repeating 1 so just jump to the 1st and only item + mAlbumArtViewPager.setCurrentItem(0, false); + } else if (MusicUtils.getShuffleMode() == MusicPlaybackService.SHUFFLE_NONE) { + // we are playing in-order, base the position on the queue position + mAlbumArtViewPager.setCurrentItem(MusicUtils.getQueuePosition(), true); + } else { + // if we are shuffling, just based our index based on the history + mAlbumArtViewPager.setCurrentItem(MusicUtils.getQueueHistorySize(), true); + } + + // Update the current time + queueNextRefresh(1); + } + + /** + * This creates the adapter based on the repeat and shuffle configuration and sets it into the + * page adapter + */ + private void createAndSetAdapter() { + mAlbumArtPagerAdapter = new AlbumArtPagerAdapter(getChildFragmentManager()); + + int repeatMode = MusicUtils.getRepeatMode(); + int targetSize = 0; + int targetIndex = 0; + int queueSize = MusicUtils.getQueueSize(); + + if (repeatMode == MusicPlaybackService.REPEAT_CURRENT) { + targetSize = 1; + targetIndex = 0; + } else if (MusicUtils.getShuffleMode() == MusicPlaybackService.SHUFFLE_NONE) { + // if we aren't shuffling, use the queue to determine where we are + targetSize = queueSize; + targetIndex = MusicUtils.getQueuePosition(); + } else { + // otherwise, set it to the max history size + targetSize = MusicPlaybackService.MAX_HISTORY_SIZE; + targetIndex = MusicUtils.getQueueHistorySize(); + } + + mAlbumArtPagerAdapter.setPlaylistLength(targetSize); + mAlbumArtViewPager.setAdapter(mAlbumArtPagerAdapter); + mAlbumArtViewPager.setCurrentItem(targetIndex); + + if(queueSize == 0) { + mAlbumArtViewPager.setVisibility(View.GONE); + mQueueEmpty.showNoResults(); + mAddToPlaylistButton.setVisibility(View.GONE); + } else { + mAlbumArtViewPager.setVisibility(View.VISIBLE); + mQueueEmpty.hideAll(); + mAddToPlaylistButton.setVisibility(View.VISIBLE); + } + } + + /** + * Sets the correct drawable states for the playback controls. + */ + private void updatePlaybackControls() { + // Set the play and pause image + mPlayPauseProgressButton.getPlayPauseButton().updateState(); + // Set the shuffle image + mShuffleButton.updateShuffleState(); + // Set the repeat image + mRepeatButton.updateRepeatState(); + } + + /** + * @param delay When to update + */ + private void queueNextRefresh(final long delay) { + if (!mIsPaused) { + final Message message = mTimeHandler.obtainMessage(REFRESH_TIME); + mTimeHandler.removeMessages(REFRESH_TIME); + mTimeHandler.sendMessageDelayed(message, delay); + } + } + + /** + * Used to seek forwards or backwards in time through the current track + * + * @param repcnt The repeat count + * @param delta The long press duration + * @param forwards Whether it was seeking forwards or backwards + */ + private void seekRelative(final int repcnt, long delta, boolean forwards) { + if (mService == null) { + return; + } + if (repcnt > 0) { + final long EXTRA_FAST_CUTOFF = 10000; + if (delta < EXTRA_FAST_CUTOFF) { + // seek at 10x speed for the first 5 seconds + delta = delta * 10; + } else { + // seek at 40x after that + delta = EXTRA_FAST_CUTOFF * 10 + (delta - EXTRA_FAST_CUTOFF) * 40; + } + + MusicUtils.seekRelative(forwards ? delta : -delta); + + refreshCurrentTime(); + } + } + + private void refreshCurrentTimeText(final long pos) { + if (mPlayPauseProgressButton.isDragging()) { + mCurrentTime.setText(MusicUtils.makeShortTimeString(getActivity(), + mPlayPauseProgressButton.getDragProgressInMs() / 1000)); + } else { + mCurrentTime.setText(MusicUtils.makeShortTimeString(getActivity(), pos / 1000)); + } + } + + /* Used to update the current time string */ + private long refreshCurrentTime() { + if (mService == null) { + return MusicUtils.UPDATE_FREQUENCY_MS; + } + try { + final long pos = MusicUtils.position(); + if (pos >= 0 && MusicUtils.duration() > 0) { + refreshCurrentTimeText(pos); + + if (mPlayPauseProgressButton.isDragging()) { + mCurrentTime.setVisibility(View.VISIBLE); + return MusicUtils.UPDATE_FREQUENCY_FAST_MS; + } else if (MusicUtils.isPlaying()) { + mCurrentTime.setVisibility(View.VISIBLE); + + // calculate the number of milliseconds until the next full second, + // so the counter can be updated at just the right time + return Math.max(20, 1000 - pos % 1000); + } else { + // blink the counter + final int vis = mCurrentTime.getVisibility(); + mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE + : View.INVISIBLE); + } + } else { + mCurrentTime.setText("--:--"); + } + } catch (final Exception ignored) { + if (ignored.getMessage() != null) { + Log.e(TAG, ignored.getMessage()); + } + } + return MusicUtils.UPDATE_FREQUENCY_MS; + } + + /** + * Used to scan backwards through the track + */ + private final RepeatingImageButton.RepeatListener mRewindListener = new RepeatingImageButton.RepeatListener() { + /** + * {@inheritDoc} + */ + @Override + public void onRepeat(final View v, final long howlong, final int repcnt) { + seekRelative(repcnt, howlong, false); + } + }; + + /** + * Used to scan ahead through the track + */ + private final RepeatingImageButton.RepeatListener mFastForwardListener = new RepeatingImageButton.RepeatListener() { + /** + * {@inheritDoc} + */ + @Override + public void onRepeat(final View v, final long howlong, final int repcnt) { + seekRelative(repcnt, howlong, true); + } + }; + + public void showPopupMenu() { + // create the popup menu + if (mPopupMenu == null) { + mPopupMenu = new PopupMenu(getActivity(), mMenuButton); + mPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + return onPopupMenuItemClick(item); + } + }); + } + + final Menu menu = mPopupMenu.getMenu(); + final MenuInflater inflater = mPopupMenu.getMenuInflater(); + menu.clear(); + + // Shuffle all + inflater.inflate(R.menu.shuffle_all, menu); + if (MusicUtils.getQueueSize() > 0) { + // ringtone, and equalizer + inflater.inflate(R.menu.audio_player, menu); + + if (!NavUtils.hasEffectsPanel(getActivity())) { + menu.removeItem(R.id.menu_audio_player_equalizer); + } + + // save queue/clear queue + inflater.inflate(R.menu.queue, menu); + } + // Settings + inflater.inflate(R.menu.activity_base, menu); + + // show the popup + mPopupMenu.show(); + } + + public boolean onPopupMenuItemClick(final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_shuffle_all: + // Shuffle all the songs + MusicUtils.shuffleAll(getActivity()); + return true; + case R.id.menu_audio_player_ringtone: + // Set the current track as a ringtone + MusicUtils.setRingtone(getActivity(), MusicUtils.getCurrentAudioId()); + return true; + case R.id.menu_audio_player_equalizer: + // Sound effects + NavUtils.openEffectsPanel(getActivity(), HomeActivity.EQUALIZER); + return true; + case R.id.menu_settings: + // Settings + NavUtils.openSettings(getActivity()); + return true; + case R.id.menu_audio_player_more_by_artist: + NavUtils.openArtistProfile(getActivity(), MusicUtils.getArtistName()); + return true; + case R.id.menu_audio_player_delete: + // Delete current song + DeleteDialog.newInstance(MusicUtils.getTrackName(), new long[]{ + MusicUtils.getCurrentAudioId() + }, null).show(getActivity().getSupportFragmentManager(), "DeleteDialog"); + return true; + case R.id.menu_save_queue: + NowPlayingCursor queue = (NowPlayingCursor) QueueLoader + .makeQueueCursor(getActivity()); + CreateNewPlaylist.getInstance(MusicUtils.getSongListForCursor(queue)).show( + getFragmentManager(), "CreatePlaylist"); + queue.close(); + return true; + case R.id.menu_clear_queue: + MusicUtils.clearQueue(); + return true; + default: + break; + } + + return false; + } + + public void dismissPopupMenu() { + if (mPopupMenu != null) { + mPopupMenu.dismiss(); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + if (item.getGroupId() == GROUP_ID) { + switch (item.getItemId()) { + case FragmentMenuItems.NEW_PLAYLIST: + CreateNewPlaylist.getInstance(new long[]{ + mSelectedId + }).show(getFragmentManager(), "CreatePlaylist"); + return true; + case FragmentMenuItems.PLAYLIST_SELECTED: + final long mPlaylistId = item.getIntent().getLongExtra("playlist", 0); + MusicUtils.addToPlaylist(getActivity(), new long[]{ + mSelectedId + }, mPlaylistId); + return true; + default: + break; + } + } + + return super.onContextItemSelected(item); + } + + public void onLyrics(String lyrics) { + if (TextUtils.isEmpty(lyrics) + || !PreferenceUtils.getInstance(getActivity()).getShowLyrics()) { + mLyricsText.animate().alpha(0).setDuration(200); + } else { + lyrics = lyrics.replace("\n", "<br/>"); + Spanned span = Html.fromHtml(lyrics); + mLyricsText.setText(span); + + mLyricsText.animate().alpha(1).setDuration(200); + } + } + + public void setVisualizerVisible(boolean visible) { + if (visible && PreferenceUtils.getInstance(getActivity()).getShowVisualizer()) { + if (PreferenceUtils.canRecordAudio(getActivity())) { + mVisualizerView.setVisible(true); + } else { + PreferenceUtils.requestRecordAudio(getActivity()); + } + } else { + mVisualizerView.setVisible(false); + } + } + + public void updateVisualizerPowerSaveMode() { + PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE); + mVisualizerView.setPowerSaveMode(pm.isPowerSaveMode()); + } + + public void setVisualizerColor(int color) { + mVisualizerView.setColor(color); + } + + /** + * Used to update the current time string + */ + private static final class TimeHandler extends Handler { + + private final WeakReference<AudioPlayerFragment> mAudioPlayer; + + /** + * Constructor of <code>TimeHandler</code> + */ + public TimeHandler(final AudioPlayerFragment player) { + mAudioPlayer = new WeakReference<AudioPlayerFragment>(player); + } + + @Override + public void handleMessage(final Message msg) { + switch (msg.what) { + case REFRESH_TIME: + final long next = mAudioPlayer.get().refreshCurrentTime(); + mAudioPlayer.get().queueNextRefresh(next); + break; + default: + break; + } + } + }; + + /** + * Used to monitor the state of playback + */ + private static final class PlaybackStatus extends BroadcastReceiver { + + private final WeakReference<AudioPlayerFragment> mReference; + + /** + * Constructor of <code>PlaybackStatus</code> + */ + public PlaybackStatus(final AudioPlayerFragment fragment) { + mReference = new WeakReference<AudioPlayerFragment>(fragment); + } + + /** + * {@inheritDoc} + */ + @Override + public void onReceive(final Context context, final Intent intent) { + final AudioPlayerFragment audioPlayerFragment = mReference.get(); + final String action = intent.getAction(); + if (action.equals(MusicPlaybackService.META_CHANGED)) { + // if we are repeating current and the track has changed, re-create the adapter + if (MusicUtils.getRepeatMode() == MusicPlaybackService.REPEAT_CURRENT) { + mReference.get().createAndSetAdapter(); + } + + // Current info + audioPlayerFragment.updateNowPlayingInfo(); + audioPlayerFragment.dismissPopupMenu(); + } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) { + // Set the play and pause image + audioPlayerFragment.mPlayPauseProgressButton.getPlayPauseButton().updateState(); + audioPlayerFragment.mVisualizerView.setPlaying(MusicUtils.isPlaying()); + } else if (action.equals(MusicPlaybackService.REPEATMODE_CHANGED) + || action.equals(MusicPlaybackService.SHUFFLEMODE_CHANGED)) { + // Set the repeat image + audioPlayerFragment.mRepeatButton.updateRepeatState(); + // Set the shuffle image + audioPlayerFragment.mShuffleButton.updateShuffleState(); + + // Update the queue + audioPlayerFragment.createAndSetAdapter(); + } else if (action.equals(MusicPlaybackService.QUEUE_CHANGED)) { + audioPlayerFragment.createAndSetAdapter(); + } else if (action.equals(MusicPlaybackService.NEW_LYRICS)) { + audioPlayerFragment.onLyrics(intent.getStringExtra("lyrics")); + } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) { + audioPlayerFragment.updateVisualizerPowerSaveMode(); + } + } + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/BaseFragment.java b/src/org/lineageos/eleven/ui/fragments/BaseFragment.java new file mode 100644 index 0000000..5a5885a --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/BaseFragment.java @@ -0,0 +1,98 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.cyanogenmod.eleven.MusicStateListener; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.ui.activities.HomeActivity; + +public abstract class BaseFragment extends Fragment implements MusicStateListener, + ISetupActionBar { + + protected ViewGroup mRootView; + + protected abstract String getTitle(); + protected abstract int getLayoutToInflate(); + + protected boolean needsElevatedActionBar() { + return true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + @Override + public void setupActionBar() { + getContainingActivity().setupActionBar(getTitle()); + getContainingActivity().setActionBarAlpha(255); + getContainingActivity().setFragmentPadding(true); + getContainingActivity().setActionBarElevation(needsElevatedActionBar()); + } + + protected HomeActivity getContainingActivity() { + return (HomeActivity) getActivity(); + } + + @Override + public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // The View for the fragment's UI + mRootView = (ViewGroup) inflater.inflate(getLayoutToInflate(), null); + // set the background color + mRootView.setBackgroundColor(getResources().getColor(R.color.background_color)); + // eat any touches that fall through to the root so they aren't + // passed on to fragments "behind" the current one. + mRootView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent me) { return true; } + }); + + setupActionBar(); + onViewCreated(); + + return mRootView; + } + + protected void onViewCreated() { + getContainingActivity().setMusicStateListenerListener(this); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + getContainingActivity().removeMusicStateListenerListener(this); + } + + @Override + public void onMetaChanged() { + + } + + @Override + public void onPlaylistChanged() { + + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/DetailFragment.java b/src/org/lineageos/eleven/ui/fragments/DetailFragment.java new file mode 100644 index 0000000..43e5274 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/DetailFragment.java @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; + +public abstract class DetailFragment extends BaseFragment { + protected PopupMenuHelper mActionMenuHelper; + + /** create the popup menu helper used by the type of item + * for which this is a detail screen */ + protected abstract PopupMenuHelper createActionMenuHelper(); + /** menu title for the shuffle option for this screen */ + protected abstract int getShuffleTitleId(); + /** action to take if the shuffle menu is selected */ + protected abstract void playShuffled(); + + @Override + protected void onViewCreated() { + super.onViewCreated(); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + inflater.inflate(R.menu.shuffle_item, menu); + menu.findItem(R.id.menu_shuffle_item).setTitle(getShuffleTitleId()); + + // use the same popup menu to provide actions for the item + // represented by this detail screen as would be used elsewhere + mActionMenuHelper = createActionMenuHelper(); + mActionMenuHelper.onPreparePopupMenu(0); + mActionMenuHelper.createPopupMenu(menu); + + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + if(item.getItemId() == R.id.menu_shuffle_item) { + playShuffled(); + return true; + } + + // delegate to the popup menu that represents the item + // for which this is a detail screen + if(mActionMenuHelper.onMenuItemClick(item)) { return true; } + + return super.onOptionsItemSelected(item); + } +}
\ No newline at end of file diff --git a/src/org/lineageos/eleven/ui/fragments/FadingBarFragment.java b/src/org/lineageos/eleven/ui/fragments/FadingBarFragment.java new file mode 100644 index 0000000..81c4009 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/FadingBarFragment.java @@ -0,0 +1,72 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.view.View; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; + +import com.cyanogenmod.eleven.ui.activities.HomeActivity; + +public abstract class FadingBarFragment extends DetailFragment implements OnScrollListener { + protected static final int ACTION_BAR_DEFAULT_OPACITY = 100; + + @Override + public void setupActionBar() { + super.setupActionBar(); + + getContainingActivity().setActionBarAlpha(ACTION_BAR_DEFAULT_OPACITY); + getContainingActivity().setFragmentPadding(false); + } + + protected abstract int getHeaderHeight(); + + protected abstract void setHeaderPosition(float y); + + @Override // OnScrollListener + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + View firstChild = view.getChildAt(0); + if (firstChild == null) { + return; + } + + float firstChildY = firstChild.getY(); + + int alpha = 255; + if (firstVisibleItem == 0) { + // move header to current top of list + setHeaderPosition(firstChildY); + // calculate alpha for the action bar + alpha = ACTION_BAR_DEFAULT_OPACITY + + (int)((255 - ACTION_BAR_DEFAULT_OPACITY) * -firstChildY / + (float)(firstChild.getHeight())); + if(alpha > 255) { alpha = 255; } + } else { + // header off screen + setHeaderPosition(-getHeaderHeight()); + } + + HomeActivity home = getContainingActivity(); + if (home != null && home.getTopFragment() == this) { + home.setActionBarAlpha(alpha); + } + } + + @Override // OnScrollListener + public void onScrollStateChanged(AbsListView view, int scrollState) { + + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/IChildFragment.java b/src/org/lineageos/eleven/ui/fragments/IChildFragment.java new file mode 100644 index 0000000..f1b5fb7 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/IChildFragment.java @@ -0,0 +1,22 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import com.cyanogenmod.eleven.adapters.PagerAdapter; + +public interface IChildFragment { + PagerAdapter.MusicFragments getMusicFragmentParent(); +} diff --git a/src/org/lineageos/eleven/ui/fragments/ISetupActionBar.java b/src/org/lineageos/eleven/ui/fragments/ISetupActionBar.java new file mode 100644 index 0000000..6ce191b --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/ISetupActionBar.java @@ -0,0 +1,20 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +public interface ISetupActionBar { + public void setupActionBar(); +} diff --git a/src/org/lineageos/eleven/ui/fragments/PlaylistDetailFragment.java b/src/org/lineageos/eleven/ui/fragments/PlaylistDetailFragment.java new file mode 100644 index 0000000..4461b27 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/PlaylistDetailFragment.java @@ -0,0 +1,420 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.view.View; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ImageView; +import android.widget.TextView; +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.adapters.ProfileSongAdapter; +import com.cyanogenmod.eleven.cache.ImageFetcher; +import com.cyanogenmod.eleven.dragdrop.DragSortListView; +import com.cyanogenmod.eleven.dragdrop.DragSortListView.DragScrollProfile; +import com.cyanogenmod.eleven.dragdrop.DragSortListView.DropListener; +import com.cyanogenmod.eleven.dragdrop.DragSortListView.RemoveListener; +import com.cyanogenmod.eleven.loaders.PlaylistSongLoader; +import com.cyanogenmod.eleven.menu.FragmentMenuItems; +import com.cyanogenmod.eleven.model.Playlist; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.recycler.RecycleHolder; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PlaylistPopupMenuHelper; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.utils.PopupMenuHelper.PopupMenuType; +import com.cyanogenmod.eleven.utils.SongPopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; + +import java.util.List; +import java.util.TreeSet; + +public class PlaylistDetailFragment extends FadingBarFragment implements + LoaderCallbacks<List<Song>>, OnItemClickListener, DropListener, + RemoveListener, DragScrollProfile, IChildFragment { + + /** + * LoaderCallbacks identifier + */ + private static final int LOADER = 0; + + private DragSortListView mListView; + private ProfileSongAdapter mAdapter; + + private View mHeaderContainer; + private ImageView mPlaylistImageView; + + private LoadingEmptyContainer mLoadingEmptyContainer; + + private TextView mNumberOfSongs; + private TextView mDurationOfPlaylist; + + /** + * The Id of the playlist the songs belong to + */ + private long mPlaylistId; + private String mPlaylistName; + + /** + * Pop up menu helper + */ + private PopupMenuHelper mPopupMenuHelper; + + @Override + protected String getTitle() { return mPlaylistName; } + + @Override + protected int getLayoutToInflate() { + return R.layout.playlist_detail; + } + + @Override + protected void onViewCreated() { + super.onViewCreated(); + setupHero(); + setupSongList(); + } + + private void lookupName() { + mPlaylistName = MusicUtils.getNameForPlaylist(getActivity(), mPlaylistId); + } + + @Override // DetailFragment + protected PopupMenuHelper createActionMenuHelper() { + return new PlaylistPopupMenuHelper( + getActivity(), getChildFragmentManager(), PopupMenuType.Playlist) { + public Playlist getPlaylist(int position) { + return new Playlist(mPlaylistId, getTitle(), 0); + } + }; + } + + @Override // DetailFragment + protected int getShuffleTitleId() { return R.string.menu_shuffle_playlist; } + + @Override // DetailFragment + protected void playShuffled() { + MusicUtils.playPlaylist(getActivity(), mPlaylistId, true); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + LoaderManager lm = getLoaderManager(); + lm.initLoader(0, getArguments(), this); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mPopupMenuHelper = new SongPopupMenuHelper(getActivity(), getFragmentManager()) { + @Override + public Song getSong(int position) { + if (position == 0) { + return null; + } + + return mAdapter.getItem(position); + } + + @Override + protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) { + super.updateMenuIds(type, set); + + set.add(FragmentMenuItems.REMOVE_FROM_PLAYLIST); + set.remove(FragmentMenuItems.DELETE); + } + + @Override + protected long getSourceId() { + return mPlaylistId; + } + + @Override + protected Config.IdType getSourceType() { + return Config.IdType.Playlist; + } + + @Override + protected void removeFromPlaylist() { + mAdapter.remove(mSong); + mAdapter.buildCache(); + mAdapter.notifyDataSetChanged(); + MusicUtils.removeFromPlaylist(getActivity(), mSong.mSongId, mPlaylistId); + getLoaderManager().restartLoader(LOADER, null, PlaylistDetailFragment.this); + } + }; + + mPlaylistId = getArguments().getLong(Config.ID); + lookupName(); + } + + private void setupHero() { + mPlaylistImageView = (ImageView)mRootView.findViewById(R.id.image); + mHeaderContainer = mRootView.findViewById(R.id.playlist_header); + mNumberOfSongs = (TextView)mRootView.findViewById(R.id.number_of_songs_text); + mDurationOfPlaylist = (TextView)mRootView.findViewById(R.id.duration_text); + + final ImageFetcher imageFetcher = ImageFetcher.getInstance(getActivity()); + imageFetcher.loadPlaylistArtistImage(mPlaylistId, mPlaylistImageView); + } + + private void setupSongList() { + mListView = (DragSortListView) mRootView.findViewById(R.id.list_base); + mListView.setOnScrollListener(PlaylistDetailFragment.this); + + mAdapter = new ProfileSongAdapter( + mPlaylistId, + getActivity(), + R.layout.edit_track_list_item, + R.layout.faux_playlist_header + ); + mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mPopupMenuHelper.showPopupMenu(v, position); + } + }); + mListView.setAdapter(mAdapter); + // Release any references to the recycled Views + mListView.setRecyclerListener(new RecycleHolder()); + // Play the selected song + mListView.setOnItemClickListener(this); + // Set the drop listener + mListView.setDropListener(this); + // Set the swipe to remove listener + mListView.setRemoveListener(this); + // Quick scroll while dragging + mListView.setDragScrollProfile(this); + + // Adjust the progress bar padding to account for the header + int padTop = getResources().getDimensionPixelSize(R.dimen.playlist_detail_header_height); + mRootView.findViewById(R.id.progressbar).setPadding(0, padTop, 0, 0); + + // set the loading and empty view container + mLoadingEmptyContainer = + (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + setupNoResultsContainer(mLoadingEmptyContainer.getNoResultsContainer()); + mListView.setEmptyView(mLoadingEmptyContainer); + } + + private void setupNoResultsContainer(final NoResultsContainer container) { + container.setMainText(R.string.empty_playlist_main); + container.setSecondaryText(R.string.empty_playlist_secondary); + } + + /** + * {@inheritDoc} + */ + @Override + public float getSpeed(final float w, final long t) { + if (w > 0.8f) { + return mAdapter.getCount() / 0.001f; + } else { + return 10.0f * w; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(final int which) { + if (which == 0) { + return; + } + + Song song = mAdapter.getItem(which); + mAdapter.remove(song); + mAdapter.buildCache(); + mAdapter.notifyDataSetChanged(); + final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", mPlaylistId); + getActivity().getContentResolver().delete(uri, + MediaStore.Audio.Playlists.Members.AUDIO_ID + "=" + song.mSongId, + null); + + MusicUtils.refresh(); + } + + /** + * {@inheritDoc} + */ + @Override + public void drop(int from, int to) { + from = Math.max(ProfileSongAdapter.NUM_HEADERS, from); + to = Math.max(ProfileSongAdapter.NUM_HEADERS, to); + + Song song = mAdapter.getItem(from); + mAdapter.remove(song); + mAdapter.insert(song, to); + mAdapter.buildCache(); + mAdapter.notifyDataSetChanged(); + + final int realFrom = from - ProfileSongAdapter.NUM_HEADERS; + final int realTo = to - ProfileSongAdapter.NUM_HEADERS; + MediaStore.Audio.Playlists.Members.moveItem(getActivity().getContentResolver(), + mPlaylistId, realFrom, realTo); + } + + /** + * {@inheritDoc} + */ + @Override + public void onItemClick(final AdapterView<?> parent, final View view, final int position, + final long id) { + if (position == 0) { + return; + } + Cursor cursor = PlaylistSongLoader.makePlaylistSongCursor(getActivity(), + mPlaylistId); + final long[] list = MusicUtils.getSongListForCursor(cursor); + MusicUtils.playAll(getActivity(), list, position - ProfileSongAdapter.NUM_HEADERS, + mPlaylistId, Config.IdType.Playlist, false); + cursor.close(); + cursor = null; + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + super.onScrollStateChanged(view, scrollState); + + if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { + mAdapter.setPauseDiskCache(true); + } else { + mAdapter.setPauseDiskCache(false); + mAdapter.notifyDataSetChanged(); + } + } + + protected int getHeaderHeight() { return mHeaderContainer.getHeight(); } + + protected void setHeaderPosition(float y) { + // Offset the header height to account for the faux header + y = y - getResources().getDimension(R.dimen.header_bar_height); + mHeaderContainer.setY(y); + } + + @Override + public Loader<List<Song>> onCreateLoader(int i, Bundle bundle) { + mLoadingEmptyContainer.showLoading(); + + return new PlaylistSongLoader(getActivity(), mPlaylistId); + } + + @Override + public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) { + if (data.isEmpty()) { + mLoadingEmptyContainer.showNoResults(); + + // hide the header container + mHeaderContainer.setVisibility(View.INVISIBLE); + + // Start fresh + mAdapter.unload(); + } else { + // show the header container + mHeaderContainer.setVisibility(View.VISIBLE); + + // pause notifying the adapter and make changes before re-enabling it so that the list + // view doesn't reset to the top of the list + mAdapter.setNotifyOnChange(false); + // Start fresh + mAdapter.unload(); + // Return the correct count + mAdapter.addAll(data); + // build the cache + mAdapter.buildCache(); + // re-enable the notify by calling notify dataset changes + mAdapter.notifyDataSetChanged(); + // set the number of songs + String numberOfSongs = MusicUtils.makeLabel(getActivity(), R.plurals.Nsongs, + data.size()); + mNumberOfSongs.setText(numberOfSongs); + + long duration = 0; + + // Add the data to the adapter + for (final Song song : data) { + duration += song.mDuration; + } + + // set the duration + String durationString = MusicUtils.makeLongTimeString(getActivity(), duration); + mDurationOfPlaylist.setText(durationString); + } + } + + @Override + public void onLoaderReset(final Loader<List<Song>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } + + @Override + public void restartLoader() { + lookupName(); // playlist name may have changed + if(mPlaylistName == null) { + // if name is null, we've been deleted, so close the this fragment + getContainingActivity().postRemoveFragment(this); + return; + } + + // since onCreateOptionsMenu can be called after onCreate it is possible for + // mActionMenuHelper to be null. In this case, don't bother updating the Name since when + // it does create it, it will use the updated name anyways + if (mActionMenuHelper != null) { + // update action bar title and popup menu handler + ((PlaylistPopupMenuHelper) mActionMenuHelper).updateName(mPlaylistName); + } + + getContainingActivity().setActionBarTitle(mPlaylistName); + // and reload the song list + getLoaderManager().restartLoader(0, getArguments(), this); + } + + @Override + public void onMetaChanged() { + super.onMetaChanged(); + + mAdapter.setCurrentlyPlayingTrack(MusicUtils.getCurrentTrack()); + } + + @Override + public void onPlaylistChanged() { + super.onPlaylistChanged(); + + restartLoader(); + } + + @Override + public PagerAdapter.MusicFragments getMusicFragmentParent() { + return PagerAdapter.MusicFragments.PLAYLIST; + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/PlaylistFragment.java b/src/org/lineageos/eleven/ui/fragments/PlaylistFragment.java new file mode 100644 index 0000000..aba8dbd --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/PlaylistFragment.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; + +import com.cyanogenmod.eleven.Config.SmartPlaylistType; +import com.cyanogenmod.eleven.MusicStateListener; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.adapters.PlaylistAdapter; +import com.cyanogenmod.eleven.loaders.PlaylistLoader; +import com.cyanogenmod.eleven.model.Playlist; +import com.cyanogenmod.eleven.recycler.RecycleHolder; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.ui.fragments.phone.MusicBrowserFragment; +import com.cyanogenmod.eleven.utils.NavUtils; +import com.cyanogenmod.eleven.utils.PlaylistPopupMenuHelper; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; + +import java.util.List; + +/** + * This class is used to display all of the playlists on a user's device. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class PlaylistFragment extends MusicBrowserFragment implements + LoaderCallbacks<List<Playlist>>, + OnItemClickListener, MusicStateListener { + + /** + * The adapter for the list + */ + private PlaylistAdapter mAdapter; + + /** + * The list view + */ + private ListView mListView; + + /** + * Pop up menu helper + */ + private PopupMenuHelper mPopupMenuHelper; + + /** + * This holds the loading progress bar as well as the no results message + */ + private LoadingEmptyContainer mLoadingEmptyContainer; + + /** + * Empty constructor as per the {@link Fragment} documentation + */ + public PlaylistFragment() { + } + + @Override + public int getLoaderId() { + return PagerAdapter.MusicFragments.PLAYLIST.ordinal(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPopupMenuHelper = new PlaylistPopupMenuHelper(getActivity(), getFragmentManager(), null) { + @Override + public Playlist getPlaylist(int position) { + return mAdapter.getItem(position); + } + }; + + // Create the adapter + mAdapter = new PlaylistAdapter(getActivity()); + mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mPopupMenuHelper.showPopupMenu(v, position); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + // The View for the fragment's UI + final ViewGroup rootView = (ViewGroup)inflater.inflate(R.layout.list_base, null); + // Initialize the list + mListView = (ListView)rootView.findViewById(R.id.list_base); + // Set the data behind the grid + mListView.setAdapter(mAdapter); + // Release any references to the recycled Views + mListView.setRecyclerListener(new RecycleHolder()); + // Play the selected song + mListView.setOnItemClickListener(this); + // Setup the loading and empty state + mLoadingEmptyContainer = + (LoadingEmptyContainer)rootView.findViewById(R.id.loading_empty_container); + mListView.setEmptyView(mLoadingEmptyContainer); + + // Register the music status listener + ((BaseActivity)getActivity()).setMusicStateListenerListener(this); + + return rootView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + ((BaseActivity)getActivity()).removeMusicStateListenerListener(this); + } + + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // Enable the options menu + setHasOptionsMenu(true); + // Start the loader + initLoader(null, this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onItemClick(final AdapterView<?> parent, final View view, final int position, + final long id) { + Playlist playlist = mAdapter.getItem(position); + + SmartPlaylistType playlistType = SmartPlaylistType.getTypeById(playlist.mPlaylistId); + if (playlistType != null) { + NavUtils.openSmartPlaylist(getActivity(), playlistType); + } else { + NavUtils.openPlaylist(getActivity(), playlist.mPlaylistId, playlist.mPlaylistName); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<List<Playlist>> onCreateLoader(final int id, final Bundle args) { + // show the loading progress bar + mLoadingEmptyContainer.showLoading(); + return new PlaylistLoader(getActivity()); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoadFinished(final Loader<List<Playlist>> loader, final List<Playlist> data) { + // Check for any errors + if (data.isEmpty()) { + mLoadingEmptyContainer.showNoResults(); + return; + } + + // Start fresh + mAdapter.unload(); + // Add the data to the adpater + for (final Playlist playlist : data) { + mAdapter.add(playlist); + } + // Build the cache + mAdapter.buildCache(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoaderReset(final Loader<List<Playlist>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } + + /** + * {@inheritDoc} + */ + @Override + public void restartLoader() { + restartLoader(null, this); + } + + @Override + public void onPlaylistChanged() { + restartLoader(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onMetaChanged() { + // Nothing to do + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/QueueFragment.java b/src/org/lineageos/eleven/ui/fragments/QueueFragment.java new file mode 100644 index 0000000..35229bc --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/QueueFragment.java @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import static com.cyanogenmod.eleven.utils.MusicUtils.mService; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.provider.MediaStore; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; + +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.MusicPlaybackService; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.SongAdapter; +import com.cyanogenmod.eleven.dragdrop.DragSortListView; +import com.cyanogenmod.eleven.dragdrop.DragSortListView.DragScrollProfile; +import com.cyanogenmod.eleven.dragdrop.DragSortListView.DropListener; +import com.cyanogenmod.eleven.dragdrop.DragSortListView.RemoveListener; +import com.cyanogenmod.eleven.loaders.NowPlayingCursor; +import com.cyanogenmod.eleven.loaders.QueueLoader; +import com.cyanogenmod.eleven.menu.DeleteDialog; +import com.cyanogenmod.eleven.menu.FragmentMenuItems; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.recycler.RecycleHolder; +import com.cyanogenmod.eleven.service.MusicPlaybackTrack; +import com.cyanogenmod.eleven.ui.activities.SlidingPanelActivity; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; +import com.cyanogenmod.eleven.widgets.PlayPauseProgressButton; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.TreeSet; + +/** + * This class is used to display all of the songs in the queue. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song>>, + OnItemClickListener, DropListener, RemoveListener, DragScrollProfile, ServiceConnection { + + /** + * LoaderCallbacks identifier + */ + private static final int LOADER = 0; + + /** + * Service token for binding to the music service + */ + private MusicUtils.ServiceToken mToken; + + /** + * The listener to the playback service that will trigger updates to the ui + */ + private QueueUpdateListener mQueueUpdateListener; + + /** + * The adapter for the list + */ + private SongAdapter mAdapter; + + /** + * The list view + */ + private DragSortListView mListView; + + /** + * Pop up menu helper + */ + private PopupMenuHelper mPopupMenuHelper; + + /** + * Root view + */ + private ViewGroup mRootView; + + /** + * This holds the loading progress bar as well as the no results message + */ + private LoadingEmptyContainer mLoadingEmptyContainer; + + /** + * Empty constructor as per the {@link Fragment} documentation + */ + public QueueFragment() { + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) { + private Song mSong; + private int mSelectedPosition; + private MusicPlaybackTrack mSelectedTrack; + + @Override + public PopupMenuType onPreparePopupMenu(int position) { + mSelectedPosition = position; + mSong = mAdapter.getItem(mSelectedPosition); + mSelectedTrack = MusicUtils.getTrack(mSelectedPosition); + + return PopupMenuType.Queue; + } + + @Override + protected long[] getIdList() { + return new long[] { mSong.mSongId }; + } + + @Override + protected long getSourceId() { + if (mSelectedTrack == null) { + return -1; + } + + return mSelectedTrack.mSourceId; + } + + @Override + protected Config.IdType getSourceType() { + if (mSelectedTrack == null) { + return Config.IdType.NA; + } + + return mSelectedTrack.mSourceType; + } + + @Override + protected String getArtistName() { + return mSong.mArtistName; + } + + @Override + protected void onDeleteClicked() { + DeleteDialog.newInstance(mSong.mSongName, + new long[] { getId() }, null).show(getFragmentManager(), "DeleteDialog"); + } + + @Override + protected void playNext() { + NowPlayingCursor queue = (NowPlayingCursor)QueueLoader + .makeQueueCursor(getActivity()); + queue.removeItem(mSelectedPosition); + queue.close(); + queue = null; + MusicUtils.playNext(getIdList(), getSourceId(), getSourceType()); + refreshQueue(); + } + + @Override + protected void removeFromQueue() { + MusicUtils.removeTrackAtPosition(getId(), mSelectedPosition); + refreshQueue(); + } + + @Override + protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) { + super.updateMenuIds(type, set); + + // Don't show more by artist if it is an unknown artist + if (MediaStore.UNKNOWN_STRING.equals(mSong.mArtistName)) { + set.remove(FragmentMenuItems.MORE_BY_ARTIST); + } + } + }; + + // Create the adapter + mAdapter = new SongAdapter(getActivity(), R.layout.edit_queue_list_item, + -1, Config.IdType.NA); + mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mPopupMenuHelper.showPopupMenu(v, position); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + // The View for the fragment's UI + mRootView = (ViewGroup)inflater.inflate(R.layout.list_base, null); + // Initialize the list + mListView = (DragSortListView)mRootView.findViewById(R.id.list_base); + // Set the data behind the list + mListView.setAdapter(mAdapter); + // Release any references to the recycled Views + mListView.setRecyclerListener(new RecycleHolder()); + // Play the selected song + mListView.setOnItemClickListener(this); + // Set the drop listener + mListView.setDropListener(this); + // Set the swipe to remove listener + mListView.setRemoveListener(this); + // Quick scroll while dragging + mListView.setDragScrollProfile(this); + // Enable fast scroll bars + mListView.setFastScrollEnabled(true); + // Setup the loading and empty state + mLoadingEmptyContainer = + (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + // Setup the container strings + setupNoResultsContainer(mLoadingEmptyContainer.getNoResultsContainer()); + mListView.setEmptyView(mLoadingEmptyContainer); + return mRootView; + } + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Initialize the broadcast receiver + mQueueUpdateListener = new QueueUpdateListener(this); + + // Bind Apollo's service + mToken = MusicUtils.bindToService(getActivity(), this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onServiceConnected(final ComponentName name, final IBinder service) { + refreshQueue(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + + @Override + public void onStart() { + super.onStart(); + + final IntentFilter filter = new IntentFilter(); + // Play and pause changes + filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED); + // Queue changes + filter.addAction(MusicPlaybackService.QUEUE_CHANGED); + // Track changes + filter.addAction(MusicPlaybackService.META_CHANGED); + + getActivity().registerReceiver(mQueueUpdateListener, filter); + + // resume the progress listeners + setPlayPauseProgressButtonStates(false); + } + + @Override + public void onStop() { + super.onStop(); + + // stops the progress listeners + setPlayPauseProgressButtonStates(true); + } + + /** + * Sets the state for any play pause progress buttons under the listview + * This is neede because the buttons update themselves so if the activity + * is hidden, we want to pause those handlers + * @param pause the state to set it to + */ + public void setPlayPauseProgressButtonStates(boolean pause) { + if (mListView != null) { + // walk through the visible list items + for (int i = mListView.getFirstVisiblePosition(); + i <= mListView.getLastVisiblePosition(); i++) { + View childView = mListView.getChildAt(i); + if (childView != null) { + PlayPauseProgressButton button = + (PlayPauseProgressButton) childView.findViewById(R.id.playPauseProgressButton); + // pause or resume based on the flag + if (pause) { + button.pause(); + } else { + button.resume(); + } + } + } + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + + try { + getActivity().unregisterReceiver(mQueueUpdateListener); + } catch (final Throwable e) { + //$FALL-THROUGH$ + } + + if (mService != null) { + MusicUtils.unbindFromService(mToken); + mToken = null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onItemClick(final AdapterView<?> parent, final View view, final int position, + final long id) { + // When selecting a track from the queue, just jump there instead of + // reloading the queue. This is both faster, and prevents accidentally + // dropping out of party shuffle. + MusicUtils.setQueuePosition(position); + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) { + mLoadingEmptyContainer.showLoading(); + return new QueueLoader(getActivity()); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) { + // pause notifying the adapter and make changes before re-enabling it so that the list + // view doesn't reset to the top of the list + mAdapter.setNotifyOnChange(false); + mAdapter.unload(); // Start fresh + + if (data.isEmpty()) { + mLoadingEmptyContainer.showNoResults(); + mAdapter.setCurrentQueuePosition(SongAdapter.NOTHING_PLAYING); + ((SlidingPanelActivity)getActivity()).clearMetaInfo(); + } else { + // Add the songs found to the adapter + for (final Song song : data) { mAdapter.add(song); } + // Build the cache + mAdapter.buildCache(); + // Set the currently playing audio + mAdapter.setCurrentQueuePosition(MusicUtils.getQueuePosition()); + } + // re-enable the notify by calling notify dataset changes + mAdapter.notifyDataSetChanged(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoaderReset(final Loader<List<Song>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } + + /** + * {@inheritDoc} + */ + @Override + public float getSpeed(final float w, final long t) { + if (w > 0.8f) { + return mAdapter.getCount() / 0.001f; + } else { + return 10.0f * w; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void remove(final int which) { + Song song = mAdapter.getItem(which); + mAdapter.remove(song); + mAdapter.notifyDataSetChanged(); + MusicUtils.removeTrackAtPosition(song.mSongId, which); + // Build the cache + mAdapter.buildCache(); + } + + /** + * {@inheritDoc} + */ + @Override + public void drop(final int from, final int to) { + Song song = mAdapter.getItem(from); + mAdapter.remove(song); + mAdapter.insert(song, to); + mAdapter.notifyDataSetChanged(); + MusicUtils.moveQueueItem(from, to); + // Build the cache + mAdapter.buildCache(); + } + + /** + * Called to restart the loader callbacks + */ + public void refreshQueue() { + if (isAdded()) { + getLoaderManager().restartLoader(LOADER, null, this); + } + } + + private void setupNoResultsContainer(NoResultsContainer empty) { + int color = getResources().getColor(R.color.no_results_light); + empty.setTextColor(color); + empty.setMainText(R.string.empty_queue_main); + empty.setSecondaryText(R.string.empty_queue_secondary); + } + + /** + * Used to monitor the state of playback + */ + private static final class QueueUpdateListener extends BroadcastReceiver { + + private final WeakReference<QueueFragment> mReference; + + /** + * Constructor of <code>PlaybackStatus</code> + */ + public QueueUpdateListener(final QueueFragment fragment) { + mReference = new WeakReference<QueueFragment>(fragment); + } + + /** + * {@inheritDoc} + */ + @Override + public void onReceive(final Context context, final Intent intent) { + // TODO: Invalid options menu if opened? + final String action = intent.getAction(); + if (action.equals(MusicPlaybackService.META_CHANGED)) { + mReference.get().mAdapter.setCurrentQueuePosition(MusicUtils.getQueuePosition()); + } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) { + mReference.get().mAdapter.notifyDataSetChanged(); + } else if (action.equals(MusicPlaybackService.QUEUE_CHANGED)) { + mReference.get().refreshQueue(); + } + } + } +}
\ No newline at end of file diff --git a/src/org/lineageos/eleven/ui/fragments/RecentFragment.java b/src/org/lineageos/eleven/ui/fragments/RecentFragment.java new file mode 100644 index 0000000..971f8aa --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/RecentFragment.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.Config.SmartPlaylistType; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.SongAdapter; +import com.cyanogenmod.eleven.loaders.TopTracksLoader; +import com.cyanogenmod.eleven.menu.FragmentMenuItems; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.sectionadapter.SectionCreator; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.ui.fragments.profile.SmartPlaylistFragment; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; + +import java.util.TreeSet; + +/** + * This class is used to display all of the recently listened to songs by the + * user. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class RecentFragment extends SmartPlaylistFragment implements ISetupActionBar { + + @Override + protected SmartPlaylistType getSmartPlaylistType() { + return Config.SmartPlaylistType.RecentlyPlayed; + } + + @Override + protected void updateMenuIds(TreeSet<Integer> set) { + set.add(FragmentMenuItems.REMOVE_FROM_RECENT); + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) { + // show the loading progress bar + mLoadingEmptyContainer.showLoading(); + + TopTracksLoader loader = new TopTracksLoader(getActivity(), + TopTracksLoader.QueryType.RecentSongs); + return new SectionCreator<Song>(getActivity(), loader, null); + } + + /** + * {@inheritDoc} + */ + @Override + public void onMetaChanged() { + super.onMetaChanged(); + + // refresh the list since a track playing means it should be recently played + restartLoader(); + } + + @Override + public void setupNoResultsContainer(NoResultsContainer empty) { + super.setupNoResultsContainer(empty); + + empty.setMainText(R.string.empty_recent_main); + empty.setSecondaryText(R.string.empty_recent); + } + + @Override + public final View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + setupActionBar(); + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + public void setupActionBar() { + ((BaseActivity)getActivity()).setupActionBar(R.string.playlist_recently_played); + ((BaseActivity)getActivity()).setActionBarElevation(true); + } + + @Override + protected long getFragmentSourceId() { + return Config.SmartPlaylistType.RecentlyPlayed.mId; + } + + @Override + protected SongAdapter createAdapter() { + return new RecentAdapter( + getActivity(), + R.layout.list_item_normal, + getFragmentSourceId(), + getFragmentSourceType() + ); + } + + private class RecentAdapter extends SongAdapter { + public RecentAdapter(Activity context, int layoutId, long sourceId, Config.IdType sourceType) { + super(context, layoutId, sourceId, sourceType); + } + + @Override + protected boolean showNowPlayingIndicator(Song song, int position) { + return position == 0 && super.showNowPlayingIndicator(song, position); + } + } + + @Override + protected int getShuffleTitleId() { return R.string.menu_shuffle_recent; } + + @Override + protected int getClearTitleId() { return R.string.clear_recent_title; } + + @Override + protected void clearList() { MusicUtils.clearRecent(getActivity()); } +}
\ No newline at end of file diff --git a/src/org/lineageos/eleven/ui/fragments/SongFragment.java b/src/org/lineageos/eleven/ui/fragments/SongFragment.java new file mode 100644 index 0000000..1e57580 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/SongFragment.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; + +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.loaders.SongLoader; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.sectionadapter.SectionCreator; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.ui.fragments.profile.BasicSongFragment; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.SectionCreatorUtils; + +/** + * This class is used to display all of the songs on a user's device. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class SongFragment extends BasicSongFragment { + + /** + * {@inheritDoc} + */ + public void playAll(int position) { + int internalPosition = mAdapter.getInternalPosition(position); + final long[] list = mAdapter.getUnderlyingAdapter().getSongIds(); + if (list != null) { + MusicUtils.playAll(getActivity(), list, internalPosition, -1, Config.IdType.NA, false); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) { + // show the loading progress bar + mLoadingEmptyContainer.showLoading(); + + // get the context + Context context = getActivity(); + + // create the underlying song loader + SongLoader songLoader = new SongLoader(context); + + // get the song comparison method to create the headers with + SectionCreatorUtils.IItemCompare<Song> songComparison = SectionCreatorUtils.createSongComparison(context); + + // return the wrapped section creator + return new SectionCreator<Song>(context, songLoader, songComparison); + } + + + @Override + public int getLoaderId() { + return PagerAdapter.MusicFragments.SONG.ordinal(); + } + + /** + * 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; + } + + int position = mAdapter.getItemPosition(trackId); + + // if for some reason we don't find the item, just jump to the top + if (position < 0) { + return 0; + } + + return position; + } + + @Override + public LoaderManager getFragmentLoaderManager() { + return getParentFragment().getLoaderManager(); + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/phone/MusicBrowserFragment.java b/src/org/lineageos/eleven/ui/fragments/phone/MusicBrowserFragment.java new file mode 100644 index 0000000..0d01e5f --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/phone/MusicBrowserFragment.java @@ -0,0 +1,43 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments.phone; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; + +/** + * This class is used for fragments under the {@link MusicBrowserFragment} + * Even though the containing view pager creates all the fragments, the loader + * does not load complete until the user navigates to that page. To get around this + * we will use the containing fragment's loader manager + */ +public abstract class MusicBrowserFragment extends Fragment { + public abstract int getLoaderId(); + + public LoaderManager getContainingLoaderManager() { + return getParentFragment().getLoaderManager(); + } + + protected void initLoader(Bundle args, LoaderCallbacks<? extends Object> callback) { + getContainingLoaderManager().initLoader(getLoaderId(), args, callback); + } + + protected void restartLoader(Bundle args, LoaderCallbacks<? extends Object> callback) { + getContainingLoaderManager().restartLoader(getLoaderId(), args, callback); + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/phone/MusicBrowserPhoneFragment.java b/src/org/lineageos/eleven/ui/fragments/phone/MusicBrowserPhoneFragment.java new file mode 100644 index 0000000..376841e --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/phone/MusicBrowserPhoneFragment.java @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments.phone; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.adapters.PagerAdapter.MusicFragments; +import com.cyanogenmod.eleven.menu.CreateNewPlaylist; +import com.cyanogenmod.eleven.ui.fragments.AlbumFragment; +import com.cyanogenmod.eleven.ui.fragments.ArtistFragment; +import com.cyanogenmod.eleven.ui.fragments.BaseFragment; +import com.cyanogenmod.eleven.ui.fragments.SongFragment; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PreferenceUtils; +import com.cyanogenmod.eleven.utils.SortOrder; +import com.cyanogenmod.eleven.widgets.ViewPagerTabs; + +/** + * This class is used to hold the {@link ViewPager} used for swiping between the + * playlists, recent, artists, albums, songs, and genre {@link Fragment} + * s for phones. + * + * @NOTE: The reason the sort orders are taken care of in this fragment rather + * than the individual fragments is to keep from showing all of the menu + * items on tablet interfaces. That being said, I have a tablet interface + * worked out, but I'm going to keep it in the Play Store version of + * Apollo for a couple of weeks or so before merging it with CM. + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class MusicBrowserPhoneFragment extends BaseFragment { + public static final int INVALID_PAGE_INDEX = -1; + + /** + * Pager + */ + private ViewPager mViewPager; + + /** + * VP's adapter + */ + private PagerAdapter mPagerAdapter; + + private PreferenceUtils mPreferences; + + /** + * A pre-defined page index to navigate to + */ + private int mDefaultPageIdx = INVALID_PAGE_INDEX; + + /** + * Empty constructor as per the {@link Fragment} documentation + */ + public MusicBrowserPhoneFragment() { + } + + @Override + protected int getLayoutToInflate() { + return R.layout.fragment_music_browser_phone; + } + + @Override + protected String getTitle() { + return getString(R.string.app_name); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Get the preferences + mPreferences = PreferenceUtils.getInstance(getActivity()); + } + + @Override + protected void onViewCreated() { + super.onViewCreated(); + + // create the adapter - on rotation the view gets created again and we need to recreate + // the child fragments (fragments of fragments cannot be retained) + mPagerAdapter = new PagerAdapter(getActivity(), getChildFragmentManager()); + final MusicFragments[] mFragments = MusicFragments.values(); + for (final MusicFragments mFragment : mFragments) { + mPagerAdapter.add(mFragment.getFragmentClass(), null); + } + + // Initialize the ViewPager + mViewPager = (ViewPager)mRootView.findViewById(R.id.fragment_home_phone_pager); + // Attch the adapter + mViewPager.setAdapter(mPagerAdapter); + // Offscreen pager loading limit + mViewPager.setOffscreenPageLimit(mPagerAdapter.getCount() - 1); + + // Initialize the tab strip + final ViewPagerTabs tabs = (ViewPagerTabs) + mRootView.findViewById(R.id.fragment_home_phone_pager_titles); + // Attach the ViewPager + tabs.setViewPager(mViewPager); + mViewPager.setOnPageChangeListener(tabs); + + if (mDefaultPageIdx != INVALID_PAGE_INDEX) { + navigateToPage(mDefaultPageIdx); + } else { + // Start on the last page the user was on + navigateToPage(mPreferences.getStartPage()); + } + } + + public void setDefaultPageIdx(final int pageIdx) { + mDefaultPageIdx = pageIdx; + navigateToPage(mDefaultPageIdx); + } + + private void navigateToPage(final int idx) { + // this may be called before the view is created, so do a check for mViewPager + if (idx != INVALID_PAGE_INDEX && mViewPager != null) { + mViewPager.setCurrentItem(idx); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // Enable the options menu + setHasOptionsMenu(true); + } + + /** + * {@inheritDoc} + */ + @Override + public void onPause() { + super.onPause(); + // Save the last page the use was on + mPreferences.setStartPage(mViewPager.getCurrentItem()); + } + + /** + * {@inheritDoc} + */ + @Override + public void onPrepareOptionsMenu(final Menu menu) { + super.onPrepareOptionsMenu(menu); + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + + inflater.inflate(R.menu.shuffle_all, menu); // Shuffle all + if (isArtistPage()) { + inflater.inflate(R.menu.artist_sort_by, menu); + } else if (isAlbumPage()) { + inflater.inflate(R.menu.album_sort_by, menu); + } else if (isSongPage()) { + inflater.inflate(R.menu.song_sort_by, menu); + } else if (isPlaylistPage()) { + inflater.inflate(R.menu.new_playlist, menu); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_shuffle_all: + // Shuffle all the songs + MusicUtils.shuffleAll(getActivity()); + return true; + case R.id.menu_sort_by_az: + if (isArtistPage()) { + mPreferences.setArtistSortOrder(SortOrder.ArtistSortOrder.ARTIST_A_Z); + getArtistFragment().refresh(); + } else if (isAlbumPage()) { + mPreferences.setAlbumSortOrder(SortOrder.AlbumSortOrder.ALBUM_A_Z); + getAlbumFragment().refresh(); + } else if (isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_A_Z); + getSongFragment().refresh(); + } + return true; + case R.id.menu_sort_by_za: + if (isArtistPage()) { + mPreferences.setArtistSortOrder(SortOrder.ArtistSortOrder.ARTIST_Z_A); + getArtistFragment().refresh(); + } else if (isAlbumPage()) { + mPreferences.setAlbumSortOrder(SortOrder.AlbumSortOrder.ALBUM_Z_A); + getAlbumFragment().refresh(); + } else if (isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_Z_A); + getSongFragment().refresh(); + } + return true; + case R.id.menu_sort_by_artist: + if (isAlbumPage()) { + mPreferences.setAlbumSortOrder(SortOrder.AlbumSortOrder.ALBUM_ARTIST); + getAlbumFragment().refresh(); + } else if (isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_ARTIST); + getSongFragment().refresh(); + } + return true; + case R.id.menu_sort_by_album: + if (isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_ALBUM); + getSongFragment().refresh(); + } + return true; + case R.id.menu_sort_by_year: + if (isAlbumPage()) { + mPreferences.setAlbumSortOrder(SortOrder.AlbumSortOrder.ALBUM_YEAR); + getAlbumFragment().refresh(); + } else if (isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_YEAR); + getSongFragment().refresh(); + } + return true; + case R.id.menu_sort_by_duration: + if (isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_DURATION); + getSongFragment().refresh(); + } + return true; + case R.id.menu_sort_by_number_of_songs: + if (isArtistPage()) { + mPreferences + .setArtistSortOrder(SortOrder.ArtistSortOrder.ARTIST_NUMBER_OF_SONGS); + getArtistFragment().refresh(); + } else if (isAlbumPage()) { + mPreferences.setAlbumSortOrder(SortOrder.AlbumSortOrder.ALBUM_NUMBER_OF_SONGS); + getAlbumFragment().refresh(); + } + return true; + case R.id.menu_sort_by_number_of_albums: + if (isArtistPage()) { + mPreferences + .setArtistSortOrder(SortOrder.ArtistSortOrder.ARTIST_NUMBER_OF_ALBUMS); + getArtistFragment().refresh(); + } + return true; + case R.id.menu_sort_by_filename: + if(isSongPage()) { + mPreferences.setSongSortOrder(SortOrder.SongSortOrder.SONG_FILENAME); + getSongFragment().refresh(); + } + return true; + case R.id.menu_new_playlist: + if(isPlaylistPage()) { + CreateNewPlaylist.getInstance(new long[0]).show(getFragmentManager(), "CreatePlaylist"); + } + return true; + default: + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + protected boolean needsElevatedActionBar() { + // our view pager already has elevation + return false; + } + + private boolean isArtistPage() { + return mViewPager.getCurrentItem() == MusicFragments.ARTIST.ordinal(); + } + + public ArtistFragment getArtistFragment() { + return (ArtistFragment)mPagerAdapter.getFragment(MusicFragments.ARTIST.ordinal()); + } + + private boolean isAlbumPage() { + return mViewPager.getCurrentItem() == MusicFragments.ALBUM.ordinal(); + } + + public AlbumFragment getAlbumFragment() { + return (AlbumFragment)mPagerAdapter.getFragment(MusicFragments.ALBUM.ordinal()); + } + + private boolean isSongPage() { + return mViewPager.getCurrentItem() == MusicFragments.SONG.ordinal(); + } + + public SongFragment getSongFragment() { + return (SongFragment)mPagerAdapter.getFragment(MusicFragments.SONG.ordinal()); + } + + @Override + public void restartLoader() { + // do nothing + } + + private boolean isPlaylistPage() { + return mViewPager.getCurrentItem() == MusicFragments.PLAYLIST.ordinal(); + } +} diff --git a/src/org/lineageos/eleven/ui/fragments/profile/BasicSongFragment.java b/src/org/lineageos/eleven/ui/fragments/profile/BasicSongFragment.java new file mode 100644 index 0000000..ba9d815 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/profile/BasicSongFragment.java @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments.profile; + +import android.os.Bundle; +import android.os.SystemClock; +import android.support.v4.app.Fragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; + +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.MusicStateListener; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.SongAdapter; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.recycler.RecycleHolder; +import com.cyanogenmod.eleven.sectionadapter.SectionAdapter; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.service.MusicPlaybackTrack; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.utils.SongPopupMenuHelper; +import com.cyanogenmod.eleven.widgets.IPopupMenuCallback; +import com.cyanogenmod.eleven.widgets.LoadingEmptyContainer; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; + +import java.util.TreeSet; + +/** + * This class is used to display all of the songs + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public abstract class BasicSongFragment extends Fragment implements + LoaderCallbacks<SectionListContainer<Song>>, OnItemClickListener, MusicStateListener { + + /** + * Fragment UI + */ + protected ViewGroup mRootView; + + /** + * The adapter for the list + */ + protected SectionAdapter<Song, SongAdapter> mAdapter; + + /** + * The list view + */ + protected ListView mListView; + + /** + * Pop up menu helper + */ + protected PopupMenuHelper mPopupMenuHelper; + + /** + * This holds the loading progress bar as well as the no results message + */ + protected LoadingEmptyContainer mLoadingEmptyContainer; + + /** + * Empty constructor as per the {@link Fragment} documentation + */ + public BasicSongFragment() { + } + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPopupMenuHelper = new SongPopupMenuHelper(getActivity(), getFragmentManager()) { + @Override + public Song getSong(int position) { + return mAdapter.getTItem(position); + } + + @Override + protected long getSourceId() { + return getFragmentSourceId(); + } + + @Override + protected Config.IdType getSourceType() { + return getFragmentSourceType(); + } + + @Override + protected void updateMenuIds(PopupMenuType type, TreeSet<Integer> set) { + super.updateMenuIds(type, set); + BasicSongFragment.this.updateMenuIds(set); + } + }; + + // Create the adapter + mAdapter = new SectionAdapter<Song, SongAdapter>(getActivity(), createAdapter()); + mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() { + @Override + public void onPopupMenuClicked(View v, int position) { + mPopupMenuHelper.showPopupMenu(v, position); + } + }); + } + + protected long getFragmentSourceId() { + return -1; + } + + protected Config.IdType getFragmentSourceType() { + return Config.IdType.NA; + } + + protected void updateMenuIds(TreeSet<Integer> set) { + // do nothing - let subclasses override + } + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + // The View for the fragment's UI + mRootView = (ViewGroup) inflater.inflate(R.layout.list_base, null); + // set the background on the root view + mRootView.setBackgroundColor(getResources().getColor(R.color.background_color)); + // Initialize the list + mListView = (ListView) mRootView.findViewById(R.id.list_base); + // Set the data behind the list + mListView.setAdapter(mAdapter); + // Release any references to the recycled Views + mListView.setRecyclerListener(new RecycleHolder()); + // Play the selected song + mListView.setOnItemClickListener(this); + // To help make scrolling smooth + mListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // Pause disk cache access to ensure smoother scrolling + if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { + mAdapter.getUnderlyingAdapter().setPauseDiskCache(true); + } else { + mAdapter.getUnderlyingAdapter().setPauseDiskCache(false); + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + + } + }); + + // Show progress bar + mLoadingEmptyContainer = (LoadingEmptyContainer)mRootView.findViewById(R.id.loading_empty_container); + // Setup the container strings + setupNoResultsContainer(mLoadingEmptyContainer.getNoResultsContainer()); + mListView.setEmptyView(mLoadingEmptyContainer); + + // Register the music status listener + ((BaseActivity)getActivity()).setMusicStateListenerListener(this); + + return mRootView; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + ((BaseActivity)getActivity()).removeMusicStateListenerListener(this); + } + + /** + * This allows subclasses to customize the look and feel of the no results container + * @param empty NoResultsContainer class + */ + public void setupNoResultsContainer(final NoResultsContainer empty) { + // do nothing + } + + /** + * {@inheritDoc} + */ + @Override + public void onActivityCreated(final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // Start the loader + getFragmentLoaderManager().initLoader(getLoaderId(), null, this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onItemClick(final AdapterView<?> parent, final View view, final int position, + final long id) { + playAll(position); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoadFinished(final Loader<SectionListContainer<Song>> loader, + final SectionListContainer<Song> data) { + if (data.mListResults.isEmpty()) { + mAdapter.unload(); + mLoadingEmptyContainer.showNoResults(); + return; + } + + mAdapter.setData(data); + } + + /** + * @return Gets the list of song ids from the adapter, or null if none + */ + protected long[] getSongIdsFromAdapter() { + if (mAdapter != null) { + final SongAdapter adapter = mAdapter.getUnderlyingAdapter(); + if (adapter != null) { + return adapter.getSongIds(); + } + } + + return null; + } + + /** + * Restarts the loader. + */ + public void refresh() { + // Wait a moment for the preference to change. + SystemClock.sleep(10); + restartLoader(); + } + + /** + * {@inheritDoc} + */ + @Override + public void restartLoader() { + // Update the list when the user deletes any items + getFragmentLoaderManager().restartLoader(getLoaderId(), null, this); + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoaderReset(final Loader<SectionListContainer<Song>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } + + /** + * If the subclasses want to use a customized SongAdapter they can override this method + * @return the Song adapter + */ + protected SongAdapter createAdapter() { + return new SongAdapter( + getActivity(), + R.layout.list_item_normal, + getFragmentSourceId(), + getFragmentSourceType() + ); + } + + /** + * Allow subclasses to specify a different loader manager + * @return Loader Manager to use + */ + public LoaderManager getFragmentLoaderManager() { + return getLoaderManager(); + } + + @Override + public void onMetaChanged() { + MusicPlaybackTrack currentTrack = MusicUtils.getCurrentTrack(); + if (mAdapter.getUnderlyingAdapter().setCurrentlyPlayingTrack(currentTrack)) { + mAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onPlaylistChanged() { + // Nothing to do + } + + /** + * LoaderCallbacks identifier + */ + public abstract int getLoaderId(); + + /** + * If the user clisk play all + * + * @param position the position of the item clicked or -1 if shuffle all + */ + public abstract void playAll(int position); + +} diff --git a/src/org/lineageos/eleven/ui/fragments/profile/LastAddedFragment.java b/src/org/lineageos/eleven/ui/fragments/profile/LastAddedFragment.java new file mode 100644 index 0000000..87300d9 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/profile/LastAddedFragment.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments.profile; + +import android.os.Bundle; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.Config.SmartPlaylistType; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.loaders.LastAddedLoader; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.sectionadapter.SectionCreator; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.ui.fragments.ISetupActionBar; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; + +/** + * This class is used to display all of the songs the user put on their device + * within the last four weeks. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class LastAddedFragment extends SmartPlaylistFragment implements ISetupActionBar { + + /** + * {@inheritDoc} + */ + @Override + public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) { + // show the loading progress bar + mLoadingEmptyContainer.showLoading(); + + LastAddedLoader loader = new LastAddedLoader(getActivity()); + return new SectionCreator<Song>(getActivity(), loader, null); + } + + @Override + public void setupNoResultsContainer(NoResultsContainer empty) { + super.setupNoResultsContainer(empty); + + empty.setMainText(R.string.empty_last_added_main); + empty.setSecondaryText(R.string.empty_last_added); + } + + @Override + public final View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + setupActionBar(); + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + public void setupActionBar() { + ((BaseActivity)getActivity()).setupActionBar(R.string.playlist_last_added); + ((BaseActivity)getActivity()).setActionBarElevation(true); + } + + @Override + protected long getFragmentSourceId() { + return Config.SmartPlaylistType.LastAdded.mId; + } + + protected SmartPlaylistType getSmartPlaylistType() { + return Config.SmartPlaylistType.LastAdded; + } + + @Override + protected int getShuffleTitleId() { return R.string.menu_shuffle_last_added; } + + @Override + protected int getClearTitleId() { return R.string.clear_last_added; } + + @Override + protected void clearList() { MusicUtils.clearLastAdded(getActivity()); } +}
\ No newline at end of file diff --git a/src/org/lineageos/eleven/ui/fragments/profile/SmartPlaylistFragment.java b/src/org/lineageos/eleven/ui/fragments/profile/SmartPlaylistFragment.java new file mode 100644 index 0000000..72b2b41 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/profile/SmartPlaylistFragment.java @@ -0,0 +1,142 @@ +/* +* Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments.profile; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import com.cyanogenmod.eleven.Config.SmartPlaylistType; +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.PagerAdapter; +import com.cyanogenmod.eleven.menu.ConfirmDialog; +import com.cyanogenmod.eleven.model.Playlist; +import com.cyanogenmod.eleven.ui.fragments.IChildFragment; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.utils.PlaylistPopupMenuHelper; +import com.cyanogenmod.eleven.utils.PopupMenuHelper; +import com.cyanogenmod.eleven.utils.PopupMenuHelper.PopupMenuType; + +public abstract class SmartPlaylistFragment extends BasicSongFragment + implements ConfirmDialog.ConfirmCallback, IChildFragment { + /** + * LoaderCallbacks identifier + */ + private static final int LOADER = 0; + private static final int CLEAR_REQUEST = 1; + private PopupMenuHelper mActionMenuHelper; + + @Override + public int getLoaderId() { return LOADER; } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + setHasOptionsMenu(true); + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + protected Config.IdType getFragmentSourceType() { + return Config.IdType.Playlist; + } + + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + inflater.inflate(R.menu.shuffle_item, menu); + menu.findItem(R.id.menu_shuffle_item).setTitle(getShuffleTitleId()); + + // use the same popup menu to provide actions for smart playlist + // as is used in the PlaylistFragment + mActionMenuHelper = new PlaylistPopupMenuHelper( + getActivity(), getChildFragmentManager(), PopupMenuType.SmartPlaylist) { + public Playlist getPlaylist(int position) { + SmartPlaylistType type = getSmartPlaylistType(); + return new Playlist(type.mId, getString(type.mTitleId), 0); + } + }; + mActionMenuHelper.onPreparePopupMenu(0); + mActionMenuHelper.createPopupMenu(menu); + + inflater.inflate(R.menu.clear_list, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch(item.getItemId()) { + case R.id.menu_shuffle_item: + playAll(-1, true); + return true; + case R.id.clear_list: + ConfirmDialog.show( + this, CLEAR_REQUEST, getClearTitleId(), R.string.clear); + return true; + default: + if(mActionMenuHelper.onMenuItemClick(item)) { return true; } + } + return super.onOptionsItemSelected(item); + } + + @Override + public void confirmOk(int requestCode) { + if(requestCode == CLEAR_REQUEST) { + mAdapter.unload(); + clearList(); + restartLoader(); + } + } + + @Override + public void playAll(int position) { + playAll(position, false); + } + + public void playAll(int position, boolean shuffle) { + // we grab the song ids from the adapter instead of querying the cursor because the user + // expects what they see to be what they play. The counter argument of updating the list + // could be made, but refreshing the smart playlists so often will be annoying and + // confusing for the user so this is an intermediate compromise. An example is the top + // tracks list is based on the # of times you play a song, but near the beginning each + // song being played will change the list and the compromise is to update only when you + // enter the page. + long[] songIds = getSongIdsFromAdapter(); + if (songIds != null) { + MusicUtils.playAll(getActivity(), songIds, position, getSmartPlaylistType().mId, + Config.IdType.Playlist, shuffle); + } + } + + public PagerAdapter.MusicFragments getMusicFragmentParent() { + return PagerAdapter.MusicFragments.PLAYLIST; + } + + protected abstract SmartPlaylistType getSmartPlaylistType(); + + /** text for menu item that shuffles items in this playlist */ + protected abstract int getShuffleTitleId(); + + /** text for confirmation dialog that clears this playlist */ + protected abstract int getClearTitleId(); + + /** action that clears this playlist */ + protected abstract void clearList(); +}
\ No newline at end of file diff --git a/src/org/lineageos/eleven/ui/fragments/profile/TopTracksFragment.java b/src/org/lineageos/eleven/ui/fragments/profile/TopTracksFragment.java new file mode 100644 index 0000000..1d71316 --- /dev/null +++ b/src/org/lineageos/eleven/ui/fragments/profile/TopTracksFragment.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 Andrew Neal + * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.eleven.ui.fragments.profile; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.content.Loader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.cyanogenmod.eleven.Config; +import com.cyanogenmod.eleven.Config.SmartPlaylistType; +import com.cyanogenmod.eleven.R; +import com.cyanogenmod.eleven.adapters.SongAdapter; +import com.cyanogenmod.eleven.loaders.TopTracksLoader; +import com.cyanogenmod.eleven.model.Song; +import com.cyanogenmod.eleven.sectionadapter.SectionCreator; +import com.cyanogenmod.eleven.sectionadapter.SectionListContainer; +import com.cyanogenmod.eleven.ui.activities.BaseActivity; +import com.cyanogenmod.eleven.ui.fragments.ISetupActionBar; +import com.cyanogenmod.eleven.utils.MusicUtils; +import com.cyanogenmod.eleven.widgets.NoResultsContainer; + +/** + * This class is used to display all of the songs the user put on their device + * within the last four weeks. + * + * @author Andrew Neal (andrewdneal@gmail.com) + */ +public class TopTracksFragment extends SmartPlaylistFragment +implements ISetupActionBar { + + @Override + protected SmartPlaylistType getSmartPlaylistType() { + return Config.SmartPlaylistType.TopTracks; + } + + /** + * {@inheritDoc} + */ + @Override + public Loader<SectionListContainer<Song>> onCreateLoader(final int id, final Bundle args) { + // show the loading progress bar + mLoadingEmptyContainer.showLoading(); + + TopTracksLoader loader = new TopTracksLoader(getActivity(), + TopTracksLoader.QueryType.TopTracks); + return new SectionCreator<Song>(getActivity(), loader, null); + } + + @Override + protected SongAdapter createAdapter() { + return new TopTracksAdapter( + getActivity(), + R.layout.list_item_top_tracks + ); + } + + @Override + public final View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + setupActionBar(); + return super.onCreateView(inflater, container, savedInstanceState); + } + + public void setupActionBar() { + ((BaseActivity)getActivity()).setupActionBar(R.string.playlist_top_tracks); + ((BaseActivity)getActivity()).setActionBarElevation(true); + } + + public class TopTracksAdapter extends SongAdapter { + public TopTracksAdapter (final Activity context, final int layoutId) { + super(context, layoutId, getFragmentSourceId(), getFragmentSourceType()); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + TextView positionText = (TextView) view.findViewById(R.id.position_number); + positionText.setText(String.valueOf(position + 1)); + return view; + } + } + + @Override + public void setupNoResultsContainer(NoResultsContainer empty) { + super.setupNoResultsContainer(empty); + + empty.setMainText(R.string.empty_top_tracks_main); + empty.setSecondaryText(R.string.empty_top_tracks_secondary); + } + + @Override + protected long getFragmentSourceId() { + return Config.SmartPlaylistType.TopTracks.mId; + } + + protected int getShuffleTitleId() { return R.string.menu_shuffle_top_tracks; } + + @Override + protected int getClearTitleId() { return R.string.clear_top_tracks_title; } + + @Override + protected void clearList() { MusicUtils.clearTopTracks(getActivity()); } +}
\ No newline at end of file |