OSDN Git Service

Merge "Add improved localized sorting (similar to contacts sorting) to Eleven" into...
[android-x86/packages-apps-Eleven.git] / src / com / cyanogenmod / eleven / ui / activities / HomeActivity.java
1 /*
2  * Copyright (C) 2014 The CyanogenMod Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.cyanogenmod.eleven.ui.activities;
17
18 import android.content.Intent;
19 import android.graphics.Bitmap;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.provider.MediaStore;
24 import android.support.v4.app.Fragment;
25 import android.support.v4.app.FragmentManager;
26 import android.support.v4.app.FragmentTransaction;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import android.view.MenuItem;
30
31 import com.cyanogenmod.eleven.Config;
32 import com.cyanogenmod.eleven.R;
33 import com.cyanogenmod.eleven.cache.ImageFetcher;
34 import com.cyanogenmod.eleven.ui.fragments.AlbumDetailFragment;
35 import com.cyanogenmod.eleven.ui.fragments.ArtistDetailFragment;
36 import com.cyanogenmod.eleven.ui.fragments.IChildFragment;
37 import com.cyanogenmod.eleven.ui.fragments.ISetupActionBar;
38 import com.cyanogenmod.eleven.ui.fragments.PlaylistDetailFragment;
39 import com.cyanogenmod.eleven.ui.fragments.RecentFragment;
40 import com.cyanogenmod.eleven.ui.fragments.phone.MusicBrowserPhoneFragment;
41 import com.cyanogenmod.eleven.ui.fragments.profile.LastAddedFragment;
42 import com.cyanogenmod.eleven.ui.fragments.profile.TopTracksFragment;
43 import com.cyanogenmod.eleven.utils.ApolloUtils;
44 import com.cyanogenmod.eleven.utils.MusicUtils;
45 import com.cyanogenmod.eleven.utils.NavUtils;
46
47 public class HomeActivity extends SlidingPanelActivity {
48     private static final String TAG = "HomeActivity";
49     private static final String ACTION_PREFIX = HomeActivity.class.getName();
50     public static final String ACTION_VIEW_ARTIST_DETAILS = ACTION_PREFIX + ".view.ArtistDetails";
51     public static final String ACTION_VIEW_ALBUM_DETAILS = ACTION_PREFIX + ".view.AlbumDetails";
52     public static final String ACTION_VIEW_PLAYLIST_DETAILS = ACTION_PREFIX + ".view.PlaylistDetails";
53     public static final String ACTION_VIEW_SMART_PLAYLIST = ACTION_PREFIX + ".view.SmartPlaylist";
54     public static final String EXTRA_BROWSE_PAGE_IDX = "BrowsePageIndex";
55
56     private static final int NEW_PHOTO = 1;
57     public static final int EQUALIZER = 2;
58
59     private String mKey;
60     private boolean mLoadedBaseFragment = false;
61     private boolean mHasPendingPlaybackRequest = false;
62     private Handler mHandler = new Handler();
63
64     /**
65      * Used by the up action to determine how to handle this
66      */
67     protected boolean mTopLevelActivity = false;
68
69
70     @Override
71     protected void onCreate(Bundle savedInstanceState) {
72         super.onCreate(savedInstanceState);
73         // if we've been launched by an intent, parse it
74         Intent launchIntent = getIntent();
75         boolean intentHandled = false;
76         if (launchIntent != null) {
77             intentHandled = parseIntentForFragment(launchIntent);
78         }
79
80         // if the intent didn't cause us to load a fragment, load the music browse one
81         if (!mLoadedBaseFragment) {
82             final MusicBrowserPhoneFragment fragment = new MusicBrowserPhoneFragment();
83             if (launchIntent != null) {
84                 fragment.setDefaultPageIdx(launchIntent.getIntExtra(EXTRA_BROWSE_PAGE_IDX,
85                         MusicBrowserPhoneFragment.INVALID_PAGE_INDEX));
86             }
87             getSupportFragmentManager().beginTransaction()
88                     .replace(R.id.activity_base_content, fragment).commit();
89
90             mLoadedBaseFragment = true;
91             mTopLevelActivity = true;
92         }
93
94         getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
95             @Override
96             public void onBackStackChanged() {
97                 Fragment topFragment = getTopFragment();
98                 if (topFragment != null) {
99                     // the fragment that has come back to the top should now have its menu items
100                     // added to the action bar -- so tell it to make it menu items visible
101                     topFragment.setMenuVisibility(true);
102                     ISetupActionBar setupActionBar = (ISetupActionBar) topFragment;
103                     setupActionBar.setupActionBar();
104
105                     if (topFragment instanceof MusicBrowserPhoneFragment) {
106                         getActionBar().setIcon(R.drawable.ic_launcher);
107                         getActionBar().setHomeButtonEnabled(false);
108                     } else {
109                         getActionBar().setIcon(R.drawable.ic_action_back_padded);
110                         getActionBar().setHomeButtonEnabled(true);
111                     }
112                 }
113             }
114         });
115
116         // if intent wasn't UI related, process it as a audio playback request
117         if ( !intentHandled ) {
118             handlePlaybackIntent(launchIntent);
119         }
120
121     }
122
123     public Fragment getTopFragment() {
124         return getSupportFragmentManager().findFragmentById(R.id.activity_base_content);
125     }
126
127     public void postRemoveFragment(final Fragment frag) {
128         mHandler.post(new Runnable() {
129             @Override
130             public void run() {
131                 // removing the fragment doesn't cause the backstack event to be triggered even if
132                 // it is the top fragment, so if it is the top fragment, we will just manually
133                 // call pop back stack
134                 if (frag == getTopFragment()) {
135                     getSupportFragmentManager().popBackStack();
136                 } else {
137                     getSupportFragmentManager().beginTransaction().remove(frag).commit();
138                 }
139             }
140         });
141     }
142
143     @Override
144     protected void onNewIntent(Intent intent) {
145         super.onNewIntent(intent);
146
147         // parse intent to ascertain whether the intent is inter UI communication
148         boolean intentHandled = parseIntentForFragment(intent);
149         // since this activity is marked 'singleTop' (launch mode), an existing activity instance
150         // could be sent media play requests
151         if ( !intentHandled) {
152             handlePlaybackIntent(intent);
153         }
154     }
155
156     private boolean parseIntentForFragment(Intent intent) {
157         boolean handled = false;
158         if (intent.getAction() != null) {
159             final String action = intent.getAction();
160             Fragment targetFragment = null;
161             FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
162
163             if (action.equals(ACTION_VIEW_SMART_PLAYLIST)) {
164                 long playlistId = intent.getExtras().getLong(Config.SMART_PLAYLIST_TYPE);
165                 switch (Config.SmartPlaylistType.getTypeById(playlistId)) {
166                     case LastAdded:
167                         targetFragment = new LastAddedFragment();
168                         break;
169                     case RecentlyPlayed:
170                         targetFragment = new RecentFragment();
171                         break;
172                     case TopTracks:
173                         targetFragment = new TopTracksFragment();
174                         break;
175                 }
176             } else if (action.equals(ACTION_VIEW_PLAYLIST_DETAILS)) {
177                 targetFragment = new PlaylistDetailFragment();
178             } else if (action.equals(ACTION_VIEW_ALBUM_DETAILS)) {
179                 targetFragment = new AlbumDetailFragment();
180             } else if (action.equals(ACTION_VIEW_ARTIST_DETAILS)) {
181                 targetFragment = new ArtistDetailFragment();
182             }
183
184             if (targetFragment != null) {
185                 targetFragment.setArguments(intent.getExtras());
186                 transaction.setCustomAnimations(0, 0, 0, R.anim.fade_out);
187                 // If we ever come back to this because of memory concerns because
188                 // none of the fragments are being removed from memory, we can fix this
189                 // by using "replace" instead of "add".  The caveat is that the performance of
190                 // returning to previous fragments is a bit more sluggish because the fragment
191                 // view needs to be recreated. If we do remove that, we can remove the back stack
192                 // change listener code above
193                 transaction.add(R.id.activity_base_content, targetFragment);
194                 if (mLoadedBaseFragment) {
195                     transaction.addToBackStack(null);
196                     showPanel(Panel.Browse);
197                 } else {
198                     // else mark the fragment as loaded so we don't load the music browse fragment.
199                     // this happens when they launch search which is its own activity and then
200                     // browse through that back to home activity
201                     mLoadedBaseFragment = true;
202                     getActionBar().setIcon(R.drawable.ic_action_back_padded);
203                     getActionBar().setHomeButtonEnabled(true);
204                 }
205                 // the current top fragment is about to be hidden by what we are replacing
206                 // it with -- so tell that fragment not to make its action bar menu items visible
207                 Fragment oldTop = getTopFragment();
208                 if(oldTop != null) { oldTop.setMenuVisibility(false); }
209
210                 transaction.commit();
211                 handled = true;
212             }
213         }
214         return handled;
215     }
216
217     @Override
218     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
219         super.onActivityResult(requestCode, resultCode, data);
220         if (requestCode == NEW_PHOTO && !TextUtils.isEmpty(mKey)) {
221             if (resultCode == RESULT_OK) {
222                 MusicUtils.removeFromCache(this, mKey);
223                 final Uri selectedImage = data.getData();
224
225                 new Thread(new Runnable() {
226                     @Override
227                     public void run() {
228                         Bitmap bitmap = ImageFetcher.decodeSampledBitmapFromUri(getContentResolver(),
229                                 selectedImage);
230
231                         ImageFetcher imageFetcher = ApolloUtils.getImageFetcher(HomeActivity.this);
232                         imageFetcher.addBitmapToCache(mKey, bitmap);
233
234                         MusicUtils.refresh();
235                     }
236                 }).start();
237             }
238         }
239     }
240
241     /**
242      * Starts an activity for result that returns an image from the Gallery.
243      */
244     public void selectNewPhoto(String key) {
245         mKey = key;
246         // Now open the gallery
247         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
248         intent.setType("image/*");
249         startActivityForResult(intent, NEW_PHOTO);
250     }
251
252     @Override
253     public boolean onOptionsItemSelected(final MenuItem item) {
254         switch (item.getItemId()) {
255             case android.R.id.home:
256                 navigateToTop();
257                 return true;
258         }
259
260         return super.onOptionsItemSelected(item);
261     }
262
263     /**
264      * Navigates to the top Activity and places the view to the correct page
265      */
266     protected void navigateToTop() {
267         final Fragment topFragment = getTopFragment();
268         int targetFragmentIndex = MusicBrowserPhoneFragment.INVALID_PAGE_INDEX;
269         if (topFragment instanceof IChildFragment) {
270             targetFragmentIndex = ((IChildFragment)topFragment).getMusicFragmentParent().ordinal();
271         }
272
273         // If we are the top activity in the stack (as determined by the activity that has loaded
274         // the MusicBrowserPhoneFragment) then clear the back stack and move the browse fragment
275         // to the appropriate page as per Android up standards
276         if (mTopLevelActivity) {
277             clearBackStack();
278             MusicBrowserPhoneFragment musicFragment = (MusicBrowserPhoneFragment) getTopFragment();
279             musicFragment.setDefaultPageIdx(targetFragmentIndex);
280             showPanel(Panel.Browse);
281         } else {
282             // I've tried all other combinations with parent activities, support.NavUtils and
283             // there is no easy way to achieve what we want that I'm aware of, so clear everything
284             // and jump to the right page
285             NavUtils.goHome(this, targetFragmentIndex);
286         }
287     }
288
289     /**
290      * Immediately clears the backstack
291      */
292     protected void clearBackStack() {
293         final FragmentManager fragmentManager = getSupportFragmentManager();
294         if (fragmentManager.getBackStackEntryCount() > 0) {
295             final int id = fragmentManager.getBackStackEntryAt(0).getId();
296             fragmentManager.popBackStackImmediate(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
297         }
298     }
299
300     @Override
301     public void handlePendingPlaybackRequests() {
302         if (mHasPendingPlaybackRequest) {
303             Intent unhandledIntent = getIntent();
304             handlePlaybackIntent(unhandledIntent);
305         }
306     }
307
308     /**
309      * Checks whether the passed intent contains a playback request,
310      * and starts playback if that's the case
311      * @return true if the intent was consumed
312      */
313     private boolean handlePlaybackIntent(Intent intent) {
314
315         if (intent == null) {
316             return false;
317         } else if ( !MusicUtils.isPlaybackServiceConnected() ) {
318             mHasPendingPlaybackRequest = true;
319             return false;
320         }
321
322         Uri uri = intent.getData();
323         String mimeType = intent.getType();
324         boolean handled = false;
325
326         if (uri != null && uri.toString().length() > 0) {
327             MusicUtils.playFile(this, uri);
328             handled = true;
329         } else if (MediaStore.Audio.Playlists.CONTENT_TYPE.equals(mimeType)) {
330             long id = parseIdFromIntent(intent, "playlistId", "playlist", -1);
331             if (id >= 0) {
332                 MusicUtils.playPlaylist(this, id, false);
333                 handled = true;
334             }
335         } else if (MediaStore.Audio.Albums.CONTENT_TYPE.equals(mimeType)) {
336             long id = parseIdFromIntent(intent, "albumId", "album", -1);
337             if (id >= 0) {
338                 int position = intent.getIntExtra("position", 0);
339                 MusicUtils.playAlbum(this, id, position, false);
340                 handled = true;
341             }
342         } else if (MediaStore.Audio.Artists.CONTENT_TYPE.equals(mimeType)) {
343             long id = parseIdFromIntent(intent, "artistId", "artist", -1);
344             if (id >= 0) {
345                 int position = intent.getIntExtra("position", 0);
346                 MusicUtils.playArtist(this, id, position, false);
347                 handled = true;
348             }
349         }
350
351         // reset intent as it was handled as a playback request
352         if (handled) {
353             setIntent(new Intent());
354         }
355
356         return handled;
357
358     }
359
360     private long parseIdFromIntent(Intent intent, String longKey,
361                                    String stringKey, long defaultId) {
362         long id = intent.getLongExtra(longKey, -1);
363         if (id < 0) {
364             String idString = intent.getStringExtra(stringKey);
365             if (idString != null) {
366                 try {
367                     id = Long.parseLong(idString);
368                 } catch (NumberFormatException e) {
369                     Log.e(TAG, e.getMessage());
370                 }
371             }
372         }
373         return id;
374     }
375
376 }