aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian S <christian@pix-art.de>2016-05-05 10:44:56 +0200
committerChristian S <christian@pix-art.de>2016-05-05 10:44:56 +0200
commitf99eb2c1d9baf9688a82bfdb63466c0e0e58fad6 (patch)
tree1cc4359c67c2bc6ff28d380967d811af37a89c3f
parent8013668850e92f3dbd6e35bc3be9acddd74a9163 (diff)
added audio player
-rw-r--r--build.gradle1
-rw-r--r--libs/audiowife/.gitignore5
-rw-r--r--libs/audiowife/build.gradle27
-rw-r--r--libs/audiowife/src/main/AndroidManifest.xml9
-rw-r--r--libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java699
-rw-r--r--libs/audiowife/src/main/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--libs/audiowife/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--libs/audiowife/src/main/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 19388 bytes
-rw-r--r--libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml15
-rw-r--r--libs/audiowife/src/main/res/layout/aw_player.xml49
-rw-r--r--libs/audiowife/src/main/res/values/strings.xml5
-rw-r--r--libs/audiowife/src/main/res/values/styles.xml20
-rw-r--r--settings.gradle3
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java46
-rw-r--r--src/main/res/layout/message_received.xml6
-rw-r--r--src/main/res/layout/message_sent.xml6
16 files changed, 889 insertions, 2 deletions
diff --git a/build.gradle b/build.gradle
index 5257b0234..6e8ad465a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,6 +23,7 @@ configurations {
dependencies {
compile project(':libs:MemorizingTrustManager')
+ compile project(':libs:audiowife')
compile 'org.sufficientlysecure:openpgp-api:10.0'
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
compile 'com.android.support:support-v13:23.2.0'
diff --git a/libs/audiowife/.gitignore b/libs/audiowife/.gitignore
new file mode 100644
index 000000000..e99645287
--- /dev/null
+++ b/libs/audiowife/.gitignore
@@ -0,0 +1,5 @@
+.gradle
+.idea
+build
+*.iml
+*.properties \ No newline at end of file
diff --git a/libs/audiowife/build.gradle b/libs/audiowife/build.gradle
new file mode 100644
index 000000000..63c2e8e78
--- /dev/null
+++ b/libs/audiowife/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.3"
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+
+ }
+ }
+
+ // It would be better to fix the issues
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/libs/audiowife/src/main/AndroidManifest.xml b/libs/audiowife/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..dad095276
--- /dev/null
+++ b/libs/audiowife/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="nl.changer.audiowife">
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name">
+
+ </application>
+
+</manifest>
diff --git a/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java b/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java
new file mode 100644
index 000000000..ade93475a
--- /dev/null
+++ b/libs/audiowife/src/main/java/nl/changer/audiowife/AudioWife.java
@@ -0,0 +1,699 @@
+/***
+ * The MIT License (MIT)
+ * <p/>
+ * Copyright (c) 2014 Jaydeep
+ * <p/>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * <p/>
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ * <p/>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.changer.audiowife;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.net.Uri;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/***
+ * A simple audio player wrapper for Android
+ ***/
+public class AudioWife {
+
+ private static final String TAG = AudioWife.class.getSimpleName();
+ /****
+ * Playback progress update time in milliseconds
+ ****/
+ private static final int AUDIO_PROGRESS_UPDATE_TIME = 100;
+ // TODO: externalize the error messages.
+ private static final String ERROR_PLAYVIEW_NULL = "Play view cannot be null";
+ private static final String ERROR_PLAYTIME_CURRENT_NEGATIVE = "Current playback time cannot be negative";
+ private static final String ERROR_PLAYTIME_TOTAL_NEGATIVE = "Total playback time cannot be negative";
+ /***
+ * Keep a single copy of this in memory unless required to create a new instance explicitly.
+ ****/
+ private static AudioWife mAudioWife;
+ /***
+ * Audio URI
+ ****/
+ private static Uri mUri;
+ private Handler mProgressUpdateHandler;
+ private MediaPlayer mMediaPlayer;
+ private SeekBar mSeekBar;
+ @Deprecated
+ /***
+ * Set both current playack time and total runtime
+ * of the audio in the UI.
+ */
+ private TextView mPlaybackTime;
+ private View mPlayButton;
+ private View mPauseButton;
+ /***
+ * Indicates the current run-time of the audio being played
+ */
+ private TextView mRunTime;
+ /***
+ * Indicates the total duration of the audio being played.
+ */
+ private TextView mTotalTime;
+ /***
+ * Set if AudioWife is using the default UI provided with the library.
+ **/
+ private boolean mHasDefaultUi;
+ /****
+ * Array to hold custom completion listeners
+ ****/
+ private ArrayList<OnCompletionListener> mCompletionListeners = new ArrayList<OnCompletionListener>();
+ private ArrayList<View.OnClickListener> mPlayListeners = new ArrayList<View.OnClickListener>();
+ private ArrayList<View.OnClickListener> mPauseListeners = new ArrayList<View.OnClickListener>();
+ private Runnable mUpdateProgress = new Runnable() {
+
+ public void run() {
+
+ if (mSeekBar == null) {
+ return;
+ }
+
+ if (mProgressUpdateHandler != null && mMediaPlayer.isPlaying()) {
+ mSeekBar.setProgress((int) mMediaPlayer.getCurrentPosition());
+ int currentTime = mMediaPlayer.getCurrentPosition();
+ updatePlaytime(currentTime);
+ updateRuntime(currentTime);
+ // repeat the process
+ mProgressUpdateHandler.postDelayed(this, AUDIO_PROGRESS_UPDATE_TIME);
+ } else {
+ // DO NOT update UI if the player is paused
+ }
+ }
+ };
+ private OnCompletionListener mOnCompletion = new OnCompletionListener() {
+
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ // set UI when audio finished playing
+ int currentPlayTime = 0;
+ mSeekBar.setProgress((int) currentPlayTime);
+ updatePlaytime(currentPlayTime);
+ updateRuntime(currentPlayTime);
+ setPlayable();
+ // ensure that our completion listener fires first.
+ // This will provide the developer to over-ride our
+ // completion listener functionality
+
+ fireCustomCompletionListeners(mp);
+ }
+ };
+ private View playerUi;
+
+ public static AudioWife getInstance() {
+
+ if (mAudioWife == null) {
+ mAudioWife = new AudioWife();
+ }
+
+ return mAudioWife;
+ }
+
+ /***
+ * Starts playing audio file associated. Before playing the audio, visibility of appropriate UI
+ * controls is made visible. Calling this method has no effect if the audio is already being
+ * played.
+ ****/
+ public void play() {
+
+ // if play button itself is null, the whole purpose of AudioWife is
+ // defeated.
+ if (mPlayButton == null) {
+ throw new IllegalStateException(ERROR_PLAYVIEW_NULL);
+ }
+
+ if (mUri == null) {
+ throw new IllegalStateException("Uri cannot be null. Call init() before calling this method");
+ }
+
+ if (mMediaPlayer == null) {
+ throw new IllegalStateException("Call init() before calling this method");
+ }
+
+ if (mMediaPlayer.isPlaying()) {
+ return;
+ }
+
+ mProgressUpdateHandler.postDelayed(mUpdateProgress, AUDIO_PROGRESS_UPDATE_TIME);
+
+ // enable visibility of all UI controls.
+ setViewsVisibility();
+
+ mMediaPlayer.start();
+
+ setPausable();
+ }
+
+ /**
+ * Ensure the views are visible before playing the audio.
+ */
+ private void setViewsVisibility() {
+
+ if (mSeekBar != null) {
+ mSeekBar.setVisibility(View.VISIBLE);
+ }
+
+ if (mPlaybackTime != null) {
+ mPlaybackTime.setVisibility(View.VISIBLE);
+ }
+
+ if (mRunTime != null) {
+ mRunTime.setVisibility(View.VISIBLE);
+ }
+
+ if (mTotalTime != null) {
+ mTotalTime.setVisibility(View.VISIBLE);
+ }
+
+ if (mPlayButton != null) {
+ mPlayButton.setVisibility(View.VISIBLE);
+ }
+
+ if (mPauseButton != null) {
+ mPauseButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /***
+ * Pause the audio being played. Calling this method has no effect if the audio is already
+ * paused
+ */
+ public void pause() {
+
+ if (mMediaPlayer == null) {
+ return;
+ }
+
+ if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ setPlayable();
+ }
+ }
+
+ @Deprecated
+ private void updatePlaytime(int currentTime) {
+
+ if (mPlaybackTime == null) {
+ return;
+ }
+
+ if (currentTime < 0) {
+ throw new IllegalArgumentException(ERROR_PLAYTIME_CURRENT_NEGATIVE);
+ }
+
+ StringBuilder playbackStr = new StringBuilder();
+
+ // set the current time
+ // its ok to show 00:00 in the UI
+ playbackStr.append(String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes((long) currentTime), TimeUnit.MILLISECONDS.toSeconds((long) currentTime) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes((long) currentTime))));
+
+ playbackStr.append("/");
+
+ // show total duration.
+ long totalDuration = 0;
+
+ if (mMediaPlayer != null) {
+ try {
+ totalDuration = mMediaPlayer.getDuration();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // set total time as the audio is being played
+ if (totalDuration != 0) {
+ playbackStr.append(String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes((long) totalDuration), TimeUnit.MILLISECONDS.toSeconds((long) totalDuration) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes((long) totalDuration))));
+ } else {
+ Log.w(TAG, "Something strage this audio track duration in zero");
+ }
+
+ mPlaybackTime.setText(playbackStr);
+
+ // DebugLog.i(currentTime + " / " + totalDuration);
+ }
+
+ private void updateRuntime(int currentTime) {
+
+ if (mRunTime == null) {
+ // this view can be null if the user
+ // does not want to use it. Don't throw
+ // an exception.
+ return;
+ }
+
+ if (currentTime < 0) {
+ throw new IllegalArgumentException(ERROR_PLAYTIME_CURRENT_NEGATIVE);
+ }
+
+ StringBuilder playbackStr = new StringBuilder();
+
+ // set the current time
+ // its ok to show 00:00 in the UI
+ playbackStr.append(String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes((long) currentTime), TimeUnit.MILLISECONDS.toSeconds((long) currentTime) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes((long) currentTime))));
+
+ mRunTime.setText(playbackStr);
+
+ // DebugLog.i(currentTime + " / " + totalDuration);
+ }
+
+ private void setTotalTime() {
+
+ if (mTotalTime == null) {
+ // this view can be null if the user
+ // does not want to use it. Don't throw
+ // an exception.
+ return;
+ }
+
+ StringBuilder playbackStr = new StringBuilder();
+ long totalDuration = 0;
+
+ // by this point the media player is brought to ready state
+ // by the call to init().
+ if (mMediaPlayer != null) {
+ try {
+ totalDuration = mMediaPlayer.getDuration();
+ } catch (IllegalStateException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (totalDuration < 0) {
+ throw new IllegalArgumentException(ERROR_PLAYTIME_TOTAL_NEGATIVE);
+ }
+
+ // set total time as the audio is being played
+ if (totalDuration != 0) {
+ playbackStr.append(String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes((long) totalDuration), TimeUnit.MILLISECONDS.toSeconds((long) totalDuration) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes((long) totalDuration))));
+ }
+
+ mTotalTime.setText(playbackStr);
+ }
+
+ /***
+ * Changes audiowife state to enable play functionality.
+ */
+ private void setPlayable() {
+ if (mPlayButton != null) {
+ mPlayButton.setVisibility(View.VISIBLE);
+ }
+
+ if (mPauseButton != null) {
+ mPauseButton.setVisibility(View.GONE);
+ }
+ }
+
+ /****
+ * Changes audio wife to enable pause functionality.
+ */
+ private void setPausable() {
+ if (mPlayButton != null) {
+ mPlayButton.setVisibility(View.GONE);
+ }
+
+ if (mPauseButton != null) {
+ mPauseButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /***
+ * Initialize the audio player. This method should be the first one to be called before starting
+ * to play audio using {@link nl.changer.audiowife.AudioWife}
+ *
+ * @param ctx {@link android.app.Activity} Context
+ * @param uri Uri of the audio to be played.
+ ****/
+ public AudioWife init(Context ctx, Uri uri) {
+
+ if (uri == null) {
+ throw new IllegalArgumentException("Uri cannot be null");
+ }
+
+ if (mAudioWife == null) {
+ mAudioWife = new AudioWife();
+ }
+
+ mUri = uri;
+
+ mProgressUpdateHandler = new Handler();
+
+ initPlayer(ctx);
+
+ return this;
+ }
+
+ /***
+ * Sets the audio play functionality on click event of this view. You can set {@link android.widget.Button} or
+ * an {@link android.widget.ImageView} as audio play control
+ *
+ * @see nl.changer.audiowife.AudioWife#addOnPauseClickListener(android.view.View.OnClickListener)
+ ****/
+ public AudioWife setPlayView(View play) {
+
+ if (play == null) {
+ throw new NullPointerException("PlayView cannot be null");
+ }
+
+ if (mHasDefaultUi) {
+ Log.w(TAG, "Already using default UI. Setting play view will have no effect");
+ return this;
+ }
+
+ mPlayButton = play;
+
+ initOnPlayClick();
+ return this;
+ }
+
+ private void initOnPlayClick() {
+ if (mPlayButton == null) {
+ throw new NullPointerException(ERROR_PLAYVIEW_NULL);
+ }
+
+ // add default click listener to the top
+ // so that it is the one that gets fired first
+ mPlayListeners.add(0, new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ play();
+ }
+ });
+
+ // Fire all the attached listeners
+ // when the play button is clicked
+ mPlayButton.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ for (View.OnClickListener listener : mPlayListeners) {
+ listener.onClick(v);
+ }
+ }
+ });
+ }
+
+ /***
+ * Sets the audio pause functionality on click event of the view passed in as a parameter. You
+ * can set {@link android.widget.Button} or an {@link android.widget.ImageView} as audio pause control. Audio pause
+ * functionality will be unavailable if this method is not called.
+ *
+ * @see nl.changer.audiowife.AudioWife#addOnPauseClickListener(android.view.View.OnClickListener)
+ ****/
+ public AudioWife setPauseView(View pause) {
+
+ if (pause == null) {
+ throw new NullPointerException("PauseView cannot be null");
+ }
+
+ if (mHasDefaultUi) {
+ Log.w(TAG, "Already using default UI. Setting pause view will have no effect");
+ return this;
+ }
+
+ mPauseButton = pause;
+
+ initOnPauseClick();
+ return this;
+ }
+
+ private void initOnPauseClick() {
+ if (mPauseButton == null) {
+ throw new NullPointerException("Pause view cannot be null");
+ }
+
+ // add default click listener to the top
+ // so that it is the one that gets fired first
+ mPauseListeners.add(0, new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ pause();
+ }
+ });
+
+ // Fire all the attached listeners
+ // when the pause button is clicked
+ mPauseButton.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ for (View.OnClickListener listener : mPauseListeners) {
+ listener.onClick(v);
+ }
+ }
+ });
+ }
+
+ /***
+ * @deprecated Use {@link nl.changer.audiowife.AudioWife#setRuntimeView(android.widget.TextView)} and
+ * {@link nl.changer.audiowife.AudioWife#setTotalTimeView(android.widget.TextView)} instead. <br/>
+ * Sets current and total playback time. Use this if you have a playback time
+ * counter in the UI.
+ ****/
+ public AudioWife setPlaytime(TextView playTime) {
+
+ if (mHasDefaultUi) {
+ Log.w(TAG, "Already using default UI. Setting play time will have no effect");
+ return this;
+ }
+
+ mPlaybackTime = playTime;
+
+ // initialize the playtime to 0
+ updatePlaytime(0);
+ return this;
+ }
+
+ /***
+ * Sets current playback time view. Use this if you have a playback time counter in the UI.
+ *
+ * @see nl.changer.audiowife.AudioWife#setTotalTimeView(android.widget.TextView)
+ ****/
+ public AudioWife setRuntimeView(TextView currentTime) {
+
+ if (mHasDefaultUi) {
+ Log.w(TAG, "Already using default UI. Setting play time will have no effect");
+ return this;
+ }
+
+ mRunTime = currentTime;
+
+ // initialize the playtime to 0
+ updateRuntime(0);
+ return this;
+ }
+
+ /***
+ * Sets the total playback time view. Use this if you have a playback time counter in the UI.
+ *
+ * @see nl.changer.audiowife.AudioWife#setRuntimeView(android.widget.TextView)
+ ****/
+ public AudioWife setTotalTimeView(TextView totalTime) {
+
+ if (mHasDefaultUi) {
+ Log.w(TAG, "Already using default UI. Setting play time will have no effect");
+ return this;
+ }
+
+ mTotalTime = totalTime;
+
+ setTotalTime();
+ return this;
+ }
+
+ public AudioWife setSeekBar(SeekBar seekbar) {
+
+ if (mHasDefaultUi) {
+ Log.w(TAG, "Already using default UI. Setting seek bar will have no effect");
+ return this;
+ }
+
+ mSeekBar = seekbar;
+ initMediaSeekBar();
+ return this;
+ }
+
+ /****
+ * Add custom playback completion listener. Adding multiple listeners will queue up all the
+ * listeners and fire them on media playback completes.
+ */
+ public AudioWife addOnCompletionListener(OnCompletionListener listener) {
+
+ // add default click listener to the top
+ // so that it is the one that gets fired first
+ mCompletionListeners.add(0, listener);
+
+ return this;
+ }
+
+ /****
+ * Add custom play view click listener. Calling this method multiple times will queue up all the
+ * listeners and fire them all together when the event occurs.
+ ***/
+ public AudioWife addOnPlayClickListener(View.OnClickListener listener) {
+
+ mPlayListeners.add(listener);
+
+ return this;
+ }
+
+ /***
+ * Add custom pause view click listener. Calling this method multiple times will queue up all
+ * the listeners and fire them all together when the event occurs.
+ ***/
+ public AudioWife addOnPauseClickListener(View.OnClickListener listener) {
+
+ mPauseListeners.add(listener);
+
+ return this;
+ }
+
+ /****
+ * Initialize and prepare the audio player
+ ****/
+ private void initPlayer(Context ctx) {
+
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+
+ try {
+ mMediaPlayer.setDataSource(ctx, mUri);
+ } catch (Exception ignored) {
+ }
+
+ try {
+ mMediaPlayer.prepare();
+ } catch (Exception ignored) {
+ }
+
+ mMediaPlayer.setOnCompletionListener(mOnCompletion);
+ }
+
+ private void initMediaSeekBar() {
+
+ if (mSeekBar == null) {
+ return;
+ }
+
+ // update seekbar
+ long finalTime = mMediaPlayer.getDuration();
+ mSeekBar.setMax((int) finalTime);
+
+ mSeekBar.setProgress(0);
+
+ mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mMediaPlayer.seekTo(seekBar.getProgress());
+
+ // if the audio is paused and seekbar is moved,
+ // update the play time in the UI.
+ updateRuntime(seekBar.getProgress());
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+
+ }
+ });
+ }
+
+ private void fireCustomCompletionListeners(MediaPlayer mp) {
+ for (OnCompletionListener listener : mCompletionListeners) {
+ listener.onCompletion(mp);
+ }
+ }
+
+ public void cleanPlayerUi() {
+ ((ViewGroup) playerUi.getParent()).removeView(playerUi);
+ }
+
+ public View getPlayerUi() {
+ return playerUi;
+ }
+
+ public AudioWife useDefaultUi(ViewGroup playerContainer, LayoutInflater inflater) {
+ if (playerContainer == null) {
+ throw new NullPointerException("Player container cannot be null");
+ }
+
+ if (inflater == null) {
+ throw new IllegalArgumentException("Inflater cannot be null");
+ }
+
+ playerUi = inflater.inflate(R.layout.aw_player, playerContainer, false); // IMPORTANT, sonst geht meine Lösung nicht xD
+
+ // init play view
+ View playView = playerUi.findViewById(R.id.play);
+ setPlayView(playView);
+
+ // init pause view
+ View pauseView = playerUi.findViewById(R.id.pause);
+ setPauseView(pauseView);
+
+ // init seekbar
+ SeekBar seekBar = (SeekBar) playerUi.findViewById(R.id.media_seekbar);
+ setSeekBar(seekBar);
+
+ // init playback time view
+ TextView playbackTime = (TextView) playerUi.findViewById(R.id.playback_time);
+ setPlaytime(playbackTime);
+
+ // this has to be set after all the views
+ // have finished initializing.
+ mHasDefaultUi = true;
+ return this;
+ }
+ public void release() {
+
+ if (mMediaPlayer != null) {
+ mMediaPlayer.stop();
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ mProgressUpdateHandler = null;
+ }
+ }
+}
diff --git a/libs/audiowife/src/main/res/drawable-mdpi/ic_launcher.png b/libs/audiowife/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/libs/audiowife/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/libs/audiowife/src/main/res/drawable-xhdpi/ic_launcher.png b/libs/audiowife/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/libs/audiowife/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/libs/audiowife/src/main/res/drawable-xxhdpi/ic_launcher.png b/libs/audiowife/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4df189464
--- /dev/null
+++ b/libs/audiowife/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml b/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml
new file mode 100644
index 000000000..409ed7ccd
--- /dev/null
+++ b/libs/audiowife/src/main/res/drawable/gray_border_wo_padding.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+
+ <!-- view border color and width -->
+ <stroke
+ android:width="1dp"
+ android:color="#F2F2F2" >
+ </stroke>
+
+ <gradient
+ android:endColor="@android:color/white"
+ android:startColor="@android:color/white" />
+
+</shape> \ No newline at end of file
diff --git a/libs/audiowife/src/main/res/layout/aw_player.xml b/libs/audiowife/src/main/res/layout/aw_player.xml
new file mode 100644
index 000000000..6d2904e31
--- /dev/null
+++ b/libs/audiowife/src/main/res/layout/aw_player.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/player_layout"
+ android:layout_width="match_parent"
+ android:layout_height="70dp"
+ android:gravity="center_vertical"
+ android:paddingLeft="4dp">
+
+ <ImageView
+ android:id="@+id/play"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="6"
+ android:src="@android:drawable/ic_media_play" />
+
+ <ImageView
+ android:id="@+id/pause"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="6"
+ android:src="@android:drawable/ic_media_pause"
+ android:visibility="gone" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="0.8"
+ android:paddingBottom="5dp"
+ android:paddingRight="10dp"
+ android:paddingTop="5dp" >
+
+ <SeekBar
+ android:id="@+id/media_seekbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/playback_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|top"
+ android:ellipsize="end"
+ android:inputType="text"
+ android:textColor="@android:color/darker_gray"
+ android:textSize="11sp" />
+ </FrameLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/libs/audiowife/src/main/res/values/strings.xml b/libs/audiowife/src/main/res/values/strings.xml
new file mode 100644
index 000000000..66966868f
--- /dev/null
+++ b/libs/audiowife/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+<resources>
+
+ <string name="app_name">LibAudioWife</string>
+
+</resources>
diff --git a/libs/audiowife/src/main/res/values/styles.xml b/libs/audiowife/src/main/res/values/styles.xml
new file mode 100644
index 000000000..6ce89c7ba
--- /dev/null
+++ b/libs/audiowife/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
diff --git a/settings.gradle b/settings.gradle
index 97df09188..57df06453 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,4 @@
include ':libs:MemorizingTrustManager'
-
+include ':libs:audiowife'
rootProject.name = 'Conversations'
+
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 88ce1dfd5..1543e4051 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.ui.adapter;
import android.content.ActivityNotFoundException;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -20,6 +21,7 @@ import android.text.style.StyleSpan;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
import android.util.Patterns;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
@@ -28,11 +30,13 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.ref.WeakReference;
import java.net.URL;
+import java.util.HashMap;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.regex.Matcher;
@@ -50,6 +54,7 @@ import eu.siacs.conversations.ui.ConversationActivity;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
+import nl.changer.audiowife.AudioWife;
public class MessageAdapter extends ArrayAdapter<Message> {
@@ -78,6 +83,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
};
private boolean mIndicateReceived = false;
+ private HashMap<Integer, AudioWife> audioPlayer;
private boolean mUseWhiteBackground = false;
public MessageAdapter(ConversationActivity activity, List<Message> messages) {
@@ -257,6 +263,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayInfoMessage(ViewHolder viewHolder, String text, boolean darkBackground) {
+ viewHolder.aw_player.setVisibility(View.GONE);
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
@@ -269,6 +276,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayDecryptionFailed(ViewHolder viewHolder, boolean darkBackground) {
+ viewHolder.aw_player.setVisibility(View.GONE);
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
@@ -282,6 +290,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayHeartMessage(final ViewHolder viewHolder, final String body) {
+ viewHolder.aw_player.setVisibility(View.GONE);
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
@@ -375,6 +384,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private void displayDownloadableMessage(ViewHolder viewHolder,
final Message message, String text) {
+ viewHolder.aw_player.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
@@ -390,6 +400,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayOpenableMessage(ViewHolder viewHolder,final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
@@ -405,6 +416,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
private void displayLocationMessage(ViewHolder viewHolder, final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.GONE);
viewHolder.messageBody.setVisibility(View.GONE);
viewHolder.download_button.setVisibility(View.VISIBLE);
@@ -421,6 +433,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
private void displayImageMessage(ViewHolder viewHolder,
final Message message) {
+ viewHolder.aw_player.setVisibility(View.GONE);
if (viewHolder.download_button != null) {
viewHolder.download_button.setVisibility(View.GONE);
}
@@ -451,6 +464,29 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.image.setOnLongClickListener(openContextMenu);
}
+ private void displayAudioMessage(ViewHolder viewHolder, final Message message, int position) {
+ if (audioPlayer == null) audioPlayer = new HashMap<>();
+ viewHolder.image.setVisibility(View.GONE);
+ viewHolder.messageBody.setVisibility(View.GONE);
+ if (viewHolder.download_button != null) viewHolder.download_button.setVisibility(View.GONE);
+ viewHolder.aw_player.setVisibility(View.VISIBLE);
+ Uri audioFile = Uri.fromFile(activity.xmppConnectionService.getFileBackend().getFile(message));
+
+ AudioWife audioWife = audioPlayer.get(position);
+ if (audioWife == null) {
+ audioWife = new AudioWife();
+ audioWife.init(getContext(), audioFile);
+ audioPlayer.put(position, audioWife);
+ RelativeLayout vg = new RelativeLayout(activity);
+ LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ audioWife.useDefaultUi(vg, layoutInflater);
+ viewHolder.aw_player.addView(audioWife.getPlayerUi());
+ } else {
+ audioWife.cleanPlayerUi();
+ viewHolder.aw_player.addView(audioWife.getPlayerUi());
+ }
+ }
+
private void loadMoreMessages(Conversation conversation) {
conversation.setLastClearHistory(0);
conversation.setHasMessagesLeftOnServer(true);
@@ -482,6 +518,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
+ viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
viewHolder.download_button = (Button) view
.findViewById(R.id.download_button);
viewHolder.indicator = (ImageView) view
@@ -505,6 +542,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
.findViewById(R.id.message_box);
viewHolder.contact_picture = (ImageView) view
.findViewById(R.id.message_photo);
+ viewHolder.aw_player = (ViewGroup) view.findViewById(R.id.aw_player);
viewHolder.download_button = (Button) view
.findViewById(R.id.download_button);
viewHolder.indicator = (ImageView) view
@@ -610,7 +648,12 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (message.getFileParams().width > 0) {
displayImageMessage(viewHolder,message);
} else {
- displayOpenableMessage(viewHolder, message);
+ String mimeType = message.getMimeType();
+ if (mimeType != null) {
+ if (message.getMimeType().startsWith("audio/"))
+ displayAudioMessage(viewHolder, message, position);
+ else displayOpenableMessage(viewHolder, message);
+ } else displayOpenableMessage(viewHolder, message);
}
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
if (activity.hasPgp()) {
@@ -732,6 +775,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
protected LinearLayout message_box;
protected Button download_button;
+ protected ViewGroup aw_player;
protected ImageView image;
protected ImageView indicator;
protected ImageView indicatorReceived;
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index 1f04a2c3d..909cf78f8 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -68,6 +68,12 @@
android:layout_height="wrap_content"
android:visibility="gone" />
+ <RelativeLayout
+ android:id="@+id/aw_player"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="visible" />
+
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 6381486d2..9118b1846 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -69,6 +69,12 @@
android:layout_height="wrap_content"
android:visibility="gone" />
+ <RelativeLayout
+ android:id="@+id/aw_player"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="visible" />
+
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"