diff options
author | H. Lehmann <ByteHamster@users.noreply.github.com> | 2020-01-26 17:08:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-26 17:08:50 +0100 |
commit | 8dd595e0e8a80e5502d42315eada06add32e00d6 (patch) | |
tree | be730e8e33890636e08ca0871e3a850b2982bfdd /core | |
parent | 429499d418d3c1e9d8756942bbc0bb6871cdc392 (diff) | |
parent | 964a519b8ccfc2f4c8835a15018d81407768e11f (diff) | |
download | AntennaPod-8dd595e0e8a80e5502d42315eada06add32e00d6.zip |
Merge pull request #3248 from maxbechtold/feat/simple-adjust-volume-per-feed
Feat/simple adjust volume per feed
Diffstat (limited to 'core')
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; + } +} |