add mute icon to the image viewer

appears then the webm track has audio and toggles it. keeps its muted
state as long as the image viewer is open.
also use the new api to disable pausing other playbacks when playing
a video.
refactor-toolbar
Floens 8 years ago
parent 4356eafc2f
commit fef2187ee0
  1. 26
      Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java
  2. 6
      Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java
  3. 29
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
  4. 67
      Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java
  5. BIN
      Clover/app/src/main/res/drawable-hdpi/ic_volume_off_white_24dp.png
  6. BIN
      Clover/app/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png
  7. BIN
      Clover/app/src/main/res/drawable-mdpi/ic_volume_off_white_24dp.png
  8. BIN
      Clover/app/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png
  9. BIN
      Clover/app/src/main/res/drawable-xhdpi/ic_volume_off_white_24dp.png
  10. BIN
      Clover/app/src/main/res/drawable-xhdpi/ic_volume_up_white_24dp.png
  11. BIN
      Clover/app/src/main/res/drawable-xxhdpi/ic_volume_off_white_24dp.png
  12. BIN
      Clover/app/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png
  13. BIN
      Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_off_white_24dp.png
  14. BIN
      Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png

@ -54,6 +54,8 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
private boolean viewPagerVisible = false; private boolean viewPagerVisible = false;
private boolean changeViewsOnInTransitionEnd = false; private boolean changeViewsOnInTransitionEnd = false;
private boolean muted = true;
public ImageViewerPresenter(Callback callback) { public ImageViewerPresenter(Callback callback) {
this.callback = callback; this.callback = callback;
inject(this); inject(this);
@ -106,6 +108,12 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
callback.showProgress(false); callback.showProgress(false);
} }
public void onVolumeClicked() {
muted = !muted;
callback.showVolumeMenuItem(true, muted);
callback.setVolume(getCurrentPostImage(), muted);
}
public List<PostImage> getAllPostImages() { public List<PostImage> getAllPostImages() {
return images; return images;
} }
@ -188,6 +196,9 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
callback.showProgress(progress.get(selectedPosition) >= 0f); callback.showProgress(progress.get(selectedPosition) >= 0f);
callback.onLoadProgress(progress.get(selectedPosition)); callback.onLoadProgress(progress.get(selectedPosition));
// If it has audio, we'll know after it is loaded.
callback.showVolumeMenuItem(false, true);
} }
// Called from either a page swipe caused a lowres image to the center or an // Called from either a page swipe caused a lowres image to the center or an
@ -307,6 +318,17 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
callback.onVideoError(multiImageView); callback.onVideoError(multiImageView);
} }
@Override
public void onVideoLoaded(MultiImageView multiImageView, boolean hasAudio) {
PostImage currentPostImage = getCurrentPostImage();
if (multiImageView.getPostImage() == currentPostImage) {
if (hasAudio) {
callback.showVolumeMenuItem(true, muted);
callback.setVolume(currentPostImage, muted);
}
}
}
private boolean imageAutoLoad(PostImage postImage) { private boolean imageAutoLoad(PostImage postImage) {
// Auto load the image when it is cached // Auto load the image when it is cached
return fileCache.exists(postImage.imageUrl.toString()) || shouldLoadForNetworkType(ChanSettings.imageAutoLoadNetwork.get()); return fileCache.exists(postImage.imageUrl.toString()) || shouldLoadForNetworkType(ChanSettings.imageAutoLoadNetwork.get());
@ -361,6 +383,8 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
void setImageMode(PostImage postImage, MultiImageView.Mode mode); void setImageMode(PostImage postImage, MultiImageView.Mode mode);
void setVolume(PostImage postImage, boolean muted);
void setTitle(PostImage postImage, int index, int count, boolean spoiler); void setTitle(PostImage postImage, int index, int count, boolean spoiler);
void scrollToImage(PostImage postImage); void scrollToImage(PostImage postImage);
@ -372,5 +396,7 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
void onLoadProgress(float progress); void onLoadProgress(float progress);
void onVideoError(MultiImageView multiImageView); void onVideoError(MultiImageView multiImageView);
void showVolumeMenuItem(boolean show, boolean muted);
} }
} }

@ -91,6 +91,12 @@ public class ImageViewerAdapter extends ViewPagerAdapter {
} }
} }
public void setVolume(PostImage postImage, boolean muted) {
// It must be loaded, or the user is not able to click the menu item.
MultiImageView view = find(postImage);
view.setVolume(muted);
}
public MultiImageView.Mode getMode(PostImage postImage) { public MultiImageView.Mode getMode(PostImage postImage) {
MultiImageView view = find(postImage); MultiImageView view = find(postImage);
if (view == null) { if (view == null) {

@ -39,6 +39,7 @@ import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.animation.DecelerateInterpolator; import android.view.animation.DecelerateInterpolator;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.Toast; import android.widget.Toast;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
@ -85,6 +86,7 @@ public class ImageViewerController extends Controller implements ImageViewerPres
private static final float TRANSITION_FINAL_ALPHA = 0.85f; private static final float TRANSITION_FINAL_ALPHA = 0.85f;
private static final int GO_POST_ID = 1; private static final int GO_POST_ID = 1;
private static final int VOLUME_ID = 3;
private static final int SAVE_ID = 2; private static final int SAVE_ID = 2;
private static final int OPEN_BROWSER_ID = 103; private static final int OPEN_BROWSER_ID = 103;
private static final int SHARE_ID = 104; private static final int SHARE_ID = 104;
@ -108,6 +110,7 @@ public class ImageViewerController extends Controller implements ImageViewerPres
private LoadingBar loadingBar; private LoadingBar loadingBar;
private ToolbarMenuItem overflowMenuItem; private ToolbarMenuItem overflowMenuItem;
private ToolbarMenuItem volumeMenuItem;
public ImageViewerController(Context context, Toolbar toolbar) { public ImageViewerController(Context context, Toolbar toolbar) {
super(context); super(context);
@ -129,6 +132,9 @@ public class ImageViewerController extends Controller implements ImageViewerPres
if (goPostCallback != null) { if (goPostCallback != null) {
navigation.menu.addItem(new ToolbarMenuItem(context, this, GO_POST_ID, R.drawable.ic_subdirectory_arrow_left_white_24dp)); navigation.menu.addItem(new ToolbarMenuItem(context, this, GO_POST_ID, R.drawable.ic_subdirectory_arrow_left_white_24dp));
} }
volumeMenuItem = navigation.menu.addItem(new ToolbarMenuItem(context,
this, VOLUME_ID, R.drawable.ic_volume_off_white_24dp));
navigation.menu.addItem(new ToolbarMenuItem(context, this, SAVE_ID, R.drawable.ic_file_download_white_24dp)); navigation.menu.addItem(new ToolbarMenuItem(context, this, SAVE_ID, R.drawable.ic_file_download_white_24dp));
List<FloatingMenuItem> items = new ArrayList<>(); List<FloatingMenuItem> items = new ArrayList<>();
@ -144,17 +150,16 @@ public class ImageViewerController extends Controller implements ImageViewerPres
pager.addOnPageChangeListener(presenter); pager.addOnPageChangeListener(presenter);
loadingBar = view.findViewById(R.id.loading_bar); loadingBar = view.findViewById(R.id.loading_bar);
showVolumeMenuItem(false, true);
// Sanity check // Sanity check
if (parentController.view.getWindowToken() == null) { if (parentController.view.getWindowToken() == null) {
throw new IllegalArgumentException("parentController.view not attached"); throw new IllegalArgumentException("parentController.view not attached");
} }
AndroidUtils.waitForLayout(parentController.view.getViewTreeObserver(), view, new AndroidUtils.OnMeasuredCallback() { AndroidUtils.waitForLayout(parentController.view.getViewTreeObserver(), view, view -> {
@Override
public boolean onMeasured(View view) {
presenter.onViewMeasured(); presenter.onViewMeasured();
return true; return true;
}
}); });
} }
@ -189,6 +194,9 @@ public class ImageViewerController extends Controller implements ImageViewerPres
case SAVE_ID: case SAVE_ID:
saveShare(false, presenter.getCurrentPostImage()); saveShare(false, presenter.getCurrentPostImage());
break; break;
case VOLUME_ID:
presenter.onVolumeClicked();
break;
} }
} }
@ -285,6 +293,11 @@ public class ImageViewerController extends Controller implements ImageViewerPres
((ImageViewerAdapter) pager.getAdapter()).setMode(postImage, mode); ((ImageViewerAdapter) pager.getAdapter()).setMode(postImage, mode);
} }
@Override
public void setVolume(PostImage postImage, boolean muted) {
((ImageViewerAdapter) pager.getAdapter()).setVolume(postImage, muted);
}
public MultiImageView.Mode getImageMode(PostImage postImage) { public MultiImageView.Mode getImageMode(PostImage postImage) {
return ((ImageViewerAdapter) pager.getAdapter()).getMode(postImage); return ((ImageViewerAdapter) pager.getAdapter()).getMode(postImage);
} }
@ -335,6 +348,14 @@ public class ImageViewerController extends Controller implements ImageViewerPres
} }
} }
@Override
public void showVolumeMenuItem(boolean show, boolean muted) {
ImageView view = volumeMenuItem.getView();
view.setVisibility(show ? View.VISIBLE : View.GONE);
view.setImageResource(muted ?
R.drawable.ic_volume_off_white_24dp : R.drawable.ic_volume_up_white_24dp);
}
public void startPreviewInTransition(PostImage postImage) { public void startPreviewInTransition(PostImage postImage) {
ThumbnailView startImageView = getTransitionImageView(postImage); ThumbnailView startImageView = getTransitionImageView(postImage);

@ -20,8 +20,10 @@ package org.floens.chan.ui.view;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.Intent; import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
@ -81,23 +83,19 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
private VideoView videoView; private VideoView videoView;
private boolean videoError = false; private boolean videoError = false;
private MediaPlayer mediaPlayer;
public MultiImageView(Context context) { public MultiImageView(Context context) {
super(context); this(context, null);
init();
} }
public MultiImageView(Context context, AttributeSet attrs) { public MultiImageView(Context context, AttributeSet attrs) {
super(context, attrs); this(context, attrs, 0);
init();
} }
public MultiImageView(Context context, AttributeSet attrs, int defStyleAttr) { public MultiImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyle);
init();
}
private void init() {
inject(this); inject(this);
setOnClickListener(this); setOnClickListener(this);
@ -165,6 +163,13 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
return bigImage; return bigImage;
} }
public void setVolume(boolean muted) {
if (mediaPlayer != null) {
final float volume = muted ? 0f : 1f;
mediaPlayer.setVolume(volume, volume);
}
}
@Override @Override
public void onClick(View v) { public void onClick(View v) {
callback.onTap(this); callback.onTap(this);
@ -374,23 +379,24 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
videoView.setZOrderOnTop(true); videoView.setZOrderOnTop(true);
videoView.setMediaController(new MediaController(getContext())); videoView.setMediaController(new MediaController(getContext()));
addView(videoView, 0, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE);
}
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { addView(videoView, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER));
@Override
public void onPrepared(MediaPlayer mp) { videoView.setOnPreparedListener(mp -> {
mediaPlayer = mp;
mp.setLooping(true); mp.setLooping(true);
mp.setVolume(0f, 0f);
onModeLoaded(Mode.MOVIE, videoView); onModeLoaded(Mode.MOVIE, videoView);
} callback.onVideoLoaded(this, hasMediaPlayerAudioTracks(mp));
}); });
videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { videoView.setOnErrorListener((mp, what, extra) -> {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
onVideoError(); onVideoError();
return true; return true;
}
}); });
videoView.setVideoPath(file.getAbsolutePath()); videoView.setVideoPath(file.getAbsolutePath());
@ -404,6 +410,21 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
} }
} }
private boolean hasMediaPlayerAudioTracks(MediaPlayer mediaPlayer) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
for (MediaPlayer.TrackInfo trackInfo : mediaPlayer.getTrackInfo()) {
if (trackInfo.getTrackType() == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO) {
return true;
}
}
return false;
} else {
// It'll just show the icon without doing anything. Remove when 4.0 is dropped.
return true;
}
}
private void onVideoError() { private void onVideoError() {
if (!videoError) { if (!videoError) {
videoError = true; videoError = true;
@ -411,6 +432,11 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
} }
} }
private void cleanupVideo(VideoView videoView) {
videoView.stopPlayback();
mediaPlayer = null;
}
private void setBitImageFileInternal(File file, boolean tiling, final Mode forMode) { private void setBitImageFileInternal(File file, boolean tiling, final Mode forMode) {
final CustomScaleImageView image = new CustomScaleImageView(getContext()); final CustomScaleImageView image = new CustomScaleImageView(getContext());
image.setImage(ImageSource.uri(file.getAbsolutePath()).tiling(tiling)); image.setImage(ImageSource.uri(file.getAbsolutePath()).tiling(tiling));
@ -482,8 +508,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
if (child != playView) { if (child != playView) {
if (child != view) { if (child != view) {
if (child instanceof VideoView) { if (child instanceof VideoView) {
VideoView item = (VideoView) child; cleanupVideo((VideoView) child);
item.stopPlayback();
} }
removeViewAt(i); removeViewAt(i);
@ -511,6 +536,8 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
void onVideoError(MultiImageView multiImageView); void onVideoError(MultiImageView multiImageView);
void onVideoLoaded(MultiImageView multiImageView, boolean hasAudio);
void onModeLoaded(MultiImageView multiImageView, Mode mode); void onModeLoaded(MultiImageView multiImageView, Mode mode);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Loading…
Cancel
Save