Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit 271bb88

Browse files
Add internal playback of audio files
Signed-off-by: Manuel Stahl <manuel.stahl@awesome-technologies.de> Change-Id: If753ce84a66d5c1ac5d176267dccb16c147cf6de
1 parent 6fe8099 commit 271bb88

10 files changed

+255
-8
lines changed

CHANGES.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ MatrixSdk:
66
- Changelog: https://github.com/matrix-org/matrix-android-sdk/releases/tag/v0.X.Y
77

88
Features:
9-
-
9+
- Add internal playback of audio files (#3207)
1010

1111
Improvements:
1212
-

vector/src/main/java/im/vector/adapters/VectorMessagesAdapter.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import android.graphics.Point;
2525
import android.graphics.Typeface;
2626
import android.graphics.drawable.Drawable;
27+
import android.media.MediaPlayer;
2728
import android.os.Build;
2829
import android.os.Handler;
2930
import android.os.Looper;
@@ -883,6 +884,7 @@ public void onBingRulesUpdate() {
883884
*/
884885
public void onPause() {
885886
mEventFormattedTsMap.clear();
887+
mMediasHelper.refreshPlayImageViews();
886888
}
887889

888890
/**
@@ -952,9 +954,9 @@ public Event getCurrentSelectedEvent() {
952954
*
953955
* @param listener teh events listener
954956
*/
955-
public void setVectorMessagesAdapterActionsListener(IMessagesAdapterActionsListener listener) {
957+
public void setVectorMessagesAdapterActionsListener(IMessagesAdapterActionsListener listener, Map<String, MediaPlayer> mediaPlayers) {
956958
mVectorMessagesAdapterEventsListener = listener;
957-
mMediasHelper.setVectorMessagesAdapterActionsListener(listener);
959+
mMediasHelper.setVectorMessagesAdapterActionsListener(listener, mediaPlayers);
958960
mHelper.setVectorMessagesAdapterActionsListener(listener);
959961

960962
if (null != mLinkMovementMethod) {
@@ -1590,6 +1592,7 @@ private View getFileView(final int position, View convertView, ViewGroup parent)
15901592

15911593
mMediasHelper.managePendingFileDownload(convertView, event, fileMessage, position);
15921594
mMediasHelper.managePendingUpload(convertView, event, ROW_TYPE_FILE, fileMessage.url);
1595+
mMediasHelper.managePlayback(convertView, event, ROW_TYPE_FILE, fileMessage.getUrl());
15931596

15941597
View fileLayout = convertView.findViewById(R.id.messagesAdapter_file_layout);
15951598
manageSubView(position, convertView, fileLayout, ROW_TYPE_FILE);

vector/src/main/java/im/vector/adapters/VectorMessagesAdapterMediasHelper.java

+73-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import android.content.Context;
2121
import android.graphics.Color;
2222
import android.media.ExifInterface;
23+
import android.media.MediaPlayer;
24+
import android.os.Handler;
2325
import android.text.TextUtils;
2426
import android.text.format.DateUtils;
2527
import android.view.View;
@@ -75,6 +77,10 @@ class VectorMessagesAdapterMediasHelper {
7577
private final int mNotSentMessageTextColor;
7678
private final int mDefaultMessageTextColor;
7779

80+
private final Handler handler = new Handler();
81+
private Map<String, ImageView> mPlayImageViews = new HashMap<>();
82+
private Map<String, MediaPlayer> mMediaPlayers;
83+
7884
VectorMessagesAdapterMediasHelper(Context context,
7985
MXSession session,
8086
int maxImageWidth,
@@ -96,8 +102,9 @@ class VectorMessagesAdapterMediasHelper {
96102
*
97103
* @param listener teh events listener
98104
*/
99-
void setVectorMessagesAdapterActionsListener(IMessagesAdapterActionsListener listener) {
105+
void setVectorMessagesAdapterActionsListener(IMessagesAdapterActionsListener listener, Map<String, MediaPlayer> mediaPlayers) {
100106
mVectorMessagesAdapterEventsListener = listener;
107+
mMediaPlayers = mediaPlayers;
101108
}
102109

103110
/**
@@ -180,6 +187,71 @@ public void onUploadComplete(final String uploadId, final String contentUri) {
180187
refreshUploadViews(event, uploadStats, uploadProgressLayout);
181188
}
182189

190+
void managePlayback(final View convertView, final Event event, final int type, final String mediaUrl) {
191+
View playbackLayout = convertView.findViewById(R.id.content_media_playback_layout);
192+
playbackLayout.setTag(mediaUrl);
193+
final View playButton = playbackLayout.findViewById(R.id.media_playback);
194+
195+
if (null == playButton) return;
196+
197+
playButton.setTag(event);
198+
playButton.setOnClickListener(new View.OnClickListener() {
199+
@Override
200+
public void onClick(View v) {
201+
if ((event == playButton.getTag()) && (null != mVectorMessagesAdapterEventsListener)) {
202+
ImageView imageView = playButton.findViewById(R.id.media_play_icon);
203+
if ((null != imageView.getTag()) && (imageView.getTag().equals("playing"))) {
204+
mVectorMessagesAdapterEventsListener.onEventAction(event, "", R.id.ic_action_pause_audio);
205+
imageView.setImageResource(R.drawable.ic_baseline_play_arrow_24px);
206+
imageView.setTag("paused");
207+
} else {
208+
mVectorMessagesAdapterEventsListener.onEventAction(event, "", R.id.ic_action_play_audio);
209+
imageView.setTag("playing");
210+
if (!mPlayImageViews.containsKey(mediaUrl)) {
211+
mPlayImageViews.put(mediaUrl, imageView);
212+
}
213+
refreshPlayImageViews();
214+
imageView.setImageResource(R.drawable.ic_baseline_pause_24px);
215+
final ProgressBar progressBar = playbackLayout.findViewById(R.id.media_progress_view);
216+
refreshPlaybackProgress(progressBar, mediaUrl);
217+
}
218+
}
219+
}
220+
});
221+
}
222+
223+
void refreshPlayImageViews() {
224+
for (ImageView iv : mPlayImageViews.values()) {
225+
iv.setImageResource(R.drawable.ic_baseline_play_arrow_24px);
226+
}
227+
}
228+
229+
void refreshPlaybackProgress(ProgressBar progressBar, String mediaUrl) {
230+
handler.postDelayed(new Runnable() {
231+
@Override
232+
public void run() {
233+
if (null == mMediaPlayers) {
234+
return;
235+
}
236+
try {
237+
MediaPlayer mediaPlayer = mMediaPlayers.get(mediaUrl);
238+
239+
if (mediaPlayer == null || mediaUrl == null)
240+
return;
241+
242+
progressBar.setProgress((int) ((float) mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration() * progressBar.getMax()));
243+
if (mediaPlayer.isPlaying()) {
244+
refreshPlaybackProgress(progressBar, mediaUrl);
245+
} else {
246+
mPlayImageViews.get(mediaUrl).setImageResource(R.drawable.ic_baseline_play_arrow_24px);
247+
}
248+
} catch (Exception e) {
249+
e.printStackTrace();
250+
}
251+
}
252+
}, 1000);
253+
}
254+
183255
// the image / video bitmaps are set to null if the matching URL is not the same
184256
// to avoid flickering
185257
private Map<String, String> mUrlByBitmapIndex = new HashMap<>();

vector/src/main/java/im/vector/fragments/VectorMessageListFragment.java

+84-4
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@
2020

2121
import android.annotation.SuppressLint;
2222
import android.app.Activity;
23+
import android.content.BroadcastReceiver;
24+
import android.content.Context;
2325
import android.content.DialogInterface;
2426
import android.content.Intent;
27+
import android.content.IntentFilter;
28+
import android.media.AudioManager;
29+
import android.media.MediaPlayer;
2530
import android.net.Uri;
2631
import android.os.Bundle;
2732
import android.text.TextUtils;
33+
import android.util.Base64;
2834
import android.view.LayoutInflater;
2935
import android.view.MotionEvent;
3036
import android.view.View;
@@ -70,7 +76,10 @@
7076
import org.matrix.androidsdk.rest.model.message.Message;
7177
import org.matrix.androidsdk.rest.model.message.VideoMessage;
7278

79+
import java.io.BufferedInputStream;
7380
import java.io.File;
81+
import java.io.FileInputStream;
82+
import java.io.IOException;
7483
import java.util.ArrayList;
7584
import java.util.Collections;
7685
import java.util.HashMap;
@@ -110,6 +119,19 @@ public class VectorMessageListFragment extends MatrixMessageListFragment<VectorM
110119
private String mPendingFilename;
111120
private EncryptedFileInfo mPendingEncryptedFileInfo;
112121

122+
private Map<String, MediaPlayer> mMediaPlayers = new HashMap<>();
123+
private IntentFilter mBecomingNoisyIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
124+
private BroadcastReceiver mBecomingNoisyReceiver = new BroadcastReceiver() {
125+
@Override
126+
public void onReceive(Context context, Intent intent) {
127+
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
128+
for (MediaPlayer mp : mMediaPlayers.values()) {
129+
mp.pause();
130+
}
131+
}
132+
}
133+
};
134+
113135
private static int VERIF_REQ_CODE = 12;
114136

115137
public interface VectorMessageListFragmentListener {
@@ -248,18 +270,32 @@ protected String getMatrixMessagesFragmentTag() {
248270
public void onPause() {
249271
super.onPause();
250272

251-
mAdapter.setVectorMessagesAdapterActionsListener(null);
273+
for (MediaPlayer mediaPlayer : mMediaPlayers.values()) {
274+
mediaPlayer.pause();
275+
}
276+
getContext().unregisterReceiver(mBecomingNoisyReceiver);
277+
mAdapter.setVectorMessagesAdapterActionsListener(null, mMediaPlayers);
252278
mAdapter.onPause();
253279

254280
mVectorImageGetter.setListener(null);
255281
}
256282

283+
@Override
284+
public void onDestroy() {
285+
super.onDestroy();
286+
287+
for (MediaPlayer mediaPlayer : mMediaPlayers.values()) {
288+
mediaPlayer.stop();
289+
mediaPlayer.release();
290+
}
291+
}
257292

258293
@Override
259294
public void onResume() {
260295
super.onResume();
261296

262-
mAdapter.setVectorMessagesAdapterActionsListener(this);
297+
getContext().registerReceiver(mBecomingNoisyReceiver, mBecomingNoisyIntentFilter);
298+
mAdapter.setVectorMessagesAdapterActionsListener(this, mMediaPlayers);
263299

264300
mVectorImageGetter.setListener(new VectorImageGetter.OnImageDownloadListener() {
265301
@Override
@@ -752,6 +788,20 @@ public void onDismiss(DialogInterface dialog) {
752788
}
753789
})
754790
.show();
791+
} else if (action == R.id.ic_action_play_audio) {
792+
Message message = JsonUtils.toMessage(event.getContent());
793+
FileMessage fileMessage = JsonUtils.toFileMessage(event.getContent());
794+
795+
if (null != fileMessage.getUrl()) {
796+
onMediaAction(ACTION_VECTOR_OPEN, fileMessage.getUrl(), fileMessage.getMimeType(), fileMessage.body, fileMessage.file);
797+
}
798+
} else if (action == R.id.ic_action_pause_audio) {
799+
Message message = JsonUtils.toMessage(event.getContent());
800+
FileMessage fileMessage = JsonUtils.toFileMessage(event.getContent());
801+
802+
if (null != fileMessage.getUrl() && mMediaPlayers.containsKey(fileMessage.getUrl())) {
803+
mMediaPlayers.get(fileMessage.getUrl()).pause();
804+
}
755805
}
756806
}
757807

@@ -894,7 +944,37 @@ public void onSuccess(File file) {
894944
return;
895945
}
896946

897-
if (menuAction == ACTION_VECTOR_SAVE || menuAction == ACTION_VECTOR_OPEN) {
947+
if (menuAction == ACTION_VECTOR_OPEN && mediaMimeType.startsWith("audio/")) {
948+
Log.e(LOG_TAG, "Using Media player " + file.getAbsolutePath());
949+
950+
for (MediaPlayer mp : mMediaPlayers.values()) {
951+
mp.pause();
952+
}
953+
954+
MediaPlayer mediaPlayer;
955+
if (!mMediaPlayers.containsKey(mediaUrl)) {
956+
mediaPlayer = new MediaPlayer();
957+
mMediaPlayers.put(mediaUrl, mediaPlayer);
958+
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
959+
try {
960+
int size = (int) file.length();
961+
byte[] callData = new byte[size];
962+
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
963+
buf.read(callData, 0, callData.length);
964+
buf.close();
965+
String base64EncodedString = Base64.encodeToString(callData, Base64.DEFAULT);
966+
967+
String url = "data:audio/amr;base64," + base64EncodedString;
968+
mediaPlayer.setDataSource(url);
969+
mediaPlayer.prepare();
970+
} catch (IOException e) {
971+
e.printStackTrace();
972+
}
973+
} else {
974+
mediaPlayer = mMediaPlayers.get(mediaUrl);
975+
}
976+
mediaPlayer.start();
977+
} else if (menuAction == ACTION_VECTOR_SAVE || menuAction == ACTION_VECTOR_OPEN) {
898978
if (PermissionsToolsKt.checkPermissions(PermissionsToolsKt.PERMISSIONS_FOR_WRITING_FILES,
899979
VectorMessageListFragment.this, PermissionsToolsKt.PERMISSION_REQUEST_CODE)) {
900980
CommonActivityUtils.saveMediaIntoDownloads(getActivity(), file, trimmedFileName, mediaMimeType, new SimpleApiCallback<String>() {
@@ -1152,7 +1232,7 @@ public void onContentClick(int position) {
11521232

11531233
getActivity().startActivity(viewImageIntent);
11541234
}
1155-
} else if (Message.MSGTYPE_FILE.equals(message.msgtype) || Message.MSGTYPE_AUDIO.equals(message.msgtype)) {
1235+
} else if (Message.MSGTYPE_FILE.equals(message.msgtype)) {
11561236
FileMessage fileMessage = JsonUtils.toFileMessage(event.getContent());
11571237

11581238
if (null != fileMessage.getUrl()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="#FF000000"
8+
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
9+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:fillColor="#FF000000"
8+
android:pathData="M8,5v14l11,-7z"/>
9+
</vector>

vector/src/main/res/layout/adapter_item_vector_message_file.xml

+10
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@
156156

157157
</RelativeLayout>
158158

159+
<RelativeLayout
160+
android:layout_width="match_parent"
161+
android:layout_height="wrap_content">
162+
163+
<include
164+
android:id="@+id/content_media_playback_layout"
165+
layout="@layout/media_playback_control" />
166+
167+
</RelativeLayout>
168+
159169
</LinearLayout>
160170

161171
</LinearLayout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:layout_width="match_parent"
5+
android:layout_height="wrap_content"
6+
android:baselineAligned="false"
7+
android:orientation="horizontal">
8+
9+
<LinearLayout
10+
android:layout_width="0dp"
11+
android:layout_height="wrap_content"
12+
android:layout_weight="1"
13+
android:orientation="vertical">
14+
15+
<TextView
16+
android:id="@+id/media_progress_text_view"
17+
android:layout_width="wrap_content"
18+
android:layout_height="wrap_content"
19+
android:layout_marginTop="10dp"
20+
android:textSize="10sp"
21+
tools:text="Information" />
22+
23+
<ProgressBar
24+
android:id="@+id/media_progress_view"
25+
style="?android:attr/progressBarStyleHorizontal"
26+
android:layout_width="match_parent"
27+
android:layout_height="20dp"
28+
android:layout_gravity="center_vertical"
29+
android:max="100"
30+
android:progress="0"
31+
tools:progress="30" />
32+
33+
</LinearLayout>
34+
35+
<FrameLayout
36+
android:id="@+id/media_playback"
37+
android:layout_width="40dp"
38+
android:layout_height="match_parent">
39+
40+
<ImageView
41+
android:id="@+id/media_play_icon"
42+
android:layout_width="match_parent"
43+
android:layout_height="match_parent"
44+
android:layout_gravity="bottom|end"
45+
android:src="@drawable/ic_baseline_play_arrow_24px"
46+
android:tint="?attr/vctr_settings_icon_tint_color" />
47+
48+
</FrameLayout>
49+
50+
</LinearLayout>

0 commit comments

Comments
 (0)