2 * Copyright (C) 2014 The CyanogenMod Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.cyanogenmod.eleven.ui.activities;
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;
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;
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";
56 private static final int NEW_PHOTO = 1;
57 public static final int EQUALIZER = 2;
60 private boolean mLoadedBaseFragment = false;
61 private boolean mHasPendingPlaybackRequest = false;
62 private Handler mHandler = new Handler();
65 * Used by the up action to determine how to handle this
67 protected boolean mTopLevelActivity = false;
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);
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));
87 getSupportFragmentManager().beginTransaction()
88 .replace(R.id.activity_base_content, fragment).commit();
90 mLoadedBaseFragment = true;
91 mTopLevelActivity = true;
94 getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
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();
105 if (topFragment instanceof MusicBrowserPhoneFragment) {
106 getActionBar().setIcon(R.drawable.ic_launcher);
107 getActionBar().setHomeButtonEnabled(false);
109 getActionBar().setIcon(R.drawable.ic_action_back_padded);
110 getActionBar().setHomeButtonEnabled(true);
116 // if intent wasn't UI related, process it as a audio playback request
117 if ( !intentHandled ) {
118 handlePlaybackIntent(launchIntent);
123 public Fragment getTopFragment() {
124 return getSupportFragmentManager().findFragmentById(R.id.activity_base_content);
127 public void postRemoveFragment(final Fragment frag) {
128 mHandler.post(new Runnable() {
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();
137 getSupportFragmentManager().beginTransaction().remove(frag).commit();
144 protected void onNewIntent(Intent intent) {
145 super.onNewIntent(intent);
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);
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();
163 if (action.equals(ACTION_VIEW_SMART_PLAYLIST)) {
164 long playlistId = intent.getExtras().getLong(Config.SMART_PLAYLIST_TYPE);
165 switch (Config.SmartPlaylistType.getTypeById(playlistId)) {
167 targetFragment = new LastAddedFragment();
170 targetFragment = new RecentFragment();
173 targetFragment = new TopTracksFragment();
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();
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);
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);
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); }
210 transaction.commit();
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();
225 new Thread(new Runnable() {
228 Bitmap bitmap = ImageFetcher.decodeSampledBitmapFromUri(getContentResolver(),
231 ImageFetcher imageFetcher = ApolloUtils.getImageFetcher(HomeActivity.this);
232 imageFetcher.addBitmapToCache(mKey, bitmap);
234 MusicUtils.refresh();
242 * Starts an activity for result that returns an image from the Gallery.
244 public void selectNewPhoto(String 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);
253 public boolean onOptionsItemSelected(final MenuItem item) {
254 switch (item.getItemId()) {
255 case android.R.id.home:
260 return super.onOptionsItemSelected(item);
264 * Navigates to the top Activity and places the view to the correct page
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();
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) {
278 MusicBrowserPhoneFragment musicFragment = (MusicBrowserPhoneFragment) getTopFragment();
279 musicFragment.setDefaultPageIdx(targetFragmentIndex);
280 showPanel(Panel.Browse);
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);
290 * Immediately clears the backstack
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);
301 public void handlePendingPlaybackRequests() {
302 if (mHasPendingPlaybackRequest) {
303 Intent unhandledIntent = getIntent();
304 handlePlaybackIntent(unhandledIntent);
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
313 private boolean handlePlaybackIntent(Intent intent) {
315 if (intent == null) {
317 } else if ( !MusicUtils.isPlaybackServiceConnected() ) {
318 mHasPendingPlaybackRequest = true;
322 Uri uri = intent.getData();
323 String mimeType = intent.getType();
324 boolean handled = false;
326 if (uri != null && uri.toString().length() > 0) {
327 MusicUtils.playFile(this, uri);
329 } else if (MediaStore.Audio.Playlists.CONTENT_TYPE.equals(mimeType)) {
330 long id = parseIdFromIntent(intent, "playlistId", "playlist", -1);
332 MusicUtils.playPlaylist(this, id, false);
335 } else if (MediaStore.Audio.Albums.CONTENT_TYPE.equals(mimeType)) {
336 long id = parseIdFromIntent(intent, "albumId", "album", -1);
338 int position = intent.getIntExtra("position", 0);
339 MusicUtils.playAlbum(this, id, position, false);
342 } else if (MediaStore.Audio.Artists.CONTENT_TYPE.equals(mimeType)) {
343 long id = parseIdFromIntent(intent, "artistId", "artist", -1);
345 int position = intent.getIntExtra("position", 0);
346 MusicUtils.playArtist(this, id, position, false);
351 // reset intent as it was handled as a playback request
353 setIntent(new Intent());
360 private long parseIdFromIntent(Intent intent, String longKey,
361 String stringKey, long defaultId) {
362 long id = intent.getLongExtra(longKey, -1);
364 String idString = intent.getStringExtra(stringKey);
365 if (idString != null) {
367 id = Long.parseLong(idString);
368 } catch (NumberFormatException e) {
369 Log.e(TAG, e.getMessage());