2 * Copyright (C) 2012 Andrew Neal
3 * Copyright (C) 2014 The CyanogenMod Project
4 * Licensed under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with the
6 * License. You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
8 * or agreed to in writing, software distributed under the License is
9 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
10 * KIND, either express or implied. See the License for the specific language
11 * governing permissions and limitations under the License.
14 package com.cyanogenmod.eleven.ui.activities;
16 import static com.cyanogenmod.eleven.utils.MusicUtils.mService;
18 import android.app.ActionBar;
19 import android.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.ServiceConnection;
25 import android.graphics.drawable.ColorDrawable;
26 import android.graphics.drawable.Drawable;
27 import android.media.AudioManager;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.support.v4.app.FragmentActivity;
31 import android.util.TypedValue;
32 import android.view.Menu;
33 import android.view.MenuItem;
34 import android.view.View;
35 import android.widget.ImageView;
36 import android.widget.TextView;
37 import android.widget.Toast;
38 import android.widget.Toolbar;
40 import com.cyanogenmod.eleven.IElevenService;
41 import com.cyanogenmod.eleven.MusicPlaybackService;
42 import com.cyanogenmod.eleven.MusicStateListener;
43 import com.cyanogenmod.eleven.R;
44 import com.cyanogenmod.eleven.cache.ICacheListener;
45 import com.cyanogenmod.eleven.cache.ImageFetcher;
46 import com.cyanogenmod.eleven.utils.ApolloUtils;
47 import com.cyanogenmod.eleven.utils.Lists;
48 import com.cyanogenmod.eleven.utils.MusicUtils;
49 import com.cyanogenmod.eleven.utils.MusicUtils.ServiceToken;
50 import com.cyanogenmod.eleven.utils.NavUtils;
51 import com.cyanogenmod.eleven.widgets.PlayPauseProgressButton;
53 import java.lang.ref.WeakReference;
54 import java.util.ArrayList;
57 * A base {@link FragmentActivity} used to update the bottom bar and
58 * bind to Apollo's service.
60 * {@link SlidingPanelActivity} extends from this skeleton.
62 * @author Andrew Neal (andrewdneal@gmail.com)
64 public abstract class BaseActivity extends FragmentActivity implements ServiceConnection,
65 MusicStateListener, ICacheListener {
68 * Playstate and meta change listener
70 private final ArrayList<MusicStateListener> mMusicStateListener = Lists.newArrayList();
72 private Toolbar mToolBar;
74 private int mActionBarHeight;
79 private ServiceToken mToken;
82 * Play pause progress button
84 private PlayPauseProgressButton mPlayPauseProgressButton;
89 private TextView mTrackName;
94 private TextView mArtistName;
99 private ImageView mAlbumArt;
104 private PlaybackStatus mPlaybackStatus;
106 private Drawable mActionBarBackground;
112 protected void onCreate(final Bundle savedInstanceState) {
113 super.onCreate(savedInstanceState);
116 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
118 // Control the media volume
119 setVolumeControlStream(AudioManager.STREAM_MUSIC);
121 // Bind Apollo's service
122 mToken = MusicUtils.bindToService(this, this);
124 // Initialize the broadcast receiver
125 mPlaybackStatus = new PlaybackStatus(this);
127 // Calculate ActionBar height
128 TypedValue value = new TypedValue();
129 if (getTheme().resolveAttribute(android.R.attr.actionBarSize, value, true))
131 mActionBarHeight = TypedValue.complexToDimensionPixelSize(value.data,
132 getResources().getDisplayMetrics());
136 setContentView(setContentView());
138 mToolBar = (Toolbar) findViewById(R.id.toolbar);
139 setActionBar(mToolBar);
141 setActionBarTitle(getString(R.string.app_name));
143 // set the background on the root view
144 getWindow().getDecorView().getRootView().setBackgroundColor(
145 getResources().getColor(R.color.background_color));
146 // Initialze the bottom action bar
147 initBottomActionBar();
149 // listen to changes to the cache status
150 ImageFetcher.getInstance(this).addCacheListener(this);
157 public void onServiceConnected(final ComponentName name, final IBinder service) {
158 mService = IElevenService.Stub.asInterface(service);
159 // Set the playback drawables
160 updatePlaybackControls();
163 // if there were any pending intents while the service was started
164 handlePendingPlaybackRequests();
171 public void onServiceDisconnected(final ComponentName name) {
179 public boolean onCreateOptionsMenu(final Menu menu) {
181 getMenuInflater().inflate(R.menu.search_btn, menu);
183 getMenuInflater().inflate(R.menu.activity_base, menu);
185 return super.onCreateOptionsMenu(menu);
192 public boolean onOptionsItemSelected(final MenuItem item) {
193 switch (item.getItemId()) {
194 case R.id.menu_settings:
196 NavUtils.openSettings(this);
199 case R.id.menu_search:
200 NavUtils.openSearch(BaseActivity.this, "");
206 return super.onOptionsItemSelected(item);
213 protected void onResume() {
215 // Set the playback drawables
216 updatePlaybackControls();
225 protected void onStart() {
227 final IntentFilter filter = new IntentFilter();
228 // Play and pause changes
229 filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED);
231 filter.addAction(MusicPlaybackService.META_CHANGED);
232 // Update a list, probably the playlist fragment's
233 filter.addAction(MusicPlaybackService.REFRESH);
234 // If a playlist has changed, notify us
235 filter.addAction(MusicPlaybackService.PLAYLIST_CHANGED);
236 // If there is an error playing a track
237 filter.addAction(MusicPlaybackService.TRACK_ERROR);
238 registerReceiver(mPlaybackStatus, filter);
240 mPlayPauseProgressButton.resume();
247 protected void onStop() {
250 mPlayPauseProgressButton.pause();
257 protected void onDestroy() {
259 // Unbind from the service
260 if (mToken != null) {
261 MusicUtils.unbindFromService(mToken);
265 // Unregister the receiver
267 unregisterReceiver(mPlaybackStatus);
268 } catch (final Throwable e) {
272 // Remove any music status listeners
273 mMusicStateListener.clear();
275 // remove cache listeners
276 ImageFetcher.getInstance(this).removeCacheListener(this);
279 public void setupActionBar(int resId) {
280 setupActionBar(getString(resId));
283 public void setupActionBar(String title) {
284 setActionBarTitle(title);
286 if (mActionBarBackground == null) {
287 final int actionBarColor = getResources().getColor(R.color.header_action_bar_color);
288 mActionBarBackground = new ColorDrawable(actionBarColor);
289 mToolBar.setBackgroundDrawable(mActionBarBackground);
293 public void setActionBarTitle(String title) {
294 ActionBar actionBar = getActionBar();
295 actionBar.setTitle(title);
298 public void setActionBarAlpha(int alpha) {
299 mActionBarBackground.setAlpha(alpha);
302 public void setActionBarElevation(boolean isElevated) {
303 float targetElevation = isElevated
304 ? getResources().getDimension(R.dimen.action_bar_elevation) : 0;
305 mToolBar.setElevation(targetElevation);
308 public void setFragmentPadding(boolean enablePadding) {
309 final int height = enablePadding ? mActionBarHeight : 0;
310 findViewById(R.id.activity_base_content).setPadding(0, height, 0, 0);
314 * Initializes the items in the bottom action bar.
316 protected void initBottomActionBar() {
317 // Play and pause button
318 mPlayPauseProgressButton = (PlayPauseProgressButton)findViewById(R.id.playPauseProgressButton);
319 mPlayPauseProgressButton.enableAndShow();
322 mTrackName = (TextView)findViewById(R.id.bottom_action_bar_line_one);
324 mArtistName = (TextView)findViewById(R.id.bottom_action_bar_line_two);
326 mAlbumArt = (ImageView)findViewById(R.id.bottom_action_bar_album_art);
327 // Open to the currently playing album profile
328 mAlbumArt.setOnClickListener(mOpenCurrentAlbumProfile);
331 protected void clearMetaInfo() {
332 mAlbumArt.setImageResource(R.drawable.default_artwork);
336 * Sets the track name, album name, and album art.
338 private void updateBottomActionBarInfo() {
339 // Set the track name
340 mTrackName.setText(MusicUtils.getTrackName());
341 // Set the artist name
342 mArtistName.setText(MusicUtils.getArtistName());
344 ApolloUtils.getImageFetcher(this).loadCurrentArtwork(mAlbumArt);
348 * Sets the correct drawable states for the playback controls.
350 private void updatePlaybackControls() {
351 // Set the play and pause image
352 mPlayPauseProgressButton.getPlayPauseButton().updateState();
356 * Opens the album profile of the currently playing album
358 private final View.OnClickListener mOpenCurrentAlbumProfile = new View.OnClickListener() {
364 public void onClick(final View v) {
365 if (MusicUtils.getCurrentAudioId() != -1) {
366 NavUtils.openAlbumProfile(BaseActivity.this, MusicUtils.getAlbumName(),
367 MusicUtils.getArtistName(), MusicUtils.getCurrentAlbumId());
369 MusicUtils.shuffleAll(BaseActivity.this);
375 * Used to monitor the state of playback
377 private final static class PlaybackStatus extends BroadcastReceiver {
379 private final WeakReference<BaseActivity> mReference;
382 * Constructor of <code>PlaybackStatus</code>
384 public PlaybackStatus(final BaseActivity activity) {
385 mReference = new WeakReference<BaseActivity>(activity);
392 public void onReceive(final Context context, final Intent intent) {
393 final String action = intent.getAction();
394 BaseActivity baseActivity = mReference.get();
395 if (baseActivity != null) {
396 if (action.equals(MusicPlaybackService.META_CHANGED)) {
397 baseActivity.onMetaChanged();
398 } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) {
399 // Set the play and pause image
400 baseActivity.mPlayPauseProgressButton.getPlayPauseButton().updateState();
401 } else if (action.equals(MusicPlaybackService.REFRESH)) {
402 baseActivity.restartLoader();
403 } else if (action.equals(MusicPlaybackService.PLAYLIST_CHANGED)) {
404 baseActivity.onPlaylistChanged();
405 } else if (action.equals(MusicPlaybackService.TRACK_ERROR)) {
406 final String errorMsg = context.getString(R.string.error_playing_track,
407 intent.getStringExtra(MusicPlaybackService.TrackErrorExtra.TRACK_NAME));
408 Toast.makeText(baseActivity, errorMsg, Toast.LENGTH_SHORT).show();
415 public void onMetaChanged() {
416 // update action bar info
417 updateBottomActionBarInfo();
419 // Let the listener know to the meta chnaged
420 for (final MusicStateListener listener : mMusicStateListener) {
421 if (listener != null) {
422 listener.onMetaChanged();
428 public void restartLoader() {
429 // Let the listener know to update a list
430 for (final MusicStateListener listener : mMusicStateListener) {
431 if (listener != null) {
432 listener.restartLoader();
438 public void onPlaylistChanged() {
439 // Let the listener know to update a list
440 for (final MusicStateListener listener : mMusicStateListener) {
441 if (listener != null) {
442 listener.onPlaylistChanged();
448 * @param status The {@link MusicStateListener} to use
450 public void setMusicStateListenerListener(final MusicStateListener status) {
451 if (status == this) {
452 throw new UnsupportedOperationException("Override the method, don't add a listener");
455 if (status != null) {
456 mMusicStateListener.add(status);
461 * @param status The {@link MusicStateListener} to use
463 public void removeMusicStateListenerListener(final MusicStateListener status) {
464 if (status != null) {
465 mMusicStateListener.remove(status);
470 public void onCacheUnpaused() {
472 ApolloUtils.getImageFetcher(this).loadCurrentArtwork(mAlbumArt);
476 * @return The resource ID to be inflated.
478 public abstract int setContentView();
481 * handle pending playback requests
483 public abstract void handlePendingPlaybackRequests();