• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source 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.android.car.media;
17 
18 import android.content.ComponentName;
19 import android.content.Intent;
20 import android.graphics.Bitmap;
21 import android.os.Bundle;
22 import android.provider.MediaStore;
23 import android.support.v4.app.Fragment;
24 import android.util.Log;
25 import android.util.Pair;
26 import android.util.TypedValue;
27 import android.view.View;
28 
29 import com.android.car.app.CarDrawerActivity;
30 import com.android.car.app.CarDrawerAdapter;
31 import com.android.car.media.drawer.MediaDrawerController;
32 
33 /**
34  * This activity controls the UI of media. It also updates the connection status for the media app
35  * by broadcast. Drawer menu is controlled by {@link MediaDrawerController}.
36  */
37 public class MediaActivity extends CarDrawerActivity {
38     private static final String ACTION_MEDIA_APP_STATE_CHANGE
39             = "android.intent.action.MEDIA_APP_STATE_CHANGE";
40     private static final String EXTRA_MEDIA_APP_FOREGROUND
41             = "android.intent.action.MEDIA_APP_STATE";
42 
43     private static final String TAG = "MediaActivity";
44 
45     /**
46      * Whether or not {@link #onResume()} has been called.
47      */
48     private static boolean sIsRunning = false;
49 
50     /**
51      * Whether or not {@link #onStart()} has been called.
52      */
53     private boolean mIsStarted;
54 
55     /**
56      * {@code true} if there was a request to change the content fragment of this Activity when
57      * it is not started. Then, when onStart() is called, the content fragment will be added.
58      *
59      * <p>This prevents a bug where the content fragment is added when the app is not running,
60      * causing a StateLossException.
61      */
62     private boolean mContentFragmentChangeQueued;
63 
64     private MediaDrawerController mDrawerController;
65     private View mScrimView;
66     private CrossfadeImageView mAlbumArtView;
67     private MediaPlaybackFragment mMediaPlaybackFragment;
68 
69     @Override
onStart()70     protected void onStart() {
71         super.onStart();
72         Intent i = new Intent(ACTION_MEDIA_APP_STATE_CHANGE);
73         i.putExtra(EXTRA_MEDIA_APP_FOREGROUND, true);
74         sendBroadcast(i);
75 
76         mIsStarted = true;
77 
78         if (mContentFragmentChangeQueued) {
79             if (Log.isLoggable(TAG, Log.DEBUG)) {
80                 Log.d(TAG, "Content fragment queued. Attaching now.");
81             }
82             attachContentFragment();
83             mContentFragmentChangeQueued = false;
84         }
85     }
86 
87     @Override
onStop()88     protected void onStop() {
89         super.onStop();
90         Intent i = new Intent(ACTION_MEDIA_APP_STATE_CHANGE);
91         i.putExtra(EXTRA_MEDIA_APP_FOREGROUND, false);
92         sendBroadcast(i);
93 
94         mIsStarted = false;
95     }
96 
97     @Override
onCreate(Bundle savedInstanceState)98     protected void onCreate(Bundle savedInstanceState) {
99         mDrawerController = new MediaDrawerController(this);
100         super.onCreate(savedInstanceState);
101 
102         setMainContent(R.layout.media_activity);
103         mScrimView = findViewById(R.id.scrim);
104         mAlbumArtView = (CrossfadeImageView) findViewById(R.id.album_art);
105         setBackgroundColor(getColor(R.color.music_default_artwork));
106         MediaManager.getInstance(this).addListener(mListener);
107     }
108 
109     @Override
onDestroy()110     public void onDestroy() {
111         super.onDestroy();
112         // Send the broadcast to let the current connected media app know it is disconnected now.
113         sendMediaConnectionStatusBroadcast(
114                 MediaManager.getInstance(this).getCurrentComponent(),
115                 MediaConstants.MEDIA_DISCONNECTED);
116         mDrawerController.cleanup();
117         MediaManager.getInstance(this).removeListener(mListener);
118     }
119 
120     @Override
getRootAdapter()121     protected CarDrawerAdapter getRootAdapter() {
122         return mDrawerController.getRootAdapter();
123     }
124 
125     @Override
onResumeFragments()126     public void onResumeFragments() {
127         if (Log.isLoggable(TAG, Log.DEBUG)) {
128             Log.d(TAG, "onResumeFragments");
129         }
130 
131         super.onResumeFragments();
132         handleIntent(getIntent());
133         sIsRunning = true;
134     }
135 
136     @Override
onPause()137     public void onPause() {
138         if (Log.isLoggable(TAG, Log.DEBUG)) {
139             Log.d(TAG, "onPause");
140         }
141 
142         super.onPause();
143         sIsRunning = false;
144     }
145 
146     @Override
onNewIntent(Intent intent)147     protected void onNewIntent(Intent intent) {
148         super.onNewIntent(intent);
149         if (Log.isLoggable(TAG, Log.VERBOSE)) {
150             Log.v(TAG, "onNewIntent(); intent: " + (intent == null ? "<< NULL >>" : intent));
151         }
152 
153         setIntent(intent);
154         closeDrawer();
155     }
156 
157     @Override
onBackPressed()158     public void onBackPressed() {
159         if (mMediaPlaybackFragment.isOverflowMenuVisible()) {
160             mMediaPlaybackFragment.closeOverflowMenu();
161         }
162         super.onBackPressed();
163     }
164 
165     /**
166      * Darken scrim when new messages are displayed in {@link MediaPlaybackFragment}.
167      */
darkenScrim(boolean dark)168     public void darkenScrim(boolean dark) {
169         if (!sIsRunning) {
170             return;
171         }
172 
173         if (dark) {
174             mScrimView.animate().alpha(0.9f)
175                     .setDuration(getResources().getInteger(R.integer.media_scrim_duration_ms));
176         } else {
177             mScrimView.animate().alpha(0.6f)
178                     .setDuration(getResources().getInteger(R.integer.media_scrim_duration_ms));
179         }
180     }
181 
182     /**
183      * Don't show scrim when there is no content, else show it.
184      */
setScrimVisibility(boolean visible)185     public void setScrimVisibility(boolean visible) {
186         if (visible) {
187             TypedValue scrimAlpha = new TypedValue();
188             getResources().getValue(R.dimen.media_scrim_alpha, scrimAlpha, true);
189             mScrimView.animate().alpha(scrimAlpha.getFloat())
190                     .setDuration(getResources().getInteger(R.integer.media_scrim_duration_ms));
191         } else {
192             mScrimView.animate().alpha(0.0f)
193                     .setDuration(getResources().getInteger(R.integer.media_scrim_duration_ms));
194 
195         }
196     }
197 
198     /**
199      * Return the dimension of the background album art in the form of <width, height>.
200      */
getAlbumArtSize()201     public Pair<Integer, Integer> getAlbumArtSize() {
202         if (mAlbumArtView != null) {
203             return Pair.create(mAlbumArtView.getWidth(), mAlbumArtView.getHeight());
204         }
205         return null;
206     }
207 
setBackgroundBitmap(Bitmap bitmap, boolean showAnimation)208     void setBackgroundBitmap(Bitmap bitmap, boolean showAnimation) {
209         mAlbumArtView.setImageBitmap(bitmap, showAnimation);
210     }
211 
setBackgroundColor(int color)212     void setBackgroundColor(int color) {
213         mAlbumArtView.setBackgroundColor(color);
214     }
215 
handleIntent(Intent intent)216     private void handleIntent(Intent intent) {
217         Bundle extras = null;
218         if (intent != null) {
219             extras = intent.getExtras();
220         }
221 
222         // If the intent has a media component name set, connect to it directly
223         if (extras != null && extras.containsKey(MediaManager.KEY_MEDIA_PACKAGE) &&
224                 extras.containsKey(MediaManager.KEY_MEDIA_CLASS)) {
225             if (Log.isLoggable(TAG, Log.DEBUG)) {
226                 Log.d(TAG, "Media component in intent.");
227             }
228 
229             ComponentName component = new ComponentName(
230                     intent.getStringExtra(MediaManager.KEY_MEDIA_PACKAGE),
231                     intent.getStringExtra(MediaManager.KEY_MEDIA_CLASS)
232             );
233             MediaManager.getInstance(this).setMediaClientComponent(component);
234         } else {
235             if (Log.isLoggable(TAG, Log.DEBUG)) {
236                 Log.d(TAG, "Launching most recent / default component.");
237             }
238 
239             // Set it to the default GPM component.
240             MediaManager.getInstance(this).connectToMostRecentMediaComponent(
241                     new CarClientServiceAdapter(getPackageManager()));
242         }
243 
244         if (isSearchIntent(intent)) {
245             MediaManager.getInstance(this).processSearchIntent(intent);
246             setIntent(null);
247         }
248     }
249 
isSearchIntent(Intent intent)250     private boolean isSearchIntent(Intent intent) {
251         return (intent != null && intent.getAction() != null &&
252                 intent.getAction().equals(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH));
253     }
254 
sendMediaConnectionStatusBroadcast( ComponentName componentName, String connectionStatus)255     private void sendMediaConnectionStatusBroadcast(
256             ComponentName componentName, String connectionStatus) {
257         // It will be no current component if no media app is chosen before.
258         if (componentName == null) {
259             return;
260         }
261 
262         Intent intent = new Intent(MediaConstants.ACTION_MEDIA_STATUS);
263         intent.setPackage(componentName.getPackageName());
264         intent.putExtra(MediaConstants.MEDIA_CONNECTION_STATUS, connectionStatus);
265         sendBroadcast(intent);
266     }
267 
attachContentFragment()268     void attachContentFragment() {
269         if (mMediaPlaybackFragment == null) {
270             mMediaPlaybackFragment = new MediaPlaybackFragment();
271         }
272 
273         setContentFragment(mMediaPlaybackFragment);
274     }
275 
276     private final MediaManager.Listener mListener = new MediaManager.Listener() {
277 
278         @Override
279         public void onMediaAppChanged(ComponentName componentName) {
280             sendMediaConnectionStatusBroadcast(componentName, MediaConstants.MEDIA_CONNECTED);
281 
282             // Since this callback happens asynchronously, ensure that the Activity has been
283             // started before changing fragments. Otherwise, the attach fragment will throw
284             // an IllegalStateException due to Fragment's checkStateLoss.
285             if (mIsStarted) {
286                 if (Log.isLoggable(TAG, Log.DEBUG)) {
287                     Log.d(TAG, "onMediaAppChanged: attaching content fragment");
288                 }
289                 attachContentFragment();
290             } else {
291                 if (Log.isLoggable(TAG, Log.DEBUG)) {
292                     Log.d(TAG, "onMediaAppChanged: queuing content fragment change");
293                 }
294                 mContentFragmentChangeQueued = true;
295             }
296         }
297 
298         @Override
299         public void onStatusMessageChanged(String msg) {}
300     };
301 
setContentFragment(Fragment fragment)302     private void setContentFragment(Fragment fragment) {
303         getSupportFragmentManager().beginTransaction()
304                 .replace(getContentContainerId(), fragment)
305                 .commit();
306     }
307 
308 
showQueueInDrawer()309     void showQueueInDrawer() {
310         mDrawerController.showQueueInDrawer();
311     }
312 }