diff --git a/app/src/main/java/org/xbmc/kore/host/HostConnectionObserver.java b/app/src/main/java/org/xbmc/kore/host/HostConnectionObserver.java index 9c67d30..2f25b31 100644 --- a/app/src/main/java/org/xbmc/kore/host/HostConnectionObserver.java +++ b/app/src/main/java/org/xbmc/kore/host/HostConnectionObserver.java @@ -857,7 +857,8 @@ public class HostConnectionObserver private boolean getItemResultChanged(ListType.ItemsAll getItemResult) { return (hostState.lastGetItemResult == null) || (hostState.lastGetItemResult.id != getItemResult.id) || - (!hostState.lastGetItemResult.label.equals(getItemResult.label)); + ((hostState.lastGetItemResult.label != null && + !hostState.lastGetItemResult.label.equals(getItemResult.label))); } /** diff --git a/app/src/main/java/org/xbmc/kore/service/ConnectionObserversManagerService.java b/app/src/main/java/org/xbmc/kore/service/ConnectionObserversManagerService.java index ad4c34c..d5f6deb 100644 --- a/app/src/main/java/org/xbmc/kore/service/ConnectionObserversManagerService.java +++ b/app/src/main/java/org/xbmc/kore/service/ConnectionObserversManagerService.java @@ -49,13 +49,13 @@ public class ConnectionObserversManagerService extends Service implements HostConnectionObserver.PlayerEventsObserver { public static final String TAG = LogUtils.makeLogTag(ConnectionObserversManagerService.class); - private HostConnectionObserver mHostConnectionObserver = null; + private HostConnectionObserver hostConnectionObserver = null; - private List mConnectionObservers = new ArrayList<>(); - private NotificationObserver mNotificationObserver; + private List observers = new ArrayList<>(); + private NotificationObserver notificationObserver; - private boolean somethingPlaying = false; - private Handler mStopHandler = new Handler(); + private boolean somethingIsPlaying = false; + private Handler stopHandler = new Handler(); @Override public void onCreate() { @@ -67,59 +67,39 @@ public class ConnectionObserversManagerService extends Service @Override public int onStartCommand(Intent intent, int flags, int startId) { LogUtils.LOGD(TAG, "onStartCommand"); - // Create the observers we are managing - createObservers(); - // If no observers created, stop immediately - if (mConnectionObservers.isEmpty()) { - LogUtils.LOGD(TAG, "No observers, stopping observer service."); - stopSelf(); - return START_NOT_STICKY; + // Create the observers we are managing and immediatelly call + // startForeground() to avoid ANRs + if (observers.isEmpty()) { + createObservers(); + } + startForeground(NotificationObserver.NOTIFICATION_ID, + notificationObserver.getCurrentNotification()); + + HostConnectionObserver connectionObserver = + HostManager.getInstance(this).getHostConnectionObserver(); + + if (hostConnectionObserver == null) { + hostConnectionObserver = connectionObserver; + hostConnectionObserver.registerPlayerObserver(this, true); + } else if (hostConnectionObserver != connectionObserver) { + // There has been a change in hosts. + // Unregister the previous one and register the current one + hostConnectionObserver.unregisterPlayerObserver(this); + hostConnectionObserver = connectionObserver; + hostConnectionObserver.registerPlayerObserver(this, true); } - // Get the connection observer here, not on create to check if - // there has been a change in hosts, and if so unregister the previous one - HostConnectionObserver connectionObserver = HostManager.getInstance(this).getHostConnectionObserver(); - - // If we are already initialized and the same host, exit - if (mHostConnectionObserver == connectionObserver) { - LogUtils.LOGD(TAG, "Already initialized"); - 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); - } - - // Register us on the connection observer - mHostConnectionObserver = connectionObserver; - mHostConnectionObserver.registerPlayerObserver(this, true); - - // If we get killed, after returning from here, don't restart + // If we get killed after returning from here, restart return START_STICKY; } private void createObservers() { - mConnectionObservers = new ArrayList<>(); + observers = new ArrayList<>(); // 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); + notificationObserver = new NotificationObserver(this); + observers.add(notificationObserver); // Check whether we should react to phone state changes and wether // we have permissions to do so @@ -128,9 +108,10 @@ public class ConnectionObserversManagerService extends Service .getBoolean(Settings.KEY_PREF_PAUSE_DURING_CALLS, Settings.DEFAULT_PREF_PAUSE_DURING_CALLS); boolean hasPhonePermission = - ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; + ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == + PackageManager.PERMISSION_GRANTED; if (shouldPause && hasPhonePermission) { - mConnectionObservers.add(new PauseCallObserver(this)); + observers.add(new PauseCallObserver(this)); } } @@ -143,13 +124,13 @@ public class ConnectionObserversManagerService extends Service @Override public void onTaskRemoved (Intent rootIntent) { // Gracefully stop - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerOnConnectionError(0, "Task removed"); } LogUtils.LOGD(TAG, "Shutting down observer service - Task removed"); - if (mHostConnectionObserver != null) { - mHostConnectionObserver.unregisterPlayerObserver(this); + if (hostConnectionObserver != null) { + hostConnectionObserver.unregisterPlayerObserver(this); } stopForeground(true); stopSelf(); @@ -158,12 +139,12 @@ public class ConnectionObserversManagerService extends Service @Override public void onDestroy() { // Gracefully stop - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerOnConnectionError(0, "Service destroyed"); } LogUtils.LOGD(TAG, "Shutting down observer service - destroyed"); - if (mHostConnectionObserver != null) { - mHostConnectionObserver.unregisterPlayerObserver(this); + if (hostConnectionObserver != null) { + hostConnectionObserver.unregisterPlayerObserver(this); } } @@ -178,36 +159,36 @@ public class ConnectionObserversManagerService extends Service public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult, PlayerType.PropertyValue getPropertiesResult, ListType.ItemsAll getItemResult) { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerOnPlay(getActivePlayerResult, getPropertiesResult, getItemResult); } - somethingPlaying = true; + somethingIsPlaying = true; } public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult, PlayerType.PropertyValue getPropertiesResult, ListType.ItemsAll getItemResult) { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerOnPause(getActivePlayerResult, getPropertiesResult, getItemResult); } - somethingPlaying = true; + somethingIsPlaying = true; } public void playerOnStop() { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerOnStop(); } - somethingPlaying = false; + somethingIsPlaying = false; // Stop service if nothing starts in a couple of seconds - mStopHandler.postDelayed(new Runnable() { + stopHandler.postDelayed(new Runnable() { @Override public void run() { - if (!somethingPlaying) { + if (!somethingIsPlaying) { LogUtils.LOGD(TAG, "Stopping service"); - if (mHostConnectionObserver != null) { - mHostConnectionObserver.unregisterPlayerObserver(ConnectionObserversManagerService.this); + if (hostConnectionObserver != null) { + hostConnectionObserver.unregisterPlayerObserver(ConnectionObserversManagerService.this); } stopForeground(true); stopSelf(); @@ -217,37 +198,37 @@ public class ConnectionObserversManagerService extends Service } public void playerNoResultsYet() { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerNoResultsYet(); } - somethingPlaying = false; + somethingIsPlaying = false; } public void playerOnConnectionError(int errorCode, String description) { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.playerOnConnectionError(errorCode, description); } - somethingPlaying = false; + somethingIsPlaying = false; // Stop service LogUtils.LOGD(TAG, "Shutting down observer service - Connection error"); - if (mHostConnectionObserver != null) { - mHostConnectionObserver.unregisterPlayerObserver(this); + if (hostConnectionObserver != null) { + hostConnectionObserver.unregisterPlayerObserver(this); } stopForeground(true); stopSelf(); } public void systemOnQuit() { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.systemOnQuit(); } - somethingPlaying = false; + somethingIsPlaying = false; // Stop service LogUtils.LOGD(TAG, "Shutting down observer service - System quit"); - if (mHostConnectionObserver != null) { - mHostConnectionObserver.unregisterPlayerObserver(this); + if (hostConnectionObserver != null) { + hostConnectionObserver.unregisterPlayerObserver(this); } stopForeground(true); stopSelf(); @@ -255,13 +236,13 @@ public class ConnectionObserversManagerService extends Service // Ignore this public void inputOnInputRequested(String title, String type, String value) { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.inputOnInputRequested(title, type, value); } } public void observerOnStopObserving() { - for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) { + for (HostConnectionObserver.PlayerEventsObserver observer : observers) { observer.observerOnStopObserving(); } // Called when the user changes host diff --git a/app/src/main/java/org/xbmc/kore/service/NotificationObserver.java b/app/src/main/java/org/xbmc/kore/service/NotificationObserver.java index 627effd..d4d771f 100644 --- a/app/src/main/java/org/xbmc/kore/service/NotificationObserver.java +++ b/app/src/main/java/org/xbmc/kore/service/NotificationObserver.java @@ -58,26 +58,26 @@ public class NotificationObserver public static final int NOTIFICATION_ID = 1; private static final String NOTIFICATION_CHANNEL = "KORE"; - private PendingIntent mRemoteStartPendingIntent; - private Service mService; - - private Notification mNothingPlayingNotification; + private PendingIntent remoteStartPendingIntent; + private Service service; + private Notification nothingPlayingNotification; + private Notification currentNotification = null; public NotificationObserver(Service service) { - this.mService = service; + this.service = service; // Create the intent to start the remote when the user taps the notification - TaskStackBuilder stackBuilder = TaskStackBuilder.create(mService); + TaskStackBuilder stackBuilder = TaskStackBuilder.create(this.service); stackBuilder.addParentStack(RemoteActivity.class); - stackBuilder.addNextIntent(new Intent(mService, RemoteActivity.class)); - mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); + stackBuilder.addNextIntent(new Intent(this.service, RemoteActivity.class)); + remoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Create the notification channel if (Utils.isOreoOrLater()) { buildNotificationChannel(); } - mNothingPlayingNotification = buildNothingPlayingNotification(); + nothingPlayingNotification = buildNothingPlayingNotification(); } @Override @@ -129,14 +129,14 @@ public class NotificationObserver NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL, - mService.getString(R.string.app_name), + service.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) service.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager != null) notificationManager.createNotificationChannel(channel); } @@ -147,25 +147,28 @@ public class NotificationObserver private Notification buildNothingPlayingNotification() { int smallIcon = R.drawable.ic_devices_white_24dp; - NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, NOTIFICATION_CHANNEL); + NotificationCompat.Builder builder = new NotificationCompat.Builder(service, 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)) + .setContentIntent(remoteStartPendingIntent) + .setContentTitle(String.format(service.getString(R.string.connected_to), + HostManager.getInstance(service).getHostInfo().getName())) + .setContentText(service.getString(R.string.nothing_playing)) .build(); } - public Notification getNothingPlayingNotification() { - if (mNothingPlayingNotification == null) { - mNothingPlayingNotification = buildNothingPlayingNotification(); + public Notification getCurrentNotification() { + if (currentNotification == null) { + if (nothingPlayingNotification == null) { + nothingPlayingNotification = buildNothingPlayingNotification(); + } + currentNotification = nothingPlayingNotification; } - return mNothingPlayingNotification; + return currentNotification; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @@ -187,7 +190,7 @@ public class NotificationObserver break; case ListType.ItemsAll.TYPE_EPISODE: title = getItemResult.title; - String seasonEpisode = String.format(mService.getString(R.string.season_episode_abbrev), + String seasonEpisode = String.format(service.getString(R.string.season_episode_abbrev), getItemResult.season, getItemResult.episode); underTitle = String.format("%s | %s", getItemResult.showtitle, seasonEpisode); poster = getItemResult.art.poster; @@ -232,7 +235,7 @@ public class NotificationObserver PendingIntent rewindPendingIntent, ffPendingIntent, playPausePendingIntent; playPausePendingIntent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_PLAY_PAUSE); boolean useSeekJump = PreferenceManager - .getDefaultSharedPreferences(this.mService) + .getDefaultSharedPreferences(this.service) .getBoolean(Settings.KEY_PREF_NOTIFICATION_SEEK_JUMP, Settings.DEFAULT_PREF_NOTIFICATION_SEEK_JUMP); if (getItemResult.type.equals(ListType.ItemsAll.TYPE_SONG)) { rewindPendingIntent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_PREVIOUS); @@ -252,17 +255,17 @@ public class NotificationObserver } final NotificationCompat.Builder builder = - new NotificationCompat.Builder(mService, NOTIFICATION_CHANNEL) + new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(smallIcon) .setShowWhen(false) .setOngoing(true) - .addAction(rewindIcon, mService.getString(R.string.rewind), rewindPendingIntent) // #0 - .addAction(playPauseIcon, mService.getString(R.string.play), playPausePendingIntent) // #1 - .addAction(ffIcon, mService.getString(R.string.fast_forward), ffPendingIntent) // #2 + .addAction(rewindIcon, service.getString(R.string.rewind), rewindPendingIntent) // #0 + .addAction(playPauseIcon, service.getString(R.string.play), playPausePendingIntent) // #1 + .addAction(ffIcon, service.getString(R.string.fast_forward), ffPendingIntent) // #2 .setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle() .setShowActionsInCompactView(0, 1, 2)) - .setContentIntent(mRemoteStartPendingIntent) + .setContentIntent(remoteStartPendingIntent) .setContentTitle(title) .setContentText(underTitle); @@ -285,7 +288,7 @@ public class NotificationObserver // // 4. We specifically resize the image to the same dimensions used in // the remote, so that Picasso reuses it in the remote and here from the cache - Resources resources = mService.getResources(); + Resources resources = service.getResources(); final int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width); final int posterHeight = isVideo? resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height): @@ -299,7 +302,7 @@ public class NotificationObserver @Override public void onBitmapFailed(Drawable errorDrawable) { - CharacterDrawable avatarDrawable = UIUtils.getCharacterAvatar(mService, title); + CharacterDrawable avatarDrawable = UIUtils.getCharacterAvatar(service, title); showNotification(Utils.drawableToBitmap(avatarDrawable, posterWidth, posterHeight)); } @@ -309,15 +312,17 @@ public class NotificationObserver private void showNotification(Bitmap bitmap) { builder.setLargeIcon(bitmap); NotificationManager notificationManager = - (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) - notificationManager.notify(NOTIFICATION_ID, builder.build()); + (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + currentNotification = builder.build(); + notificationManager.notify(NOTIFICATION_ID, currentNotification); + } picassoTarget = null; } }; // Load the image - HostManager hostManager = HostManager.getInstance(mService); + HostManager hostManager = HostManager.getInstance(service); hostManager.getPicasso() .load(hostManager.getHostInfo().getImageUrl(poster)) .resize(posterWidth, posterHeight) @@ -326,24 +331,28 @@ public class NotificationObserver } private PendingIntent buildActionPendingIntent(int playerId, String action) { - Intent intent = new Intent(mService, IntentActionsService.class) + Intent intent = new Intent(service, IntentActionsService.class) .setAction(action) .putExtra(IntentActionsService.EXTRA_PLAYER_ID, playerId); - return PendingIntent.getService(mService, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getService(service, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } private void removeNotification() { NotificationManager notificationManager = - (NotificationManager)mService.getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) + (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { notificationManager.cancel(NOTIFICATION_ID); + currentNotification = null; + } } private void notifyNothingPlaying() { NotificationManager notificationManager = - (NotificationManager)mService.getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) - notificationManager.notify(NOTIFICATION_ID, mNothingPlayingNotification); + (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) { + notificationManager.notify(NOTIFICATION_ID, nothingPlayingNotification); + currentNotification = nothingPlayingNotification; + } } }