diff --git a/app/src/main/java/eu/faircode/email/AdapterMedia.java b/app/src/main/java/eu/faircode/email/AdapterMedia.java index 8d7a3399cc..5552bb87a0 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMedia.java +++ b/app/src/main/java/eu/faircode/email/AdapterMedia.java @@ -28,9 +28,13 @@ 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; @@ -90,12 +94,24 @@ public class AdapterMedia extends RecyclerView.Adapter view.setOnLongClickListener(null); } + private void showPlayerState(EntityAttachment attachment) { + if (attachment.player == null) + ivImage.setImageResource(R.drawable.twotone_play_arrow_48); + else + ivImage.setImageResource(R.drawable.twotone_stop_48); + } + private void bindTo(EntityAttachment attachment) { tvCaption.setText(attachment.name); tvCaption.setVisibility(TextUtils.isEmpty(attachment.name) ? View.GONE : View.VISIBLE); tvProperties.setVisibility(View.GONE); if (attachment.available) { + if (attachment.isAudio()) { + showPlayerState(attachment); + return; + } + Bundle args = new Bundle(); args.putSerializable("file", attachment.getFile(context)); args.putString("type", attachment.getMimeType()); @@ -243,13 +259,71 @@ public class AdapterMedia extends RecyclerView.Adapter return; EntityAttachment attachment = items.get(pos); - if (attachment.available) - try { - Helper.share(context, attachment.getFile(context), attachment.getMimeType(), attachment.name); - } catch (Throwable ex) { - Log.unexpectedError(parentFragment.getParentFragmentManager(), ex); + 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; + } + + showPlayerState(attachment); + } catch (Throwable ex) { + attachment.player = null; + ivImage.setImageResource(R.drawable.twotone_warning_24); + Log.unexpectedError(parentFragment, ex); + } + } else { + try { + Helper.share(context, attachment.getFile(context), attachment.getMimeType(), attachment.name); + } catch (Throwable ex) { + Log.unexpectedError(parentFragment.getParentFragmentManager(), ex); + } } - else { + } else { if (attachment.progress == null) { Bundle args = new Bundle(); args.putLong("id", attachment.id); @@ -337,6 +411,15 @@ 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; + } AdapterMedia.this.parentFragment = null; owner.getLifecycle().removeObserver(this); } diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index b613ae294b..05638de146 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -302,6 +302,8 @@ public class AdapterMessage extends RecyclerView.Adapter 0 ? days + " " : "") + DateUtils.formatElapsedTime(seconds) + - (ms == 0 ? "" : "." + ms); + (ms == 0 || !withFraction ? "" : "." + ms); } static String formatNumber(Integer number, long max, NumberFormat nf) { diff --git a/app/src/main/res/drawable/twotone_play_arrow_48.xml b/app/src/main/res/drawable/twotone_play_arrow_48.xml new file mode 100644 index 0000000000..5682aebc59 --- /dev/null +++ b/app/src/main/res/drawable/twotone_play_arrow_48.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/drawable/twotone_stop_48.xml b/app/src/main/res/drawable/twotone_stop_48.xml new file mode 100644 index 0000000000..3e2f146ebe --- /dev/null +++ b/app/src/main/res/drawable/twotone_stop_48.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/layout/fragment_options_display.xml b/app/src/main/res/layout/fragment_options_display.xml index ecea01383d..3f28482033 100644 --- a/app/src/main/res/layout/fragment_options_display.xml +++ b/app/src/main/res/layout/fragment_options_display.xml @@ -2047,6 +2047,18 @@ app:layout_constraintTop_toBottomOf="@id/swThumbnails" app:switchPadding="12dp" /> + + Show attachments after the message text Show image thumbnails after the message text Show PDF thumbnails + Show embedded audio player Default message text zoom: %1$s %% Zoom message text also in the message editor Zoom original messages to fit the screen