summaryrefslogtreecommitdiffstats
path: root/src/org/lineageos/eleven/ui/activities/HomeActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/lineageos/eleven/ui/activities/HomeActivity.java')
-rw-r--r--src/org/lineageos/eleven/ui/activities/HomeActivity.java543
1 files changed, 543 insertions, 0 deletions
diff --git a/src/org/lineageos/eleven/ui/activities/HomeActivity.java b/src/org/lineageos/eleven/ui/activities/HomeActivity.java
new file mode 100644
index 0000000..6499a20
--- /dev/null
+++ b/src/org/lineageos/eleven/ui/activities/HomeActivity.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.cyanogenmod.eleven.ui.activities;
+
+import android.Manifest;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.Window;
+
+import com.cyanogenmod.eleven.Config;
+import com.cyanogenmod.eleven.R;
+import com.cyanogenmod.eleven.cache.ImageFetcher;
+import com.cyanogenmod.eleven.ui.fragments.AlbumDetailFragment;
+import com.cyanogenmod.eleven.ui.fragments.ArtistDetailFragment;
+import com.cyanogenmod.eleven.ui.fragments.AudioPlayerFragment;
+import com.cyanogenmod.eleven.ui.fragments.IChildFragment;
+import com.cyanogenmod.eleven.ui.fragments.ISetupActionBar;
+import com.cyanogenmod.eleven.ui.fragments.PlaylistDetailFragment;
+import com.cyanogenmod.eleven.ui.fragments.RecentFragment;
+import com.cyanogenmod.eleven.ui.fragments.phone.MusicBrowserPhoneFragment;
+import com.cyanogenmod.eleven.ui.fragments.profile.LastAddedFragment;
+import com.cyanogenmod.eleven.ui.fragments.profile.TopTracksFragment;
+import com.cyanogenmod.eleven.utils.ApolloUtils;
+import com.cyanogenmod.eleven.utils.BitmapWithColors;
+import com.cyanogenmod.eleven.utils.MusicUtils;
+import com.cyanogenmod.eleven.utils.NavUtils;
+
+import java.util.ArrayList;
+
+public class HomeActivity extends SlidingPanelActivity implements
+ FragmentManager.OnBackStackChangedListener {
+ private static final String TAG = "HomeActivity";
+ private static final String ACTION_PREFIX = HomeActivity.class.getName();
+ public static final String ACTION_VIEW_ARTIST_DETAILS = ACTION_PREFIX + ".view.ArtistDetails";
+ public static final String ACTION_VIEW_ALBUM_DETAILS = ACTION_PREFIX + ".view.AlbumDetails";
+ public static final String ACTION_VIEW_PLAYLIST_DETAILS = ACTION_PREFIX + ".view.PlaylistDetails";
+ public static final String ACTION_VIEW_SMART_PLAYLIST = ACTION_PREFIX + ".view.SmartPlaylist";
+ public static final String EXTRA_BROWSE_PAGE_IDX = "BrowsePageIndex";
+
+ private static final String STATE_KEY_BASE_FRAGMENT = "BaseFragment";
+
+ private static final int NEW_PHOTO = 1;
+ public static final int EQUALIZER = 2;
+
+ private static final int PERMISSION_REQUEST_STORAGE = 1;
+ private Bundle mSavedInstanceState;
+
+ private String mKey;
+ private boolean mLoadedBaseFragment = false;
+ private boolean mHasPendingPlaybackRequest = false;
+ private Handler mHandler = new Handler();
+ private boolean mBrowsePanelActive = true;
+
+ /**
+ * Used by the up action to determine how to handle this
+ */
+ protected boolean mTopLevelActivity = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSavedInstanceState = savedInstanceState;
+
+ if (!needRequestStoragePermission()) {
+ init();
+ }
+ }
+
+ private void init() {
+ // if we've been launched by an intent, parse it
+ Intent launchIntent = getIntent();
+ boolean intentHandled = false;
+ if (launchIntent != null) {
+ intentHandled = parseIntentForFragment(launchIntent);
+ }
+
+ // if the intent didn't cause us to load a fragment, load the music browse one
+ if (mSavedInstanceState == null && !mLoadedBaseFragment) {
+ final MusicBrowserPhoneFragment fragment = new MusicBrowserPhoneFragment();
+ if (launchIntent != null) {
+ fragment.setDefaultPageIdx(launchIntent.getIntExtra(EXTRA_BROWSE_PAGE_IDX,
+ MusicBrowserPhoneFragment.INVALID_PAGE_INDEX));
+ }
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.activity_base_content, fragment)
+ .commit();
+
+ mLoadedBaseFragment = true;
+ mTopLevelActivity = true;
+ }
+
+ getSupportFragmentManager().addOnBackStackChangedListener(this);
+
+
+ // if we are resuming from a saved instance state
+ if (mSavedInstanceState != null) {
+ // track which fragments are loaded and if this is the top level activity
+ mTopLevelActivity = mSavedInstanceState.getBoolean(STATE_KEY_BASE_FRAGMENT);
+ mLoadedBaseFragment = mTopLevelActivity;
+
+ // update the action bar based on the top most fragment
+ onBackStackChanged();
+
+ // figure which panel we are on and update the status bar
+ mBrowsePanelActive = (getCurrentPanel() == Panel.Browse);
+ updateStatusBarColor();
+ }
+
+ // if intent wasn't UI related, process it as a audio playback request
+ if (!intentHandled) {
+ handlePlaybackIntent(launchIntent);
+ }
+
+ mSavedInstanceState = null;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(STATE_KEY_BASE_FRAGMENT, mTopLevelActivity);
+ }
+
+ public Fragment getTopFragment() {
+ return getSupportFragmentManager().findFragmentById(R.id.activity_base_content);
+ }
+
+ public void postRemoveFragment(final Fragment frag) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // removing the fragment doesn't cause the backstack event to be triggered even if
+ // it is the top fragment, so if it is the top fragment, we will just manually
+ // call pop back stack
+ if (frag == getTopFragment()) {
+ getSupportFragmentManager().popBackStack();
+ } else {
+ getSupportFragmentManager().beginTransaction().remove(frag).commit();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ // parse intent to ascertain whether the intent is inter UI communication
+ boolean intentHandled = parseIntentForFragment(intent);
+ // since this activity is marked 'singleTop' (launch mode), an existing activity instance
+ // could be sent media play requests
+ if ( !intentHandled) {
+ handlePlaybackIntent(intent);
+ }
+ }
+
+ @Override
+ public void onMetaChanged() {
+ super.onMetaChanged();
+ updateStatusBarColor();
+ }
+
+ @Override
+ protected void onSlide(float slideOffset) {
+ boolean isInBrowser = getCurrentPanel() == Panel.Browse && slideOffset < 0.7f;
+ if (isInBrowser != mBrowsePanelActive) {
+ mBrowsePanelActive = isInBrowser;
+ updateStatusBarColor();
+ }
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+
+ if (getCurrentPanel() == Panel.MusicPlayer) {
+ getAudioPlayerFragment().setVisualizerVisible(hasFocus);
+ }
+ }
+
+ private void updateStatusBarColor() {
+ if (mBrowsePanelActive || MusicUtils.getCurrentAlbumId() < 0) {
+ updateStatusBarColor(Color.TRANSPARENT);
+ } else {
+ new AsyncTask<Void, Void, BitmapWithColors>() {
+ @Override
+ protected BitmapWithColors doInBackground(Void... params) {
+ ImageFetcher imageFetcher = ImageFetcher.getInstance(HomeActivity.this);
+ return imageFetcher.getArtwork(
+ MusicUtils.getAlbumName(), MusicUtils.getCurrentAlbumId(),
+ MusicUtils.getArtistName(), true);
+ }
+ @Override
+ protected void onPostExecute(BitmapWithColors bmc) {
+ updateVisualizerColor(bmc != null
+ ? bmc.getContrastingColor() : Color.TRANSPARENT);
+ updateStatusBarColor(bmc != null
+ ? bmc.getVibrantDarkColor() : Color.TRANSPARENT);
+ }
+ }.execute();
+ }
+ }
+
+ private void updateVisualizerColor(int color) {
+ if (color == Color.TRANSPARENT) {
+ color = getResources().getColor(R.color.visualizer_fill_color);
+ }
+
+ // check for null since updatestatusBarColor is a async task
+ AudioPlayerFragment fragment = getAudioPlayerFragment();
+ if (fragment != null) {
+ fragment.setVisualizerColor(color);
+ }
+ }
+
+ private void updateStatusBarColor(int color) {
+ if (color == Color.TRANSPARENT) {
+ color = getResources().getColor(R.color.primary_dark);
+ }
+ final Window window = getWindow();
+ ObjectAnimator animator = ObjectAnimator.ofInt(window,
+ "statusBarColor", window.getStatusBarColor(), color);
+ animator.setEvaluator(new ArgbEvaluator());
+ animator.setDuration(300);
+ animator.start();
+ }
+
+ private boolean parseIntentForFragment(Intent intent) {
+ boolean handled = false;
+ if (intent.getAction() != null) {
+ final String action = intent.getAction();
+ Fragment targetFragment = null;
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ if (action.equals(ACTION_VIEW_SMART_PLAYLIST)) {
+ long playlistId = intent.getExtras().getLong(Config.SMART_PLAYLIST_TYPE);
+ switch (Config.SmartPlaylistType.getTypeById(playlistId)) {
+ case LastAdded:
+ targetFragment = new LastAddedFragment();
+ break;
+ case RecentlyPlayed:
+ targetFragment = new RecentFragment();
+ break;
+ case TopTracks:
+ targetFragment = new TopTracksFragment();
+ break;
+ }
+ } else if (action.equals(ACTION_VIEW_PLAYLIST_DETAILS)) {
+ targetFragment = new PlaylistDetailFragment();
+ } else if (action.equals(ACTION_VIEW_ALBUM_DETAILS)) {
+ targetFragment = new AlbumDetailFragment();
+ } else if (action.equals(ACTION_VIEW_ARTIST_DETAILS)) {
+ targetFragment = new ArtistDetailFragment();
+ }
+
+ if (targetFragment != null) {
+ targetFragment.setArguments(intent.getExtras());
+ transaction.setCustomAnimations(0, 0, 0, R.anim.fade_out);
+ // If we ever come back to this because of memory concerns because
+ // none of the fragments are being removed from memory, we can fix this
+ // by using "replace" instead of "add". The caveat is that the performance of
+ // returning to previous fragments is a bit more sluggish because the fragment
+ // view needs to be recreated. If we do remove that, we can remove the back stack
+ // change listener code above
+ transaction.add(R.id.activity_base_content, targetFragment);
+ if (mLoadedBaseFragment) {
+ transaction.addToBackStack(null);
+ showPanel(Panel.Browse);
+ } else {
+ // else mark the fragment as loaded so we don't load the music browse fragment.
+ // this happens when they launch search which is its own activity and then
+ // browse through that back to home activity
+ mLoadedBaseFragment = true;
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ // the current top fragment is about to be hidden by what we are replacing
+ // it with -- so tell that fragment not to make its action bar menu items visible
+ Fragment oldTop = getTopFragment();
+ if (oldTop != null) {
+ oldTop.setMenuVisibility(false);
+ }
+
+ transaction.commit();
+ handled = true;
+ }
+ }
+ return handled;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == NEW_PHOTO && !TextUtils.isEmpty(mKey)) {
+ if (resultCode == RESULT_OK) {
+ MusicUtils.removeFromCache(this, mKey);
+ final Uri selectedImage = data.getData();
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Bitmap bitmap = ImageFetcher.decodeSampledBitmapFromUri(getContentResolver(),
+ selectedImage);
+
+ ImageFetcher imageFetcher = ApolloUtils.getImageFetcher(HomeActivity.this);
+ imageFetcher.addBitmapToCache(mKey, bitmap);
+
+ MusicUtils.refresh();
+ }
+ }).start();
+ }
+ }
+ }
+
+ /**
+ * Starts an activity for result that returns an image from the Gallery.
+ */
+ public void selectNewPhoto(String key) {
+ mKey = key;
+ // Now open the gallery
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+ intent.setType("image/*");
+ startActivityForResult(intent, NEW_PHOTO);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ navigateToTop();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Navigates to the top Activity and places the view to the correct page
+ */
+ protected void navigateToTop() {
+ final Fragment topFragment = getTopFragment();
+ int targetFragmentIndex = MusicBrowserPhoneFragment.INVALID_PAGE_INDEX;
+ if (topFragment instanceof IChildFragment) {
+ targetFragmentIndex = ((IChildFragment)topFragment).getMusicFragmentParent().ordinal();
+ }
+
+ // If we are the top activity in the stack (as determined by the activity that has loaded
+ // the MusicBrowserPhoneFragment) then clear the back stack and move the browse fragment
+ // to the appropriate page as per Android up standards
+ if (mTopLevelActivity) {
+ clearBackStack();
+ MusicBrowserPhoneFragment musicFragment = (MusicBrowserPhoneFragment) getTopFragment();
+ musicFragment.setDefaultPageIdx(targetFragmentIndex);
+ showPanel(Panel.Browse);
+ } else {
+ // I've tried all other combinations with parent activities, support.NavUtils and
+ // there is no easy way to achieve what we want that I'm aware of, so clear everything
+ // and jump to the right page
+ NavUtils.goHome(this, targetFragmentIndex);
+ }
+ }
+
+ /**
+ * Immediately clears the backstack
+ */
+ protected void clearBackStack() {
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ if (fragmentManager.getBackStackEntryCount() > 0) {
+ final int id = fragmentManager.getBackStackEntryAt(0).getId();
+ fragmentManager.popBackStackImmediate(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ }
+ }
+
+ @Override
+ public void handlePendingPlaybackRequests() {
+ if (mHasPendingPlaybackRequest) {
+ Intent unhandledIntent = getIntent();
+ handlePlaybackIntent(unhandledIntent);
+ }
+ }
+
+ /**
+ * Checks whether the passed intent contains a playback request,
+ * and starts playback if that's the case
+ * @return true if the intent was consumed
+ */
+ private boolean handlePlaybackIntent(Intent intent) {
+
+ if (intent == null) {
+ return false;
+ } else if ( !MusicUtils.isPlaybackServiceConnected() ) {
+ mHasPendingPlaybackRequest = true;
+ return false;
+ }
+
+ String mimeType = intent.getType();
+ boolean handled = false;
+
+ if (MediaStore.Audio.Playlists.CONTENT_TYPE.equals(mimeType)) {
+ long id = parseIdFromIntent(intent, "playlistId", "playlist", -1);
+ if (id >= 0) {
+ MusicUtils.playPlaylist(this, id, false);
+ handled = true;
+ }
+ } else if (MediaStore.Audio.Albums.CONTENT_TYPE.equals(mimeType)) {
+ long id = parseIdFromIntent(intent, "albumId", "album", -1);
+ if (id >= 0) {
+ int position = intent.getIntExtra("position", 0);
+ MusicUtils.playAlbum(this, id, position, false);
+ handled = true;
+ }
+ } else if (MediaStore.Audio.Artists.CONTENT_TYPE.equals(mimeType)) {
+ long id = parseIdFromIntent(intent, "artistId", "artist", -1);
+ if (id >= 0) {
+ int position = intent.getIntExtra("position", 0);
+ MusicUtils.playArtist(this, id, position, false);
+ handled = true;
+ }
+ }
+
+ // reset intent as it was handled as a playback request
+ if (handled) {
+ setIntent(new Intent());
+ }
+
+ return handled;
+
+ }
+
+ private long parseIdFromIntent(Intent intent, String longKey,
+ String stringKey, long defaultId) {
+ long id = intent.getLongExtra(longKey, -1);
+ if (id < 0) {
+ String idString = intent.getStringExtra(stringKey);
+ if (idString != null) {
+ try {
+ id = Long.parseLong(idString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+ }
+ return id;
+ }
+
+ @Override
+ public void onBackStackChanged() {
+ Fragment topFragment = getTopFragment();
+ if (topFragment != null) {
+ // the fragment that has come back to the top should now have its menu items
+ // added to the action bar -- so tell it to make it menu items visible
+ topFragment.setMenuVisibility(true);
+ ISetupActionBar setupActionBar = (ISetupActionBar) topFragment;
+ setupActionBar.setupActionBar();
+
+ getActionBar().setDisplayHomeAsUpEnabled(
+ !(topFragment instanceof MusicBrowserPhoneFragment));
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String permissions[],
+ int[] grantResults) {
+ switch (requestCode) {
+ case PERMISSION_REQUEST_STORAGE: {
+ if (checkPermissionGrantResults(grantResults)) {
+ init();
+ } else {
+ finish();
+ }
+ }
+ }
+ }
+
+ private boolean needRequestStoragePermission() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false;
+
+ boolean needRequest = false;
+ String[] permissions = {
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ };
+ ArrayList<String> permissionList = new ArrayList<String>();
+ for (String permission : permissions) {
+ if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ permissionList.add(permission);
+ needRequest = true;
+ }
+ }
+
+ if (needRequest) {
+ int count = permissionList.size();
+ if (count > 0) {
+ String[] permissionArray = new String[count];
+ for (int i = 0; i < count; i++) {
+ permissionArray[i] = permissionList.get(i);
+ }
+
+ requestPermissions(permissionArray, PERMISSION_REQUEST_STORAGE);
+ }
+ }
+
+ return needRequest;
+ }
+
+ private boolean checkPermissionGrantResults(int[] grantResults) {
+ for (int result : grantResults) {
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}