OSDN Git Service

Eleven: lots of ui tweaks (fonts, padding, grid layout) and cancel tasks smarter
[android-x86/packages-apps-Eleven.git] / src / com / cyngn / eleven / ui / fragments / ArtistFragment.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 android.app.Activity;
15 import android.content.Context;
16 import android.os.Bundle;
17 import android.os.SystemClock;
18 import android.support.v4.app.Fragment;
19 import android.support.v4.app.LoaderManager.LoaderCallbacks;
20 import android.support.v4.content.Loader;
21 import android.text.TextUtils;
22 import android.view.ContextMenu;
23 import android.view.ContextMenu.ContextMenuInfo;
24 import android.view.LayoutInflater;
25 import android.view.Menu;
26 import android.view.SubMenu;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.AbsListView;
30 import android.widget.AbsListView.OnScrollListener;
31 import android.widget.AdapterView;
32 import android.widget.AdapterView.AdapterContextMenuInfo;
33 import android.widget.AdapterView.OnItemClickListener;
34 import android.widget.GridView;
35 import android.widget.ListView;
36 import android.widget.TextView;
37
38 import com.cyngn.eleven.MusicStateListener;
39 import com.cyngn.eleven.R;
40 import com.cyngn.eleven.adapters.ArtistAdapter;
41 import com.cyngn.eleven.loaders.ArtistLoader;
42 import com.cyngn.eleven.menu.CreateNewPlaylist;
43 import com.cyngn.eleven.menu.DeleteDialog;
44 import com.cyngn.eleven.menu.FragmentMenuItems;
45 import com.cyngn.eleven.model.Artist;
46 import com.cyngn.eleven.recycler.RecycleHolder;
47 import com.cyngn.eleven.sectionadapter.SectionAdapter;
48 import com.cyngn.eleven.sectionadapter.SectionCreator;
49 import com.cyngn.eleven.sectionadapter.SectionListContainer;
50 import com.cyngn.eleven.ui.activities.BaseActivity;
51 import com.cyngn.eleven.utils.ApolloUtils;
52 import com.cyngn.eleven.utils.MusicUtils;
53 import com.cyngn.eleven.utils.NavUtils;
54 import com.cyngn.eleven.utils.PreferenceUtils;
55 import com.cyngn.eleven.utils.SectionCreatorUtils;
56 import com.cyngn.eleven.utils.SectionCreatorUtils.IItemCompare;
57 import com.viewpagerindicator.TitlePageIndicator;
58
59 import java.util.List;
60
61 /**
62  * This class is used to display all of the artists on a user's device.
63  * 
64  * @author Andrew Neal (andrewdneal@gmail.com)
65  */
66 public class ArtistFragment extends Fragment implements LoaderCallbacks<SectionListContainer<Artist>>,
67         OnScrollListener, OnItemClickListener, MusicStateListener {
68
69     /**
70      * Used to keep context menu items from bleeding into other fragments
71      */
72     private static final int GROUP_ID = 2;
73
74     /**
75      * Grid view column count. ONE - list, TWO - normal grid, FOUR - landscape
76      */
77     private static final int ONE = 1, TWO = 2, FOUR = 4;
78
79     /**
80      * LoaderCallbacks identifier
81      */
82     private static final int LOADER = 0;
83
84     /**
85      * Fragment UI
86      */
87     private ViewGroup mRootView;
88
89     /**
90      * The adapter for the grid
91      */
92     private SectionAdapter<Artist, ArtistAdapter> mAdapter;
93
94     /**
95      * The grid view
96      */
97     private GridView mGridView;
98
99     /**
100      * The list view
101      */
102     private ListView mListView;
103
104     /**
105      * Artist song list
106      */
107     private long[] mArtistList;
108
109     /**
110      * Represents an artist
111      */
112     private Artist mArtist;
113
114     /**
115      * True if the list should execute {@code #restartLoader()}.
116      */
117     private boolean mShouldRefresh = false;
118
119     /**
120      * Empty constructor as per the {@link Fragment} documentation
121      */
122     public ArtistFragment() {
123     }
124
125     /**
126      * {@inheritDoc}
127      */
128     @Override
129     public void onAttach(final Activity activity) {
130         super.onAttach(activity);
131         // Register the music status listener
132         ((BaseActivity)activity).setMusicStateListenerListener(this);
133     }
134
135     /**
136      * {@inheritDoc}
137      */
138     @Override
139     public void onCreate(final Bundle savedInstanceState) {
140         super.onCreate(savedInstanceState);
141         // Create the adpater
142         final int layout = R.layout.list_item_normal;
143         ArtistAdapter adapter = new ArtistAdapter(getActivity(), layout);
144         mAdapter = new SectionAdapter<Artist, ArtistAdapter>(getActivity(), adapter);
145     }
146
147     /**
148      * {@inheritDoc}
149      */
150     @Override
151     public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
152             final Bundle savedInstanceState) {
153         // The View for the fragment's UI
154         mRootView = (ViewGroup)inflater.inflate(R.layout.list_base, null);
155         initListView();
156         return mRootView;
157     }
158
159     /**
160      * {@inheritDoc}
161      */
162     @Override
163     public void onActivityCreated(final Bundle savedInstanceState) {
164         super.onActivityCreated(savedInstanceState);
165         // Enable the options menu
166         setHasOptionsMenu(true);
167         // Start the loader
168         getLoaderManager().initLoader(LOADER, null, this);
169     }
170
171     /**
172      * {@inheritDoc}
173      */
174     @Override
175     public void onPause() {
176         super.onPause();
177         mAdapter.flush();
178     }
179
180     /**
181      * {@inheritDoc}
182      */
183     @Override
184     public void onCreateContextMenu(final ContextMenu menu, final View v,
185             final ContextMenuInfo menuInfo) {
186         super.onCreateContextMenu(menu, v, menuInfo);
187
188         // Get the position of the selected item
189         final AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
190         // Creat a new model
191         mArtist = mAdapter.getTItem(info.position);
192         // Create a list of the artist's songs
193         mArtistList = MusicUtils.getSongListForArtist(getActivity(), mArtist.mArtistId);
194
195         // Play the artist
196         menu.add(GROUP_ID, FragmentMenuItems.PLAY_SELECTION, Menu.NONE,
197                 getString(R.string.context_menu_play_selection));
198
199         // Add the artist to the queue
200         menu.add(GROUP_ID, FragmentMenuItems.ADD_TO_QUEUE, Menu.NONE,
201                 getString(R.string.add_to_queue));
202
203         // Add the artist to a playlist
204         final SubMenu subMenu = menu.addSubMenu(GROUP_ID, FragmentMenuItems.ADD_TO_PLAYLIST,
205                 Menu.NONE, R.string.add_to_playlist);
206         MusicUtils.makePlaylistMenu(getActivity(), GROUP_ID, subMenu);
207
208         // Delete the artist
209         menu.add(GROUP_ID, FragmentMenuItems.DELETE, Menu.NONE,
210                 getString(R.string.context_menu_delete));
211     }
212
213     /**
214      * {@inheritDoc}
215      */
216     @Override
217     public boolean onContextItemSelected(final android.view.MenuItem item) {
218         // Avoid leaking context menu selections
219         if (item.getGroupId() == GROUP_ID) {
220             switch (item.getItemId()) {
221                 case FragmentMenuItems.PLAY_SELECTION:
222                     MusicUtils.playAll(getActivity(), mArtistList, 0, true);
223                     return true;
224                 case FragmentMenuItems.ADD_TO_QUEUE:
225                     MusicUtils.addToQueue(getActivity(), mArtistList);
226                     return true;
227                 case FragmentMenuItems.NEW_PLAYLIST:
228                     CreateNewPlaylist.getInstance(mArtistList).show(getFragmentManager(),
229                             "CreatePlaylist");
230                     return true;
231                 case FragmentMenuItems.PLAYLIST_SELECTED:
232                     final long id = item.getIntent().getLongExtra("playlist", 0);
233                     MusicUtils.addToPlaylist(getActivity(), mArtistList, id);
234                     return true;
235                 case FragmentMenuItems.DELETE:
236                     mShouldRefresh = true;
237                     final String artist = mArtist.mArtistName;
238                     DeleteDialog.newInstance(artist, mArtistList, artist).show(
239                             getFragmentManager(), "DeleteDialog");
240                     return true;
241                 default:
242                     break;
243             }
244         }
245         return super.onContextItemSelected(item);
246     }
247
248     /**
249      * {@inheritDoc}
250      */
251     @Override
252     public void onScrollStateChanged(final AbsListView view, final int scrollState) {
253         // Pause disk cache access to ensure smoother scrolling
254         if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING
255                 || scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
256             mAdapter.getUnderlyingAdapter().setPauseDiskCache(true);
257         } else {
258             mAdapter.getUnderlyingAdapter().setPauseDiskCache(false);
259             mAdapter.notifyDataSetChanged();
260         }
261     }
262
263     /**
264      * {@inheritDoc}
265      */
266     @Override
267     public void onItemClick(final AdapterView<?> parent, final View view, final int position,
268             final long id) {
269         mArtist = mAdapter.getTItem(position);
270         NavUtils.openArtistProfile(getActivity(), mArtist.mArtistName);
271     }
272
273     /**
274      * {@inheritDoc}
275      */
276     @Override
277     public Loader<SectionListContainer<Artist>> onCreateLoader(final int id, final Bundle args) {
278         final Context context = getActivity();
279         IItemCompare<Artist> comparator = SectionCreatorUtils.createArtistComparison(context);
280         return new SectionCreator<Artist>(getActivity(), new ArtistLoader(context), comparator);
281     }
282
283     /**
284      * {@inheritDoc}
285      */
286     @Override
287     public void onLoadFinished(final Loader<SectionListContainer<Artist>> loader,
288                                final SectionListContainer<Artist> data) {
289         // Check for any errors
290         if (data.mListResults.isEmpty()) {
291             // Set the empty text
292             final TextView empty = (TextView)mRootView.findViewById(R.id.empty);
293             empty.setText(getString(R.string.empty_music));
294             mListView.setEmptyView(empty);
295             return;
296         }
297
298         // Set the data
299         mAdapter.setData(data);
300     }
301
302     /**
303      * {@inheritDoc}
304      */
305     @Override
306     public void onLoaderReset(final Loader<SectionListContainer<Artist>> loader) {
307         // Clear the data in the adapter
308         mAdapter.unload();
309     }
310
311     /**
312      * Scrolls the list to the currently playing artist when the user touches
313      * the header in the {@link TitlePageIndicator}.
314      */
315     public void scrollToCurrentArtist() {
316         final int currentArtistPosition = getItemPositionByArtist();
317
318         if (currentArtistPosition != 0) {
319             mListView.setSelection(currentArtistPosition);
320         }
321     }
322
323     /**
324      * @return The position of an item in the list or grid based on the name of
325      *         the currently playing artist.
326      */
327     private int getItemPositionByArtist() {
328         final long artistId = MusicUtils.getCurrentArtistId();
329         if (mAdapter == null) {
330             return 0;
331         }
332
333         int position = mAdapter.getItemPosition(artistId);
334
335         // if for some reason we don't find the item, just jump to the top
336         if (position < 0) {
337             return 0;
338         }
339
340         return position;
341     }
342
343     /**
344      * Restarts the loader.
345      */
346     public void refresh() {
347         // Wait a moment for the preference to change.
348         SystemClock.sleep(10);
349         getLoaderManager().restartLoader(LOADER, null, this);
350     }
351
352     /**
353      * {@inheritDoc}
354      */
355     @Override
356     public void onScroll(final AbsListView view, final int firstVisibleItem,
357             final int visibleItemCount, final int totalItemCount) {
358         // Nothing to do
359     }
360
361     /**
362      * {@inheritDoc}
363      */
364     @Override
365     public void restartLoader() {
366         // Update the list when the user deletes any items
367         if (mShouldRefresh) {
368             getLoaderManager().restartLoader(LOADER, null, this);
369         }
370         mShouldRefresh = false;
371     }
372
373     /**
374      * {@inheritDoc}
375      */
376     @Override
377     public void onMetaChanged() {
378         // Nothing to do
379     }
380
381     /**
382      * Sets up various helpers for both the list and grid
383      * 
384      * @param list The list or grid
385      */
386     private void initAbsListView(final AbsListView list) {
387         // Release any references to the recycled Views
388         list.setRecyclerListener(new RecycleHolder());
389         // Listen for ContextMenus to be created
390         list.setOnCreateContextMenuListener(this);
391         // Show the albums and songs from the selected artist
392         list.setOnItemClickListener(this);
393         // To help make scrolling smooth
394         list.setOnScrollListener(this);
395     }
396
397     /**
398      * Sets up the list view
399      */
400     private void initListView() {
401         // Initialize the grid
402         mListView = (ListView)mRootView.findViewById(R.id.list_base);
403         // Set the data behind the list
404         mListView.setAdapter(mAdapter);
405         // Set up the helpers
406         initAbsListView(mListView);
407     }
408 }