OSDN Git Service

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