From fef2187ee02d12124d885da3e6126ffd29e5b8e7 Mon Sep 17 00:00:00 2001 From: Floens Date: Fri, 12 Jan 2018 19:58:52 +0100 Subject: [PATCH] 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. --- .../core/presenter/ImageViewerPresenter.java | 26 ++++++ .../chan/ui/adapter/ImageViewerAdapter.java | 6 ++ .../ui/controller/ImageViewerController.java | 33 ++++++-- .../floens/chan/ui/view/MultiImageView.java | 75 ++++++++++++------ .../ic_volume_off_white_24dp.png | Bin 0 -> 433 bytes .../drawable-hdpi/ic_volume_up_white_24dp.png | Bin 0 -> 365 bytes .../ic_volume_off_white_24dp.png | Bin 0 -> 301 bytes .../drawable-mdpi/ic_volume_up_white_24dp.png | Bin 0 -> 251 bytes .../ic_volume_off_white_24dp.png | Bin 0 -> 532 bytes .../ic_volume_up_white_24dp.png | Bin 0 -> 455 bytes .../ic_volume_off_white_24dp.png | Bin 0 -> 753 bytes .../ic_volume_up_white_24dp.png | Bin 0 -> 654 bytes .../ic_volume_off_white_24dp.png | Bin 0 -> 1005 bytes .../ic_volume_up_white_24dp.png | Bin 0 -> 878 bytes 14 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_volume_off_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_volume_off_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_volume_off_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_volume_up_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_volume_off_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_off_white_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java index e3907227..353e7348 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java @@ -54,6 +54,8 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. private boolean viewPagerVisible = false; private boolean changeViewsOnInTransitionEnd = false; + private boolean muted = true; + public ImageViewerPresenter(Callback callback) { this.callback = callback; inject(this); @@ -106,6 +108,12 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. callback.showProgress(false); } + public void onVolumeClicked() { + muted = !muted; + callback.showVolumeMenuItem(true, muted); + callback.setVolume(getCurrentPostImage(), muted); + } + public List getAllPostImages() { return images; } @@ -188,6 +196,9 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. callback.showProgress(progress.get(selectedPosition) >= 0f); 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 @@ -307,6 +318,17 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. 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) { // Auto load the image when it is cached 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 setVolume(PostImage postImage, boolean muted); + void setTitle(PostImage postImage, int index, int count, boolean spoiler); void scrollToImage(PostImage postImage); @@ -372,5 +396,7 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. void onLoadProgress(float progress); void onVideoError(MultiImageView multiImageView); + + void showVolumeMenuItem(boolean show, boolean muted); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java index c567ce42..28143e22 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/ImageViewerAdapter.java @@ -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) { MultiImageView view = find(postImage); if (view == null) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java index 68684ce7..a3a74068 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java @@ -39,6 +39,7 @@ import android.view.Window; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.CheckBox; +import android.widget.ImageView; import android.widget.Toast; 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 int GO_POST_ID = 1; + private static final int VOLUME_ID = 3; private static final int SAVE_ID = 2; private static final int OPEN_BROWSER_ID = 103; private static final int SHARE_ID = 104; @@ -108,6 +110,7 @@ public class ImageViewerController extends Controller implements ImageViewerPres private LoadingBar loadingBar; private ToolbarMenuItem overflowMenuItem; + private ToolbarMenuItem volumeMenuItem; public ImageViewerController(Context context, Toolbar toolbar) { super(context); @@ -129,6 +132,9 @@ public class ImageViewerController extends Controller implements ImageViewerPres if (goPostCallback != null) { 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)); List items = new ArrayList<>(); @@ -144,17 +150,16 @@ public class ImageViewerController extends Controller implements ImageViewerPres pager.addOnPageChangeListener(presenter); loadingBar = view.findViewById(R.id.loading_bar); + showVolumeMenuItem(false, true); + // Sanity check if (parentController.view.getWindowToken() == null) { throw new IllegalArgumentException("parentController.view not attached"); } - AndroidUtils.waitForLayout(parentController.view.getViewTreeObserver(), view, new AndroidUtils.OnMeasuredCallback() { - @Override - public boolean onMeasured(View view) { - presenter.onViewMeasured(); - return true; - } + AndroidUtils.waitForLayout(parentController.view.getViewTreeObserver(), view, view -> { + presenter.onViewMeasured(); + return true; }); } @@ -189,6 +194,9 @@ public class ImageViewerController extends Controller implements ImageViewerPres case SAVE_ID: saveShare(false, presenter.getCurrentPostImage()); break; + case VOLUME_ID: + presenter.onVolumeClicked(); + break; } } @@ -285,6 +293,11 @@ public class ImageViewerController extends Controller implements ImageViewerPres ((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) { 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) { ThumbnailView startImageView = getTransitionImageView(postImage); 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 00222763..c48d418f 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 @@ -20,8 +20,10 @@ package org.floens.chan.ui.view; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; +import android.os.Build; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -81,23 +83,19 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener private VideoView videoView; private boolean videoError = false; + private MediaPlayer mediaPlayer; public MultiImageView(Context context) { - super(context); - init(); + this(context, null); } public MultiImageView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); + this(context, attrs, 0); } - public MultiImageView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } + public MultiImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); - private void init() { inject(this); setOnClickListener(this); @@ -165,6 +163,13 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener return bigImage; } + public void setVolume(boolean muted) { + if (mediaPlayer != null) { + final float volume = muted ? 0f : 1f; + mediaPlayer.setVolume(volume, volume); + } + } + @Override public void onClick(View v) { callback.onTap(this); @@ -374,23 +379,24 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener videoView.setZOrderOnTop(true); 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() { - @Override - public void onPrepared(MediaPlayer mp) { - mp.setLooping(true); - onModeLoaded(Mode.MOVIE, videoView); - } + addView(videoView, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER)); + + videoView.setOnPreparedListener(mp -> { + mediaPlayer = mp; + mp.setLooping(true); + mp.setVolume(0f, 0f); + onModeLoaded(Mode.MOVIE, videoView); + callback.onVideoLoaded(this, hasMediaPlayerAudioTracks(mp)); }); - videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - onVideoError(); + videoView.setOnErrorListener((mp, what, extra) -> { + onVideoError(); - return true; - } + return true; }); 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() { if (!videoError) { 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) { final CustomScaleImageView image = new CustomScaleImageView(getContext()); 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 != view) { if (child instanceof VideoView) { - VideoView item = (VideoView) child; - item.stopPlayback(); + cleanupVideo((VideoView) child); } removeViewAt(i); @@ -511,6 +536,8 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener void onVideoError(MultiImageView multiImageView); + void onVideoLoaded(MultiImageView multiImageView, boolean hasAudio); + void onModeLoaded(MultiImageView multiImageView, Mode mode); } diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_volume_off_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_volume_off_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ce0c214274a1d2ca5c28086c94f0cb4fb32264c3 GIT binary patch literal 433 zcmV;i0Z#sjP)94Q@DD5mOUqQ3*aV3?kCChu)~nIKY3GHN?V|Y1ku9AXGP5G3 z9>*0pu~9?KPEb38l6@lk4fy3j4Q1351mu!^-vI|T)XWFec}wVxU-lkwzlNGcP;*Ul z!IHuFIj>`Z8#RHJzwxm!XB)lzCrE1`sHIaNRjHH2>Z zN=DUCP?2AE;1n{dW`c?g;LIzrzzRyJ0rb(W-fv=-Lv{T0#+|`|{9I=`WNp-+6=(L; b_$Q!0QcJrdam&p700000NkvXXu0mjf<9xz% literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_volume_up_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..57d787163e90cc34c16eca4e924adca106deb12c GIT binary patch literal 365 zcmV-z0h0cSP)*Pj zyZkh7Ub{XL@4QGHYZ5VC@2&xYC#cf}!13}qb1co#owXXu+ ztYpS~fIYLb1dH;tFT zz2;wIYCx29Cvl4hF#TPz{h+!vd#T$3+p(akfyB*#i&zkgi5mou@u1B#OYrgsKnsnz z+Kc|g&E`w?Zt8ZxR$M_|>P-9X5lqFj$dR5jE`mEv`I@#*87$`;Bkj(?wbnd?t;=ch zeiL~{?YOJtp!4y=f6Rd@cvWn~cl2a7{|8yIuMqOdjdereUy*tT*z=o!_*C|LdS6 z)2{6aGx(^0Dp^b5a}pRN62dB;CxIp-yEx52)*)#kBW*pKRgjS=iKu~$r1yexB2|!) zwBPWRlkgJ>0Uj(H;I;(yPHd!G`npvxx!(uhys3kFBv5bd00000NkvXXu0mjfnKghk literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_volume_up_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7cfd4c7b88b8d9bc366f0781ea713262c472f2ef GIT binary patch literal 251 zcmV_3jXM$Xr=X;u-(=$+=p?pXDw?0dQEU`vlso0* zdA>~MdnZ|}a_K;?hMpz~crq;DB#C3M_^{K7{|JeJE4*9lz<-5A&ow3;cstUgmBg|) z?9YRLs_i}`i4$DZ1hx|Ec&Q3Ui9Wun!eJ|XR)x*P2A*reeUcdAyt39iO`>@L8ae*^XoFKAT_l2{jc+M@LCgjl3$e3^g4zof`3OmGE31A3I|DW%_zkqYD(i|`L@bPg zo$RL3#!gxDh)g&fhO;++NILVJDwDbQ*UZ38%NE@-npCt%!t^+!>`VA*@&(wE2!Ktx zte^u_0JX%r19Bs->p+X`TH-Evz#7od08eaF68FFX6@MJ@j-wioSc?hnlS?*d8L(r- zCyvB`Ml^Wu0h^1AjCe~!0ZL+Rx^{ROe@?z)w2v_akzz>wvn(1;IbUt({{+?z8|1L~hfqyd>AJ z0d&Id#SL4zP40;fL`1u|7mw}f%jAX%pc7y1>0{)+D}YY?au+W5TL5&z9m`d6I|`r^ z`|ary=Zu16sQhcZ6dqly)VCVh4S+i9bn60&2*5;>f*zm< zHbA08N&`>?3sB(`39cFX6F|yiDU+1=u$HPL!nZt*K26j57QOxew>+*#A7|o~`~hxx zI`~P>#Vxq5Z+SZA^wTnK%l%*YmPechIV*2Q_SA+8@GFma&}PBPJv4s(JHT(k&TW&G zd4|YjjB5eh=e;!AjwS$BMA)QH7w{%Z(kSo20il*?B+L?( zMVSxtBQJ}xXOt67z?Jpss?oOf0Iy_Zo|TL-t;6nxyp>xveogDRt} literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_volume_off_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_volume_off_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..474aae51e0a95406327e4390eff8fc1ee1dc4ec9 GIT binary patch literal 753 zcmV7NP}C4 zG`hbZw=f9Q8YaO{) zTFiJ7WLD)UKS*}Y14T%ymGqCh_6W&(A|RnFL7GUBRx$2?ghf0|ed1O+pde|?0SR+c zr27siNNQuu0SOCvg9iINP@I&!n8j=@KhfUO8UqR44oP%8WfNj{3=IxBpa{tfCOK(N zn|O})Dh{%wuDT$AE9QJB8f?o7^72X3oiKlSf_6PSC`y`c(p*b@XrHNO2gOL*Gb*Xa z?gZL~?4TIw16um^ZpePpd{C6srX=o}_b2T}0OXRo>yT>9L3FgE4Fx~}($>@+TN@34 z0;Img=>oK`0Z@Q+Dsk3|Rttavq$H>b?RNkaAnihQ%t!kk00l_Nt(mEdx|WgDh4!FG zP)A16g_*fAf@CB;LOWb2C=+QJQ)ugp2Kl7pXrHJQ3i3$iv3oW83fu34a*;Ospa@BF zl3pM_C_<8)r2Rf9Mv`2l6*T*x7)f%H#0SMl5+_+ugd{;y0Hh=zBwU0%Bq2a@L9@BU z6j+iEijpj-N-ys)c_blDvY;g#VFVjKNr;myXcF6$b9u)>N-`kZ7k1F@km4XEIiNw7 zA_%)ZQXJ&}d+KH4TgRRR=}rXX&w_^OrK#>u@poySInXGV*{CO$WI08G+T0(RZ^xhZ j#jgaFpb}Jq{)_zqlCG{tEu));00000NkvXXu0mjf+4M_7 literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_volume_up_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2e751a40f53b82208e70aaa474d4395a81910a12 GIT binary patch literal 654 zcmV;90&)F`P)_7O&ty4On2@XE3V9dzP8Jcq(p`*@*6|CyVZUeYC9;eb zA>``#g5Qc*fu2;h#B;V4-S|F6)h@MgH|yvD-!%2rmRDRQ3kKsD;oImHj%x0}G)R>vLm+2n`D%IHY!LVa`UVo*UbQ@Y6=< zYHq9>p;a}ZKk<_RaB#(+x@oiQ*R8nhpIQh#R@rYLOj-yHs_f4Y&W9J8R)_0G+6cQt z3$@v+a$i9B9{&0<=H#A1mH)8T6bzp1I>>IT!Mw z=!;z}Av9TI`;^|o99c$eiw!-ho;GD$j@imfvMkVNS+E+QNuHB-CB06@StQRjyQ1eY z-l4f~Qx@)}?va6eBsPWf7A}8DtJGRpV5myXmnpVYr#Lm;e9(07*qoM6N<$f`lO}%>V!Z literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_off_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_off_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..df06b06b53981604fbefaad7b694736068676264 GIT binary patch literal 1005 zcmV!Bw#o7cDgiF17ViLZK~vtx4YZ>qgKsIngkrlFvD%l0{r!n5&)pjS3Jd-9uQw&N%(hXd698B zz+b;6VKxBxmf6p66M#8@nuJ-10j0j@VLhN=z9!)fs0e^Q?{ZBIK!Pw&k+^;gh&#oE z2p}N5326W<^QZ*i2n!j4yM7FqVv#@Ds8y!&05#zNlmVq}P$VnDb%IZD)3cIE*p!s8{v|37VAWqlXeAptnTp^Ajv^b!E$etuzn z-!YZ|)F3Rxgap92iyv6u!8|}mc#K{GVBE@&lpF9+9-t>!#ofU<%AH|U4agI$@*dz1 z%Dtchc*5a|gkAKo(uS}Nw~un?*`f#J2v*t||FhDLQ0`?tAVCNQFweCc{w7?(_Zy=^ zfPer1C%6R*_bTNk1%L$M5Jd$5EO9dyYzxcyzS$fgRf6zOCy3v{UconDn+zZz_y>{r zVS5GNJ{dqj@a@KeeU9?Lx*mcsuFllU$%BmxKsJ$%v2%MGmJyH^j$6XN%U<5;k7 z@$GE~0Ea6OCW6yH!1taWpd-u#r$2*lQ4i1&J_}CYiSLvipd&<=+m7#NJwQiT4o<%j z-vvEDM_36?AH%nz2j~cE!RbTz`g(wla5gx79A8fl&=KwqPM^T{s~#XERQNM`x?Mn? z@EpFQ?E&UCMOeUhusuMrDZ&!IXWIi|eTlUQNE61n zf^ToTfIMNBBdoDC4XCi$9H0i_7?ua<2v(c$L>{0dSWUuD4Cet#g4HBEkL3YMg4H5C z!gYComS9;zZbs7tYnEU%O|WJOM$-gqmS8kZux1H{l#m9D@D|h|7-B*Sa1SQ|>JSV) zp&H;O-e(n{9>Hjq5CgXHHWxLo|3;&P05HiSs{o)N7!4Cr?-?t3g3%Zu4LHemKFt!0 z1_=p3pLZC=DEK5tFq$S*0i5PR?C-^d1wBCZ`Tg(AlwMg1KE^j-j{qQ<2b|+2|3Ono zh_Bx=pwHJ#F^a{b;G_8Z?E@}zgz5PElYkIkzg55pnXiTSV0u71>j3(7fDX_BIzR{L b03F~j6Qf!ZBH|cH00000NkvXXu0mjfRf4!d literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_volume_up_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..82972b4e59ebd6648f6132267d70e0eb3ed2645a GIT binary patch literal 878 zcmV-!1CjiRP)V7pmJYSR~eKS{(!oQ$RJCI zh#(`Wz*HnCx`?o&;uIG7G1K&P;oX|`PNO+9d&7SA?b>Vk!28a-KbE2F;qiDp9*@W4 zA)nAvju6=%Q{1JEVs{1RN$`-pMBEjaXOz>dwp+j?3kaedBi~j51FC>zg#T<5FrWzN zB)Gydy95jb3#bfIZjXS0bOZ#G?657!U$-Y~lhF=p@*mTLA+? zKpzFP^9_xkH04gfAV?X#jr(ZKQIi`11Crm)X=RpVzEF{U0fRys^Zwxz$-E+xT>*nm zI&({TLo(O0DPTY+&G|*VRbJ-mYzY{GNjFCcqezAcXG6e{iuCjR%M6lJnHMmyAc4FT zxmu7+P>@*x0}B)wW@n0gJxI=GQoz811pvBO9{47F7xTvmXG*}pA_btIje1tjBWcKt zfPp^{fDtyRc~>$iGCWzFz)}&Mw_o3baZY{@*kY#ubm@z2Vj@S2tpc!1t$kZW_1Y^i zqJ9_ZMbuaFY!yJ=LKYGiQGeSj@Jy}#TtpqVR{-@e<+_NuYO_F_QvHC4>9$$mrc%8| z#JsjyK>Op$M9fE<1tyj1Yemdwn+4)Z^@xaxd4XX3BQ)UpOMFAAUhC4&Xsc5Fpi3XJ zQl)yQi0QCb;Hg@@Uqm(AD^Rc0UPD|&{bj4bu)0-g6j4+5ZfNvFt~(;?fvsE0PPOu8 z`2camPJuomYUOr$tU{5E2U|m|Q}eDODl$B_@o2G+^=W6_W8dL!XJB7MDbr&9|Ag&2 zULT@1MZR7nXI#4AIErM974BRyRWXa?ggcjfWsD&iB;S>5)~&oplAzL+i|z{EkxYjx ztp-($lgtakZZvzGBt|k(%3Wx;+0G+0W~jEm;i-_zOrw#Y&i0nGVlELyCr*>yO@I67 zAx02$>~^{Ba-3?H8(&{i;%@8l4=xgRxA}X3N_X4u$7v?d)qjBaz;U_WcSRu1J?i9Q z*$sgxw>Tubgi_@%07*qoM6N<$ Ef==9+1ONa4 literal 0 HcmV?d00001