2 * Copyright 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.moiseum.wolnelektury.view.player.service;
19 import android.app.Notification;
20 import android.app.NotificationChannel;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.graphics.Color;
26 import android.os.Build;
27 import android.support.annotation.NonNull;
28 import android.support.annotation.RequiresApi;
29 import android.support.v4.app.NotificationCompat;
30 import android.support.v4.content.ContextCompat;
31 import android.support.v4.media.MediaDescriptionCompat;
32 import android.support.v4.media.MediaMetadataCompat;
33 import android.support.v4.media.app.NotificationCompat.MediaStyle;
34 import android.support.v4.media.session.MediaButtonReceiver;
35 import android.support.v4.media.session.MediaSessionCompat;
36 import android.support.v4.media.session.PlaybackStateCompat;
37 import android.util.Log;
39 import com.bumptech.glide.Glide;
40 import com.moiseum.wolnelektury.R;
41 import com.moiseum.wolnelektury.view.main.MainActivity;
43 import java.util.concurrent.ExecutionException;
47 * Keeps track of a notification and updates it automatically for a given MediaSession. This is
48 * required so that the music service don't get killed during playback.
50 public class MediaNotificationManager {
52 public static final int NOTIFICATION_ID = 412;
54 private static final String TAG = MediaNotificationManager.class.getSimpleName();
55 private static final String CHANNEL_ID = "com.moiseum.wolnelektury.audiobookplayer.channel";
56 private static final int REQUEST_CODE = 501;
58 private final AudiobookService mService;
60 private final NotificationCompat.Action mPlayAction;
61 private final NotificationCompat.Action mPauseAction;
62 private final NotificationCompat.Action mNextAction;
63 private final NotificationCompat.Action mPrevAction;
64 private final NotificationManager mNotificationManager;
66 public MediaNotificationManager(AudiobookService service) {
69 mNotificationManager =
70 (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
73 new NotificationCompat.Action(
74 R.drawable.ic_play_arrow_white_24dp,
75 mService.getString(R.string.label_play),
76 MediaButtonReceiver.buildMediaButtonPendingIntent(
78 PlaybackStateCompat.ACTION_PLAY));
80 new NotificationCompat.Action(
81 R.drawable.ic_pause_white_24dp,
82 mService.getString(R.string.label_pause),
83 MediaButtonReceiver.buildMediaButtonPendingIntent(
85 PlaybackStateCompat.ACTION_PAUSE));
87 new NotificationCompat.Action(
88 R.drawable.ic_skip_next_white_24dp,
89 mService.getString(R.string.label_next),
90 MediaButtonReceiver.buildMediaButtonPendingIntent(
92 PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
94 new NotificationCompat.Action(
95 R.drawable.ic_skip_previous_white_24dp,
96 mService.getString(R.string.label_previous),
97 MediaButtonReceiver.buildMediaButtonPendingIntent(
99 PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
101 // Cancel all notifications to handle the case where the Service was killed and
102 // restarted by the system.
103 mNotificationManager.cancelAll();
106 public void onDestroy() {
107 Log.d(TAG, "onDestroy: ");
110 public NotificationManager getNotificationManager() {
111 return mNotificationManager;
114 public Notification getNotification(MediaMetadataCompat metadata,
115 @NonNull PlaybackStateCompat state,
116 MediaSessionCompat.Token token) {
117 boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
118 MediaDescriptionCompat description = metadata.getDescription();
119 NotificationCompat.Builder builder =
120 buildNotification(state, token, isPlaying, description);
121 return builder.build();
124 private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
125 MediaSessionCompat.Token token,
127 MediaDescriptionCompat description) {
129 // Create the (mandatory) notification channel when running on Android Oreo.
130 if (isAndroidOOrHigher()) {
134 NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID);
137 .setMediaSession(token)
138 .setShowActionsInCompactView(0, 1, 2)
139 // For backwards compatibility with Android L and earlier.
140 .setShowCancelButton(true)
141 .setCancelButtonIntent(
142 MediaButtonReceiver.buildMediaButtonPendingIntent(
144 PlaybackStateCompat.ACTION_STOP)))
145 .setColor(ContextCompat.getColor(mService, R.color.colorAccent))
146 .setSmallIcon(R.drawable.ic_notification_player)
147 // Pending intent that is fired when user clicks on notification.
148 .setContentIntent(createContentIntent())
149 // Title - Usually Song name.
150 .setContentTitle(description.getTitle())
151 // Subtitle - Usually Artist name.
152 .setContentText(description.getSubtitle())
153 // .setLargeIcon(AudiobookLibrary.getAlbumBitmap(mService, description.getMediaId()))
154 // When notification is deleted (when playback is paused and notification can be
155 // deleted) fire MediaButtonPendingIntent with ACTION_STOP.
156 .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(
157 mService, PlaybackStateCompat.ACTION_STOP))
158 // Show controls on lock screen even when user hides sensitive content.
159 .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
161 // If skip to next action is enabled.
162 if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
163 builder.addAction(mPrevAction);
166 builder.addAction(isPlaying ? mPauseAction : mPlayAction);
168 // If skip to prev action is enabled.
169 if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
170 builder.addAction(mNextAction);
176 // Does nothing on versions of Android earlier than O.
177 @RequiresApi(Build.VERSION_CODES.O)
178 private void createChannel() {
179 if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
180 // The user-visible name of the channel.
181 CharSequence name = "MediaSession";
182 // The user-visible description of the channel.
183 String description = "MediaSession and MediaPlayer";
184 int importance = NotificationManager.IMPORTANCE_LOW;
185 NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
186 // Configure the notification channel.
187 mChannel.setDescription(description);
188 mChannel.enableLights(true);
189 // Sets the notification light color for notifications posted to this
190 // channel, if the device supports this feature.
191 mChannel.setLightColor(Color.RED);
192 mChannel.enableVibration(true);
193 mChannel.setVibrationPattern(
194 new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
195 mNotificationManager.createNotificationChannel(mChannel);
196 Log.d(TAG, "createChannel: New channel created");
198 Log.d(TAG, "createChannel: Existing channel reused");
202 private boolean isAndroidOOrHigher() {
203 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
206 private PendingIntent createContentIntent() {
207 Intent openUI = new Intent(mService, MainActivity.class);
208 openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
209 return PendingIntent.getActivity(
210 mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT);