diff --git a/res/drawable/ic_chevron_up.xml b/res/drawable/ic_chevron_up.xml
new file mode 100644
index 0000000000..c54db3bcc6
--- /dev/null
+++ b/res/drawable/ic_chevron_up.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/drawable/ic_lock.xml b/res/drawable/ic_lock.xml
new file mode 100644
index 0000000000..b642ae75e5
--- /dev/null
+++ b/res/drawable/ic_lock.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/layout/conversation_input_panel.xml b/res/layout/conversation_input_panel.xml
index c456ba99a0..cd9ac5b9a0 100644
--- a/res/layout/conversation_input_panel.xml
+++ b/res/layout/conversation_input_panel.xml
@@ -124,31 +124,12 @@
-
-
-
+
@@ -177,51 +158,7 @@
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/res/layout/microphone_recorder_view.xml b/res/layout/microphone_recorder_view.xml
new file mode 100644
index 0000000000..986bed2dba
--- /dev/null
+++ b/res/layout/microphone_recorder_view.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/recording_layout.xml b/res/layout/recording_layout.xml
new file mode 100644
index 0000000000..587242bf6f
--- /dev/null
+++ b/res/layout/recording_layout.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index f96130fb2d..b17e25e061 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -99,4 +99,6 @@
14dp
+ -150dp
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fed4f0d8b2..471d40e60a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -869,10 +869,12 @@
Attachment Thumbnail
Toggle quick camera attachment drawer
Record and send audio attachment
+ Lock recording of audio attachment
Enable Signal for SMS
- SLIDE TO CANCEL
+ Slide to cancel
+ Cancel
Media message
diff --git a/src/org/thoughtcrime/securesms/components/InputPanel.java b/src/org/thoughtcrime/securesms/components/InputPanel.java
index 7752c4f937..7cb170c827 100644
--- a/src/org/thoughtcrime/securesms/components/InputPanel.java
+++ b/src/org/thoughtcrime/securesms/components/InputPanel.java
@@ -5,6 +5,7 @@ import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.DimenRes;
+import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
@@ -38,7 +39,6 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
public class InputPanel extends LinearLayout
implements MicrophoneRecorderView.Listener,
@@ -58,6 +58,7 @@ public class InputPanel extends LinearLayout
private View quickAudioToggle;
private View buttonToggle;
private View recordingContainer;
+ private View recordLockCancel;
private MicrophoneRecorderView microphoneRecorderView;
private SlideToCancel slideToCancel;
@@ -93,10 +94,15 @@ public class InputPanel extends LinearLayout
this.quickAudioToggle = findViewById(R.id.quick_audio_toggle);
this.buttonToggle = findViewById(R.id.button_toggle);
this.recordingContainer = findViewById(R.id.recording_container);
- this.recordTime = new RecordTime(findViewById(R.id.record_time));
+ this.recordLockCancel = findViewById(R.id.record_cancel);
this.slideToCancel = new SlideToCancel(findViewById(R.id.slide_to_cancel));
this.microphoneRecorderView = findViewById(R.id.recorder_view);
this.microphoneRecorderView.setListener(this);
+ this.recordTime = new RecordTime(findViewById(R.id.record_time),
+ TimeUnit.HOURS.toSeconds(1),
+ () -> microphoneRecorderView.cancelAction());
+
+ this.recordLockCancel.setOnClickListener(v -> microphoneRecorderView.cancelAction());
if (TextSecurePreferences.isSystemEmojiPreferred(getContext())) {
emojiToggle.setVisibility(View.GONE);
@@ -181,21 +187,21 @@ public class InputPanel extends LinearLayout
}
@Override
- public void onRecordPressed(float startPositionX) {
+ public void onRecordPressed() {
if (listener != null) listener.onRecorderStarted();
recordTime.display();
- slideToCancel.display(startPositionX);
+ slideToCancel.display();
if (emojiVisible) ViewUtil.fadeOut(emojiToggle, FADE_TIME, View.INVISIBLE);
ViewUtil.fadeOut(composeText, FADE_TIME, View.INVISIBLE);
ViewUtil.fadeOut(quickCameraToggle, FADE_TIME, View.INVISIBLE);
ViewUtil.fadeOut(quickAudioToggle, FADE_TIME, View.INVISIBLE);
- ViewUtil.fadeOut(buttonToggle, FADE_TIME, View.INVISIBLE);
+ buttonToggle.animate().alpha(0).setDuration(FADE_TIME).start();
}
@Override
- public void onRecordReleased(float x) {
- long elapsedTime = onRecordHideEvent(x);
+ public void onRecordReleased() {
+ long elapsedTime = onRecordHideEvent();
if (listener != null) {
Log.d(TAG, "Elapsed time: " + elapsedTime);
@@ -209,8 +215,8 @@ public class InputPanel extends LinearLayout
}
@Override
- public void onRecordMoved(float x, float absoluteX) {
- slideToCancel.moveTo(x);
+ public void onRecordMoved(float offsetX, float absoluteX) {
+ slideToCancel.moveTo(offsetX);
int direction = ViewCompat.getLayoutDirection(this);
float position = absoluteX / recordingContainer.getWidth();
@@ -223,11 +229,19 @@ public class InputPanel extends LinearLayout
}
@Override
- public void onRecordCanceled(float x) {
- onRecordHideEvent(x);
+ public void onRecordCanceled() {
+ onRecordHideEvent();
if (listener != null) listener.onRecorderCanceled();
}
+ @Override
+ public void onRecordLocked() {
+ slideToCancel.hide();
+ recordLockCancel.setVisibility(View.VISIBLE);
+ buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start();
+ if (listener != null) listener.onRecorderLocked();
+ }
+
public void onPause() {
this.microphoneRecorderView.cancelAction();
}
@@ -239,8 +253,10 @@ public class InputPanel extends LinearLayout
quickCameraToggle.setEnabled(enabled);
}
- private long onRecordHideEvent(float x) {
- ListenableFuture future = slideToCancel.hide(x);
+ private long onRecordHideEvent() {
+ recordLockCancel.setVisibility(View.GONE);
+
+ ListenableFuture future = slideToCancel.hide();
long elapsedTime = recordTime.hide();
future.addListener(new AssertedSuccessListener() {
@@ -250,7 +266,7 @@ public class InputPanel extends LinearLayout
ViewUtil.fadeIn(composeText, FADE_TIME);
ViewUtil.fadeIn(quickCameraToggle, FADE_TIME);
ViewUtil.fadeIn(quickAudioToggle, FADE_TIME);
- ViewUtil.fadeIn(buttonToggle, FADE_TIME);
+ buttonToggle.animate().alpha(1).setDuration(FADE_TIME).start();
}
});
@@ -276,9 +292,17 @@ public class InputPanel extends LinearLayout
return getResources().getDimensionPixelSize(dimenRes);
}
+ public boolean isRecordingInLockedMode() {
+ return microphoneRecorderView.isRecordingLocked();
+ }
+
+ public void releaseRecordingLock() {
+ microphoneRecorderView.unlockAction();
+ }
public interface Listener {
void onRecorderStarted();
+ void onRecorderLocked();
void onRecorderFinished();
void onRecorderCanceled();
void onRecorderPermissionRequired();
@@ -290,23 +314,19 @@ public class InputPanel extends LinearLayout
private final View slideToCancelView;
- private float startPositionX;
-
- public SlideToCancel(View slideToCancelView) {
+ SlideToCancel(View slideToCancelView) {
this.slideToCancelView = slideToCancelView;
}
- public void display(float startPositionX) {
- this.startPositionX = startPositionX;
+ public void display() {
ViewUtil.fadeIn(this.slideToCancelView, FADE_TIME);
}
- public ListenableFuture hide(float x) {
+ public ListenableFuture hide() {
final SettableFuture future = new SettableFuture<>();
- float offset = getOffset(x);
AnimationSet animation = new AnimationSet(true);
- animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, offset,
+ animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, slideToCancelView.getTranslationX(),
Animation.ABSOLUTE, 0,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0));
@@ -323,8 +343,7 @@ public class InputPanel extends LinearLayout
return future;
}
- public void moveTo(float x) {
- float offset = getOffset(x);
+ void moveTo(float offset) {
Animation animation = new TranslateAnimation(Animation.ABSOLUTE, offset,
Animation.ABSOLUTE, offset,
Animation.RELATIVE_TO_SELF, 0,
@@ -336,49 +355,55 @@ public class InputPanel extends LinearLayout
slideToCancelView.startAnimation(animation);
}
-
- private float getOffset(float x) {
- return ViewCompat.getLayoutDirection(slideToCancelView) == ViewCompat.LAYOUT_DIRECTION_LTR ?
- -Math.max(0, this.startPositionX - x) : Math.max(0, x - this.startPositionX);
- }
-
}
private static class RecordTime implements Runnable {
private final TextView recordTimeView;
- private final AtomicLong startTime = new AtomicLong(0);
+ private final long limitSeconds;
+ private final Runnable onLimitHit;
+ private long startTime;
- private RecordTime(TextView recordTimeView) {
+ private RecordTime(TextView recordTimeView, long limitSeconds, Runnable onLimitHit) {
this.recordTimeView = recordTimeView;
+ this.limitSeconds = limitSeconds;
+ this.onLimitHit = onLimitHit;
}
+ @MainThread
public void display() {
- this.startTime.set(System.currentTimeMillis());
+ this.startTime = System.currentTimeMillis();
this.recordTimeView.setText(DateUtils.formatElapsedTime(0));
ViewUtil.fadeIn(this.recordTimeView, FADE_TIME);
Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
}
+ @MainThread
public long hide() {
- long elapsedtime = System.currentTimeMillis() - startTime.get();
- this.startTime.set(0);
+ long elapsedTime = System.currentTimeMillis() - startTime;
+ this.startTime = 0;
ViewUtil.fadeOut(this.recordTimeView, FADE_TIME, View.INVISIBLE);
- return elapsedtime;
+ return elapsedTime;
}
@Override
+ @MainThread
public void run() {
- long localStartTime = startTime.get();
+ long localStartTime = startTime;
if (localStartTime > 0) {
long elapsedTime = System.currentTimeMillis() - localStartTime;
- recordTimeView.setText(DateUtils.formatElapsedTime(TimeUnit.MILLISECONDS.toSeconds(elapsedTime)));
- Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
+ long elapsedSeconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTime);
+ if (elapsedSeconds >= limitSeconds) {
+ onLimitHit.run();
+ } else {
+ recordTimeView.setText(DateUtils.formatElapsedTime(elapsedSeconds));
+ Util.runOnMainDelayed(this, TimeUnit.SECONDS.toMillis(1));
+ }
}
}
}
public interface MediaListener {
- public void onMediaSelected(@NonNull Uri uri, String contentType);
+ void onMediaSelected(@NonNull Uri uri, String contentType);
}
}
diff --git a/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java b/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
index 4b1138c473..b35c2367fb 100644
--- a/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
+++ b/src/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.components;
import android.Manifest;
import android.content.Context;
import android.graphics.PorterDuff;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
@@ -12,6 +13,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnticipateOvershootInterpolator;
import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
@@ -22,13 +24,20 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.ViewUtil;
-public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchListener {
+public final class MicrophoneRecorderView extends FrameLayout implements View.OnTouchListener {
+
+ enum State {
+ NOT_RUNNING,
+ RUNNING_HELD,
+ RUNNING_LOCKED
+ }
public static final int ANIMATION_DURATION = 200;
private FloatingRecordButton floatingRecordButton;
+ private LockDropTarget lockDropTarget;
private @Nullable Listener listener;
- private boolean actionInProgress;
+ private @NonNull State state = State.NOT_RUNNING;
public MicrophoneRecorderView(Context context) {
super(context);
@@ -42,22 +51,49 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
public void onFinishInflate() {
super.onFinishInflate();
- ImageView recordButtonFab = ViewUtil.findById(this, R.id.quick_audio_fab);
- this.floatingRecordButton = new FloatingRecordButton(getContext(), recordButtonFab);
+ floatingRecordButton = new FloatingRecordButton(getContext(), findViewById(R.id.quick_audio_fab));
+ lockDropTarget = new LockDropTarget (getContext(), findViewById(R.id.lock_drop_target));
View recordButton = ViewUtil.findById(this, R.id.quick_audio_toggle);
recordButton.setOnTouchListener(this);
}
public void cancelAction() {
- if (this.actionInProgress) {
- this.actionInProgress = false;
- this.floatingRecordButton.hide(this.floatingRecordButton.lastPositionX);
+ if (state != State.NOT_RUNNING) {
+ state = State.NOT_RUNNING;
+ hideUi();
- if (listener != null) listener.onRecordCanceled(this.floatingRecordButton.lastPositionX);
+ if (listener != null) listener.onRecordCanceled();
}
}
+ public boolean isRecordingLocked() {
+ return state == State.RUNNING_LOCKED;
+ }
+
+ private void lockAction() {
+ if (state == State.RUNNING_HELD) {
+ state = State.RUNNING_LOCKED;
+ hideUi();
+
+ if (listener != null) listener.onRecordLocked();
+ }
+ }
+
+ public void unlockAction() {
+ if (state == State.RUNNING_LOCKED) {
+ state = State.NOT_RUNNING;
+ hideUi();
+
+ if (listener != null) listener.onRecordReleased();
+ }
+ }
+
+ private void hideUi() {
+ floatingRecordButton.hide();
+ lockDropTarget.hide();
+ }
+
@Override
public boolean onTouch(View v, final MotionEvent event) {
switch (event.getAction()) {
@@ -65,23 +101,29 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
if (!Permissions.hasAll(getContext(), Manifest.permission.RECORD_AUDIO)) {
if (listener != null) listener.onRecordPermissionRequired();
} else {
- this.actionInProgress = true;
- this.floatingRecordButton.display(event.getX());
- if (listener != null) listener.onRecordPressed(event.getX());
+ state = State.RUNNING_HELD;
+ floatingRecordButton.display(event.getX(), event.getY());
+ lockDropTarget.display();
+ if (listener != null) listener.onRecordPressed();
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- if (this.actionInProgress) {
- this.actionInProgress = false;
- this.floatingRecordButton.hide(event.getX());
- if (listener != null) listener.onRecordReleased(event.getX());
+ if (this.state == State.RUNNING_HELD) {
+ state = State.NOT_RUNNING;
+ hideUi();
+ if (listener != null) listener.onRecordReleased();
}
break;
case MotionEvent.ACTION_MOVE:
- if (this.actionInProgress) {
- this.floatingRecordButton.moveTo(event.getX());
- if (listener != null) listener.onRecordMoved(event.getX(), event.getRawX());
+ if (this.state == State.RUNNING_HELD) {
+ this.floatingRecordButton.moveTo(event.getX(), event.getY());
+ if (listener != null) listener.onRecordMoved(floatingRecordButton.lastOffsetX, event.getRawX());
+
+ int dimensionPixelSize = getResources().getDimensionPixelSize(R.dimen.recording_voice_lock_target);
+ if (floatingRecordButton.lastOffsetY <= dimensionPixelSize) {
+ lockAction();
+ }
}
break;
}
@@ -94,10 +136,11 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
}
public interface Listener {
- void onRecordPressed(float x);
- void onRecordReleased(float x);
- void onRecordCanceled(float x);
- void onRecordMoved(float x, float absoluteX);
+ void onRecordPressed();
+ void onRecordReleased();
+ void onRecordCanceled();
+ void onRecordLocked();
+ void onRecordMoved(float offsetX, float absoluteX);
void onRecordPermissionRequired();
}
@@ -106,81 +149,73 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
private final ImageView recordButtonFab;
private float startPositionX;
- private float lastPositionX;
+ private float startPositionY;
+ private float lastOffsetX;
+ private float lastOffsetY;
- public FloatingRecordButton(Context context, ImageView recordButtonFab) {
+ FloatingRecordButton(Context context, ImageView recordButtonFab) {
this.recordButtonFab = recordButtonFab;
this.recordButtonFab.getBackground().setColorFilter(context.getResources()
.getColor(R.color.red_500),
PorterDuff.Mode.SRC_IN);
}
- public void display(float x) {
+ void display(float x, float y) {
this.startPositionX = x;
- this.lastPositionX = x;
+ this.startPositionY = y;
recordButtonFab.setVisibility(View.VISIBLE);
- float translation = ViewCompat.getLayoutDirection(recordButtonFab) ==
- ViewCompat.LAYOUT_DIRECTION_LTR ? -.25f : .25f;
-
AnimationSet animation = new AnimationSet(true);
- animation.addAnimation(new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, -.25f,
- Animation.RELATIVE_TO_SELF, -.25f));
+ animation.addAnimation(new TranslateAnimation(Animation.ABSOLUTE, 0,
+ Animation.ABSOLUTE, 0,
+ Animation.ABSOLUTE, 0,
+ Animation.ABSOLUTE, 0));
animation.addAnimation(new ScaleAnimation(.5f, 1f, .5f, 1f,
Animation.RELATIVE_TO_SELF, .5f,
Animation.RELATIVE_TO_SELF, .5f));
- animation.setFillBefore(true);
- animation.setFillAfter(true);
animation.setDuration(ANIMATION_DURATION);
animation.setInterpolator(new OvershootInterpolator());
recordButtonFab.startAnimation(animation);
}
- public void moveTo(float x) {
- this.lastPositionX = x;
+ void moveTo(float x, float y) {
+ lastOffsetX = getXOffset(x);
+ lastOffsetY = getYOffset(y);
- float offset = getOffset(x);
+ if (Math.abs(lastOffsetX) > Math.abs(lastOffsetY)) {
+ lastOffsetY = 0;
+ } else {
+ lastOffsetX = 0;
+ }
- Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset,
- Animation.ABSOLUTE, offset,
- Animation.RELATIVE_TO_SELF, -.25f,
- Animation.RELATIVE_TO_SELF, -.25f);
-
- translateAnimation.setDuration(0);
- translateAnimation.setFillAfter(true);
- translateAnimation.setFillBefore(true);
-
- recordButtonFab.startAnimation(translateAnimation);
+ recordButtonFab.setTranslationX(lastOffsetX);
+ recordButtonFab.setTranslationY(lastOffsetY);
}
- public void hide(float x) {
- this.lastPositionX = x;
-
- float offset = getOffset(x);
+ void hide() {
+ recordButtonFab.setTranslationX(0);
+ recordButtonFab.setTranslationY(0);
+ if (recordButtonFab.getVisibility() != VISIBLE) return;
AnimationSet animation = new AnimationSet(false);
Animation scaleAnimation = new ScaleAnimation(1, 0.5f, 1, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
- Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset,
+ Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, lastOffsetX,
Animation.ABSOLUTE, 0,
- Animation.RELATIVE_TO_SELF, -.25f,
- Animation.RELATIVE_TO_SELF, -.25f);
+ Animation.ABSOLUTE, lastOffsetY,
+ Animation.ABSOLUTE, 0);
scaleAnimation.setInterpolator(new AnticipateOvershootInterpolator(1.5f));
translateAnimation.setInterpolator(new DecelerateInterpolator());
animation.addAnimation(scaleAnimation);
animation.addAnimation(translateAnimation);
animation.setDuration(ANIMATION_DURATION);
- animation.setFillBefore(true);
- animation.setFillAfter(false);
animation.setInterpolator(new AnticipateOvershootInterpolator(1.5f));
recordButtonFab.setVisibility(View.GONE);
@@ -188,16 +223,48 @@ public class MicrophoneRecorderView extends FrameLayout implements View.OnTouchL
recordButtonFab.startAnimation(animation);
}
- private float getOffset(float x) {
+ private float getXOffset(float x) {
return ViewCompat.getLayoutDirection(recordButtonFab) == ViewCompat.LAYOUT_DIRECTION_LTR ?
-Math.max(0, this.startPositionX - x) : Math.max(0, x - this.startPositionX);
}
- private int getWidthAdjustment() {
- int width = recordButtonFab.getWidth() / 4;
- return ViewCompat.getLayoutDirection(recordButtonFab) == ViewCompat.LAYOUT_DIRECTION_LTR ? -width : width;
+ private float getYOffset(float y) {
+ return Math.min(0, y - this.startPositionY);
}
-
}
+ private static class LockDropTarget {
+
+ private final View lockDropTarget;
+ private final int dropTargetPosition;
+
+ LockDropTarget(Context context, View lockDropTarget) {
+ this.lockDropTarget = lockDropTarget;
+ this.dropTargetPosition = context.getResources().getDimensionPixelSize(R.dimen.recording_voice_lock_target);
+ }
+
+ void display() {
+ lockDropTarget.setScaleX(1);
+ lockDropTarget.setScaleY(1);
+ lockDropTarget.setAlpha(0);
+ lockDropTarget.setTranslationY(0);
+ lockDropTarget.setVisibility(VISIBLE);
+ lockDropTarget.animate()
+ .setStartDelay(ANIMATION_DURATION * 2)
+ .setDuration(ANIMATION_DURATION)
+ .setInterpolator(new DecelerateInterpolator())
+ .translationY(dropTargetPosition)
+ .alpha(1)
+ .start();
+ }
+
+ void hide() {
+ lockDropTarget.animate()
+ .setStartDelay(0)
+ .setDuration(ANIMATION_DURATION)
+ .setInterpolator(new LinearInterpolator())
+ .scaleX(0).scaleY(0)
+ .start();
+ }
+ }
}
diff --git a/src/org/thoughtcrime/securesms/components/SendButton.java b/src/org/thoughtcrime/securesms/components/SendButton.java
index cbbf722069..7d15791899 100644
--- a/src/org/thoughtcrime/securesms/components/SendButton.java
+++ b/src/org/thoughtcrime/securesms/components/SendButton.java
@@ -45,6 +45,8 @@ public class SendButton extends ImageButton
}
private TransportOptions initializeTransportOptions(boolean media) {
+ if (isInEditMode()) return null;
+
TransportOptions transportOptions = new TransportOptions(getContext(), media);
transportOptions.addOnTransportChangedListener(this);
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
index c4f32b4fa3..5079462ca1 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
@@ -1995,6 +1995,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void sendMessage() {
+ if (inputPanel.isRecordingInLockedMode()) {
+ inputPanel.releaseRecordingLock();
+ return;
+ }
+
try {
Recipient recipient = getRecipient();
@@ -2176,6 +2181,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void updateToggleButtonState() {
+ if (inputPanel.isRecordingInLockedMode()) {
+ buttonToggle.display(sendButton);
+ quickAttachmentToggle.show();
+ inlineAttachmentToggle.hide();
+ return;
+ }
+
if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) {
buttonToggle.display(attachButton);
quickAttachmentToggle.show();
@@ -2232,8 +2244,14 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
audioRecorder.startRecording();
}
+ @Override
+ public void onRecorderLocked() {
+ updateToggleButtonState();
+ }
+
@Override
public void onRecorderFinished() {
+ updateToggleButtonState();
Vibrator vibrator = ServiceUtil.getVibrator(this);
vibrator.vibrate(20);
@@ -2274,6 +2292,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void onRecorderCanceled() {
+ updateToggleButtonState();
Vibrator vibrator = ServiceUtil.getVibrator(this);
vibrator.vibrate(50);