play audio files inline
|
@ -868,6 +868,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
if (activity == null || !activity.isChangingConfigurations()) {
|
||||||
|
messageListAdapter.stopAudioPlayer();
|
||||||
|
}
|
||||||
if (this.conversation != null) {
|
if (this.conversation != null) {
|
||||||
final String msg = mEditMessage.getText().toString();
|
final String msg = mEditMessage.getText().toString();
|
||||||
this.conversation.setNextMessage(msg);
|
this.conversation.setNextMessage(msg);
|
||||||
|
@ -894,6 +897,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
|
||||||
this.conversation.setNextMessage(msg);
|
this.conversation.setNextMessage(msg);
|
||||||
if (this.conversation != conversation) {
|
if (this.conversation != conversation) {
|
||||||
updateChatState(this.conversation, msg);
|
updateChatState(this.conversation, msg);
|
||||||
|
messageListAdapter.stopAudioPlayer();
|
||||||
}
|
}
|
||||||
this.conversation.trim();
|
this.conversation.trim();
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.services.MessageArchiveService;
|
import eu.siacs.conversations.services.MessageArchiveService;
|
||||||
import eu.siacs.conversations.services.NotificationService;
|
import eu.siacs.conversations.services.NotificationService;
|
||||||
import eu.siacs.conversations.ui.ConversationActivity;
|
import eu.siacs.conversations.ui.ConversationActivity;
|
||||||
|
import eu.siacs.conversations.ui.service.AudioPlayer;
|
||||||
import eu.siacs.conversations.ui.text.DividerSpan;
|
import eu.siacs.conversations.ui.text.DividerSpan;
|
||||||
import eu.siacs.conversations.ui.text.QuoteSpan;
|
import eu.siacs.conversations.ui.text.QuoteSpan;
|
||||||
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
|
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
|
||||||
|
@ -120,8 +122,11 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
|
|
||||||
private final ListSelectionManager listSelectionManager = new ListSelectionManager();
|
private final ListSelectionManager listSelectionManager = new ListSelectionManager();
|
||||||
|
|
||||||
|
private final AudioPlayer audioPlayer;
|
||||||
|
|
||||||
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
|
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
|
||||||
super(activity, 0, messages);
|
super(activity, 0, messages);
|
||||||
|
this.audioPlayer = new AudioPlayer(this);
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
metrics = getContext().getResources().getDisplayMetrics();
|
metrics = getContext().getResources().getDisplayMetrics();
|
||||||
updatePreferences();
|
updatePreferences();
|
||||||
|
@ -164,7 +169,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
return this.getItemViewType(getItem(position));
|
return this.getItemViewType(getItem(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getMessageTextColor(boolean onDark, boolean primary) {
|
public int getMessageTextColor(boolean onDark, boolean primary) {
|
||||||
if (onDark) {
|
if (onDark) {
|
||||||
return ContextCompat.getColor(activity, primary ? R.color.white : R.color.white70);
|
return ContextCompat.getColor(activity, primary ? R.color.white : R.color.white70);
|
||||||
} else {
|
} else {
|
||||||
|
@ -299,9 +304,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
|
private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
|
||||||
if (viewHolder.download_button != null) {
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.GONE);
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
}
|
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||||
viewHolder.messageBody.setText(text);
|
viewHolder.messageBody.setText(text);
|
||||||
|
@ -311,10 +315,9 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
|
private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
|
||||||
if (viewHolder.download_button != null) {
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||||
viewHolder.messageBody.setText(getContext().getString(
|
viewHolder.messageBody.setText(getContext().getString(
|
||||||
R.string.decryption_failed));
|
R.string.decryption_failed));
|
||||||
|
@ -324,9 +327,8 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayEmojiMessage(final ViewHolder viewHolder, final String body) {
|
private void displayEmojiMessage(final ViewHolder viewHolder, final String body) {
|
||||||
if (viewHolder.download_button != null) {
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.GONE);
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
}
|
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||||
viewHolder.messageBody.setIncludeFontPadding(false);
|
viewHolder.messageBody.setIncludeFontPadding(false);
|
||||||
|
@ -406,10 +408,9 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground, int type) {
|
private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground, int type) {
|
||||||
if (viewHolder.download_button != null) {
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
viewHolder.messageBody.setVisibility(View.VISIBLE);
|
||||||
viewHolder.messageBody.setIncludeFontPadding(true);
|
viewHolder.messageBody.setIncludeFontPadding(true);
|
||||||
if (message.getBody() != null) {
|
if (message.getBody() != null) {
|
||||||
|
@ -492,10 +493,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
|
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayDownloadableMessage(ViewHolder viewHolder,
|
private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text) {
|
||||||
final Message message, String text) {
|
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.GONE);
|
viewHolder.messageBody.setVisibility(View.GONE);
|
||||||
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.VISIBLE);
|
viewHolder.download_button.setVisibility(View.VISIBLE);
|
||||||
viewHolder.download_button.setText(text);
|
viewHolder.download_button.setText(text);
|
||||||
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
||||||
|
@ -510,6 +511,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
|
private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.GONE);
|
viewHolder.messageBody.setVisibility(View.GONE);
|
||||||
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.VISIBLE);
|
viewHolder.download_button.setVisibility(View.VISIBLE);
|
||||||
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
|
viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message)));
|
||||||
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
||||||
|
@ -524,6 +526,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
|
private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
|
||||||
viewHolder.image.setVisibility(View.GONE);
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
viewHolder.messageBody.setVisibility(View.GONE);
|
viewHolder.messageBody.setVisibility(View.GONE);
|
||||||
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
viewHolder.download_button.setVisibility(View.VISIBLE);
|
viewHolder.download_button.setVisibility(View.VISIBLE);
|
||||||
viewHolder.download_button.setText(R.string.show_location);
|
viewHolder.download_button.setText(R.string.show_location);
|
||||||
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
viewHolder.download_button.setOnClickListener(new OnClickListener() {
|
||||||
|
@ -535,12 +538,21 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayImageMessage(ViewHolder viewHolder,
|
private void displayAudioMessage(ViewHolder viewHolder, Message message, boolean darkBackground) {
|
||||||
final Message message) {
|
viewHolder.image.setVisibility(View.GONE);
|
||||||
if (viewHolder.download_button != null) {
|
|
||||||
viewHolder.download_button.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
viewHolder.messageBody.setVisibility(View.GONE);
|
viewHolder.messageBody.setVisibility(View.GONE);
|
||||||
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
|
final RelativeLayout audioPlayer = viewHolder.audioPlayer;
|
||||||
|
audioPlayer.setVisibility(View.VISIBLE);
|
||||||
|
AudioPlayer.ViewHolder.get(audioPlayer).setDarkBackground(darkBackground);
|
||||||
|
this.audioPlayer.init(audioPlayer, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void displayImageMessage(ViewHolder viewHolder, final Message message) {
|
||||||
|
viewHolder.download_button.setVisibility(View.GONE);
|
||||||
|
viewHolder.messageBody.setVisibility(View.GONE);
|
||||||
|
viewHolder.audioPlayer.setVisibility(View.GONE);
|
||||||
viewHolder.image.setVisibility(View.VISIBLE);
|
viewHolder.image.setVisibility(View.VISIBLE);
|
||||||
FileParams params = message.getFileParams();
|
FileParams params = message.getFileParams();
|
||||||
double target = metrics.density * 288;
|
double target = metrics.density * 288;
|
||||||
|
@ -627,6 +639,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
.findViewById(R.id.message_time);
|
.findViewById(R.id.message_time);
|
||||||
viewHolder.indicatorReceived = (ImageView) view
|
viewHolder.indicatorReceived = (ImageView) view
|
||||||
.findViewById(R.id.indicator_received);
|
.findViewById(R.id.indicator_received);
|
||||||
|
viewHolder.audioPlayer = (RelativeLayout) view.findViewById(R.id.audio_player);
|
||||||
break;
|
break;
|
||||||
case RECEIVED:
|
case RECEIVED:
|
||||||
view = activity.getLayoutInflater().inflate(
|
view = activity.getLayoutInflater().inflate(
|
||||||
|
@ -649,6 +662,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
viewHolder.indicatorReceived = (ImageView) view
|
viewHolder.indicatorReceived = (ImageView) view
|
||||||
.findViewById(R.id.indicator_received);
|
.findViewById(R.id.indicator_received);
|
||||||
viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
|
viewHolder.encryption = (TextView) view.findViewById(R.id.message_encryption);
|
||||||
|
viewHolder.audioPlayer = (RelativeLayout) view.findViewById(R.id.audio_player);
|
||||||
break;
|
break;
|
||||||
case STATUS:
|
case STATUS:
|
||||||
view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
|
view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
|
||||||
|
@ -762,8 +776,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
} else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
|
} else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
|
||||||
displayImageMessage(viewHolder, message);
|
displayImageMessage(viewHolder, message);
|
||||||
} else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
|
} else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
|
||||||
if (message.getFileParams().width > 0) {
|
if (message.getFileParams().width > 0 && message.getFileParams().height > 0) {
|
||||||
displayImageMessage(viewHolder,message);
|
displayImageMessage(viewHolder, message);
|
||||||
|
} else if (message.getFileParams().runtime > 0) {
|
||||||
|
displayAudioMessage(viewHolder, message, darkBackground);
|
||||||
} else {
|
} else {
|
||||||
displayOpenableMessage(viewHolder, message);
|
displayOpenableMessage(viewHolder, message);
|
||||||
}
|
}
|
||||||
|
@ -877,6 +893,14 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FileBackend getFileBackend() {
|
||||||
|
return activity.xmppConnectionService.getFileBackend();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopAudioPlayer() {
|
||||||
|
audioPlayer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public interface OnQuoteListener {
|
public interface OnQuoteListener {
|
||||||
public void onQuote(String text);
|
public void onQuote(String text);
|
||||||
}
|
}
|
||||||
|
@ -1004,6 +1028,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
protected TextView encryption;
|
protected TextView encryption;
|
||||||
public Button load_more_messages;
|
public Button load_more_messages;
|
||||||
public ImageView edit_indicator;
|
public ImageView edit_indicator;
|
||||||
|
public RelativeLayout audioPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
|
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
package eu.siacs.conversations.ui.service;
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.ui.adapter.MessageAdapter;
|
||||||
|
import eu.siacs.conversations.utils.WeakReferenceSet;
|
||||||
|
|
||||||
|
public class AudioPlayer implements View.OnClickListener, MediaPlayer.OnCompletionListener, SeekBar.OnSeekBarChangeListener, Runnable {
|
||||||
|
|
||||||
|
private static final int REFRESH_INTERVAL = 250;
|
||||||
|
private static final Object LOCK = new Object();
|
||||||
|
private static MediaPlayer player = null;
|
||||||
|
private static Message currentlyPlayingMessage = null;
|
||||||
|
private final MessageAdapter messageAdapter;
|
||||||
|
private final WeakReferenceSet<RelativeLayout> audioPlayerLayouts = new WeakReferenceSet<>();
|
||||||
|
|
||||||
|
private final Handler handler = new Handler();
|
||||||
|
|
||||||
|
public AudioPlayer(MessageAdapter adapter) {
|
||||||
|
this.messageAdapter = adapter;
|
||||||
|
synchronized (AudioPlayer.LOCK) {
|
||||||
|
if (AudioPlayer.player != null) {
|
||||||
|
AudioPlayer.player.setOnCompletionListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatTime(int ms) {
|
||||||
|
return String.format(Locale.ENGLISH, "%d:%02d", ms / 60000, Math.min(Math.round((ms % 60000) / 1000f), 59));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(RelativeLayout audioPlayer, Message message) {
|
||||||
|
synchronized (AudioPlayer.LOCK) {
|
||||||
|
audioPlayer.setTag(message);
|
||||||
|
if (init(ViewHolder.get(audioPlayer), message)) {
|
||||||
|
this.audioPlayerLayouts.addWeakReferenceTo(audioPlayer);
|
||||||
|
this.stopRefresher(true);
|
||||||
|
} else {
|
||||||
|
this.audioPlayerLayouts.removeWeakReferenceTo(audioPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean init(ViewHolder viewHolder, Message message) {
|
||||||
|
viewHolder.runtime.setTextColor(this.messageAdapter.getMessageTextColor(viewHolder.darkBackground, false));
|
||||||
|
viewHolder.progress.setOnSeekBarChangeListener(this);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
ColorStateList color = ContextCompat.getColorStateList(messageAdapter.getContext(), viewHolder.darkBackground ? R.color.white70 : R.color.bubble);
|
||||||
|
viewHolder.progress.setThumbTintList(color);
|
||||||
|
viewHolder.progress.setProgressTintList(color);
|
||||||
|
}
|
||||||
|
viewHolder.playPause.setAlpha(viewHolder.darkBackground ? 0.7f : 0.57f);
|
||||||
|
viewHolder.playPause.setOnClickListener(this);
|
||||||
|
if (message == currentlyPlayingMessage) {
|
||||||
|
if (AudioPlayer.player != null && AudioPlayer.player.isPlaying()) {
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp);
|
||||||
|
viewHolder.progress.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp);
|
||||||
|
viewHolder.progress.setEnabled(false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp);
|
||||||
|
viewHolder.runtime.setText(formatTime(message.getFileParams().runtime));
|
||||||
|
viewHolder.progress.setProgress(0);
|
||||||
|
viewHolder.progress.setEnabled(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void onClick(View v) {
|
||||||
|
if (v.getId() == R.id.play_pause) {
|
||||||
|
synchronized (LOCK) {
|
||||||
|
startStop((ImageButton) v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startStop(ImageButton playPause) {
|
||||||
|
final RelativeLayout audioPlayer = (RelativeLayout) playPause.getParent();
|
||||||
|
final ViewHolder viewHolder = ViewHolder.get(audioPlayer);
|
||||||
|
final Message message = (Message) audioPlayer.getTag();
|
||||||
|
if (startStop(viewHolder, message)) {
|
||||||
|
this.audioPlayerLayouts.clear();
|
||||||
|
this.audioPlayerLayouts.addWeakReferenceTo(audioPlayer);
|
||||||
|
stopRefresher(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean playPauseCurrent(ViewHolder viewHolder) {
|
||||||
|
viewHolder.playPause.setAlpha(viewHolder.darkBackground ? 0.7f : 0.57f);
|
||||||
|
if (player.isPlaying()) {
|
||||||
|
viewHolder.progress.setEnabled(false);
|
||||||
|
player.pause();
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp);
|
||||||
|
} else {
|
||||||
|
viewHolder.progress.setEnabled(true);
|
||||||
|
player.start();
|
||||||
|
this.stopRefresher(true);
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean play(ViewHolder viewHolder, Message message) {
|
||||||
|
AudioPlayer.player = new MediaPlayer();
|
||||||
|
try {
|
||||||
|
AudioPlayer.currentlyPlayingMessage = message;
|
||||||
|
AudioPlayer.player.setDataSource(messageAdapter.getFileBackend().getFile(message).getAbsolutePath());
|
||||||
|
AudioPlayer.player.setOnCompletionListener(this);
|
||||||
|
AudioPlayer.player.prepare();
|
||||||
|
AudioPlayer.player.start();
|
||||||
|
viewHolder.progress.setEnabled(true);
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_pause_white_36dp : R.drawable.ic_pause_black_36dp);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
AudioPlayer.currentlyPlayingMessage = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean startStop(ViewHolder viewHolder, Message message) {
|
||||||
|
if (message == currentlyPlayingMessage && player != null) {
|
||||||
|
return playPauseCurrent(viewHolder);
|
||||||
|
}
|
||||||
|
if (AudioPlayer.player != null) {
|
||||||
|
stopCurrent();
|
||||||
|
}
|
||||||
|
return play(viewHolder, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopCurrent() {
|
||||||
|
if (AudioPlayer.player.isPlaying()) {
|
||||||
|
AudioPlayer.player.stop();
|
||||||
|
}
|
||||||
|
AudioPlayer.player.release();
|
||||||
|
AudioPlayer.player = null;
|
||||||
|
resetPlayerUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetPlayerUi() {
|
||||||
|
for (WeakReference<RelativeLayout> audioPlayer : audioPlayerLayouts) {
|
||||||
|
resetPlayerUi(audioPlayer.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetPlayerUi(RelativeLayout audioPlayer) {
|
||||||
|
if (audioPlayer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ViewHolder viewHolder = ViewHolder.get(audioPlayer);
|
||||||
|
final Message message = (Message) audioPlayer.getTag();
|
||||||
|
viewHolder.playPause.setImageResource(viewHolder.darkBackground ? R.drawable.ic_play_arrow_white_36dp : R.drawable.ic_play_arrow_black_36dp);
|
||||||
|
if (message != null) {
|
||||||
|
viewHolder.runtime.setText(formatTime(message.getFileParams().runtime));
|
||||||
|
}
|
||||||
|
viewHolder.progress.setProgress(0);
|
||||||
|
viewHolder.progress.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCompletion(MediaPlayer mediaPlayer) {
|
||||||
|
synchronized (AudioPlayer.LOCK) {
|
||||||
|
this.stopRefresher(false);
|
||||||
|
if (AudioPlayer.player == mediaPlayer) {
|
||||||
|
AudioPlayer.currentlyPlayingMessage = null;
|
||||||
|
AudioPlayer.player = null;
|
||||||
|
}
|
||||||
|
mediaPlayer.release();
|
||||||
|
resetPlayerUi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
synchronized (AudioPlayer.LOCK) {
|
||||||
|
final RelativeLayout audioPlayer = (RelativeLayout) seekBar.getParent();
|
||||||
|
final Message message = (Message) audioPlayer.getTag();
|
||||||
|
if (fromUser && message == AudioPlayer.currentlyPlayingMessage) {
|
||||||
|
float percent = progress / 100f;
|
||||||
|
int duration = AudioPlayer.player.getDuration();
|
||||||
|
int seekTo = Math.round(duration * percent);
|
||||||
|
AudioPlayer.player.seekTo(seekTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
synchronized (AudioPlayer.LOCK) {
|
||||||
|
stopRefresher(false);
|
||||||
|
if (AudioPlayer.player != null) {
|
||||||
|
stopCurrent();
|
||||||
|
}
|
||||||
|
AudioPlayer.currentlyPlayingMessage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopRefresher(boolean runOnceMore) {
|
||||||
|
this.handler.removeCallbacks(this);
|
||||||
|
if (runOnceMore) {
|
||||||
|
this.handler.post(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (AudioPlayer.LOCK) {
|
||||||
|
if (AudioPlayer.player != null) {
|
||||||
|
boolean renew = false;
|
||||||
|
final int current = player.getCurrentPosition();
|
||||||
|
final int duration = player.getDuration();
|
||||||
|
for (WeakReference<RelativeLayout> audioPlayer : audioPlayerLayouts) {
|
||||||
|
renew |= refreshAudioPlayer(audioPlayer.get(), current, duration);
|
||||||
|
}
|
||||||
|
if (renew && AudioPlayer.player.isPlaying()) {
|
||||||
|
handler.postDelayed(this, REFRESH_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean refreshAudioPlayer(RelativeLayout audioPlayer, int current, int duration) {
|
||||||
|
if (audioPlayer == null || audioPlayer.getVisibility() != View.VISIBLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final ViewHolder viewHolder = ViewHolder.get(audioPlayer);
|
||||||
|
viewHolder.progress.setProgress(current * 100 / duration);
|
||||||
|
viewHolder.runtime.setText(formatTime(current) + " / " + formatTime(duration));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder {
|
||||||
|
private TextView runtime;
|
||||||
|
private SeekBar progress;
|
||||||
|
private ImageButton playPause;
|
||||||
|
private boolean darkBackground = false;
|
||||||
|
|
||||||
|
public static ViewHolder get(RelativeLayout audioPlayer) {
|
||||||
|
ViewHolder viewHolder = (ViewHolder) audioPlayer.getTag(R.id.TAG_AUDIO_PLAYER_VIEW_HOLDER);
|
||||||
|
if (viewHolder == null) {
|
||||||
|
viewHolder = new ViewHolder();
|
||||||
|
viewHolder.runtime = (TextView) audioPlayer.findViewById(R.id.runtime);
|
||||||
|
viewHolder.progress = (SeekBar) audioPlayer.findViewById(R.id.progress);
|
||||||
|
viewHolder.playPause = (ImageButton) audioPlayer.findViewById(R.id.play_pause);
|
||||||
|
audioPlayer.setTag(R.id.TAG_AUDIO_PLAYER_VIEW_HOLDER, viewHolder);
|
||||||
|
}
|
||||||
|
return viewHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDarkBackground(boolean darkBackground) {
|
||||||
|
this.darkBackground = darkBackground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public class WeakReferenceSet<T> extends HashSet<WeakReference<T>> {
|
||||||
|
|
||||||
|
public void removeWeakReferenceTo(T reference) {
|
||||||
|
for (Iterator<WeakReference<T>> iterator = iterator(); iterator.hasNext(); ) {
|
||||||
|
if (reference == iterator.next().get()) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addWeakReferenceTo(T reference) {
|
||||||
|
for (WeakReference<T> weakReference : this) {
|
||||||
|
if (reference == weakReference.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.add(new WeakReference<>(reference));
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 123 B |
After Width: | Height: | Size: 124 B |
After Width: | Height: | Size: 236 B |
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 102 B |
After Width: | Height: | Size: 105 B |
After Width: | Height: | Size: 194 B |
After Width: | Height: | Size: 195 B |
After Width: | Height: | Size: 109 B |
After Width: | Height: | Size: 92 B |
After Width: | Height: | Size: 265 B |
After Width: | Height: | Size: 283 B |
After Width: | Height: | Size: 143 B |
After Width: | Height: | Size: 158 B |
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 127 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 394 B |
After Width: | Height: | Size: 461 B |
|
@ -30,4 +30,41 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:longClickable="true"
|
android:longClickable="true"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/audio_player"
|
||||||
|
android:layout_width="288dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/play_pause"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:alpha="?attr/icon_alpha"
|
||||||
|
android:background="?android:selectableItemBackground"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/runtime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:textColor="?attr/color_text_secondary"
|
||||||
|
android:textSize="?attr/TextSizeInfo"/>
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/runtime"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_toRightOf="@+id/play_pause"
|
||||||
|
android:progress="100"/>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
</merge>
|
</merge>
|
|
@ -3,4 +3,5 @@
|
||||||
<item type="id" name="TAG_ACCOUNT"/>
|
<item type="id" name="TAG_ACCOUNT"/>
|
||||||
<item type="id" name="TAG_FINGERPRINT"/>
|
<item type="id" name="TAG_FINGERPRINT"/>
|
||||||
<item type="id" name="TAG_FINGERPRINT_STATUS"/>
|
<item type="id" name="TAG_FINGERPRINT_STATUS"/>
|
||||||
|
<item type="id" name="TAG_AUDIO_PLAYER_VIEW_HOLDER"/>
|
||||||
</resources>
|
</resources>
|