diff options
18 files changed, 370 insertions, 230 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4ef165c..db70871 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -146,6 +146,13 @@ android:screenOrientation="portrait" android:theme="@style/Eleven.Theme.ActionBar.Overlay"> </activity> + <!-- Playlist Detail Activity --> + <activity + android:name=".ui.activities.PlaylistDetailActivity" + android:excludeFromRecents="true" + android:screenOrientation="portrait" + android:theme="@style/Eleven.Theme.ActionBar.Overlay"> + </activity> <!-- Shortcut launcher Activity --> <activity android:name=".ui.activities.ShortcutActivity" diff --git a/res/drawable-xxhdpi/playlist_icon.png b/res/drawable-xxhdpi/playlist_icon.png Binary files differnew file mode 100644 index 0000000..1be226b --- /dev/null +++ b/res/drawable-xxhdpi/playlist_icon.png diff --git a/res/drawable-xxhdpi/stopwatch_icon_white.png b/res/drawable-xxhdpi/stopwatch_icon_white.png Binary files differnew file mode 100644 index 0000000..2f4059a --- /dev/null +++ b/res/drawable-xxhdpi/stopwatch_icon_white.png diff --git a/res/layout/faux_playlist_header.xml b/res/layout/faux_playlist_header.xml new file mode 100644 index 0000000..59ab140 --- /dev/null +++ b/res/layout/faux_playlist_header.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2012 Andrew Neal + + 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. +--> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + + <include + layout="@layout/playlist_detail_header" + android:visibility="invisible" /> + +</FrameLayout>
\ No newline at end of file diff --git a/res/layout/playlist_detail.xml b/res/layout/playlist_detail.xml new file mode 100644 index 0000000..458737e --- /dev/null +++ b/res/layout/playlist_detail.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 Cyanogen, Inc. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <include + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + layout="@layout/list_base_nopadding" /> + + <include + android:id="@+id/playlist_header" + layout="@layout/playlist_detail_header" /> +</RelativeLayout>
\ No newline at end of file diff --git a/res/layout/playlist_detail_header.xml b/res/layout/playlist_detail_header.xml new file mode 100644 index 0000000..93c2429 --- /dev/null +++ b/res/layout/playlist_detail_header.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + Copyright (C) 2014 Cyanogen, Inc. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="@dimen/playlist_detail_header_height"> + + <com.cyngn.eleven.widgets.LayoutSuppressingImageView + android:id="@+id/image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:contentDescription="@null" + android:scaleType="centerCrop" /> + + <FrameLayout + android:layout_width="fill_parent" + android:layout_height="@dimen/playlist_detail_header_bottom_height" + android:layout_alignParentBottom="true" + android:background="@color/header_shadow_color"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:scaleType="centerInside" + android:src="@drawable/playlist_icon" /> + + <TextView + android:id="@+id/number_of_songs_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="sans-serif-light" + android:paddingRight="16dp" + android:textColor="@color/white" + android:textSize="@dimen/text_size_micro" /> + + <ImageView + android:layout_width="20dp" + android:layout_height="20dp" + android:scaleType="centerInside" + android:src="@drawable/stopwatch_icon_white" /> + + <TextView + android:id="@+id/duration_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="sans-serif-light" + android:textColor="@color/white" + android:textSize="@dimen/text_size_micro" /> + </LinearLayout> + </FrameLayout> +</RelativeLayout> diff --git a/res/values/colors.xml b/res/values/colors.xml index 62579cb..1cc9430 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -66,6 +66,8 @@ <color name="header_action_bar_text_color">@color/white</color> <color name="bottom_action_bar_text_color">@color/default_text_color_light</color> + <!-- Color for background for shadow on playlist page --> + <color name="header_shadow_color">#ea31353f</color> <!-- Color for the pop up menu --> <color name="menu_divider_color">#4ca19d9e</color> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 6437a23..7fd95f0 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -94,6 +94,9 @@ <dimen name="profile_carousel_label_height">45.0dip</dimen> <dimen name="profile_indicator_height">5.0dip</dimen> <dimen name="profile_label_padding">16.0dip</dimen> + <!-- playlist detail header --> + <dimen name="playlist_detail_header_height">240.0dip</dimen> + <dimen name="playlist_detail_header_bottom_height">40.0dip</dimen> <!-- Audio player Buttons (play/pause/shuffle/repeat/next/previous)--> <dimen name="audio_player_controls_end_button_width">30.0dip</dimen> diff --git a/res/values/strings.xml b/res/values/strings.xml index ec7e719..84ac14c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -202,4 +202,8 @@ <string name="header_5_plus_albums">5+ Albums</string> <string name="combine_two_strings">%1$s | %2$s</string> + + <string name="duration_mins"><xliff:g id="format">%3$dm %4$ds</xliff:g></string> + <string name="duration_hours"><xliff:g id="format">%2$dh %3$dm %4$ds</xliff:g></string> + <string name="duration_days"><xliff:g id="format">%1$dd %2$dd %3$dm %4$ds</xliff:g></string> </resources> diff --git a/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java b/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java index 7d80133..52f4bf1 100644 --- a/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java +++ b/src/com/cyngn/eleven/adapters/ProfileSongAdapter.java @@ -12,7 +12,6 @@ package com.cyngn.eleven.adapters; import android.app.Activity; -import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -27,7 +26,7 @@ import com.cyngn.eleven.ui.fragments.profile.AlbumSongFragment; import com.cyngn.eleven.ui.fragments.profile.ArtistSongFragment; import com.cyngn.eleven.ui.fragments.profile.GenreSongFragment; import com.cyngn.eleven.ui.fragments.profile.LastAddedFragment; -import com.cyngn.eleven.ui.fragments.profile.PlaylistSongFragment; +import com.cyngn.eleven.ui.activities.PlaylistDetailActivity; import com.cyngn.eleven.utils.ApolloUtils; import com.cyngn.eleven.utils.Lists; import com.cyngn.eleven.utils.MusicUtils; @@ -37,7 +36,7 @@ import java.util.List; /** * This {@link ArrayAdapter} is used to display the songs for a particular * artist, album, playlist, or genre for {@link ArtistSongFragment}, - * {@link AlbumSongFragment},{@link PlaylistSongFragment}, + * {@link AlbumSongFragment},{@link PlaylistDetailActivity}, * {@link GenreSongFragment},{@link LastAddedFragment}. * * @author Andrew Neal (andrewdneal@gmail.com) @@ -116,12 +115,12 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> { * @param layoutId The resource Id of the view to inflate. * @param setting defines the content of the second line */ - public ProfileSongAdapter(final Activity activity, final int layoutId, final int setting) { + public ProfileSongAdapter(final Activity activity, final int layoutId, final int headerId, final int setting) { super(activity, 0); // Used to create the custom layout mInflater = LayoutInflater.from(activity); // Cache the header - mHeader = mInflater.inflate(R.layout.faux_carousel, null); + mHeader = mInflater.inflate(headerId, null); // Get the layout Id mLayoutId = layoutId; // Know what to put in line two @@ -137,7 +136,7 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> { * @param layoutId The resource Id of the view to inflate. */ public ProfileSongAdapter(final Activity activity, final int layoutId) { - this(activity, layoutId, DISPLAY_DEFAULT_SETTING); + this(activity, layoutId, R.layout.faux_carousel, DISPLAY_DEFAULT_SETTING); } /** @@ -177,7 +176,7 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> { holder.mLineOneRight.get().setVisibility(View.GONE); holder.mLineTwo.get().setText( - MusicUtils.makeTimeString(getContext(), song.mDuration)); + MusicUtils.makeShortTimeString(getContext(), song.mDuration)); break; case DISPLAY_PLAYLIST_SETTING: if (song.mDuration == -1) { @@ -185,7 +184,7 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> { } else { holder.mLineOneRight.get().setVisibility(View.VISIBLE); holder.mLineOneRight.get().setText( - MusicUtils.makeTimeString(getContext(), song.mDuration)); + MusicUtils.makeShortTimeString(getContext(), song.mDuration)); } final StringBuilder sb = new StringBuilder(song.mArtistName); @@ -204,7 +203,7 @@ public class ProfileSongAdapter extends ArrayAdapter<Song> { holder.mLineOneRight.get().setVisibility(View.VISIBLE); holder.mLineOneRight.get().setText( - MusicUtils.makeTimeString(getContext(), song.mDuration)); + MusicUtils.makeShortTimeString(getContext(), song.mDuration)); holder.mLineTwo.get().setText(song.mAlbumName); break; } diff --git a/src/com/cyngn/eleven/adapters/SongAdapter.java b/src/com/cyngn/eleven/adapters/SongAdapter.java index ac38cf0..6e3c8d2 100644 --- a/src/com/cyngn/eleven/adapters/SongAdapter.java +++ b/src/com/cyngn/eleven/adapters/SongAdapter.java @@ -162,7 +162,7 @@ public class SongAdapter extends ArrayAdapter<Song> implements SectionAdapter.Ba // Song names (line one) mData[i].mLineOne = song.mSongName; // Song duration (line one, right) - mData[i].mLineOneRight = MusicUtils.makeTimeString(getContext(), song.mDuration); + mData[i].mLineOneRight = MusicUtils.makeShortTimeString(getContext(), song.mDuration); // Album names (line two) mData[i].mLineTwo = song.mAlbumName; } diff --git a/src/com/cyngn/eleven/ui/fragments/profile/PlaylistSongFragment.java b/src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java index e67be06..e6134b7 100644 --- a/src/com/cyngn/eleven/ui/fragments/profile/PlaylistSongFragment.java +++ b/src/com/cyngn/eleven/ui/activities/PlaylistDetailActivity.java @@ -1,40 +1,33 @@ -/* - * Copyright (C) 2012 Andrew Neal 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.cyngn.eleven.ui.fragments.profile; - -import android.app.Activity; +package com.cyngn.eleven.ui.activities; + +import android.app.ActionBar; import android.database.Cursor; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; import android.provider.MediaStore; -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.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuItem; import android.view.SubMenu; 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.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; +import android.widget.ImageView; +import android.widget.TextView; import com.cyngn.eleven.Config; import com.cyngn.eleven.R; import com.cyngn.eleven.adapters.ProfileSongAdapter; +import com.cyngn.eleven.cache.ImageFetcher; import com.cyngn.eleven.dragdrop.DragSortListView; import com.cyngn.eleven.dragdrop.DragSortListView.DragScrollProfile; import com.cyngn.eleven.dragdrop.DragSortListView.DropListener; @@ -47,18 +40,13 @@ import com.cyngn.eleven.model.Song; import com.cyngn.eleven.recycler.RecycleHolder; import com.cyngn.eleven.utils.MusicUtils; import com.cyngn.eleven.utils.NavUtils; -import com.cyngn.eleven.widgets.ProfileTabCarousel; -import com.cyngn.eleven.widgets.VerticalScrollListener; import java.util.List; +import java.util.Locale; -/** - * This class is used to display all of the songs from a particular playlist. - * - * @author Andrew Neal (andrewdneal@gmail.com) - */ -public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<List<Song>>, - OnItemClickListener, DropListener, RemoveListener, DragScrollProfile { +public class PlaylistDetailActivity extends SlidingPanelActivity implements + OnScrollListener, LoaderCallbacks<List<Song>>, OnItemClickListener, DropListener, + RemoveListener, DragScrollProfile { /** * Used to keep context menu items from bleeding into other fragments @@ -70,15 +58,17 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li */ private static final int LOADER = 0; - /** - * The adapter for the list - */ - private ProfileSongAdapter mAdapter; + private static final int ACTION_BAR_DEFAULT_OPACITY = 65; + private Drawable mActionBarBackground; - /** - * The list view - */ private DragSortListView mListView; + private ProfileSongAdapter mAdapter; + + private View mHeaderContainer; + private ImageView mPlaylistImageView; + + private TextView mNumberOfSongs; + private TextView mDurationOfPlaylist; /** * Represents a song @@ -101,56 +91,54 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li private String mSongName, mAlbumName, mArtistName; /** - * Profile header - */ - private ProfileTabCarousel mProfileTabCarousel; - - /** * The Id of the playlist the songs belong to */ private long mPlaylistId; - /** - * Empty constructor as per the {@link Fragment} documentation - */ - public PlaylistSongFragment() { - } - - /** - * {@inheritDoc} - */ @Override - public void onAttach(final Activity activity) { - super.onAttach(activity); - mProfileTabCarousel = (ProfileTabCarousel)activity - .findViewById(R.id.acivity_profile_base_tab_carousel); + protected int getLayoutToInflate() { + return R.layout.playlist_detail; } - /** - * {@inheritDoc} - */ @Override - public void onCreate(final Bundle savedInstanceState) { + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Create the adpater + + Bundle arguments = getIntent().getExtras(); + String playlistName = arguments.getString(Config.NAME); + mPlaylistId = arguments.getLong(Config.ID); + + setupActionBar(playlistName); + + ViewGroup root = (ViewGroup) findViewById(R.id.activity_base_content); + root.setPadding(0, 0, 0, 0); // clear default padding + + setupHero(); + setupSongList(root); + + LoaderManager lm = getSupportLoaderManager(); + lm.initLoader(0, arguments, this); + } + + private void setupHero() { + mPlaylistImageView = (ImageView)findViewById(R.id.image); + mHeaderContainer = findViewById(R.id.playlist_header); + mNumberOfSongs = (TextView)findViewById(R.id.number_of_songs_text); + mDurationOfPlaylist = (TextView)findViewById(R.id.duration_text); + + // TODO: Get the top artist image - do this in the next patch + // ImageFetcher.getInstance(this).loadCurrentArtwork(mPlaylistImageView); + } + + private void setupSongList(ViewGroup root) { + mListView = (DragSortListView) root.findViewById(R.id.list_base); + mListView.setOnScrollListener(this); mAdapter = new ProfileSongAdapter( - getActivity(), + this, R.layout.edit_track_list_item, + R.layout.faux_playlist_header, ProfileSongAdapter.DISPLAY_PLAYLIST_SETTING ); - } - - /** - * {@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_nopadding, null); - // Initialize the list - mListView = (DragSortListView)rootView.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()); @@ -164,52 +152,21 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li mListView.setRemoveListener(this); // Quick scroll while dragging mListView.setDragScrollProfile(this); - // To help make scrolling smooth - mListView.setOnScrollListener(new VerticalScrollListener(null, mProfileTabCarousel, 0) { - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - super.onScrollStateChanged(view, scrollState); - - // Pause disk cache access to ensure smoother scrolling - if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING - || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { - mAdapter.setPauseDiskCache(true); - } else { - mAdapter.setPauseDiskCache(false); - mAdapter.notifyDataSetChanged(); - } - } - }); // Remove the scrollbars and padding for the fast scroll mListView.setVerticalScrollBarEnabled(false); mListView.setFastScrollEnabled(false); mListView.setPadding(0, 0, 0, 0); - return rootView; } - /** - * {@inheritDoc} - */ - @Override - public void onActivityCreated(final Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - // Enable the options menu - setHasOptionsMenu(true); - // Start the loader - final Bundle arguments = getArguments(); - if (arguments != null) { - mPlaylistId = arguments.getLong(Config.ID); - getLoaderManager().initLoader(LOADER, arguments, this); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onSaveInstanceState(final Bundle outState) { - super.onSaveInstanceState(outState); - outState.putAll(getArguments() != null ? getArguments() : new Bundle()); + private void setupActionBar(String playlistName) { + ActionBar actionBar = getActionBar(); + actionBar.setTitle(playlistName.toUpperCase(Locale.getDefault())); + actionBar.setIcon(R.drawable.ic_action_back); + actionBar.setHomeButtonEnabled(true); + // change action bar background to a drawable we can control + mActionBarBackground = new ColorDrawable(getResources().getColor(R.color.header_action_bar_color)); + mActionBarBackground.setAlpha(ACTION_BAR_DEFAULT_OPACITY); + actionBar.setBackgroundDrawable(mActionBarBackground); } /** @@ -217,10 +174,10 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li */ @Override public void onCreateContextMenu(final ContextMenu menu, final View v, - final ContextMenuInfo menuInfo) { + final ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); // Get the position of the selected item - final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo; + final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; mSelectedPosition = info.position - 1; // Creat a new song mSong = mAdapter.getItem(mSelectedPosition); @@ -244,7 +201,7 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li // Add the song to a playlist final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST, Menu.NONE, R.string.add_to_playlist); - MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu); + MusicUtils.makePlaylistMenu(this, GROUP_ID, subMenu); // View more content by the song artist menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE, @@ -268,50 +225,50 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li if (item.getGroupId() == GROUP_ID) { switch (item.getItemId()) { case FragmentMenuItems.PLAY_SELECTION: - MusicUtils.playAll(getActivity(), new long[] { - mSelectedId + MusicUtils.playAll(this, new long[]{ + mSelectedId }, 0, false); return true; case FragmentMenuItems.PLAY_NEXT: - MusicUtils.playNext(new long[] { - mSelectedId + MusicUtils.playNext(new long[]{ + mSelectedId }); return true; case FragmentMenuItems.ADD_TO_QUEUE: - MusicUtils.addToQueue(getActivity(), new long[] { - mSelectedId + MusicUtils.addToQueue(this, new long[]{ + mSelectedId }); return true; case FragmentMenuItems.NEW_PLAYLIST: - CreateNewPlaylist.getInstance(new long[] { - mSelectedId - }).show(getFragmentManager(), "CreatePlaylist"); + CreateNewPlaylist.getInstance(new long[]{ + mSelectedId + }).show(getSupportFragmentManager(), "CreatePlaylist"); return true; case FragmentMenuItems.PLAYLIST_SELECTED: final long playlistId = item.getIntent().getLongExtra("playlist", 0); - MusicUtils.addToPlaylist(getActivity(), new long[] { - mSelectedId + MusicUtils.addToPlaylist(this, new long[]{ + mSelectedId }, playlistId); return true; case FragmentMenuItems.MORE_BY_ARTIST: - NavUtils.openArtistProfile(getActivity(), mArtistName); + NavUtils.openArtistProfile(this, mArtistName); return true; case FragmentMenuItems.USE_AS_RINGTONE: - MusicUtils.setRingtone(getActivity(), mSelectedId); + MusicUtils.setRingtone(this, mSelectedId); return true; case FragmentMenuItems.DELETE: - DeleteDialog.newInstance(mSong.mSongName, new long[] { - mSelectedId - }, null).show(getFragmentManager(), "DeleteDialog"); + DeleteDialog.newInstance(mSong.mSongName, new long[]{ + mSelectedId + }, null).show(getSupportFragmentManager(), "DeleteDialog"); SystemClock.sleep(10); mAdapter.notifyDataSetChanged(); - getLoaderManager().restartLoader(LOADER, null, this); + getSupportLoaderManager().restartLoader(LOADER, null, this); return true; case FragmentMenuItems.REMOVE_FROM_PLAYLIST: mAdapter.remove(mSong); mAdapter.notifyDataSetChanged(); - MusicUtils.removeFromPlaylist(getActivity(), mSong.mSongId, mPlaylistId); - getLoaderManager().restartLoader(LOADER, null, this); + MusicUtils.removeFromPlaylist(this, mSong.mSongId, mPlaylistId); + getSupportLoaderManager().restartLoader(LOADER, null, this); return true; default: break; @@ -324,60 +281,6 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li * {@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(), - getArguments().getLong(Config.ID)); - final long[] list = MusicUtils.getSongListForCursor(cursor); - MusicUtils.playAll(getActivity(), list, position - 1, false); - cursor.close(); - cursor = null; - } - - /** - * {@inheritDoc} - */ - @Override - public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) { - return new PlaylistSongLoader(getActivity(), mPlaylistId); - } - - /** - * {@inheritDoc} - */ - @Override - public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) { - // Check for any errors - if (data.isEmpty()) { - return; - } - - // Start fresh - mAdapter.unload(); - // Return the correct count - mAdapter.setCount(data); - // Add the data to the adpater - for (final Song song : data) { - mAdapter.add(song); - } - } - - /** - * {@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; @@ -395,7 +298,7 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li mAdapter.remove(mSong); mAdapter.notifyDataSetChanged(); final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", mPlaylistId); - getActivity().getContentResolver().delete(uri, + getContentResolver().delete(uri, MediaStore.Audio.Playlists.Members.AUDIO_ID + "=" + mSong.mSongId, null); } @@ -415,7 +318,115 @@ public class PlaylistSongFragment extends Fragment implements LoaderCallbacks<Li mAdapter.remove(mSong); mAdapter.insert(mSong, realTo); mAdapter.notifyDataSetChanged(); - MediaStore.Audio.Playlists.Members.moveItem(getActivity().getContentResolver(), + MediaStore.Audio.Playlists.Members.moveItem(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(this, + mPlaylistId); + final long[] list = MusicUtils.getSongListForCursor(cursor); + MusicUtils.playAll(this, list, position - 1, false); + cursor.close(); + cursor = null; + } + + /** + * cause action bar icon tap to act like back -- boo-urns! + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // Pause disk cache access to ensure smoother scrolling + if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING + || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + mAdapter.setPauseDiskCache(true); + } else { + mAdapter.setPauseDiskCache(false); + mAdapter.notifyDataSetChanged(); + } + } + + @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(); + + // if the first fake header is off the screen, + // set opaque and header container to be off screen + if (firstVisibleItem != 0) { + mHeaderContainer.setY(-mHeaderContainer.getHeight()); + mActionBarBackground.setAlpha(255); + } else { + // otherwise set the offset and calculate the alpha + mHeaderContainer.setY(firstChildY); + int alpha = ACTION_BAR_DEFAULT_OPACITY + + (int)((255 - ACTION_BAR_DEFAULT_OPACITY) * -firstChildY / + (float)mHeaderContainer.getHeight()); + + alpha = Math.min(255, alpha); + mActionBarBackground.setAlpha(alpha); + } + } + + @Override + public Loader<List<Song>> onCreateLoader(int i, Bundle bundle) { + return new PlaylistSongLoader(this, mPlaylistId); + } + + @Override + public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) { + // Check for any errors + if (data.isEmpty()) { + return; + } + + // Start fresh + mAdapter.unload(); + // Return the correct count + mAdapter.setCount(data); + // set the number of songs + String numberOfSongs = MusicUtils.makeLabel(this, R.plurals.Nsongs, data.size()); + mNumberOfSongs.setText(numberOfSongs); + + long duration = 0; + + // Add the data to the adapter + for (final Song song : data) { + mAdapter.add(song); + duration += song.mDuration; + } + + // set the duration + String durationString = MusicUtils.makeLongTimeString(this, duration); + mDurationOfPlaylist.setText(durationString); + } + + @Override + public void onLoaderReset(final Loader<List<Song>> loader) { + // Clear the data in the adapter + mAdapter.unload(); + } +}
\ No newline at end of file diff --git a/src/com/cyngn/eleven/ui/activities/ProfileActivity.java b/src/com/cyngn/eleven/ui/activities/ProfileActivity.java index 8d570dd..6b7951d 100644 --- a/src/com/cyngn/eleven/ui/activities/ProfileActivity.java +++ b/src/com/cyngn/eleven/ui/activities/ProfileActivity.java @@ -39,7 +39,6 @@ import com.cyngn.eleven.ui.fragments.profile.ArtistAlbumFragment; import com.cyngn.eleven.ui.fragments.profile.ArtistSongFragment; import com.cyngn.eleven.ui.fragments.profile.GenreSongFragment; import com.cyngn.eleven.ui.fragments.profile.LastAddedFragment; -import com.cyngn.eleven.ui.fragments.profile.PlaylistSongFragment; import com.cyngn.eleven.utils.ApolloUtils; import com.cyngn.eleven.utils.MusicUtils; import com.cyngn.eleven.utils.NavUtils; @@ -191,17 +190,6 @@ public class ProfileActivity extends SlidingPanelActivity implements OnPageChang // Action bar title = Last added getActionBar().setTitle(mProfileName); } else - // Set up the user playlist profile - if (isPlaylist()) { - // Add the carousel images - mTabCarousel.setPlaylistOrGenreProfileHeader(this, mProfileName); - - // Playlist profile fragments - mPagerAdapter.add(PlaylistSongFragment.class, mArguments); - - // Action bar title = playlist name - getActionBar().setTitle(mProfileName); - } else // Set up the genre profile if (isGenre()) { // Add the carousel images @@ -248,7 +236,7 @@ public class ProfileActivity extends SlidingPanelActivity implements OnPageChang // Set the shuffle all title to "play all" if a playlist. final MenuItem shuffle = menu.findItem(R.id.menu_shuffle); String title = null; - if (isLastAdded() || isPlaylist()) { + if (isLastAdded()) { title = getString(R.string.menu_play_all); } else { title = getString(R.string.menu_shuffle); @@ -301,9 +289,7 @@ public class ProfileActivity extends SlidingPanelActivity implements OnPageChang } else if (isGenre()) { list = MusicUtils.getSongListForGenre(this, id); } - if (isPlaylist()) { - MusicUtils.playPlaylist(this, id); - } else if (isLastAdded()) { + if (isLastAdded()) { MusicUtils.playLastAdded(this); } else { if (list != null && list.length > 0) { @@ -620,14 +606,6 @@ public class ProfileActivity extends SlidingPanelActivity implements OnPageChang } /** - * @return True if the MIME type is vnd.android.cursor.dir/playlist, false - * otherwise. - */ - private final boolean isPlaylist() { - return mType.equals(MediaStore.Audio.Playlists.CONTENT_TYPE); - } - - /** * @return True if the MIME type is "LastAdded", false otherwise. */ private final boolean isLastAdded() { diff --git a/src/com/cyngn/eleven/ui/fragments/AudioPlayerFragment.java b/src/com/cyngn/eleven/ui/fragments/AudioPlayerFragment.java index a47d96b..dbf85d4 100644 --- a/src/com/cyngn/eleven/ui/fragments/AudioPlayerFragment.java +++ b/src/com/cyngn/eleven/ui/fragments/AudioPlayerFragment.java @@ -356,7 +356,7 @@ public class AudioPlayerFragment extends Fragment implements ServiceConnection { mTrackName.setText(MusicUtils.getTrackName()); // Set the total time - String totalTime = MusicUtils.makeTimeString(getActivity(), MusicUtils.duration() / 1000); + String totalTime = MusicUtils.makeShortTimeString(getActivity(), MusicUtils.duration() / 1000); if (!mTotalTime.getText().equals(totalTime)) { mTotalTime.setText(totalTime); } @@ -579,7 +579,7 @@ public class AudioPlayerFragment extends Fragment implements ServiceConnection { } private void refreshCurrentTimeText(final long pos) { - mCurrentTime.setText(MusicUtils.makeTimeString(getActivity(), pos / 1000)); + mCurrentTime.setText(MusicUtils.makeShortTimeString(getActivity(), pos / 1000)); } /* Used to update the current time string */ diff --git a/src/com/cyngn/eleven/ui/fragments/profile/AlbumSongFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/AlbumSongFragment.java index b99fe8b..46aa066 100644 --- a/src/com/cyngn/eleven/ui/fragments/profile/AlbumSongFragment.java +++ b/src/com/cyngn/eleven/ui/fragments/profile/AlbumSongFragment.java @@ -124,6 +124,7 @@ public class AlbumSongFragment extends Fragment implements LoaderCallbacks<List< mAdapter = new ProfileSongAdapter( getActivity(), R.layout.list_item_simple, + R.layout.faux_carousel, ProfileSongAdapter.DISPLAY_ALBUM_SETTING ); } diff --git a/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java b/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java index ffbf4b6..96a4a18 100644 --- a/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java +++ b/src/com/cyngn/eleven/ui/fragments/profile/LastAddedFragment.java @@ -132,6 +132,7 @@ public class LastAddedFragment extends Fragment implements LoaderCallbacks<List< mAdapter = new ProfileSongAdapter( getActivity(), R.layout.list_item_normal, + R.layout.faux_carousel, ProfileSongAdapter.DISPLAY_PLAYLIST_SETTING ); } diff --git a/src/com/cyngn/eleven/utils/MusicUtils.java b/src/com/cyngn/eleven/utils/MusicUtils.java index d4067ba..edd741e 100644 --- a/src/com/cyngn/eleven/utils/MusicUtils.java +++ b/src/com/cyngn/eleven/utils/MusicUtils.java @@ -190,13 +190,13 @@ public final class MusicUtils { * @param secs The track in seconds. * @return Duration of a track that's properly formatted. */ - public static final String makeTimeString(final Context context, long secs) { + public static final String makeShortTimeString(final Context context, long secs) { long hours, mins; hours = secs / 3600; - secs -= hours * 3600; + secs %= 3600; mins = secs / 60; - secs -= mins * 60; + secs %= 60; final String durationFormat = context.getResources().getString( hours == 0 ? R.string.durationformatshort : R.string.durationformatlong); @@ -204,6 +204,34 @@ public final class MusicUtils { } /** + * * Used to create a formatted time string in the format of #d #h #m #s + * + * @param context The {@link Context} to use. + * @param secs The duration seconds. + * @return Duration properly formatted in #d #h #m #s format + */ + public static final String makeLongTimeString(final Context context, long secs) { + long days, hours, mins; + + days = secs / (3600 * 24); + secs %= (3600 * 24); + hours = secs / 3600; + secs %= 3600; + mins = secs / 60; + secs %= 60; + + int stringId = R.string.duration_mins; + if (days != 0) { + stringId = R.string.duration_days; + } else if (hours != 0) { + stringId = R.string.duration_hours; + } + + final String durationFormat = context.getResources().getString(stringId); + return String.format(durationFormat, days, hours, mins, secs); + } + + /** * Changes to the next track */ public static void next() { @@ -1288,6 +1316,7 @@ public final class MusicUtils { try { return mService.duration(); } catch (final RemoteException ignored) { + } catch (final IllegalStateException ignored) { } } return 0; diff --git a/src/com/cyngn/eleven/utils/NavUtils.java b/src/com/cyngn/eleven/utils/NavUtils.java index f18f861..4fdfacb 100644 --- a/src/com/cyngn/eleven/utils/NavUtils.java +++ b/src/com/cyngn/eleven/utils/NavUtils.java @@ -23,6 +23,7 @@ import com.cyngn.eleven.Config; import com.cyngn.eleven.R; import com.cyngn.eleven.ui.activities.ArtistDetailActivity; import com.cyngn.eleven.ui.activities.HomeActivity; +import com.cyngn.eleven.ui.activities.PlaylistDetailActivity; import com.cyngn.eleven.ui.activities.ProfileActivity; import com.cyngn.eleven.ui.activities.SearchActivity; import com.cyngn.eleven.ui.activities.SettingsActivity; @@ -101,7 +102,7 @@ public final class NavUtils { bundle.putString(Config.NAME, playlistName); // Create the intent to launch the profile activity - final Intent intent = new Intent(context, ProfileActivity.class); + final Intent intent = new Intent(context, PlaylistDetailActivity.class); intent.putExtras(bundle); context.startActivity(intent); } |