summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java14
-rw-r--r--core/src/main/res/drawable/ic_volume_adaption_grey.xml9
-rw-r--r--core/src/main/res/drawable/ic_volume_adaption_white.xml9
-rw-r--r--core/src/main/res/values/arrays.xml12
-rw-r--r--core/src/main/res/values/attrs.xml1
-rw-r--r--core/src/main/res/values/strings.xml5
-rw-r--r--core/src/main/res/values/styles.xml2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java64
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java225
18 files changed, 475 insertions, 19 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
new file mode 100644
index 000000000..3ed84f6a8
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
@@ -0,0 +1,21 @@
+package de.danoeh.antennapod.core.event.settings;
+
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+
+public class VolumeAdaptionChangedEvent {
+ private final VolumeAdaptionSetting volumeAdaptionSetting;
+ private final long feedId;
+
+ public VolumeAdaptionChangedEvent(VolumeAdaptionSetting volumeAdaptionSetting, long feedId) {
+ this.volumeAdaptionSetting = volumeAdaptionSetting;
+ this.feedId = feedId;
+ }
+
+ public VolumeAdaptionSetting getVolumeAdaptionSetting() {
+ return volumeAdaptionSetting;
+ }
+
+ public long getFeedId() {
+ return feedId;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
index d078ec1d1..b1598f111 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
@@ -171,7 +171,7 @@ public class Feed extends FeedFile implements ImageResource {
*/
public Feed(String url, String lastUpdate, String title, String username, String password) {
this(url, lastUpdate, title);
- preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, username, password);
+ preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password);
}
public static Feed fromCursor(Cursor cursor) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
index 8fdf2034f..b24c52266 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
@@ -28,19 +28,23 @@ public class FeedPreferences {
NO
}
private AutoDeleteAction auto_delete_action;
+
+ private VolumeAdaptionSetting volumeAdaptionSetting;
+
private String username;
private String password;
private float feedPlaybackSpeed;
- public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, String username, String password) {
- this(feedID, autoDownload, true, auto_delete_action, username, password, new FeedFilter(), SPEED_USE_GLOBAL);
+ public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password) {
+ this(feedID, autoDownload, true, auto_delete_action, volumeAdaptionSetting, username, password, new FeedFilter(), SPEED_USE_GLOBAL);
}
- private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed) {
+ private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed) {
this.feedID = feedID;
this.autoDownload = autoDownload;
this.keepUpdated = keepUpdated;
this.auto_delete_action = auto_delete_action;
+ this.volumeAdaptionSetting = volumeAdaptionSetting;
this.username = username;
this.password = password;
this.filter = filter;
@@ -52,6 +56,7 @@ public class FeedPreferences {
int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD);
int indexAutoRefresh = cursor.getColumnIndex(PodDBAdapter.KEY_KEEP_UPDATED);
int indexAutoDeleteAction = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DELETE_ACTION);
+ int indexVolumeAdaption = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_VOLUME_ADAPTION);
int indexUsername = cursor.getColumnIndex(PodDBAdapter.KEY_USERNAME);
int indexPassword = cursor.getColumnIndex(PodDBAdapter.KEY_PASSWORD);
int indexIncludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_INCLUDE_FILTER);
@@ -63,12 +68,14 @@ public class FeedPreferences {
boolean autoRefresh = cursor.getInt(indexAutoRefresh) > 0;
int autoDeleteActionIndex = cursor.getInt(indexAutoDeleteAction);
AutoDeleteAction autoDeleteAction = AutoDeleteAction.values()[autoDeleteActionIndex];
+ int volumeAdaptionValue = cursor.getInt(indexVolumeAdaption);
+ VolumeAdaptionSetting volumeAdaptionSetting = VolumeAdaptionSetting.fromInteger(volumeAdaptionValue);
String username = cursor.getString(indexUsername);
String password = cursor.getString(indexPassword);
String includeFilter = cursor.getString(indexIncludeFilter);
String excludeFilter = cursor.getString(indexExcludeFilter);
float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed);
- return new FeedPreferences(feedId, autoDownload, autoRefresh, autoDeleteAction, username, password, new FeedFilter(includeFilter, excludeFilter), feedPlaybackSpeed);
+ return new FeedPreferences(feedId, autoDownload, autoRefresh, autoDeleteAction, volumeAdaptionSetting, username, password, new FeedFilter(includeFilter, excludeFilter), feedPlaybackSpeed);
}
/**
@@ -144,10 +151,18 @@ public class FeedPreferences {
return auto_delete_action;
}
+ public VolumeAdaptionSetting getVolumeAdaptionSetting() {
+ return volumeAdaptionSetting;
+ }
+
public void setAutoDeleteAction(AutoDeleteAction auto_delete_action) {
this.auto_delete_action = auto_delete_action;
}
+ public void setVolumeAdaptionSetting(VolumeAdaptionSetting volumeAdaptionSetting) {
+ this.volumeAdaptionSetting = volumeAdaptionSetting;
+ }
+
public boolean getCurrentAutoDelete() {
switch (auto_delete_action) {
case GLOBAL:
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java
new file mode 100644
index 000000000..bf4fc582a
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java
@@ -0,0 +1,32 @@
+package de.danoeh.antennapod.core.feed;
+
+public enum VolumeAdaptionSetting {
+ OFF(0, 1.0f),
+ LIGHT_REDUCTION(1, 0.5f),
+ HEAVY_REDUCTION(2, 0.2f);
+
+ private final int value;
+ private float adaptionFactor;
+
+ VolumeAdaptionSetting(int value, float adaptionFactor) {
+ this.value = value;
+ this.adaptionFactor = adaptionFactor;
+ }
+
+ public static VolumeAdaptionSetting fromInteger(int value) {
+ for (VolumeAdaptionSetting setting : values()) {
+ if (setting.value == value) {
+ return setting;
+ }
+ }
+ throw new IllegalArgumentException("Cannot map value to VolumeAdaptionSetting: " + value);
+ }
+
+ public int toInteger() {
+ return value;
+ }
+
+ public float getAdaptionFactor() {
+ return adaptionFactor;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
index 10d5bfa15..5900f84fc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
@@ -4,6 +4,7 @@ import android.util.Log;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -37,7 +38,7 @@ public class FeedParserTask implements Callable<FeedHandlerResult> {
feed.setId(request.getFeedfileId());
feed.setDownloaded(true);
feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
- request.getUsername(), request.getPassword()));
+ VolumeAdaptionSetting.OFF, request.getUsername(), request.getPassword()));
feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
DownloadError reason = null;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
index 485349cb1..9c32e42e0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
@@ -12,11 +12,11 @@ import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
-import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import org.antennapod.audio.MediaPlayer;
import java.io.File;
import java.io.IOException;
+import java.util.EnumSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
@@ -26,13 +26,17 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
import de.danoeh.antennapod.core.util.playback.AudioPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.core.util.playback.VideoPlayer;
/**
@@ -308,7 +312,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
acquireWifiLockIfNecessary();
setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
- setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
+
+ float leftVolume = UserPreferences.getLeftVolume();
+ float rightVolume = UserPreferences.getRightVolume();
+ setVolume(leftVolume, rightVolume);
if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) {
int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
@@ -661,6 +668,15 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
*/
private void setVolumeSync(float volumeLeft, float volumeRight) {
playerLock.lock();
+ Playable playable = getPlayable();
+ if (playable instanceof FeedMedia) {
+ FeedMedia feedMedia = (FeedMedia) playable;
+ FeedPreferences preferences = feedMedia.getItem().getFeed().getPreferences();
+ VolumeAdaptionSetting volumeAdaptionSetting = preferences.getVolumeAdaptionSetting();
+ float adaptionFactor = volumeAdaptionSetting.getAdaptionFactor();
+ volumeLeft *= adaptionFactor;
+ volumeRight *= adaptionFactor;
+ }
mediaPlayer.setVolume(volumeLeft, volumeRight);
Log.d(TAG, "Media player volume was set to " + volumeLeft + " " + volumeRight);
playerLock.unlock();
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 d53f7d669..2fb37cc05 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
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.core.service.playback;
-import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -51,6 +50,7 @@ import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -70,7 +70,6 @@ import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -79,6 +78,7 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Controls the MediaPlayer that plays a FeedMedia-file
@@ -277,6 +277,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(ACTION_SKIP_CURRENT_EPISODE));
registerReceiver(pausePlayCurrentEpisodeReceiver, new IntentFilter(ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ EventBus.getDefault().register(this);
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
flavorHelper = new PlaybackServiceFlavorHelper(PlaybackService.this, flavorHelperCallback);
@@ -1436,6 +1437,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
};
+ @Subscribe
+ public void volumeAdaptionChanged(VolumeAdaptionChangedEvent event) {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, event.getFeedId(), event.getVolumeAdaptionSetting());
+ }
+
public static MediaType getCurrentMediaType() {
return currentMediaType;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
new file mode 100644
index 000000000..d03830387
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
@@ -0,0 +1,36 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+class PlaybackVolumeUpdater {
+
+ public void updateVolumeIfNecessary(PlaybackServiceMediaPlayer mediaPlayer, long feedId,
+ VolumeAdaptionSetting volumeAdaptionSetting) {
+ Playable playable = mediaPlayer.getPlayable();
+
+ if (playable instanceof FeedMedia) {
+ updateFeedMediaVolumeIfNecessary(mediaPlayer, feedId, volumeAdaptionSetting, (FeedMedia) playable);
+ }
+ }
+
+ private void updateFeedMediaVolumeIfNecessary(PlaybackServiceMediaPlayer mediaPlayer, long feedId,
+ VolumeAdaptionSetting volumeAdaptionSetting, FeedMedia feedMedia) {
+ if (feedMedia.getItem().getFeed().getId() == feedId) {
+ FeedPreferences preferences = feedMedia.getItem().getFeed().getPreferences();
+ preferences.setVolumeAdaptionSetting(volumeAdaptionSetting);
+
+ if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
+ forceUpdateVolume(mediaPlayer);
+ }
+ }
+ }
+
+ private void forceUpdateVolume(PlaybackServiceMediaPlayer mediaPlayer) {
+ mediaPlayer.pause(false, false);
+ mediaPlayer.resume();
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
index 0c8f89348..234ce8367 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
@@ -294,11 +294,14 @@ class DBUpgrader {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL);
}
-
if (oldVersion < 1070401) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_SORT_ORDER + " TEXT");
}
+ if (oldVersion < 1090000) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0");
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index a7b9e44e6..17b79a3da 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -12,7 +12,6 @@ import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
-import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
@@ -97,6 +96,7 @@ public class PodDBAdapter {
public static final String KEY_AUTO_DOWNLOAD = "auto_download";
public static final String KEY_KEEP_UPDATED = "keep_updated";
public static final String KEY_AUTO_DELETE_ACTION = "auto_delete_action";
+ public static final String KEY_FEED_VOLUME_ADAPTION = "feed_volume_adaption";
public static final String KEY_PLAYED_DURATION = "played_duration";
public static final String KEY_USERNAME = "username";
public static final String KEY_PASSWORD = "password";
@@ -144,7 +144,8 @@ public class PodDBAdapter {
+ KEY_SORT_ORDER + " TEXT,"
+ KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0,"
+ KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0,"
- + KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + ")";
+ + KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + ","
+ + KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0)";
private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
@@ -241,6 +242,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_SORT_ORDER,
TABLE_NAME_FEEDS + "." + KEY_LAST_UPDATE_FAILED,
TABLE_NAME_FEEDS + "." + KEY_AUTO_DELETE_ACTION,
+ TABLE_NAME_FEEDS + "." + KEY_FEED_VOLUME_ADAPTION,
TABLE_NAME_FEEDS + "." + KEY_INCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_FEED_PLAYBACK_SPEED
@@ -403,6 +405,7 @@ public class PodDBAdapter {
values.put(KEY_AUTO_DOWNLOAD, prefs.getAutoDownload());
values.put(KEY_KEEP_UPDATED, prefs.getKeepUpdated());
values.put(KEY_AUTO_DELETE_ACTION, prefs.getAutoDeleteAction().ordinal());
+ values.put(KEY_FEED_VOLUME_ADAPTION, prefs.getVolumeAdaptionSetting().toInteger());
values.put(KEY_USERNAME, prefs.getUsername());
values.put(KEY_PASSWORD, prefs.getPassword());
values.put(KEY_INCLUDE_FILTER, prefs.getFilter().getIncludeFilter());
@@ -1333,10 +1336,7 @@ public class PodDBAdapter {
* Helper class for opening the Antennapod database.
*/
private static class PodDBHelper extends SQLiteOpenHelper {
-
- private static final int VERSION = 1070401;
-
- private final Context context;
+ private static final int VERSION = 1090000;
/**
* Constructor.
@@ -1348,7 +1348,6 @@ public class PodDBAdapter {
public PodDBHelper(final Context context, final String name,
final CursorFactory factory) {
super(context, name, factory, VERSION, new PodDbErrorHandler());
- this.context = context;
}
@Override
@@ -1367,7 +1366,6 @@ public class PodDBAdapter {
db.execSQL(CREATE_INDEX_FEEDMEDIA_FEEDITEM);
db.execSQL(CREATE_INDEX_QUEUE_FEEDITEM);
db.execSQL(CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
-
}
@Override
diff --git a/core/src/main/res/drawable/ic_volume_adaption_grey.xml b/core/src/main/res/drawable/ic_volume_adaption_grey.xml
new file mode 100644
index 000000000..fe39e1c70
--- /dev/null
+++ b/core/src/main/res/drawable/ic_volume_adaption_grey.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF757575"
+ android:pathData="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_volume_adaption_white.xml b/core/src/main/res/drawable/ic_volume_adaption_white.xml
new file mode 100644
index 000000000..27d7c6e7b
--- /dev/null
+++ b/core/src/main/res/drawable/ic_volume_adaption_white.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index facde2c59..dc79905cd 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -13,6 +13,18 @@
<item>never</item>
</string-array>
+ <string-array name="spnVolumeReductionItems">
+ <item>@string/feed_volume_reduction_off</item>
+ <item>@string/feed_volume_reduction_light</item>
+ <item>@string/feed_volume_reduction_heavy</item>
+ </string-array>
+
+ <string-array name="spnVolumeReductionValues">
+ <item>off</item>
+ <item>light</item>
+ <item>heavy</item>
+ </string-array>
+
<string-array name="smart_mark_as_played_values">
<item>0</item>
<item>15</item>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 755bbac58..53cf7b211 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -52,6 +52,7 @@
<attr name="ic_select_none" format="reference"/>
<attr name="ic_sort" format="reference"/>
<attr name="ic_key" format="reference"/>
+ <attr name="ic_volume_adaption" format="reference"/>
<attr name="ic_sd_storage" format="reference"/>
<attr name="ic_create_new_folder" format="reference"/>
<attr name="ic_cast_disconnect" format="reference"/>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index e0b1e206c..88fa07049 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -96,6 +96,11 @@
<string name="auto_download_apply_to_items_title">Apply to Previous Episodes</string>
<string name="auto_download_apply_to_items_message">The new <i>Auto Download</i> setting will automatically be applied to new episodes.\nDo you also want to apply it to previously published episodes?</string>
<string name="auto_delete_label">Auto Delete Episode</string>
+ <string name="feed_volume_reduction">Volume Reduction</string>
+ <string name="feed_volume_reduction_summary">Turn down volume for episodes of this feed: \%s</string>
+ <string name="feed_volume_reduction_off">Off</string>
+ <string name="feed_volume_reduction_light">Light</string>
+ <string name="feed_volume_reduction_heavy">Heavy</string>
<string name="parallel_downloads_suffix">\u0020parallel downloads</string>
<string name="feed_auto_download_global">Global default</string>
<string name="feed_auto_download_always">Always</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 2ccd48353..f5b43629c 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -76,6 +76,7 @@
<item name="ic_bookmark">@drawable/ic_bookmark_grey600_24dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
<item name="ic_key">@drawable/ic_key_grey600</item>
+ <item name="ic_volume_adaption">@drawable/ic_volume_adaption_grey</item>
<item name="master_switch_background">@color/master_switch_background_light</item>
<item name="currently_playing_background">@color/highlight_light</item>
@@ -164,6 +165,7 @@
<item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
<item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
<item name="ic_key">@drawable/ic_key_white</item>
+ <item name="ic_volume_adaption">@drawable/ic_volume_adaption_white</item>
<item name="master_switch_background">@color/master_switch_background_dark</item>
<item name="currently_playing_background">@color/highlight_dark</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
new file mode 100644
index 000000000..45c86cb83
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.core.feed;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class VolumeAdaptionSettingTest {
+
+ @Test
+ public void mapOffToInteger() {
+ VolumeAdaptionSetting setting = VolumeAdaptionSetting.OFF;
+ assertThat(setting.toInteger(), is(equalTo(0)));
+ }
+
+ @Test
+ public void mapLightReductionToInteger() {
+ VolumeAdaptionSetting setting = VolumeAdaptionSetting.LIGHT_REDUCTION;
+
+ assertThat(setting.toInteger(), is(equalTo(1)));
+ }
+
+ @Test
+ public void mapHeavyReductionToInteger() {
+ VolumeAdaptionSetting setting = VolumeAdaptionSetting.HEAVY_REDUCTION;
+
+ assertThat(setting.toInteger(), is(equalTo(2)));
+ }
+
+ @Test
+ public void mapIntegerToVolumeAdaptionSetting() {
+ assertThat(VolumeAdaptionSetting.fromInteger(0), is(equalTo(VolumeAdaptionSetting.OFF)));
+ assertThat(VolumeAdaptionSetting.fromInteger(1), is(equalTo(VolumeAdaptionSetting.LIGHT_REDUCTION)));
+ assertThat(VolumeAdaptionSetting.fromInteger(2), is(equalTo(VolumeAdaptionSetting.HEAVY_REDUCTION)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotMapNegativeValues() {
+ VolumeAdaptionSetting.fromInteger(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotMapValuesOutOfRange() {
+ VolumeAdaptionSetting.fromInteger(3);
+ }
+
+ @Test
+ public void noAdaptionIfTurnedOff() {
+ float adaptionFactor = VolumeAdaptionSetting.OFF.getAdaptionFactor();
+ assertEquals(1.0f, adaptionFactor, 0.01f);
+ }
+
+ @Test
+ public void lightReductionYieldsHigherValueThanHeavyReduction() {
+ float lightReductionFactor = VolumeAdaptionSetting.LIGHT_REDUCTION.getAdaptionFactor();
+
+ float heavyReductionFactor = VolumeAdaptionSetting.HEAVY_REDUCTION.getAdaptionFactor();
+
+ assertTrue("Light reduction must have higher factor than heavy reduction", lightReductionFactor > heavyReductionFactor);
+ }
+} \ No newline at end of file
diff --git a/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java b/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
new file mode 100644
index 000000000..22f67933f
--- /dev/null
+++ b/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
@@ -0,0 +1,225 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class PlaybackVolumeUpdaterTest {
+
+ private static final long FEED_ID = 42;
+
+ private PlaybackServiceMediaPlayer mediaPlayer;
+
+ @Before
+ public void setUp() {
+ mediaPlayer = mock(PlaybackServiceMediaPlayer.class);
+ }
+
+ @Test
+ public void noChangeIfNoFeedMediaPlaying() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PAUSED);
+
+ Playable noFeedMedia = mock(Playable.class);
+ when(mediaPlayer.getPlayable()).thenReturn(noFeedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayerStatusIsError() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.ERROR);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayerStatusIsIndeterminate() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.INDETERMINATE);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayerStatusIsStopped() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.STOPPED);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void noChangeIfPlayableIsNoItemOfAffectedFeed() {
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PLAYING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ when(feedMedia.getItem().getFeed().getId()).thenReturn(FEED_ID + 1);
+
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.OFF);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsPaused() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PAUSED);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsPrepared() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PREPARED);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsInitializing() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.INITIALIZING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsPreparing() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PREPARING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesForLoadedFeedMediaIfPlayerStatusIsSeeking() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.SEEKING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.LIGHT_REDUCTION);
+
+ verify(mediaPlayer, never()).pause(anyBoolean(), anyBoolean());
+ verify(mediaPlayer, never()).resume();
+ }
+
+ @Test
+ public void updatesPreferencesAndForcesVolumeChangeForLoadedFeedMediaIfPlayerStatusIsPlaying() {
+ PlaybackVolumeUpdater playbackVolumeUpdater = new PlaybackVolumeUpdater();
+
+ when(mediaPlayer.getPlayerStatus()).thenReturn(PlayerStatus.PLAYING);
+
+ FeedMedia feedMedia = mockFeedMedia();
+ when(mediaPlayer.getPlayable()).thenReturn(feedMedia);
+ FeedPreferences feedPreferences = feedMedia.getItem().getFeed().getPreferences();
+
+ playbackVolumeUpdater.updateVolumeIfNecessary(mediaPlayer, FEED_ID, VolumeAdaptionSetting.HEAVY_REDUCTION);
+
+ verify(feedPreferences, times(1)).setVolumeAdaptionSetting(VolumeAdaptionSetting.HEAVY_REDUCTION);
+
+ verify(mediaPlayer, times(1)).pause(false, false);
+ verify(mediaPlayer, times(1)).resume();
+ }
+
+ private FeedMedia mockFeedMedia() {
+ FeedMedia feedMedia = mock(FeedMedia.class);
+ FeedItem feedItem = mock(FeedItem.class);
+ Feed feed = mock(Feed.class);
+ FeedPreferences feedPreferences = mock(FeedPreferences.class);
+
+ when(feedMedia.getItem()).thenReturn(feedItem);
+ when(feedItem.getFeed()).thenReturn(feed);
+ when(feed.getId()).thenReturn(FEED_ID);
+ when(feed.getPreferences()).thenReturn(feedPreferences);
+ return feedMedia;
+ }
+}