From e4871a5170157e7362d0fe196edd82e35ba3eecb Mon Sep 17 00:00:00 2001 From: M66B Date: Tue, 23 Jan 2024 17:42:07 +0100 Subject: [PATCH] Single instance embedded music player --- .../java/eu/faircode/email/AdapterMedia.java | 78 ++++--------------- .../eu/faircode/email/EntityAttachment.java | 3 - .../eu/faircode/email/MediaPlayerHelper.java | 63 +++++++++++++++ 3 files changed, 78 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/AdapterMedia.java b/app/src/main/java/eu/faircode/email/AdapterMedia.java index 5552bb87a0..538ce8cfc7 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMedia.java +++ b/app/src/main/java/eu/faircode/email/AdapterMedia.java @@ -28,13 +28,10 @@ import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.pdf.PdfRenderer; -import android.media.AudioAttributes; -import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; -import android.os.PowerManager; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -95,10 +92,10 @@ public class AdapterMedia extends RecyclerView.Adapter } private void showPlayerState(EntityAttachment attachment) { - if (attachment.player == null) - ivImage.setImageResource(R.drawable.twotone_play_arrow_48); - else + if (MediaPlayerHelper.isPlaying(attachment.getUri(context))) ivImage.setImageResource(R.drawable.twotone_stop_48); + else + ivImage.setImageResource(R.drawable.twotone_play_arrow_48); } private void bindTo(EntityAttachment attachment) { @@ -262,57 +259,20 @@ public class AdapterMedia extends RecyclerView.Adapter if (attachment.available) { if (attachment.isAudio()) { try { - if (attachment.player == null) { - final Runnable updateTime = new Runnable() { - @Override - public void run() { - MediaPlayer player = attachment.player; - tvProperties.setVisibility(player == null ? View.GONE : View.VISIBLE); - if (player != null) { - String pos = Helper.formatDuration(player.getCurrentPosition(), false); - String duration = Helper.formatDuration(player.getDuration()); - tvProperties.setText(pos + " / " + duration); - view.postDelayed(this, 1000L); - } - } - }; - - Uri uri = FileProviderEx.getUri(context, BuildConfig.APPLICATION_ID, attachment.getFile(context), attachment.name); - - attachment.player = new MediaPlayer(); - attachment.player.setAudioAttributes( - new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) - .setUsage(AudioAttributes.USAGE_MEDIA) - .build() - ); - attachment.player.setDataSource(context, uri); - attachment.player.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); - attachment.player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { - @Override - public void onPrepared(MediaPlayer mp) { - mp.start(); - // https://issuetracker.google.com/issues/36921987 - if (BuildConfig.DEBUG) - view.postDelayed(updateTime, 500L); - } - }); - attachment.player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - attachment.player = null; - showPlayerState(attachment); - } - }); - attachment.player.prepareAsync(); - } else { - attachment.player.stop(); - attachment.player = null; - } + Uri uri = attachment.getUri(context); + if (MediaPlayerHelper.isPlaying(uri)) + MediaPlayerHelper.stopMusic(context); + else + MediaPlayerHelper.startMusic(context, uri, + new RunnableEx("player") { + @Override + public void delegate() { + showPlayerState(attachment); + } + }); showPlayerState(attachment); } catch (Throwable ex) { - attachment.player = null; ivImage.setImageResource(R.drawable.twotone_warning_24); Log.unexpectedError(parentFragment, ex); } @@ -411,15 +371,7 @@ public class AdapterMedia extends RecyclerView.Adapter @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) public void onDestroyed() { Log.d(AdapterMedia.this + " parent destroyed"); - for (EntityAttachment attachment : items) - if (attachment.player != null) - try { - attachment.player.stop(); - } catch (Throwable ex) { - Log.w(ex); - } finally { - attachment.player = null; - } + MediaPlayerHelper.stopMusic(context); AdapterMedia.this.parentFragment = null; owner.getLifecycle().removeObserver(this); } diff --git a/app/src/main/java/eu/faircode/email/EntityAttachment.java b/app/src/main/java/eu/faircode/email/EntityAttachment.java index 3562333cb4..45c574ee13 100644 --- a/app/src/main/java/eu/faircode/email/EntityAttachment.java +++ b/app/src/main/java/eu/faircode/email/EntityAttachment.java @@ -102,9 +102,6 @@ public class EntityAttachment { @Ignore public boolean selected = false; - @Ignore - public MediaPlayer player = null; - // Gmail sends inline images as attachments with a name and cid boolean isInline() { diff --git a/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java b/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java index d4b777d840..490762a180 100644 --- a/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java +++ b/app/src/main/java/eu/faircode/email/MediaPlayerHelper.java @@ -28,6 +28,7 @@ import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; +import android.os.PowerManager; import androidx.core.app.NotificationCompat; import androidx.lifecycle.Lifecycle; @@ -36,6 +37,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.OnLifecycleEvent; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -46,6 +48,10 @@ public class MediaPlayerHelper { private static Semaphore sem; private static final Object lock = new Object(); + private static MediaPlayer player = null; + private static Uri uri = null; + private static Runnable onCompleted = null; + static void stop(Context context) { EntityLog.log(context, "Alarm stop"); synchronized (lock) { @@ -155,6 +161,63 @@ public class MediaPlayerHelper { } } + static void startMusic(Context context, Uri uri, Runnable onCompleted) throws IOException { + stopMusic(context); + + synchronized (lock) { + MediaPlayerHelper.uri = uri; + MediaPlayerHelper.onCompleted = onCompleted; + + MediaPlayerHelper.player = new MediaPlayer(); + MediaPlayerHelper.player.setAudioAttributes( + new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .setUsage(AudioAttributes.USAGE_MEDIA) + .build() + ); + MediaPlayerHelper.player.setDataSource(context, uri); + MediaPlayerHelper.player.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK); + MediaPlayerHelper.player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mp) { + // https://issuetracker.google.com/issues/36921987 + mp.start(); + } + }); + MediaPlayerHelper.player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + stopMusic(context); + } + }); + MediaPlayerHelper.player.prepareAsync(); + } + } + + static void stopMusic(Context context) { + synchronized (lock) { + if (MediaPlayerHelper.player != null) + try { + MediaPlayerHelper.player.stop(); + } catch (Throwable ex) { + Log.e(ex); + } finally { + MediaPlayerHelper.player = null; + } + MediaPlayerHelper.uri = null; + if (MediaPlayerHelper.onCompleted != null) { + MediaPlayerHelper.onCompleted.run(); + MediaPlayerHelper.onCompleted = null; + } + } + } + + static boolean isPlaying(Uri uri) { + synchronized (lock) { + return (Objects.equals(MediaPlayerHelper.uri, uri)); + } + } + static void liveInCall(Context context, LifecycleOwner owner, IInCall intf) { AudioManager am = Helper.getSystemService(context, AudioManager.class); if (am == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {