Fix notifications to work with Android Oreo
Whan the background service is launcher, it is launched on the foreground and a notification is always shown. The service stays on while something is playing. It stops itself on a connection error, quitting Kodi or 5 seconds after stopping Make the Pause Phone Calls preference dependent on the Show Notification preference, as we always need to show a notification when the service is running
This commit is contained in:
parent
250c1f0fd1
commit
3077653dd9
|
@ -19,6 +19,7 @@ import android.Manifest;
|
|||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
@ -51,6 +52,10 @@ public class ConnectionObserversManagerService extends Service
|
|||
private HostConnectionObserver mHostConnectionObserver = null;
|
||||
|
||||
private List<HostConnectionObserver.PlayerEventsObserver> mConnectionObservers = new ArrayList<>();
|
||||
private NotificationObserver mNotificationObserver;
|
||||
|
||||
private boolean somethingPlaying = false;
|
||||
private Handler mStopHandler = new Handler();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
@ -69,6 +74,7 @@ public class ConnectionObserversManagerService extends Service
|
|||
if (mConnectionObservers.isEmpty()) {
|
||||
LogUtils.LOGD(TAG, "No observers, stopping observer service.");
|
||||
stopSelf();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
// Get the connection observer here, not on create to check if
|
||||
|
@ -81,6 +87,16 @@ public class ConnectionObserversManagerService extends Service
|
|||
return START_STICKY;
|
||||
}
|
||||
|
||||
// Create the observers we are managing
|
||||
createObservers();
|
||||
if (mConnectionObservers.isEmpty()) {
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
startForeground(NotificationObserver.NOTIFICATION_ID, mNotificationObserver.getNothingPlayingNotification());
|
||||
|
||||
// If there's a change in hosts, unregister from the previous one
|
||||
if (mHostConnectionObserver != null) {
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
|
@ -97,14 +113,13 @@ public class ConnectionObserversManagerService extends Service
|
|||
private void createObservers() {
|
||||
mConnectionObservers = new ArrayList<>();
|
||||
|
||||
// Check whether we should show a notification
|
||||
boolean showNotification = PreferenceManager
|
||||
.getDefaultSharedPreferences(this)
|
||||
.getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION,
|
||||
Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
|
||||
if (showNotification) {
|
||||
mConnectionObservers.add(new NotificationObserver(this));
|
||||
}
|
||||
// Always show a notification
|
||||
// boolean showNotification = PreferenceManager
|
||||
// .getDefaultSharedPreferences(this)
|
||||
// .getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION,
|
||||
// Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
|
||||
mNotificationObserver = new NotificationObserver(this);
|
||||
mConnectionObservers.add(mNotificationObserver);
|
||||
|
||||
// Check whether we should react to phone state changes and wether
|
||||
// we have permissions to do so
|
||||
|
@ -136,6 +151,7 @@ public class ConnectionObserversManagerService extends Service
|
|||
if (mHostConnectionObserver != null) {
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
}
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
|
@ -165,6 +181,7 @@ public class ConnectionObserversManagerService extends Service
|
|||
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||
observer.playerOnPlay(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
}
|
||||
somethingPlaying = true;
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
|
@ -173,6 +190,7 @@ public class ConnectionObserversManagerService extends Service
|
|||
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||
observer.playerOnPause(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
}
|
||||
somethingPlaying = true;
|
||||
}
|
||||
|
||||
public void playerOnStop() {
|
||||
|
@ -180,30 +198,43 @@ public class ConnectionObserversManagerService extends Service
|
|||
observer.playerOnStop();
|
||||
}
|
||||
|
||||
// Stop service
|
||||
LogUtils.LOGD(TAG, "Player stopped");
|
||||
// if (mHostConnectionObserver != null) {
|
||||
// mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
// }
|
||||
// stopSelf();
|
||||
somethingPlaying = false;
|
||||
|
||||
// Stop service if nothing starts in a couple of seconds
|
||||
mStopHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!somethingPlaying) {
|
||||
LogUtils.LOGD(TAG, "Stopping service");
|
||||
if (mHostConnectionObserver != null) {
|
||||
mHostConnectionObserver.unregisterPlayerObserver(ConnectionObserversManagerService.this);
|
||||
}
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
public void playerNoResultsYet() {
|
||||
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||
observer.playerNoResultsYet();
|
||||
}
|
||||
somethingPlaying = false;
|
||||
}
|
||||
|
||||
public void playerOnConnectionError(int errorCode, String description) {
|
||||
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||
observer.playerOnConnectionError(errorCode, description);
|
||||
}
|
||||
somethingPlaying = false;
|
||||
|
||||
// Stop service
|
||||
LogUtils.LOGD(TAG, "Shutting down observer service - Connection error");
|
||||
if (mHostConnectionObserver != null) {
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
}
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
|
@ -211,12 +242,14 @@ public class ConnectionObserversManagerService extends Service
|
|||
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||
observer.systemOnQuit();
|
||||
}
|
||||
somethingPlaying = false;
|
||||
|
||||
// Stop service
|
||||
LogUtils.LOGD(TAG, "Shutting down observer service - System quit");
|
||||
if (mHostConnectionObserver != null) {
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
}
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
|
@ -233,6 +266,7 @@ public class ConnectionObserversManagerService extends Service
|
|||
}
|
||||
// Called when the user changes host
|
||||
LogUtils.LOGD(TAG, "Shutting down observer service - Stop observing");
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,11 @@ package org.xbmc.kore.service;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -52,11 +55,15 @@ public class NotificationObserver
|
|||
implements HostConnectionObserver.PlayerEventsObserver {
|
||||
public static final String TAG = LogUtils.makeLogTag(NotificationObserver.class);
|
||||
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
public static final int NOTIFICATION_ID = 1;
|
||||
public static final String NOTIFICATION_CHANNEL = "KORE";
|
||||
|
||||
private PendingIntent mRemoteStartPendingIntent;
|
||||
private Service mService;
|
||||
|
||||
private Notification mNothingPlayingNotification;
|
||||
|
||||
|
||||
public NotificationObserver(Service service) {
|
||||
this.mService = service;
|
||||
|
||||
|
@ -65,6 +72,12 @@ public class NotificationObserver
|
|||
stackBuilder.addParentStack(RemoteActivity.class);
|
||||
stackBuilder.addNextIntent(new Intent(mService, RemoteActivity.class));
|
||||
mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
// Create the notification channel
|
||||
if (Utils.isOreoOrLater()) {
|
||||
buildNotificationChannel();
|
||||
}
|
||||
mNothingPlayingNotification = buildNothingPlayingNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,21 +91,21 @@ public class NotificationObserver
|
|||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
buildNotification(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
notifyPlaying(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
buildNotification(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
notifyPlaying(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
}
|
||||
|
||||
public void playerOnStop() {
|
||||
removeNotification();
|
||||
notifyNothingPlaying();
|
||||
}
|
||||
|
||||
public void playerNoResultsYet() {
|
||||
removeNotification();
|
||||
notifyNothingPlaying();
|
||||
}
|
||||
|
||||
public void playerOnConnectionError(int errorCode, String description) {
|
||||
|
@ -111,13 +124,53 @@ public class NotificationObserver
|
|||
removeNotification();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private void buildNotificationChannel() {
|
||||
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(NOTIFICATION_CHANNEL,
|
||||
mService.getString(R.string.app_name),
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
channel.enableLights(false);
|
||||
channel.enableVibration(false);
|
||||
channel.setShowBadge(false);
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
// Picasso target that will be used to load images
|
||||
private static Target picassoTarget = null;
|
||||
|
||||
private Notification buildNothingPlayingNotification() {
|
||||
int smallIcon = R.drawable.ic_devices_white_24dp;
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, NOTIFICATION_CHANNEL);
|
||||
return builder
|
||||
.setSmallIcon(smallIcon)
|
||||
.setShowWhen(false)
|
||||
.setOngoing(true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
||||
.setContentIntent(mRemoteStartPendingIntent)
|
||||
.setContentTitle(String.format(mService.getString(R.string.connected_to),
|
||||
HostManager.getInstance(mService).getHostInfo().getName()))
|
||||
.setContentText(mService.getString(R.string.nothing_playing))
|
||||
.build();
|
||||
}
|
||||
|
||||
public Notification getNothingPlayingNotification() {
|
||||
if (mNothingPlayingNotification == null) {
|
||||
mNothingPlayingNotification = buildNothingPlayingNotification();
|
||||
}
|
||||
return mNothingPlayingNotification;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private void buildNotification(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
private void notifyPlaying(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
final String title, underTitle, poster;
|
||||
int smallIcon, playPauseIcon, rewindIcon, ffIcon;
|
||||
|
||||
|
@ -212,7 +265,7 @@ public class NotificationObserver
|
|||
}
|
||||
|
||||
// Build the notification
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, NOTIFICATION_CHANNEL);
|
||||
final Notification notification = builder
|
||||
.setSmallIcon(smallIcon)
|
||||
.setShowWhen(false)
|
||||
|
@ -270,10 +323,9 @@ public class NotificationObserver
|
|||
expandedRV.setImageViewBitmap(expandedIconResId, bitmap);
|
||||
}
|
||||
|
||||
// NotificationManager notificationManager =
|
||||
// (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
mService.startForeground(NOTIFICATION_ID, notification);
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
picassoTarget = null;
|
||||
}
|
||||
};
|
||||
|
@ -296,9 +348,14 @@ public class NotificationObserver
|
|||
}
|
||||
|
||||
private void removeNotification() {
|
||||
// NotificationManager notificationManager =
|
||||
// (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
// notificationManager.cancel(NOTIFICATION_ID);
|
||||
mService.stopForeground(true);
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
private void notifyNothingPlaying() {
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(NOTIFICATION_ID, mNothingPlayingNotification);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ import org.xbmc.kore.ui.volumecontrollers.VolumeKeyActionHandler;
|
|||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.TabsAdapter;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
import org.xbmc.kore.utils.Utils;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
@ -623,32 +624,20 @@ public class RemoteActivity extends BaseActivity
|
|||
}
|
||||
lastImageUrl = imageUrl;
|
||||
|
||||
// Start service that manages connection observers
|
||||
LogUtils.LOGD(TAG, "Starting observer service");
|
||||
startService(new Intent(this, ConnectionObserversManagerService.class));
|
||||
|
||||
|
||||
// // Check whether we should show a notification
|
||||
// boolean showNotification = PreferenceManager
|
||||
// .getDefaultSharedPreferences(this)
|
||||
// .getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION,
|
||||
// Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
|
||||
// if (showNotification) {
|
||||
// // Let's start the notification service
|
||||
// LogUtils.LOGD(TAG, "Starting notification service");
|
||||
// startService(new Intent(this, NotificationObserver.class));
|
||||
// }
|
||||
//
|
||||
// // Check whether we should react to phone state changes
|
||||
// boolean shouldPause = PreferenceManager
|
||||
// .getDefaultSharedPreferences(this)
|
||||
// .getBoolean(Settings.KEY_PREF_USE_HARDWARE_VOLUME_KEYS,
|
||||
// Settings.DEFAULT_PREF_USE_HARDWARE_VOLUME_KEYS);
|
||||
// if (shouldPause) {
|
||||
// // Let's start the listening service
|
||||
// LogUtils.LOGD(TAG, "Starting phone state listener");
|
||||
// startService(new Intent(this, PauseCallObserver.class));
|
||||
// }
|
||||
// Check whether we should show a notification
|
||||
boolean showNotification = PreferenceManager
|
||||
.getDefaultSharedPreferences(this)
|
||||
.getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION,
|
||||
Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
|
||||
if (showNotification) {
|
||||
// Start service that manages connection observers
|
||||
LogUtils.LOGD(TAG, "Starting observer service");
|
||||
if (Utils.isOreoOrLater()) {
|
||||
startForegroundService(new Intent(this, ConnectionObserversManagerService.class));
|
||||
} else {
|
||||
startService(new Intent(this, ConnectionObserversManagerService.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
package org.xbmc.kore.ui.sections.settings;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
|
@ -40,6 +42,8 @@ import org.xbmc.kore.utils.Utils;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.xbmc.kore.service.NotificationObserver.NOTIFICATION_ID;
|
||||
|
||||
/**
|
||||
* Simple fragment to display preferences screen
|
||||
*/
|
||||
|
@ -144,12 +148,22 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||
}
|
||||
}
|
||||
|
||||
// If one of the settings that use the observer service are modified, restart it
|
||||
// If one of the settings that use the observer service are modified, restart it
|
||||
if (key.equals(Settings.KEY_PREF_SHOW_NOTIFICATION) || key.equals(Settings.KEY_PREF_PAUSE_DURING_CALLS)) {
|
||||
LogUtils.LOGD(TAG, "Stoping connection observer service");
|
||||
Intent intent = new Intent(getActivity(), ConnectionObserversManagerService.class);
|
||||
getActivity().stopService(intent);
|
||||
getActivity().startService(intent);
|
||||
if (sharedPreferences.getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION, Settings.DEFAULT_PREF_SHOW_NOTIFICATION)) {
|
||||
if (Utils.isOreoOrLater()) {
|
||||
getActivity().startForegroundService(intent);
|
||||
} else {
|
||||
getActivity().startService(intent);
|
||||
}
|
||||
} else {
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,10 @@ public class Utils {
|
|||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
||||
}
|
||||
|
||||
public static boolean isOreoOrLater() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
}
|
||||
|
||||
public static boolean isLollipopAndPreOreo() {
|
||||
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) &&
|
||||
(Build.VERSION.SDK_INT < 27);
|
||||
|
|
|
@ -64,17 +64,18 @@
|
|||
android:title="@string/show_notification"
|
||||
android:defaultValue="false"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="pref_pause_during_calls"
|
||||
android:title="@string/pause_during_calls"
|
||||
android:defaultValue="false"
|
||||
android:dependency="pref_show_notification"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="pref_show_nowplayingpanel"
|
||||
android:title="@string/show_now_playing_panel"
|
||||
android:summary="@string/show_now_playing_panel_summary"
|
||||
android:defaultValue="true"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="pref_pause_during_calls"
|
||||
android:title="@string/pause_during_calls"
|
||||
android:defaultValue="false"/>
|
||||
|
||||
<MultiSelectListPreference
|
||||
android:key="pref_nav_drawer_items"
|
||||
android:title="@string/nav_drawer_items"
|
||||
|
|
Loading…
Reference in New Issue