From 0bbc3372b99d46c72fb29bb8322977f669bffd71 Mon Sep 17 00:00:00 2001 From: Alex Eyre <2012eyre@students.mggs.org> Date: Sat, 6 Oct 2018 14:30:55 +0000 Subject: [PATCH 1/2] Implement ExoPlayer - Add ExoPlayer logic to MultiImageView.java - Add option to enable/disable ExoPlayer in MediaSettings (defaults to enabled). --- Clover/app/build.gradle | 3 ++ .../chan/core/settings/ChanSettings.java | 2 + .../controller/MediaSettingsController.java | 4 ++ .../floens/chan/ui/view/MultiImageView.java | 54 +++++++++++++++++-- Clover/app/src/main/res/values/strings.xml | 2 + 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index 201a6518..752a2242 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -139,6 +139,9 @@ dependencies { implementation "com.android.support:customtabs:${supportVersion}" implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation 'com.google.android.exoplayer:exoplayer-core:2.9.0' + implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.0' + implementation 'com.squareup.okhttp3:okhttp:3.10.0' //noinspection GradleDependency implementation 'com.j256.ormlite:ormlite-core:4.48' diff --git a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java index ff794195..7ccdd250 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java +++ b/Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java @@ -100,6 +100,7 @@ public class ChanSettings { public static final OptionsSetting imageAutoLoadNetwork; public static final OptionsSetting videoAutoLoadNetwork; public static final BooleanSetting videoOpenExternal; + public static final BooleanSetting videoUseExoplayer; public static final BooleanSetting textOnly; public static final BooleanSetting videoErrorIgnore; public static final OptionsSetting boardViewMode; @@ -174,6 +175,7 @@ public class ChanSettings { imageAutoLoadNetwork = new OptionsSetting<>(p, "preference_image_auto_load_network", MediaAutoLoadMode.class, MediaAutoLoadMode.WIFI); videoAutoLoadNetwork = new OptionsSetting<>(p, "preference_video_auto_load_network", MediaAutoLoadMode.class, MediaAutoLoadMode.WIFI); videoOpenExternal = new BooleanSetting(p, "preference_video_external", false); + videoUseExoplayer = new BooleanSetting(p, "preference_video_exoplayer", true); textOnly = new BooleanSetting(p, "preference_text_only", false); videoErrorIgnore = new BooleanSetting(p, "preference_video_error_ignore", false); boardViewMode = new OptionsSetting<>(p, "preference_board_view_mode", PostViewMode.class, PostViewMode.LIST); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/MediaSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/MediaSettingsController.java index ec6253cc..11834b41 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/MediaSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/MediaSettingsController.java @@ -109,6 +109,10 @@ public class MediaSettingsController extends SettingsController { R.string.setting_video_open_external, R.string.setting_video_open_external_description)); + media.add(new BooleanSettingView(this, ChanSettings.videoUseExoplayer, + R.string.setting_video_exoplayer, + R.string.setting_video_exoplayer_description)); + media.add(new BooleanSettingView(this, ChanSettings.shareUrl, R.string.setting_share_url, R.string.setting_share_url_description)); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java index 8b5a63f6..702f26e7 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java @@ -36,6 +36,16 @@ import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.ImageLoader.ImageContainer; import com.davemorrissey.labs.subscaleview.ImageSource; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.ExoPlayerFactory; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.ui.PlayerView; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.util.Util; import org.floens.chan.R; import org.floens.chan.core.cache.FileCache; @@ -74,7 +84,6 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener private PostImage postImage; private Callback callback; - private Mode mode = Mode.UNLOADED; private boolean hasContent = false; @@ -84,8 +93,10 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener private FileCacheDownloader videoRequest; private VideoView videoView; + private PlayerView exoVideoView; private boolean videoError = false; private MediaPlayer mediaPlayer; + private SimpleExoPlayer exoPlayer; public MultiImageView(Context context) { this(context, null); @@ -166,9 +177,15 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } public void setVolume(boolean muted) { - if (mediaPlayer != null) { - final float volume = muted ? 0f : 1f; - mediaPlayer.setVolume(volume, volume); + final float volume = muted ? 0f : 1f; + if(ChanSettings.videoUseExoplayer.get()){ + if(exoPlayer != null) { + exoPlayer.getAudioComponent().setVolume(volume); + } + } else { + if (mediaPlayer != null) { + mediaPlayer.setVolume(volume, volume); + } } } @@ -391,6 +408,21 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener AndroidUtils.openIntent(intent); onModeLoaded(Mode.MOVIE, videoView); + } else if (ChanSettings.videoUseExoplayer.get()) { + exoVideoView = new PlayerView(getContext()); + exoPlayer = ExoPlayerFactory.newSimpleInstance(getContext()); + exoVideoView.setPlayer(exoPlayer); + DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(), + Util.getUserAgent(getContext(),"Clover")); + MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory) + .createMediaSource(android.net.Uri.fromFile(file)); + + exoPlayer.setRepeatMode(Player.REPEAT_MODE_ALL); //Repeat forever + + exoPlayer.prepare(videoSource); + callback.onVideoLoaded(this,hasMediaPlayerAudioTracks(exoPlayer)); + addView(exoVideoView); + exoPlayer.setPlayWhenReady(true); } else { Context proxyContext = new NoMusicServiceCommandContext(getContext()); @@ -451,6 +483,14 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } } + private boolean hasMediaPlayerAudioTracks(ExoPlayer mediaPlayer) { + if(mediaPlayer.getAudioComponent() == null) { + return false; + } else { + return true; + } + } + private void onVideoError() { if (!videoError) { videoError = true; @@ -463,6 +503,10 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener mediaPlayer = null; } + private void cleanupVideo(PlayerView videoView) { + videoView.getPlayer().release(); + } + private void setBitImageFileInternal(File file, boolean tiling, final Mode forMode) { final CustomScaleImageView image = new CustomScaleImageView(getContext()); image.setImage(ImageSource.uri(file.getAbsolutePath()).tiling(tiling)); @@ -535,6 +579,8 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener if (child != view) { if (child instanceof VideoView) { cleanupVideo((VideoView) child); + } else if (child instanceof PlayerView) { + cleanupVideo((PlayerView) child); } removeViewAt(i); diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index 7ad5bfd8..1b2e9716 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -508,6 +508,8 @@ If disabled, save the image with the filename the uploader assigned." If a video has audio, mute it by default. Play videos with external player Play videos in an external media player app + Enable new ExoPlayer + Use the new ExoPlayer for internal media player Share URL to image Share the URL to the image instead of the image itself Reveal image spoilers From 6c23e4238f575cbaffd3bdb57e02c8248b66c8db Mon Sep 17 00:00:00 2001 From: Alex Eyre <2012eyre@students.mggs.org> Date: Sat, 6 Oct 2018 15:32:32 +0000 Subject: [PATCH 2/2] Tidy code - Ran a code formatter to sort out my spacing - Refactored hasMediaPlayerAudioTracks(..) - Injected UserAgent rather than hardcoding a string --- .../floens/chan/ui/view/MultiImageView.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java index 702f26e7..80d9c195 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java @@ -52,6 +52,7 @@ import org.floens.chan.core.cache.FileCache; import org.floens.chan.core.cache.FileCacheDownloader; import org.floens.chan.core.cache.FileCacheListener; import org.floens.chan.core.cache.FileCacheProvider; +import org.floens.chan.core.di.UserAgentProvider; import org.floens.chan.core.model.PostImage; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.utils.AndroidUtils; @@ -80,6 +81,9 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener @Inject ImageLoader imageLoader; + @Inject + UserAgentProvider userAgent; + private ImageView playView; private PostImage postImage; @@ -178,8 +182,8 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener public void setVolume(boolean muted) { final float volume = muted ? 0f : 1f; - if(ChanSettings.videoUseExoplayer.get()){ - if(exoPlayer != null) { + if (ChanSettings.videoUseExoplayer.get()) { + if (exoPlayer != null) { exoPlayer.getAudioComponent().setVolume(volume); } } else { @@ -413,14 +417,14 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener exoPlayer = ExoPlayerFactory.newSimpleInstance(getContext()); exoVideoView.setPlayer(exoPlayer); DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(), - Util.getUserAgent(getContext(),"Clover")); + Util.getUserAgent(getContext(), userAgent.getUserAgent())); MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory) .createMediaSource(android.net.Uri.fromFile(file)); exoPlayer.setRepeatMode(Player.REPEAT_MODE_ALL); //Repeat forever exoPlayer.prepare(videoSource); - callback.onVideoLoaded(this,hasMediaPlayerAudioTracks(exoPlayer)); + callback.onVideoLoaded(this, hasMediaPlayerAudioTracks(exoPlayer)); addView(exoVideoView); exoPlayer.setPlayWhenReady(true); } else { @@ -484,11 +488,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener } private boolean hasMediaPlayerAudioTracks(ExoPlayer mediaPlayer) { - if(mediaPlayer.getAudioComponent() == null) { - return false; - } else { - return true; - } + return mediaPlayer.getAudioComponent() != null; } private void onVideoError() {