/** * Build a new notification. To update the progress on the notification, use {@link * #updateProgress(int, int)} instead. * * @param episode The episode playing. * @param paused Playback state, <code>true</code> for paused. * @param canSeek If the currently played media is seekable. * @param position The current playback progress. * @param duration The length of the current episode. * @param session The media session representing current playback. * @return The notification to display. */ @NonNull public Notification build( Episode episode, boolean paused, boolean canSeek, int position, int duration, MediaSessionCompat session) { // 0. Prepare the main intent (leading back to the app) appIntent.putExtra(PODCAST_URL_KEY, episode.getPodcast().getUrl()); appIntent.putExtra(EPISODE_URL_KEY, episode.getMediaUrl()); final PendingIntent backToAppIntent = PendingIntent.getActivity(context, 0, appIntent, PendingIntent.FLAG_UPDATE_CURRENT); // 1. Create the notification builder and set values notificationBuilder = new NotificationCompat.Builder(context); notificationBuilder .setContentIntent(backToAppIntent) .setTicker(episode.getName()) .setSmallIcon(R.drawable.ic_stat) .setContentTitle(episode.getName()) .setContentText(episode.getPodcast().getName()) .setWhen(0) .setProgress(duration, position, false) .setOngoing(true); // 2. Load large image if available, see onBitmapLoaded() below if (episode.getPodcast().hasLogoUrl()) Picasso.with(context) .load(episode.getPodcast().getLogoUrl()) .resizeDimen( android.R.dimen.notification_large_icon_width, android.R.dimen.notification_large_icon_height) .into(this); // 3. Add actions to notification notificationBuilder.addAction(stopAction); if (canSeek) notificationBuilder.addAction(rewindAction); if (paused) notificationBuilder.addAction(playAction); else notificationBuilder.addAction(pauseAction); if (canSeek) notificationBuilder.addAction(forwardAction); // 4. Apply other notification features NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle().setMediaSession(session.getSessionToken()); // Make sure not to show rew/ff icons for live streams if (canSeek) style.setShowActionsInCompactView(1, 2, 3); // rewind, toggle play, forward else style.setShowActionsInCompactView(0, 1); // stop, toggle play notificationBuilder.setStyle(style); notificationBuilder.setColor(ContextCompat.getColor(context, R.color.theme_dark)); notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); notificationBuilder.setCategory(NotificationCompat.CATEGORY_TRANSPORT); return notificationBuilder.build(); }
/** * Returns a token to this object's MediaSession. The MediaSession should only be used for * notifications at the moment. * * @return The MediaSessionCompat.Token object. */ public MediaSessionCompat.Token getSessionToken() { return mediaSession.getSessionToken(); }
/* * (non-Javadoc) * @see android.app.Service#onCreate() */ @Override public void onCreate() { super.onCreate(); LogHelper.d(TAG, "onCreate"); mPlayingQueue = new ArrayList<>(); mMusicProvider = new MusicProvider(); mPackageValidator = new PackageValidator(this); ComponentName mediaButtonReceiver = new ComponentName(this, RemoteControlReceiver.class); Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.setComponent(mediaButtonReceiver); PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0); // Start a new MediaSessionCompat mSession = new MediaSessionCompat(this, "MusicService", mediaButtonReceiver, mediaPendingIntent); /* final MediaSessionCallback cb = new MediaSessionCallback(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Shouldn't really have to do this but the MediaSessionCompat method uses // an internal proxy class, which doesn't forward events such as // onPlayFromMediaId when running on Lollipop. final MediaSession session = (MediaSession) mSession.getMediaSession(); session.setCallback(new MediaSessionCallbackProxy(cb)); } else { mSession.setCallback(cb); } */ setSessionToken(mSession.getSessionToken()); mSession.setCallback(new MediaSessionCallback()); mSession.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); mPlayback = new LocalPlayback(this, mMusicProvider); mPlayback.setState(PlaybackStateCompat.STATE_NONE); mPlayback.setCallback(this); mPlayback.start(); Context context = getApplicationContext(); Intent intent = new Intent(context, NowPlayingActivity.class); PendingIntent pi = PendingIntent.getActivity( context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT); mSession.setSessionActivity(pi); mSessionExtras = new Bundle(); CarHelper.setSlotReservationFlags(mSessionExtras, true, true, true); WearHelper.setSlotReservationFlags(mSessionExtras, true, true); WearHelper.setUseBackgroundFromTheme(mSessionExtras, true); mSession.setExtras(mSessionExtras); updatePlaybackState(null); mMediaNotificationManager = new MediaNotificationManager(this); mCastManager = VideoCastManager.getInstance(); mCastManager.addVideoCastConsumer(mCastConsumer); mMediaRouter = MediaRouter.getInstance(getApplicationContext()); IntentFilter filter = new IntentFilter(CarHelper.ACTION_MEDIA_STATUS); mCarConnectionReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String connectionEvent = intent.getStringExtra(CarHelper.MEDIA_CONNECTION_STATUS); mIsConnectedToCar = CarHelper.MEDIA_CONNECTED.equals(connectionEvent); LogHelper.i( TAG, "Connection event to Android Auto: ", connectionEvent, " isConnectedToCar=", mIsConnectedToCar); } }; registerReceiver(mCarConnectionReceiver, filter); }