summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
authororionlee <orionlee@yahoo.com>2019-01-05 19:12:29 -0800
committerorionlee <orionlee@yahoo.com>2019-02-27 14:52:34 -0800
commit3f14fac4790f54a911f5b4b5ee8a81cf2ee0eea3 (patch)
treec1345147f7c300bb3d582126495b4ebddaff3fbd /core/src/main
parente26a54bdbccac6eb9d9060b725192c575b3913bb (diff)
downloadAntennaPod-3f14fac4790f54a911f5b4b5ee8a81cf2ee0eea3.zip
remove static PlaybackService.started, in favor of the start state
managed by inner ServiceManager. Also add a generic java8-like Optional class for use with RxJava2 where null was to be returned (RxJava2 requires non-null).
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Optional.java213
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java32
3 files changed, 240 insertions, 37 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index f34866c25..10175b59c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -201,10 +201,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*/
public static boolean isRunning = false;
/**
- * Is true if service has received a valid start command.
- */
- public static boolean started = false;
- /**
* Is true if the service was running, but paused due to headphone disconnect
*/
private static boolean transientPause = false;
@@ -349,7 +345,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service is about to be destroyed");
stopForeground(true); // TODO: [2716] unclear if stopForeground is still needed
isRunning = false;
- started = false;
currentMediaType = MediaType.UNKNOWN;
PreferenceManager.getDefaultSharedPreferences(this)
@@ -488,7 +483,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return Service.START_NOT_STICKY;
}
} else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
- started = true;
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
true);
boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
@@ -576,7 +570,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case KeyEvent.KEYCODE_MEDIA_STOP:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(true, true);
- started = false;
}
stopForeground(true); // gets rid of persistent notification // TODO: [2716] unclear if stopForeground is still needed
@@ -595,7 +588,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(getApplicationContext());
if (playable != null) {
mediaPlayer.playMediaObject(playable, false, true, true);
- started = true;
PlaybackService.this.updateMediaSessionMetadata(playable);
}
}
@@ -685,7 +677,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case PLAYING:
writePlayerStatusPlaybackPreferences();
- started = true;
// set sleep timer if auto-enabled
if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
@@ -1176,7 +1167,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, imageLocation);
}
}
- if (!Thread.currentThread().isInterrupted() && started) {
+ if (!Thread.currentThread().isInterrupted() && isStarted()) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT));
@@ -1205,7 +1196,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (playable == null) {
Log.d(TAG, "setupNotification: playable is null");
- if (!started) {
+ if (!isStarted()) {
stopService();
}
return;
@@ -1219,7 +1210,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (mediaPlayer == null) {
Log.d(TAG, "notificationSetupTask: mediaPlayer is null");
- if (!started) {
+ if (!isStarted()) {
stopService();
}
return;
@@ -1247,7 +1238,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
Log.v(TAG, "notificationSetupTask: playerStatus=" + playerStatus);
- if (!Thread.currentThread().isInterrupted() && started) {
+ if (!Thread.currentThread().isInterrupted() && isStarted()) {
String contentText = playable.getEpisodeTitle();
String contentTitle = playable.getFeedTitle();
Notification notification;
@@ -1909,6 +1900,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
};
+ private boolean isStarted() {
+ return serviceManager.serviceInStartedState;
+ }
+
/**
* The helper that manages PlaybackService's foreground service life cycle and the associated
* notification control.
@@ -1918,8 +1913,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*
*/
private class ServiceManager {
- private boolean mServiceInStartedState; // TODO: rationalize it with parent's static started
- // TODO LATER: rename the variable, do not use m prefix
+ private boolean serviceInStartedState;
/**
*
@@ -1948,13 +1942,13 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
private void moveServiceToStartedState(PlaybackStateCompat state) {
- Log.v(TAG, "DBG - ServiceManager.moveServiceToStartedState() - mServiceInStartedState:" + mServiceInStartedState);
+ Log.v(TAG, "DBG - ServiceManager.moveServiceToStartedState() - serviceInStartedState:" + serviceInStartedState);
- if (!mServiceInStartedState) {
+ if (!serviceInStartedState) {
ContextCompat.startForegroundService(
PlaybackService.this,
new Intent(PlaybackService.this, PlaybackService.class));
- mServiceInStartedState = true;
+ serviceInStartedState = true;
}
doSetupNotification();
@@ -1967,7 +1961,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private void moveServiceOutOfStartedState(PlaybackStateCompat state) {
stopForeground(true);
stopSelf();
- mServiceInStartedState = false;
+ serviceInStartedState = false;
}
private void doSetupNotification() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
new file mode 100644
index 000000000..0fe11ec53
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package de.danoeh.antennapod.core.util;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+// AntennaPod's stripped-down version of Java/Android platform's java.util.Optional
+// so that it can be used on lower API level (API level 14)
+
+// Android-changed: removed ValueBased paragraph.
+/**
+ * A container object which may or may not contain a non-null value.
+ * If a value is present, {@code isPresent()} will return {@code true} and
+ * {@code get()} will return the value.
+ *
+ * <p>Additional methods that depend on the presence or absence of a contained
+ * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
+ * (return a default value if value not present) and
+ * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
+ * of code if the value is present).
+ *
+ * @since 1.8
+ */
+public final class Optional<T> {
+ /**
+ * Common instance for {@code empty()}.
+ */
+ private static final Optional<?> EMPTY = new Optional<>();
+
+ /**
+ * If non-null, the value; if null, indicates no value is present
+ */
+ private final T value;
+
+ /**
+ * Constructs an empty instance.
+ *
+ * @implNote Generally only one empty instance, {@link Optional#EMPTY},
+ * should exist per VM.
+ */
+ private Optional() {
+ this.value = null;
+ }
+
+ /**
+ * Returns an empty {@code Optional} instance. No value is present for this
+ * Optional.
+ *
+ * @apiNote Though it may be tempting to do so, avoid testing if an object
+ * is empty by comparing with {@code ==} against instances returned by
+ * {@code Option.empty()}. There is no guarantee that it is a singleton.
+ * Instead, use {@link #isPresent()}.
+ *
+ * @param <T> Type of the non-existent value
+ * @return an empty {@code Optional}
+ */
+ public static<T> Optional<T> empty() {
+ @SuppressWarnings("unchecked")
+ Optional<T> t = (Optional<T>) EMPTY;
+ return t;
+ }
+
+ /**
+ * Constructs an instance with the value present.
+ *
+ * @param value the non-null value to be present
+ * @throws NullPointerException if value is null
+ */
+ private Optional(T value) {
+ this.value = Objects.requireNonNull(value);
+ }
+
+ /**
+ * Returns an {@code Optional} with the specified present non-null value.
+ *
+ * @param <T> the class of the value
+ * @param value the value to be present, which must be non-null
+ * @return an {@code Optional} with the value present
+ * @throws NullPointerException if value is null
+ */
+ public static <T> Optional<T> of(T value) {
+ return new Optional<>(value);
+ }
+
+ /**
+ * Returns an {@code Optional} describing the specified value, if non-null,
+ * otherwise returns an empty {@code Optional}.
+ *
+ * @param <T> the class of the value
+ * @param value the possibly-null value to describe
+ * @return an {@code Optional} with a present value if the specified value
+ * is non-null, otherwise an empty {@code Optional}
+ */
+ public static <T> Optional<T> ofNullable(T value) {
+ return value == null ? empty() : of(value);
+ }
+
+ /**
+ * If a value is present in this {@code Optional}, returns the value,
+ * otherwise throws {@code NoSuchElementException}.
+ *
+ * @return the non-null value held by this {@code Optional}
+ * @throws NoSuchElementException if there is no value present
+ *
+ * @see Optional#isPresent()
+ */
+ public T get() {
+ if (value == null) {
+ throw new NoSuchElementException("No value present");
+ }
+ return value;
+ }
+
+ /**
+ * Return {@code true} if there is a value present, otherwise {@code false}.
+ *
+ * @return {@code true} if there is a value present, otherwise {@code false}
+ */
+ public boolean isPresent() {
+ return value != null;
+ }
+
+
+ /**
+ * Return the value if present, otherwise return {@code other}.
+ *
+ * @param other the value to be returned if there is no value present, may
+ * be null
+ * @return the value, if present, otherwise {@code other}
+ */
+ public T orElse(T other) {
+ return value != null ? value : other;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this Optional. The
+ * other object is considered equal if:
+ * <ul>
+ * <li>it is also an {@code Optional} and;
+ * <li>both instances have no value present or;
+ * <li>the present values are "equal to" each other via {@code equals()}.
+ * </ul>
+ *
+ * @param obj an object to be tested for equality
+ * @return {code true} if the other object is "equal to" this object
+ * otherwise {@code false}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Optional)) {
+ return false;
+ }
+
+ Optional<?> other = (Optional<?>) obj;
+ return (value == other.value) || (value != null && value.equals(other.value));
+ }
+
+ /**
+ * Returns the hash code value of the present value, if any, or 0 (zero) if
+ * no value is present.
+ *
+ * @return hash code value of the present value or 0 if no value is present
+ */
+ @Override
+ public int hashCode() {
+ return value != null ? value.hashCode() : 0;
+ }
+
+ /**
+ * Returns a non-empty string representation of this Optional suitable for
+ * debugging. The exact presentation format is unspecified and may vary
+ * between implementations and versions.
+ *
+ * @implSpec If a value is present the result must include its string
+ * representation in the result. Empty and present Optionals must be
+ * unambiguously differentiable.
+ *
+ * @return the string representation of this instance
+ */
+ @Override
+ public String toString() {
+ return value != null
+ ? String.format("Optional[%s]", value)
+ : "Optional.empty";
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index 565ef8a96..fd87c9905 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -12,7 +12,6 @@ import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -36,6 +35,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.Optional;
import de.danoeh.antennapod.core.util.playback.Playable.PlayableUtils;
import io.reactivex.Maybe;
import io.reactivex.MaybeOnSubscribe;
@@ -187,21 +187,15 @@ public abstract class PlaybackController {
serviceBinder = Observable.fromCallable(this::getPlayLastPlayedMediaIntent)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(intent -> {
+ .subscribe(optionalIntent -> {
boolean bound = false;
- if (!PlaybackService.started) {
- if (intent != null) {
- Log.d(TAG, "Calling start service");
- bound = activity.bindService(intent, mConnection, 0);
- } else {
- status = PlayerStatus.STOPPED;
- setupGUI();
- handleStatus();
- }
+ if (optionalIntent.isPresent()) {
+ Log.d(TAG, "Calling bind service");
+ bound = activity.bindService(optionalIntent.get(), mConnection, 0);
} else {
- Log.d(TAG, "PlaybackService is running, trying to connect without start command.");
- bound = activity.bindService(new Intent(activity, PlaybackService.class),
- mConnection, 0);
+ status = PlayerStatus.STOPPED;
+ setupGUI();
+ handleStatus();
}
Log.d(TAG, "Result for service binding: " + bound);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
@@ -211,24 +205,26 @@ public abstract class PlaybackController {
* Returns an intent that starts the PlaybackService and plays the last
* played media or null if no last played media could be found.
*/
- @Nullable private Intent getPlayLastPlayedMediaIntent() {
+ @NonNull
+ private Optional<Intent> getPlayLastPlayedMediaIntent() {
Log.d(TAG, "Trying to restore last played media");
Playable media = PlayableUtils.createInstanceFromPreferences(activity);
if (media == null) {
Log.d(TAG, "No last played media found");
- return null;
+ return Optional.empty();
}
+
boolean fileExists = media.localFileAvailable();
boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
}
- return new PlaybackServiceStarter(activity, media)
+ return Optional.of(new PlaybackServiceStarter(activity, media)
.startWhenPrepared(false)
.shouldStream(lastIsStream || !fileExists)
- .getIntent();
+ .getIntent());
}