OSDN Git Service

c42972f74e822ffb994f23e1c62babdfa80215ca
[android-x86/packages-apps-Eleven.git] / src / com / cyngn / eleven / 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.cyngn.eleven.ui.fragments;
13
14 import static com.cyngn.eleven.utils.MusicUtils.mService;
15
16 import android.content.BroadcastReceiver;
17 import android.content.ComponentName;
18 import android.content.Context;
19 import android.content.Intent;
20 import android.content.IntentFilter;
21 import android.content.ServiceConnection;
22 import android.os.Bundle;
23 import android.os.IBinder;
24 import android.support.v4.app.Fragment;
25 import android.support.v4.app.LoaderManager.LoaderCallbacks;
26 import android.support.v4.content.Loader;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.widget.AdapterView;
31 import android.widget.AdapterView.OnItemClickListener;
32
33 import com.cyngn.eleven.MusicPlaybackService;
34 import com.cyngn.eleven.R;
35 import com.cyngn.eleven.adapters.SongAdapter;
36 import com.cyngn.eleven.dragdrop.DragSortListView;
37 import com.cyngn.eleven.dragdrop.DragSortListView.DragScrollProfile;
38 import com.cyngn.eleven.dragdrop.DragSortListView.DropListener;
39 import com.cyngn.eleven.dragdrop.DragSortListView.RemoveListener;
40 import com.cyngn.eleven.loaders.NowPlayingCursor;
41 import com.cyngn.eleven.loaders.QueueLoader;
42 import com.cyngn.eleven.menu.DeleteDialog;
43 import com.cyngn.eleven.model.Song;
44 import com.cyngn.eleven.recycler.RecycleHolder;
45 import com.cyngn.eleven.ui.activities.SlidingPanelActivity;
46 import com.cyngn.eleven.utils.MusicUtils;
47 import com.cyngn.eleven.utils.PopupMenuHelper;
48 import com.cyngn.eleven.widgets.IPopupMenuCallback;
49 import com.cyngn.eleven.widgets.NoResultsContainer;
50 import com.cyngn.eleven.widgets.PlayPauseProgressButton;
51
52 import java.lang.ref.WeakReference;
53 import java.util.List;
54
55 /**
56  * This class is used to display all of the songs in the queue.
57  *
58  * @author Andrew Neal (andrewdneal@gmail.com)
59  */
60 public class QueueFragment extends Fragment implements LoaderCallbacks<List<Song>>,
61         OnItemClickListener, DropListener, RemoveListener, DragScrollProfile, ServiceConnection {
62
63     /**
64      * LoaderCallbacks identifier
65      */
66     private static final int LOADER = 0;
67
68     /**
69      * Service token for binding to the music service
70      */
71     private MusicUtils.ServiceToken mToken;
72
73     /**
74      * The listener to the playback service that will trigger updates to the ui
75      */
76     private QueueUpdateListener mQueueUpdateListener;
77
78     /**
79      * The adapter for the list
80      */
81     private SongAdapter mAdapter;
82
83     /**
84      * The list view
85      */
86     private DragSortListView mListView;
87
88     /**
89      * Pop up menu helper
90      */
91     private PopupMenuHelper mPopupMenuHelper;
92
93     private ViewGroup mRootView;
94
95     /**
96      * Empty constructor as per the {@link Fragment} documentation
97      */
98     public QueueFragment() {
99     }
100
101     /**
102      * {@inheritDoc}
103      */
104     @Override
105     public void onCreate(final Bundle savedInstanceState) {
106         super.onCreate(savedInstanceState);
107         mPopupMenuHelper = new PopupMenuHelper(getActivity(), getFragmentManager()) {
108             private Song mSong;
109             private int mSelectedPosition;
110
111             @Override
112             protected PopupMenuType onPreparePopupMenu(int position) {
113                 mSelectedPosition = position;
114                 mSong = mAdapter.getItem(mSelectedPosition);
115
116                 return PopupMenuType.Queue;
117             }
118
119             @Override
120             protected long[] getIdList() {
121                 return new long[] { mSong.mSongId };
122             }
123
124             @Override
125             protected String getArtistName() {
126                 return mSong.mArtistName;
127             }
128
129             @Override
130             protected void onDeleteClicked() {
131                 DeleteDialog.newInstance(mSong.mSongName,
132                         new long[] { getId() }, null).show(getFragmentManager(), "DeleteDialog");
133             }
134
135             @Override
136             protected void playNext() {
137                 NowPlayingCursor queue = (NowPlayingCursor)QueueLoader
138                         .makeQueueCursor(getActivity());
139                 queue.removeItem(mSelectedPosition);
140                 queue.close();
141                 queue = null;
142                 MusicUtils.playNext(getIdList());
143                 refreshQueue();
144             }
145
146             @Override
147             protected void removeFromQueue() {
148                 MusicUtils.removeTrack(getId());
149                 refreshQueue();
150             }
151         };
152
153         // Create the adapter
154         mAdapter = new SongAdapter(getActivity(), R.layout.edit_queue_list_item);
155         mAdapter.setPopupMenuClickedListener(new IPopupMenuCallback.IListener() {
156             @Override
157             public void onPopupMenuClicked(View v, int position) {
158                 mPopupMenuHelper.showPopupMenu(v, position);
159             }
160         });
161     }
162
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
168             final Bundle savedInstanceState) {
169         // The View for the fragment's UI
170         mRootView = (ViewGroup)inflater.inflate(R.layout.list_base, null);
171         // Initialize the list
172         mListView = (DragSortListView)mRootView.findViewById(R.id.list_base);
173         // Set the data behind the list
174         mListView.setAdapter(mAdapter);
175         // Release any references to the recycled Views
176         mListView.setRecyclerListener(new RecycleHolder());
177         // Play the selected song
178         mListView.setOnItemClickListener(this);
179         // Set the drop listener
180         mListView.setDropListener(this);
181         // Set the swipe to remove listener
182         mListView.setRemoveListener(this);
183         // Quick scroll while dragging
184         mListView.setDragScrollProfile(this);
185         // Enable fast scroll bars
186         mListView.setFastScrollEnabled(true);
187         return mRootView;
188     }
189
190     /**
191      * {@inheritDoc}
192      */
193     @Override
194     public void onActivityCreated(final Bundle savedInstanceState) {
195         super.onActivityCreated(savedInstanceState);
196
197         // Initialize the broadcast receiver
198         mQueueUpdateListener = new QueueUpdateListener(this);
199
200         // Bind Apollo's service
201         mToken = MusicUtils.bindToService(getActivity(), this);
202     }
203
204     /**
205      * {@inheritDoc}
206      */
207     @Override
208     public void onServiceConnected(final ComponentName name, final IBinder service) {
209         refreshQueue();
210     }
211
212     @Override
213     public void onServiceDisconnected(ComponentName name) {
214     }
215
216     @Override
217     public void onStart() {
218         super.onStart();
219
220         final IntentFilter filter = new IntentFilter();
221         // Play and pause changes
222         filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED);
223         // Queue changes
224         filter.addAction(MusicPlaybackService.QUEUE_CHANGED);
225         // Track changes
226         filter.addAction(MusicPlaybackService.META_CHANGED);
227
228         getActivity().registerReceiver(mQueueUpdateListener, filter);
229
230         // resume the progress listeners
231         setPlayPauseProgressButtonStates(false);
232     }
233
234     @Override
235     public void onStop() {
236         super.onStop();
237
238         // stops the progress listeners
239         setPlayPauseProgressButtonStates(true);
240     }
241
242     /**
243      * Sets the state for any play pause progress buttons under the listview
244      * This is neede because the buttons update themselves so if the activity
245      * is hidden, we want to pause those handlers
246      * @param pause the state to set it to
247      */
248     public void setPlayPauseProgressButtonStates(boolean pause) {
249         if (mListView != null) {
250             // walk through the visible list items
251             for (int i = mListView.getFirstVisiblePosition();
252                  i <= mListView.getLastVisiblePosition(); i++) {
253                 View childView = mListView.getChildAt(i);
254                 if (childView != null) {
255                     PlayPauseProgressButton button = (PlayPauseProgressButton) childView.findViewById(R.id.playPauseProgressButton);
256
257                     // pause or resume based on the flag
258                     if (pause) {
259                         button.pause();
260                     } else {
261                         button.resume();
262                     }
263                 }
264             }
265         }
266     }
267
268     @Override
269     public void onDestroy() {
270         super.onDestroy();
271
272         try {
273             getActivity().unregisterReceiver(mQueueUpdateListener);
274         } catch (final Throwable e) {
275             //$FALL-THROUGH$
276         }
277
278         if (mService != null) {
279             MusicUtils.unbindFromService(mToken);
280             mToken = null;
281         }
282     }
283
284     /**
285      * {@inheritDoc}
286      */
287     @Override
288     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
289             final long id) {
290         // When selecting a track from the queue, just jump there instead of
291         // reloading the queue. This is both faster, and prevents accidentally
292         // dropping out of party shuffle.
293         MusicUtils.setQueuePosition(position);
294     }
295
296     /**
297      * {@inheritDoc}
298      */
299     @Override
300     public Loader<List<Song>> onCreateLoader(final int id, final Bundle args) {
301         return new QueueLoader(getActivity());
302     }
303
304     /**
305      * {@inheritDoc}
306      */
307     @Override
308     public void onLoadFinished(final Loader<List<Song>> loader, final List<Song> data) {
309         mAdapter.unload(); // Start fresh
310
311         if (data.isEmpty()) {
312             // No songs found, bring up the empty view
313             final NoResultsContainer empty =
314                     (NoResultsContainer)mRootView.findViewById(R.id.no_results_container);
315             // Setup the container strings
316             setupNoResultsContainer(empty);
317             // set the empty view into the list view
318             mListView.setEmptyView(empty);
319             mAdapter.setCurrentlyPlayingSongId(SongAdapter.NOTHING_PLAYING);
320             ((SlidingPanelActivity)getActivity()).clearMetaInfo();
321         } else {
322             // Add the songs found to the adapter
323             for (final Song song : data) { mAdapter.add(song); }
324             // Build the cache
325             mAdapter.buildCache();
326             // Set the currently playing audio
327             mAdapter.setCurrentlyPlayingSongId(MusicUtils.getCurrentAudioId());
328         }
329     }
330
331     /**
332      * {@inheritDoc}
333      */
334     @Override
335     public void onLoaderReset(final Loader<List<Song>> loader) {
336         // Clear the data in the adapter
337         mAdapter.unload();
338     }
339
340     /**
341      * {@inheritDoc}
342      */
343     @Override
344     public float getSpeed(final float w, final long t) {
345         if (w > 0.8f) {
346             return mAdapter.getCount() / 0.001f;
347         } else {
348             return 10.0f * w;
349         }
350     }
351
352     /**
353      * {@inheritDoc}
354      */
355     @Override
356     public void remove(final int which) {
357         Song song = mAdapter.getItem(which);
358         mAdapter.remove(song);
359         mAdapter.notifyDataSetChanged();
360         MusicUtils.removeTrack(song.mSongId);
361         // Build the cache
362         mAdapter.buildCache();
363     }
364
365     /**
366      * {@inheritDoc}
367      */
368     @Override
369     public void drop(final int from, final int to) {
370         Song song = mAdapter.getItem(from);
371         mAdapter.remove(song);
372         mAdapter.insert(song, to);
373         mAdapter.notifyDataSetChanged();
374         MusicUtils.moveQueueItem(from, to);
375         // Build the cache
376         mAdapter.buildCache();
377     }
378
379     /**
380      * Called to restart the loader callbacks
381      */
382     public void refreshQueue() {
383         if (isAdded()) {
384             getLoaderManager().restartLoader(LOADER, null, this);
385         }
386     }
387
388     private void setupNoResultsContainer(NoResultsContainer empty) {
389         int color = getResources().getColor(R.color.no_results_light);
390         empty.setTextColor(color);
391         empty.setMainText(R.string.empty_queue_main);
392         empty.setSecondaryText(R.string.empty_queue_secondary);
393     }
394
395     /**
396      * Used to monitor the state of playback
397      */
398     private static final class QueueUpdateListener extends BroadcastReceiver {
399
400         private final WeakReference<QueueFragment> mReference;
401
402         /**
403          * Constructor of <code>PlaybackStatus</code>
404          */
405         public QueueUpdateListener(final QueueFragment fragment) {
406             mReference = new WeakReference<QueueFragment>(fragment);
407         }
408
409         /**
410          * {@inheritDoc}
411          */
412         @Override
413         public void onReceive(final Context context, final Intent intent) {
414             // TODO: Invalid options menu if opened?
415             final String action = intent.getAction();
416             if (action.equals(MusicPlaybackService.META_CHANGED)) {
417                 mReference.get().mAdapter.setCurrentlyPlayingSongId(MusicUtils.getCurrentAudioId());
418             } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) {
419                 mReference.get().mAdapter.notifyDataSetChanged();
420             } else if (action.equals(MusicPlaybackService.QUEUE_CHANGED)) {
421                 mReference.get().refreshQueue();
422             }
423         }
424     }
425 }