diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index bed8c08e6..72c6107bc 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1266,6 +1266,7 @@ public View onCreateView( messageListAdapter.setOnContactPictureClicked(this); messageListAdapter.setOnContactPictureLongClicked(this); binding.messagesView.setAdapter(messageListAdapter); + messageListAdapter.setListView(binding.messagesView); registerForContextMenu(binding.messagesView); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 97828bbf9..c4250c32a 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -114,6 +114,7 @@ public class MessageAdapter extends ArrayAdapter { private OnContactPictureLongClicked mOnContactPictureLongClickedListener; private BubbleDesign bubbleDesign = new BubbleDesign(false, false, false, true, true); private final boolean mForceNames; + private android.widget.ListView listView; public MessageAdapter( final XmppActivity activity, final List messages, final boolean forceNames) { @@ -1323,6 +1324,16 @@ public FileBackend getFileBackend() { return activity.xmppConnectionService.getFileBackend(); } + public void setListView(final android.widget.ListView listView) { + this.listView = listView; + } + + public void smoothScrollToPosition(final int position) { + if (listView != null) { + listView.smoothScrollToPosition(position); + } + } + public void stopAudioPlayer() { audioPlayer.stop(); } diff --git a/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java b/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java index 07507c77d..3c1f133d1 100644 --- a/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java +++ b/src/main/java/eu/siacs/conversations/ui/service/AudioPlayer.java @@ -48,6 +48,7 @@ public class AudioPlayer private static PowerManager.WakeLock wakeLock; private final MessageAdapter messageAdapter; private final WeakReferenceSet audioPlayerLayouts = new WeakReferenceSet<>(); + private final WeakReferenceSet allAudioPlayerLayouts = new WeakReferenceSet<>(); private final SensorManager sensorManager; private final Sensor proximitySensor; private final PendingItem> pendingOnClickView = @@ -100,6 +101,7 @@ private void initializeProximityWakeLock(Context context) { public void init(RelativeLayout audioPlayer, Message message) { synchronized (AudioPlayer.LOCK) { audioPlayer.setTag(message); + this.allAudioPlayerLayouts.addWeakReferenceTo(audioPlayer); if (init(ViewHolder.get(audioPlayer), message)) { this.audioPlayerLayouts.addWeakReferenceTo(audioPlayer); executor.execute(() -> this.stopRefresher(true)); @@ -286,6 +288,7 @@ private void resetPlayerUi(final RelativeLayout audioPlayer) { public void onCompletion(android.media.MediaPlayer mediaPlayer) { synchronized (AudioPlayer.LOCK) { this.stopRefresher(false); + final Message justFinished = AudioPlayer.currentlyPlayingMessage; if (AudioPlayer.player == mediaPlayer) { AudioPlayer.currentlyPlayingMessage = null; AudioPlayer.player = null; @@ -295,6 +298,69 @@ public void onCompletion(android.media.MediaPlayer mediaPlayer) { releaseProximityWakeLock(); resetPlayerUi(); sensorManager.unregisterListener(this); + handler.post(() -> { + synchronized (LOCK) { + playNext(justFinished); + } + }); + } + } + + private static boolean isAudioMessage(final Message message) { + if (!message.isFileOrImage()) { + return false; + } + if (message.getEncryption() == Message.ENCRYPTION_PGP + || message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { + return false; + } + final Message.FileParams p = message.getFileParams(); + return p != null && p.runtime > 0 && p.width == 0 && p.height == 0; + } + + private void playNext(final Message finished) { + if (finished == null) { + return; + } + final int count = messageAdapter.getCount(); + int startPos = -1; + for (int i = 0; i < count; i++) { + if (messageAdapter.getItem(i) == finished) { + startPos = i; + break; + } + } + if (startPos < 0) { + return; + } + for (int i = startPos + 1; i < count; i++) { + final Message next = messageAdapter.getItem(i); + if (next == null || !isAudioMessage(next)) { + continue; + } + for (WeakReference ref : allAudioPlayerLayouts) { + final RelativeLayout layout = ref.get(); + if (layout != null && layout.getTag() == next) { + audioPlayerLayouts.clear(); + audioPlayerLayouts.addWeakReferenceTo(layout); + final ViewHolder vh = ViewHolder.get(layout); + if (play(vh, next, false)) { + init(vh, next); + stopRefresher(true); + } + return; + } + } + // View not yet visible — scroll to it and retry after layout + messageAdapter.smoothScrollToPosition(i); + handler.postDelayed( + () -> { + synchronized (LOCK) { + playNext(finished); + } + }, + 250); + return; } }