Added Android code
[wl-app.git] / Android / app / src / main / java / com / moiseum / wolnelektury / view / player / service / MediaNotificationManager.java
1 /*
2  * Copyright 2017 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
17 package com.moiseum.wolnelektury.view.player.service;
18
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;
38
39 import com.bumptech.glide.Glide;
40 import com.moiseum.wolnelektury.R;
41 import com.moiseum.wolnelektury.view.main.MainActivity;
42
43 import java.util.concurrent.ExecutionException;
44
45
46 /**
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.
49  */
50 public class MediaNotificationManager {
51
52     public static final int NOTIFICATION_ID = 412;
53
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;
57
58     private final AudiobookService mService;
59
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;
65
66     public MediaNotificationManager(AudiobookService service) {
67         mService = service;
68
69         mNotificationManager =
70                 (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
71
72         mPlayAction =
73                 new NotificationCompat.Action(
74                         R.drawable.ic_play_arrow_white_24dp,
75                         mService.getString(R.string.label_play),
76                         MediaButtonReceiver.buildMediaButtonPendingIntent(
77                                 mService,
78                                 PlaybackStateCompat.ACTION_PLAY));
79         mPauseAction =
80                 new NotificationCompat.Action(
81                         R.drawable.ic_pause_white_24dp,
82                         mService.getString(R.string.label_pause),
83                         MediaButtonReceiver.buildMediaButtonPendingIntent(
84                                 mService,
85                                 PlaybackStateCompat.ACTION_PAUSE));
86         mNextAction =
87                 new NotificationCompat.Action(
88                         R.drawable.ic_skip_next_white_24dp,
89                         mService.getString(R.string.label_next),
90                         MediaButtonReceiver.buildMediaButtonPendingIntent(
91                                 mService,
92                                 PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
93         mPrevAction =
94                 new NotificationCompat.Action(
95                         R.drawable.ic_skip_previous_white_24dp,
96                         mService.getString(R.string.label_previous),
97                         MediaButtonReceiver.buildMediaButtonPendingIntent(
98                                 mService,
99                                 PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
100
101         // Cancel all notifications to handle the case where the Service was killed and
102         // restarted by the system.
103         mNotificationManager.cancelAll();
104     }
105
106     public void onDestroy() {
107         Log.d(TAG, "onDestroy: ");
108     }
109
110     public NotificationManager getNotificationManager() {
111         return mNotificationManager;
112     }
113
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();
122     }
123
124     private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
125                                                          MediaSessionCompat.Token token,
126                                                          boolean isPlaying,
127                                                          MediaDescriptionCompat description) {
128
129         // Create the (mandatory) notification channel when running on Android Oreo.
130         if (isAndroidOOrHigher()) {
131             createChannel();
132         }
133
134         NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID);
135         builder.setStyle(
136                 new MediaStyle()
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(
143                                         mService,
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);
160
161         // If skip to next action is enabled.
162         if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
163             builder.addAction(mPrevAction);
164         }
165
166         builder.addAction(isPlaying ? mPauseAction : mPlayAction);
167
168         // If skip to prev action is enabled.
169         if ((state.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
170             builder.addAction(mNextAction);
171         }
172
173         return builder;
174     }
175
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");
197         } else {
198             Log.d(TAG, "createChannel: Existing channel reused");
199         }
200     }
201
202     private boolean isAndroidOOrHigher() {
203         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
204     }
205
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);
211     }
212
213 }