Audinaut-subsonic-app-android/app/src/main/java/net/nullsum/audinaut/util/Notifications.java

279 lines
14 KiB
Java
Raw Normal View History

2016-12-18 18:41:30 +01:00
/*
This file is part of Subsonic.
2018-02-28 03:26:35 +01:00
Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 (C) Scott Jackson
2016-12-18 18:41:30 +01:00
*/
2017-01-09 08:01:12 +01:00
package net.nullsum.audinaut.util;
2016-12-18 18:41:30 +01:00
import android.app.Notification;
2018-02-28 03:26:35 +01:00
import android.app.NotificationChannel;
2016-12-18 18:41:30 +01:00
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
2016-12-18 18:41:30 +01:00
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
2019-08-25 05:06:03 +02:00
import androidx.core.app.NotificationCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.MediaMetadataCompat;
2019-08-25 05:06:03 +02:00
2017-01-09 08:01:12 +01:00
import net.nullsum.audinaut.R;
import net.nullsum.audinaut.activity.SubsonicActivity;
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
import net.nullsum.audinaut.domain.MusicDirectory;
import net.nullsum.audinaut.domain.PlayerState;
import net.nullsum.audinaut.provider.AudinautWidgetProvider;
import net.nullsum.audinaut.service.DownloadFile;
import net.nullsum.audinaut.service.DownloadService;
2018-02-28 03:26:35 +01:00
import static android.content.Context.NOTIFICATION_SERVICE;
2016-12-18 18:41:30 +01:00
public final class Notifications {
2018-03-25 03:28:28 +02:00
private static final int NOTIFICATION_ID_PLAYING = 100;
private static final int NOTIFICATION_ID_DOWNLOADING = 102;
private static final String CHANNEL_PLAYING_ID = "playback_controls";
private static final String CHANNEL_DOWNLOADING_ID = "media_download";
2018-02-28 03:26:35 +01:00
private static final String TAG = Notifications.class.getSimpleName();
private static boolean playShowing = false;
private static boolean downloadShowing = false;
private static boolean downloadForeground = false;
private static boolean persistentPlayingShowing = false;
2016-12-18 18:41:30 +01:00
2018-02-28 03:26:35 +01:00
public static void showPlayingNotification(final Context context, final DownloadService downloadService, final Handler handler, MusicDirectory.Entry song) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
2016-12-18 18:41:30 +01:00
2018-02-28 03:26:35 +01:00
NotificationChannel mChannel = new NotificationChannel(
CHANNEL_PLAYING_ID, context.getString(R.string.channel_playing_name), importance);
mChannel.setDescription(context.getString(R.string.channel_playing_description));
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(mChannel);
}
final boolean playing = downloadService.getPlayerState() == PlayerState.STARTED;
Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Intent cancelIntent = new Intent("KEYCODE_MEDIA_STOP")
.setComponent(new ComponentName(context, DownloadService.class))
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP));
int[] compactActions = new int[]{0, 1, 2};
MediaSessionCompat mediaSession = new MediaSessionCompat(context, "Audinaut");
MediaSessionCompat.Token mediaToken = mediaSession.getSessionToken();
MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder();
mediaSession.setMetadata(metadataBuilder
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1)
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getAlbumArt(context, song))
//.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, R.drawable.notification_logo)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.getTitle())
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.getArtist())
.build() );
MediaStyle mediaStyle = new MediaStyle()
.setShowActionsInCompactView(compactActions)
.setShowCancelButton(true)
.setCancelButtonIntent(PendingIntent.getService(context, 0, cancelIntent, 0))
.setMediaSession(mediaToken);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_PLAYING_ID)
2018-02-28 03:26:35 +01:00
.setChannelId(CHANNEL_PLAYING_ID)
.setContentTitle(song.getTitle())
.setContentText(song.getArtist())
.setSubText(song.getAlbum())
.setTicker(song.getTitle())
2018-02-28 03:26:35 +01:00
.setOngoing(playing)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setShowWhen(false)
.setLargeIcon(getAlbumArt(context, song))
.setSmallIcon(R.drawable.notification_logo)
.setStyle(mediaStyle)
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, 0));
addActions(context, builder, playing);
final Notification notification = builder.build();
if(playing) {
notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
}
2018-02-28 03:26:35 +01:00
playShowing = true;
2018-03-25 03:28:28 +02:00
if (downloadForeground && downloadShowing) {
2018-02-28 03:26:35 +01:00
downloadForeground = false;
2018-03-25 03:28:28 +02:00
handler.post(() -> {
downloadService.stopForeground(true);
showDownloadingNotification(context, downloadService, handler, downloadService.getCurrentDownloading(), downloadService.getBackgroundDownloads().size());
downloadService.startForeground(NOTIFICATION_ID_PLAYING, notification);
2018-02-28 03:26:35 +01:00
});
} else {
2018-03-25 03:28:28 +02:00
handler.post(() -> {
if (playing) {
downloadService.startForeground(NOTIFICATION_ID_PLAYING, notification);
} else {
playShowing = false;
persistentPlayingShowing = true;
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
downloadService.stopForeground(false);
notificationManager.notify(NOTIFICATION_ID_PLAYING, notification);
2018-02-28 03:26:35 +01:00
}
});
}
// Update widget
AudinautWidgetProvider.notifyInstances(context, downloadService, playing);
}
private static Bitmap getAlbumArt(Context context, MusicDirectory.Entry song) {
2018-02-28 03:26:35 +01:00
try {
ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
Bitmap bitmap = null;
2018-03-25 03:28:28 +02:00
if (imageLoader != null) {
2018-02-28 03:26:35 +01:00
bitmap = imageLoader.getCachedImage(context, song, false);
}
if (bitmap == null) {
// set default album art
return BitmapFactory.decodeResource(context.getResources(), R.drawable.unknown_album);
2018-02-28 03:26:35 +01:00
} else {
return bitmap;
2018-02-28 03:26:35 +01:00
}
} catch (Exception x) {
Log.w(TAG, "Failed to get notification cover art", x);
return BitmapFactory.decodeResource(context.getResources(), R.drawable.unknown_album);
2018-02-28 03:26:35 +01:00
}
}
2018-02-28 03:26:35 +01:00
private static void addActions(final Context context, final NotificationCompat.Builder builder, final boolean playing) {
2018-02-28 03:26:35 +01:00
PendingIntent pendingIntent;
Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
builder.addAction(R.drawable.notification_media_backward, "Previous", pendingIntent);
2018-04-25 02:20:20 +02:00
if (playing) {
Intent pauseIntent = new Intent("KEYCODE_MEDIA_PLAY_PAUSE");
pauseIntent.setComponent(new ComponentName(context, DownloadService.class));
pauseIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
pendingIntent = PendingIntent.getService(context, 0, pauseIntent, 0);
builder.addAction(R.drawable.notification_media_pause, "Pause", pendingIntent);
2018-04-25 02:20:20 +02:00
} else {
Intent playIntent = new Intent("KEYCODE_MEDIA_PLAY");
playIntent.setComponent(new ComponentName(context, DownloadService.class));
playIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY));
pendingIntent = PendingIntent.getService(context, 0, playIntent, 0);
builder.addAction(R.drawable.notification_media_start, "Play", pendingIntent);
2018-02-28 03:26:35 +01:00
}
2018-04-25 02:20:20 +02:00
Intent nextIntent = new Intent("KEYCODE_MEDIA_NEXT");
nextIntent.setComponent(new ComponentName(context, DownloadService.class));
nextIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
pendingIntent = PendingIntent.getService(context, 0, nextIntent, 0);
builder.addAction(R.drawable.notification_media_forward, "Next", pendingIntent);
2018-02-28 03:26:35 +01:00
}
public static void hidePlayingNotification(final Context context, final DownloadService downloadService, Handler handler) {
playShowing = false;
// Remove notification and remove the service from the foreground
2018-03-25 03:28:28 +02:00
handler.post(() -> {
downloadService.stopForeground(true);
2018-02-28 03:26:35 +01:00
2018-03-25 03:28:28 +02:00
if (persistentPlayingShowing) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID_PLAYING);
persistentPlayingShowing = false;
2018-02-28 03:26:35 +01:00
}
});
// Get downloadNotification in foreground if playing
2018-03-25 03:28:28 +02:00
if (downloadShowing) {
2018-02-28 03:26:35 +01:00
showDownloadingNotification(context, downloadService, handler, downloadService.getCurrentDownloading(), downloadService.getBackgroundDownloads().size());
}
// Update widget
AudinautWidgetProvider.notifyInstances(context, downloadService, false);
}
public static void showDownloadingNotification(final Context context, final DownloadService downloadService, Handler handler, DownloadFile file, int size) {
Intent cancelIntent = new Intent(context, DownloadService.class);
cancelIntent.setAction(DownloadService.CANCEL_DOWNLOADS);
PendingIntent cancelPI = PendingIntent.getService(context, 0, cancelIntent, 0);
String currentDownloading, currentSize;
2018-03-25 03:28:28 +02:00
if (file != null) {
2018-02-28 03:26:35 +01:00
currentDownloading = file.getSong().getTitle();
currentSize = Util.formatLocalizedBytes(file.getEstimatedSize(), context);
} else {
currentDownloading = "none";
currentSize = "0";
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(
CHANNEL_DOWNLOADING_ID, context.getString(R.string.channel_download_name), importance);
mChannel.setDescription(context.getString(R.string.channel_download_description));
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(mChannel);
}
Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD_VIEW, true);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Notification notification = new NotificationCompat.Builder(context, CHANNEL_DOWNLOADING_ID)
.setChannelId(CHANNEL_DOWNLOADING_ID)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(context.getResources().getString(R.string.download_downloading_title, size))
.setContentText(context.getResources().getString(R.string.download_downloading_summary, currentDownloading))
.setOngoing(true)
.addAction(R.drawable.notification_close,
context.getResources().getString(R.string.common_cancel),
cancelPI)
.setContentIntent(PendingIntent.getActivity(context, 2, notificationIntent, 0))
.setStyle(new NotificationCompat.BigTextStyle()
2018-03-25 03:28:28 +02:00
.bigText(context.getResources().getString(R.string.download_downloading_summary_expanded, currentDownloading, currentSize)))
2018-02-28 03:26:35 +01:00
.setProgress(10, 5, true).build();
downloadShowing = true;
2018-03-25 03:28:28 +02:00
if (playShowing) {
2018-02-28 03:26:35 +01:00
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID_DOWNLOADING, notification);
} else {
downloadForeground = true;
2018-03-25 03:28:28 +02:00
handler.post(() -> downloadService.startForeground(NOTIFICATION_ID_DOWNLOADING, notification));
2018-02-28 03:26:35 +01:00
}
}
2018-03-25 03:28:28 +02:00
2018-02-28 03:26:35 +01:00
public static void hideDownloadingNotification(final Context context, final DownloadService downloadService, Handler handler) {
downloadShowing = false;
2018-03-25 03:28:28 +02:00
if (playShowing) {
2018-02-28 03:26:35 +01:00
NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID_DOWNLOADING);
} else {
downloadForeground = false;
2018-03-25 03:28:28 +02:00
handler.post(() -> downloadService.stopForeground(true));
2018-02-28 03:26:35 +01:00
}
}
2016-12-18 18:41:30 +01:00
}