| /* |
| * Copyright 2017 The Android Open Source 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.example.android.mediasession.service.notifications; |
| |
| import android.app.Notification; |
| import android.app.NotificationChannel; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.Color; |
| import android.os.Build; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.RequiresApi; |
| import android.support.v4.app.NotificationCompat; |
| import android.support.v4.content.ContextCompat; |
| import android.support.v4.media.MediaDescriptionCompat; |
| import android.support.v4.media.MediaMetadataCompat; |
| import android.support.v4.media.app.NotificationCompat.MediaStyle; |
| import android.support.v4.media.session.MediaButtonReceiver; |
| import android.support.v4.media.session.MediaSessionCompat; |
| import android.support.v4.media.session.PlaybackStateCompat; |
| import android.support.v4.os.BuildCompat; |
| import android.util.Log; |
| |
| import com.example.android.mediasession.R; |
| import com.example.android.mediasession.service.MusicService; |
| import com.example.android.mediasession.service.PlaybackInfoListener; |
| import com.example.android.mediasession.service.contentcatalogs.MusicLibrary; |
| import com.example.android.mediasession.ui.MainActivity; |
| |
| |
| /** |
| * Keeps track of a notification and updates it automatically for a given MediaSession. This is |
| * required so that the music service don't get killed during playback. |
| */ |
| public class MediaNotificationManager { |
| |
| public static final int NOTIFICATION_ID = 412; |
| |
| private static final String TAG = MediaNotificationManager.class.getSimpleName(); |
| private static final String CHANNEL_ID = "com.example.android.musicplayer.channel"; |
| private static final int REQUEST_CODE = 501; |
| |
| private final MusicService mService; |
| |
| private final NotificationCompat.Action mPlayAction; |
| private final NotificationCompat.Action mPauseAction; |
| private final NotificationCompat.Action mNextAction; |
| private final NotificationCompat.Action mPrevAction; |
| private final NotificationManager mNotificationManager; |
| |
| public MediaNotificationManager(MusicService service) { |
| mService = service; |
| |
| mNotificationManager = |
| (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE); |
| |
| mPlayAction = |
| new NotificationCompat.Action( |
| R.drawable.ic_play_arrow_white_24dp, |
| mService.getString(R.string.label_play), |
| MediaButtonReceiver.buildMediaButtonPendingIntent( |
| mService, |
| PlaybackStateCompat.ACTION_PLAY)); |
| mPauseAction = |
| new NotificationCompat.Action( |
| R.drawable.ic_pause_white_24dp, |
| mService.getString(R.string.label_pause), |
| MediaButtonReceiver.buildMediaButtonPendingIntent( |
| mService, |
| PlaybackStateCompat.ACTION_PAUSE)); |
| mNextAction = |
| new NotificationCompat.Action( |
| R.drawable.ic_skip_next_white_24dp, |
| mService.getString(R.string.label_next), |
| MediaButtonReceiver.buildMediaButtonPendingIntent( |
| mService, |
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT)); |
| mPrevAction = |
| new NotificationCompat.Action( |
| R.drawable.ic_skip_previous_white_24dp, |
| mService.getString(R.string.label_previous), |
| MediaButtonReceiver.buildMediaButtonPendingIntent( |
| mService, |
| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)); |
| |
| // Cancel all notifications to handle the case where the Service was killed and |
| // restarted by the system. |
| mNotificationManager.cancelAll(); |
| } |
| |
| public void onDestroy() { |
| Log.d(TAG, "onDestroy: "); |
| } |
| |
| public NotificationManager getNotificationManager() { |
| return mNotificationManager; |
| } |
| |
| public Notification getNotification(MediaMetadataCompat metadata, |
| @NonNull PlaybackStateCompat state, |
| MediaSessionCompat.Token token) { |
| boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING; |
| MediaDescriptionCompat description = metadata.getDescription(); |
| NotificationCompat.Builder builder = |
| buildNotification(state, token, isPlaying, description); |
| return builder.build(); |
| } |
| |
| private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state, |
| MediaSessionCompat.Token token, |
| boolean isPlaying, |
| MediaDescriptionCompat description) { |
| |
| // Create the (mandatory) notification channel when running on Android Oreo. |
| if (isAndroidOOrHigher()) { |
| createChannel(); |
| } |
| |
| NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID); |
| builder.setStyle( |
| new MediaStyle() |
| .setMediaSession(token) |
| .setShowActionsInCompactView(0, 1, 2) |
| // For backwards compatibility with Android L and earlier. |
| .setShowCancelButton(true) |
| .setCancelButtonIntent( |
| MediaButtonReceiver.buildMediaButtonPendingIntent( |
| mService, |
| PlaybackStateCompat.ACTION_STOP))) |
| .setColor(ContextCompat.getColor(mService, R.color.notification_bg)) |
| .setSmallIcon(R.drawable.ic_stat_image_audiotrack) |
| // Pending intent that is fired when user clicks on notification. |
| .setContentIntent(createContentIntent()) |
| // Title - Usually Song name. |
| .setContentTitle(description.getTitle()) |
| // Subtitle - Usually Artist name. |
| .setContentText(description.getSubtitle()) |
| .setLargeIcon(MusicLibrary.getAlbumBitmap(mService, description.getMediaId())) |
| // When notification is deleted (when playback is paused and notification can be |
| // deleted) fire MediaButtonPendingIntent with ACTION_STOP. |
| .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent( |
| mService, PlaybackStateCompat.ACTION_STOP)) |
| // Show controls on lock screen even when user hides sensitive content. |
| .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); |
| |
| // If skip to next action is enabled. |
| if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) { |
| builder.addAction(mPrevAction); |
| } |
| |
| builder.addAction(isPlaying ? mPauseAction : mPlayAction); |
| |
| // If skip to prev action is enabled. |
| if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) { |
| builder.addAction(mNextAction); |
| } |
| |
| return builder; |
| } |
| |
| // Does nothing on versions of Android earlier than O. |
| @RequiresApi(Build.VERSION_CODES.O) |
| private void createChannel() { |
| if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) { |
| // The user-visible name of the channel. |
| CharSequence name = "MediaSession"; |
| // The user-visible description of the channel. |
| String description = "MediaSession and MediaPlayer"; |
| int importance = NotificationManager.IMPORTANCE_LOW; |
| NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance); |
| // Configure the notification channel. |
| mChannel.setDescription(description); |
| mChannel.enableLights(true); |
| // Sets the notification light color for notifications posted to this |
| // channel, if the device supports this feature. |
| mChannel.setLightColor(Color.RED); |
| mChannel.enableVibration(true); |
| mChannel.setVibrationPattern( |
| new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400}); |
| mNotificationManager.createNotificationChannel(mChannel); |
| Log.d(TAG, "createChannel: New channel created"); |
| } else { |
| Log.d(TAG, "createChannel: Existing channel reused"); |
| } |
| } |
| |
| private boolean isAndroidOOrHigher() { |
| return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; |
| } |
| |
| private PendingIntent createContentIntent() { |
| Intent openUI = new Intent(mService, MainActivity.class); |
| openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); |
| return PendingIntent.getActivity( |
| mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT); |
| } |
| |
| } |