Merge branch 'nitehu-feature/upgrade-to-api28' into develop
This commit is contained in:
commit
ece7620175
|
@ -206,6 +206,7 @@ public class BookmarkActivity extends SubsonicTabActivity
|
|||
if (!getSelectedSongs(albumListView).isEmpty())
|
||||
{
|
||||
int position = songs.get(0).getBookmarkPosition();
|
||||
if (getDownloadService() == null) return;
|
||||
getDownloadService().restore(songs, 0, position, true, true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
|
|
|
@ -97,11 +97,8 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
applyTheme();
|
||||
super.onCreate(bundle);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(new Intent(this, DownloadServiceImpl.class));
|
||||
} else {
|
||||
startService(new Intent(this, DownloadServiceImpl.class));
|
||||
}
|
||||
// This should always succeed as it is called when Ultrasonic is in the foreground
|
||||
startService(new Intent(this, DownloadServiceImpl.class));
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
|
||||
if (bundle != null)
|
||||
|
@ -778,11 +775,16 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
}
|
||||
|
||||
Log.w(TAG, "DownloadService not running. Attempting to start it.");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(new Intent(this, DownloadServiceImpl.class));
|
||||
} else {
|
||||
|
||||
try
|
||||
{
|
||||
startService(new Intent(this, DownloadServiceImpl.class));
|
||||
}
|
||||
catch (IllegalStateException exception)
|
||||
{
|
||||
Log.w(TAG, "getDownloadService couldn't start DownloadServiceImpl because the application was in the background.");
|
||||
return null;
|
||||
}
|
||||
Util.sleepQuietly(50L);
|
||||
}
|
||||
|
||||
|
|
|
@ -108,6 +108,13 @@ public class SettingsFragment extends PreferenceFragment
|
|||
setupClearSearchPreference();
|
||||
setupGaplessControlSettingsV14();
|
||||
setupFeatureFlagsPreferences();
|
||||
|
||||
// After API26 foreground services must be used for music playback, and they must have a notification
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PreferenceCategory notificationsCategory = (PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_CATEGORY_NOTIFICATIONS);
|
||||
notificationsCategory.removePreference(findPreference(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION));
|
||||
notificationsCategory.removePreference(findPreference(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.moire.ultrasonic.service.DownloadServiceImpl;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
@ -58,11 +59,32 @@ public class MediaButtonIntentReceiver extends BroadcastReceiver
|
|||
|
||||
Intent serviceIntent = new Intent(context, DownloadServiceImpl.class);
|
||||
serviceIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent);
|
||||
} else {
|
||||
|
||||
try
|
||||
{
|
||||
context.startService(serviceIntent);
|
||||
}
|
||||
catch (IllegalStateException exception)
|
||||
{
|
||||
Log.i(TAG, "MediaButtonIntentReceiver couldn't start DownloadServiceImpl because the application was in the background.");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
{
|
||||
KeyEvent keyEvent = (KeyEvent) event;
|
||||
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN && keyEvent.getRepeatCount() == 0)
|
||||
{
|
||||
int keyCode = keyEvent.getKeyCode();
|
||||
if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
|
||||
keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
|
||||
keyCode == KeyEvent.KEYCODE_MEDIA_PLAY)
|
||||
{
|
||||
// TODO: The only time it is OK to start DownloadServiceImpl as a foreground service is when we now it will display its notification.
|
||||
// When DownloadServiceImpl is refactored to a proper foreground service, this can be removed.
|
||||
context.startForegroundService(serviceIntent);
|
||||
Log.i(TAG, "MediaButtonIntentReceiver started DownloadServiceImpl as foreground service");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -154,6 +154,7 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
private final static int lockScreenBitmapSize = 500;
|
||||
|
||||
private boolean isInForeground = false;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
|
||||
static
|
||||
{
|
||||
|
@ -262,13 +263,30 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
|
||||
instance = this;
|
||||
lifecycleSupport.onCreate();
|
||||
|
||||
// Create Notification Channel
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
//The suggested importance of a startForeground service notification is IMPORTANCE_LOW
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setLightColor(android.R.color.holo_blue_dark);
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// We should use a single notification builder, otherwise the notification may not be updated
|
||||
notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
|
||||
|
||||
Log.i(TAG, "DownloadServiceImpl created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId)
|
||||
{
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
|
||||
lifecycleSupport.onStart(intent);
|
||||
Log.i(TAG, "DownloadServiceImpl started with intent");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
|
@ -324,6 +342,8 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
catch (Throwable ignored)
|
||||
{
|
||||
}
|
||||
|
||||
Log.i(TAG, "DownloadServiceImpl stopped");
|
||||
}
|
||||
|
||||
public static DownloadService getInstance()
|
||||
|
@ -723,10 +743,7 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
if (currentPlaying != null)
|
||||
{
|
||||
if (tabInstance != null) {
|
||||
if (Util.isNotificationEnabled(this)) {
|
||||
startForeground(NOTIFICATION_ID, buildForegroundNotification());
|
||||
isInForeground = true;
|
||||
}
|
||||
updateNotification();
|
||||
tabInstance.showNowPlaying();
|
||||
}
|
||||
}
|
||||
|
@ -735,6 +752,7 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
if (tabInstance != null)
|
||||
{
|
||||
stopForeground(true);
|
||||
clearRemoteControl();
|
||||
isInForeground = false;
|
||||
tabInstance.hideNowPlaying();
|
||||
}
|
||||
|
@ -1260,6 +1278,7 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
if (tabInstance != null)
|
||||
{
|
||||
stopForeground(true);
|
||||
clearRemoteControl();
|
||||
isInForeground = false;
|
||||
tabInstance.hideNowPlaying();
|
||||
}
|
||||
|
@ -2085,9 +2104,15 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
{
|
||||
if (Util.isNotificationEnabled(this)) {
|
||||
if (isInForeground == true) {
|
||||
final NotificationManagerCompat notificationManager =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(NOTIFICATION_ID, buildForegroundNotification());
|
||||
}
|
||||
else {
|
||||
final NotificationManagerCompat notificationManager =
|
||||
NotificationManagerCompat.from(this);
|
||||
notificationManager.notify(NOTIFICATION_ID, buildForegroundNotification());
|
||||
notificationManager.notify(NOTIFICATION_ID, buildForegroundNotification());
|
||||
}
|
||||
Log.w(TAG, "--- Updated notification");
|
||||
}
|
||||
else {
|
||||
|
@ -2100,31 +2125,24 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
|
||||
@SuppressWarnings("IconColors")
|
||||
private Notification buildForegroundNotification() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
|
||||
channel.setLightColor(android.R.color.holo_blue_dark);
|
||||
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.createNotificationChannel(channel);
|
||||
notificationBuilder.setSmallIcon(R.drawable.ic_stat_ultrasonic);
|
||||
|
||||
}
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
|
||||
builder.setSmallIcon(R.drawable.ic_stat_ultrasonic);
|
||||
|
||||
builder.setAutoCancel(false);
|
||||
builder.setOngoing(true);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||
notificationBuilder.setAutoCancel(false);
|
||||
notificationBuilder.setOngoing(true);
|
||||
notificationBuilder.setOnlyAlertOnce(true);
|
||||
notificationBuilder.setWhen(System.currentTimeMillis());
|
||||
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||
notificationBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
|
||||
RemoteViews contentView = new RemoteViews(this.getPackageName(), R.layout.notification);
|
||||
Util.linkButtons(this, contentView, false);
|
||||
RemoteViews bigView = new RemoteViews(this.getPackageName(), R.layout.notification_large);
|
||||
Util.linkButtons(this, bigView, false);
|
||||
|
||||
builder.setContent(contentView);
|
||||
notificationBuilder.setContent(contentView);
|
||||
|
||||
Intent notificationIntent = new Intent(this, DownloadActivity.class);
|
||||
builder.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
|
||||
notificationBuilder.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
|
||||
|
||||
if (playerState == PlayerState.PAUSED || playerState == PlayerState.IDLE) {
|
||||
contentView.setImageViewResource(R.id.control_play, R.drawable.media_start_normal_dark);
|
||||
|
@ -2134,47 +2152,50 @@ public class DownloadServiceImpl extends Service implements DownloadService
|
|||
bigView.setImageViewResource(R.id.control_play, R.drawable.media_pause_normal_dark);
|
||||
}
|
||||
|
||||
final Entry song = currentPlaying.getSong();
|
||||
final String title = song.getTitle();
|
||||
final String text = song.getArtist();
|
||||
final String album = song.getAlbum();
|
||||
final int rating = song.getUserRating() == null ? 0 : song.getUserRating();
|
||||
final int imageSize = Util.getNotificationImageSize(this);
|
||||
if (currentPlaying != null) {
|
||||
final Entry song = currentPlaying.getSong();
|
||||
final String title = song.getTitle();
|
||||
final String text = song.getArtist();
|
||||
final String album = song.getAlbum();
|
||||
final int rating = song.getUserRating() == null ? 0 : song.getUserRating();
|
||||
final int imageSize = Util.getNotificationImageSize(this);
|
||||
|
||||
try {
|
||||
final Bitmap nowPlayingImage = FileUtil.getAlbumArtBitmap(this, currentPlaying.getSong(), imageSize, true);
|
||||
if (nowPlayingImage == null) {
|
||||
contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
bigView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
} else {
|
||||
contentView.setImageViewBitmap(R.id.notification_image, nowPlayingImage);
|
||||
bigView.setImageViewBitmap(R.id.notification_image, nowPlayingImage);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
Log.w(TAG, "Failed to get notification cover art", x);
|
||||
contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
bigView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
}
|
||||
try {
|
||||
final Bitmap nowPlayingImage = FileUtil.getAlbumArtBitmap(this, currentPlaying.getSong(), imageSize, true);
|
||||
if (nowPlayingImage == null) {
|
||||
contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
bigView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
} else {
|
||||
contentView.setImageViewBitmap(R.id.notification_image, nowPlayingImage);
|
||||
bigView.setImageViewBitmap(R.id.notification_image, nowPlayingImage);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
Log.w(TAG, "Failed to get notification cover art", x);
|
||||
contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
bigView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
}
|
||||
|
||||
contentView.setTextViewText(R.id.trackname, title);
|
||||
bigView.setTextViewText(R.id.trackname, title);
|
||||
contentView.setTextViewText(R.id.artist, text);
|
||||
bigView.setTextViewText(R.id.artist, text);
|
||||
contentView.setTextViewText(R.id.album, album);
|
||||
bigView.setTextViewText(R.id.album, album);
|
||||
|
||||
boolean useFiveStarRating = KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING);
|
||||
if (!useFiveStarRating) bigView.setViewVisibility(R.id.notification_rating, View.INVISIBLE);
|
||||
else
|
||||
{
|
||||
bigView.setImageViewResource(R.id.notification_five_star_1, rating > 0 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_2, rating > 1 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_3, rating > 2 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_4, rating > 3 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_5, rating > 4 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
contentView.setTextViewText(R.id.trackname, title);
|
||||
bigView.setTextViewText(R.id.trackname, title);
|
||||
contentView.setTextViewText(R.id.artist, text);
|
||||
bigView.setTextViewText(R.id.artist, text);
|
||||
contentView.setTextViewText(R.id.album, album);
|
||||
bigView.setTextViewText(R.id.album, album);
|
||||
|
||||
boolean useFiveStarRating = KoinJavaComponent.get(FeatureStorage.class).isFeatureEnabled(Feature.FIVE_STAR_RATING);
|
||||
if (!useFiveStarRating)
|
||||
bigView.setViewVisibility(R.id.notification_rating, View.INVISIBLE);
|
||||
else {
|
||||
bigView.setImageViewResource(R.id.notification_five_star_1, rating > 0 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_2, rating > 1 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_3, rating > 2 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_4, rating > 3 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
bigView.setImageViewResource(R.id.notification_five_star_5, rating > 4 ? R.drawable.ic_star_full_dark : R.drawable.ic_star_hollow_dark);
|
||||
}
|
||||
}
|
||||
|
||||
Notification notification = builder.build();
|
||||
Notification notification = notificationBuilder.build();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
notification.bigContentView = bigView;
|
||||
}
|
||||
|
|
|
@ -289,6 +289,7 @@ public class DownloadServiceLifecycleSupport
|
|||
return;
|
||||
}
|
||||
Log.i(TAG, "Deserialized currentPlayingIndex: " + state.currentPlayingIndex + ", currentPlayingPosition: " + state.currentPlayingPosition);
|
||||
// TODO: here the autoPlay = false creates problems when Ultrasonic is started by a Play MediaButton as the player won't start this way.
|
||||
downloadService.restore(state.songs, state.currentPlayingIndex, state.currentPlayingPosition, false, false);
|
||||
|
||||
// Work-around: Serialize again, as the restore() method creates a serialization without current playing info.
|
||||
|
@ -321,7 +322,11 @@ public class DownloadServiceLifecycleSupport
|
|||
downloadService.stop();
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
if (downloadService.getPlayerState() != PlayerState.STARTED)
|
||||
if (downloadService.getPlayerState() == PlayerState.IDLE)
|
||||
{
|
||||
downloadService.play();
|
||||
}
|
||||
else if (downloadService.getPlayerState() != PlayerState.STARTED)
|
||||
{
|
||||
downloadService.start();
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ public final class Constants
|
|||
public static final String PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY = "imageLoaderConcurrency";
|
||||
public static final String PREFERENCES_KEY_FF_IMAGE_LOADER = "ff_new_image_loader";
|
||||
public static final String PREFERENCES_KEY_USE_FIVE_STAR_RATING = "use_five_star_rating";
|
||||
public static final String PREFERENCES_KEY_CATEGORY_NOTIFICATIONS = "notificationsCategory";
|
||||
|
||||
// Number of free trial days for non-licensed servers.
|
||||
public static final int FREE_TRIAL_DAYS = 30;
|
||||
|
|
|
@ -36,6 +36,7 @@ import android.net.ConnectivityManager;
|
|||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
|
@ -155,12 +156,16 @@ public class Util extends DownloadActivity
|
|||
|
||||
public static boolean isNotificationEnabled(Context context)
|
||||
{
|
||||
// After API26 foreground services must be used for music playback, and they must have a notification
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) return true;
|
||||
SharedPreferences preferences = getPreferences(context);
|
||||
return preferences.getBoolean(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION, false);
|
||||
}
|
||||
|
||||
public static boolean isNotificationAlwaysEnabled(Context context)
|
||||
{
|
||||
// After API26 foreground services must be used for music playback, and they must have a notification
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) return true;
|
||||
SharedPreferences preferences = getPreferences(context);
|
||||
return preferences.getBoolean(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION, false);
|
||||
}
|
||||
|
|
|
@ -107,7 +107,9 @@
|
|||
a:summary="@string/settings.playback.resume_play_on_headphones_plug.summary"
|
||||
/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.notifications_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.notifications_title"
|
||||
a:key="notificationsCategory">
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="showNowPlaying"
|
||||
|
|
Loading…
Reference in New Issue