OSDN Git Service

3103cd4ef636d1a47b3484387947b942da0bbbf1
[android-x86/packages-apps-Eleven.git] / src / com / andrew / apollo / ui / fragments / QueueFragment.java
1 /*
2  * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0
3  * (the "License"); you may not use this file except in compliance with the
4  * License. You may obtain a copy of the License at
5  * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
6  * or agreed to in writing, software distributed under the License is
7  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8  * KIND, either express or implied. See the License for the specific language
9  * governing permissions and limitations under the License.
10  */
11
12 package com.andrew.apollo.ui.fragments;
13
14 import android.os.Bundle;
15 import android.support.v4.app.Fragment;
16 import android.support.v4.app.LoaderManager.LoaderCallbacks;
17 import android.support.v4.content.Loader;
18 import android.text.TextUtils;
19 import android.view.ContextMenu;
20 import android.view.ContextMenu.ContextMenuInfo;
21 import android.view.LayoutInflater;
22 import android.view.Menu;
23 import android.view.MenuInflater;
24 import android.view.MenuItem;
25 import android.view.SubMenu;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.AdapterView;
29 import android.widget.AdapterView.AdapterContextMenuInfo;
30 import android.widget.AdapterView.OnItemClickListener;
31
32 import com.andrew.apollo.R;
33 import com.andrew.apollo.adapters.SongAdapter;
34 import com.andrew.apollo.dragdrop.DragSortListView;
35 import com.andrew.apollo.dragdrop.DragSortListView.DragScrollProfile;
36 import com.andrew.apollo.dragdrop.DragSortListView.DropListener;
37 import com.andrew.apollo.dragdrop.DragSortListView.RemoveListener;
38 import com.andrew.apollo.loaders.NowPlayingCursor;
39 import com.andrew.apollo.loaders.QueueLoader;
40 import com.andrew.apollo.menu.CreateNewPlaylist;
41 import com.andrew.apollo.menu.DeleteDialog;
42 import com.andrew.apollo.menu.FragmentMenuItems;
43 import com.andrew.apollo.model.Song;
44 import com.andrew.apollo.provider.FavoritesStore;
45 import com.andrew.apollo.recycler.RecycleHolder;
46 import com.andrew.apollo.utils.MusicUtils;
47 import com.andrew.apollo.utils.NavUtils;
48 import com.viewpagerindicator.TitlePageIndicator;
49
50 import java.util.List;
51
52 /**
53  * This class is used to display all of the songs in the queue.
54  * 
55  * @author Andrew Neal (andrewdneal@gmail.com)
56  */
57 public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song>>,
58         OnItemClickListener, DropListener, RemoveListener, DragScrollProfile {
59
60     /**
61      * Used to keep context menu items from bleeding into other fragments
62      */
63     private static final int GROUP_ID = 13;
64
65     /**
66      * LoaderCallbacks identifier
67      */
68     private static final int LOADER = 0;
69
70     /**
71      * The adapter for the list
72      */
73     private SongAdapter mAdapter;
74
75     /**
76      * The list view
77      */
78     private DragSortListView mListView;
79
80     /**
81      * Represents a song
82      */
83     private Song mSong;
84
85     /**
86      * Position of a context menu item
87      */
88     private int mSelectedPosition;
89
90     /**
91      * Id of a context menu item
92      */
93     private long mSelectedId;
94
95     /**
96      * Song, album, and artist name used in the context menu
97      */
98     private String mSongName, mAlbumName, mArtistName;
99
100     /**
101      * Empty constructor as per the {@link Fragment} documentation
102      */
103     public QueueFragment() {
104     }
105
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     public void onCreate(final Bundle savedInstanceState) {
111         super.onCreate(savedInstanceState);
112         // Create the adpater
113         mAdapter = new SongAdapter(getActivity(), R.layout.edit_track_list_item);
114     }
115
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
121             final Bundle savedInstanceState) {
122         // The View for the fragment's UI
123         final ViewGroup rootView = (ViewGroup)inflater.inflate(R.layout.list_base, null);
124         // Initialize the list
125         mListView = (DragSortListView)rootView.findViewById(R.id.list_base);
126         // Set the data behind the list
127         mListView.setAdapter(mAdapter);
128         // Release any references to the recycled Views
129         mListView.setRecyclerListener(new RecycleHolder());
130         // Listen for ContextMenus to be created
131         mListView.setOnCreateContextMenuListener(this);
132         // Play the selected song
133         mListView.setOnItemClickListener(this);
134         // Set the drop listener
135         mListView.setDropListener(this);
136         // Set the swipe to remove listener
137         mListView.setRemoveListener(this);
138         // Quick scroll while dragging
139         mListView.setDragScrollProfile(this);
140         return rootView;
141     }
142
143     /**
144      * {@inheritDoc}
145      */
146     @Override
147     public void onActivityCreated(final Bundle savedInstanceState) {
148         super.onActivityCreated(savedInstanceState);
149         // Enable the options menu
150         setHasOptionsMenu(true);
151         // Start the loader
152         getLoaderManager().initLoader(LOADER, null, this);
153     }
154
155     /**
156      * {@inheritDoc}
157      */
158     @Override
159     public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
160         inflater.inflate(R.menu.queue, menu);
161         super.onCreateOptionsMenu(menu, inflater);
162     }
163
164     /**
165      * {@inheritDoc}
166      */
167     @Override
168     public boolean onOptionsItemSelected(final MenuItem item) {
169         switch (item.getItemId()) {
170             case R.id.menu_save_queue:
171                 NowPlayingCursor queue = (NowPlayingCursor)QueueLoader
172                         .makeQueueCursor(getActivity());
173                 CreateNewPlaylist.getInstance(MusicUtils.getSongListForCursor(queue)).show(
174                         getFragmentManager(), "CreatePlaylist");
175                 queue.close();
176                 queue = null;
177                 return true;
178             case R.id.menu_clear_queue:
179                 MusicUtils.clearQueue();
180                 NavUtils.goHome(getActivity());
181                 return true;
182             default:
183                 break;
184         }
185         return super.onOptionsItemSelected(item);
186     }
187
188     /**
189      * {@inheritDoc}
190      */
191     @Override
192     public void onCreateContextMenu(final ContextMenu menu, final View v,
193             final ContextMenuInfo menuInfo) {
194         super.onCreateContextMenu(menu, v, menuInfo);
195         // Get the position of the selected item
196         final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
197         mSelectedPosition = info.position;
198         // Creat a new song
199         mSong = mAdapter.getItem(mSelectedPosition);
200         mSelectedId = mSong.mSongId;
201         mSongName = mSong.mSongName;
202         mAlbumName = mSong.mAlbumName;
203         mArtistName = mSong.mArtistName;
204
205         // Play the song next
206         menu.add(GROUP_ID, FragmentMenuItems.PLAY_NEXT, Menu.NONE,
207                 getString(R.string.context_menu_play_next));
208
209         // Add the song to a playlist
210         final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
211                 Menu.NONE, R.string.add_to_playlist);
212         MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu, true);
213
214         // Remove the song from the queue
215         menu.add(GROUP_ID, FragmentMenuItems.REMOVE_FROM_QUEUE, Menu.NONE,
216                 getString(R.string.remove_from_queue));
217
218         // View more content by the song artist
219         menu.add(GROUP_ID, FragmentMenuItems.MORE_BY_ARTIST, Menu.NONE,
220                 getString(R.string.context_menu_more_by_artist));
221
222         // Make the song a ringtone
223         menu.add(GROUP_ID, FragmentMenuItems.USE_AS_RINGTONE, Menu.NONE,
224                 getString(R.string.context_menu_use_as_ringtone));
225
226         // Delete the song
227         menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
228                 getString(R.string.context_menu_delete));
229     }
230
231     /**
232      * {@inheritDoc}
233      */
234     @Override
235     public boolean onContextItemSelected(final android.view.MenuItem item) {
236         if (item.getGroupId() == GROUP_ID) {
237             switch (item.getItemId()) {
238                 case FragmentMenuItems.PLAY_NEXT:
239                     NowPlayingCursor queue = (NowPlayingCursor)QueueLoader
240                             .makeQueueCursor(getActivity());
241                     queue.removeItem(mSelectedPosition);
242                     queue.close();
243                     queue = null;
244                     MusicUtils.playNext(new long[] {
245                         mSelectedId
246                     });
247                     refreshQueue();
248                     return true;
249                 case FragmentMenuItems.REMOVE_FROM_QUEUE:
250                     MusicUtils.removeTrack(mSelectedId);
251                     refreshQueue();
252                     return true;
253                 case FragmentMenuItems.ADD_TO_FAVORITES:
254                     FavoritesStore.getInstance(getActivity()).addSongId(
255                             mSelectedId, mSongName, mAlbumName, mArtistName);
256                     return true;
257                 case FragmentMenuItems.NEW_PLAYLIST:
258                     CreateNewPlaylist.getInstance(new long[] {
259                         mSelectedId
260                     }).show(getFragmentManager(), "CreatePlaylist");
261                     return true;
262                 case FragmentMenuItems.PLAYLIST_SELECTED:
263                     final long mPlaylistId = item.getIntent().getLongExtra("playlist", 0);
264                     MusicUtils.addToPlaylist(getActivity(), new long[] {
265                         mSelectedId
266                     }, mPlaylistId);
267                     return true;
268                 case FragmentMenuItems.MORE_BY_ARTIST:
269                     NavUtils.openArtistProfile(getActivity(), mArtistName);
270                     return true;
271                 case FragmentMenuItems.USE_AS_RINGTONE:
272                     MusicUtils.setRingtone(getActivity(), mSelectedId);
273                     return true;
274                 case FragmentMenuItems.DELETE:
275                     DeleteDialog.newInstance(mSong.mSongName, new long[] {
276                         mSelectedId
277                     }, null).show(getFragmentManager(), "DeleteDialog");
278                     return true;
279                 default:
280                     break;
281             }
282         }
283         return super.onContextItemSelected(item);
284     }
285
286     /**
287      * {@inheritDoc}
288      */
289     @Override
290     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
291             final long id) {
292         // When selecting a track from the queue, just jump there instead of
293         // reloading the queue. This is both faster, and prevents accidentally
294         // dropping out of party shuffle.
295         MusicUtils.setQueuePosition(position);
296     }
297
298     /**
299      * {@inheritDoc}
300      */
301     @Override
302     public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
303         return new QueueLoader(getActivity());
304     }
305
306     /**
307      * {@inheritDoc}
308      */
309     @Override
310     public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) {
311         // Check for any errors
312         if (data.isEmpty()) {
313             return;
314         }
315
316         // Start fresh
317         mAdapter.unload();
318         // Add the data to the adpater
319         for (final Song song : data) {
320             mAdapter.add(song);
321         }
322         // Build the cache
323         mAdapter.buildCache();
324     }
325
326     /**
327      * {@inheritDoc}
328      */
329     @Override
330     public void onLoaderReset(final Loader<List<Song>> loader) {
331         // Clear the data in the adapter
332         mAdapter.unload();
333     }
334
335     /**
336      * {@inheritDoc}
337      */
338     @Override
339     public float getSpeed(final float w, final long t) {
340         if (w > 0.8f) {
341             return mAdapter.getCount() / 0.001f;
342         } else {
343             return 10.0f * w;
344         }
345     }
346
347     /**
348      * {@inheritDoc}
349      */
350     @Override
351     public void remove(final int which) {
352         mSong = mAdapter.getItem(which);
353         mAdapter.remove(mSong);
354         mAdapter.notifyDataSetChanged();
355         MusicUtils.removeTrack(mSong.mSongId);
356         // Build the cache
357         mAdapter.buildCache();
358     }
359
360     /**
361      * {@inheritDoc}
362      */
363     @Override
364     public void drop(final int from, final int to) {
365         mSong = mAdapter.getItem(from);
366         mAdapter.remove(mSong);
367         mAdapter.insert(mSong, to);
368         mAdapter.notifyDataSetChanged();
369         MusicUtils.moveQueueItem(from, to);
370         // Build the cache
371         mAdapter.buildCache();
372     }
373
374     /**
375      * Scrolls the list to the currently playing song when the user touches the
376      * header in the {@link TitlePageIndicator}.
377      */
378     public void scrollToCurrentSong() {
379         final int currentSongPosition = getItemPositionBySong();
380
381         if (currentSongPosition != 0) {
382             mListView.setSelection(currentSongPosition);
383         }
384     }
385
386     /**
387      * @return The position of an item in the list based on the name of the
388      *         currently playing song.
389      */
390     private int getItemPositionBySong() {
391         final long trackId = MusicUtils.getCurrentAudioId();
392         if (mAdapter == null) {
393             return 0;
394         }
395         for (int i = 0; i < mAdapter.getCount(); i++) {
396             if (mAdapter.getItem(i).mSongId == trackId) {
397                 return i;
398             }
399         }
400         return 0;
401     }
402
403     /**
404      * Called to restart the loader callbacks
405      */
406     public void refreshQueue() {
407         if (isAdded()) {
408             getLoaderManager().restartLoader(LOADER, null, this);
409         }
410     }
411 }