From e842f7845786cb998e0dfd09a604566eb8f85464 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Thu, 28 Mar 2019 15:04:38 -0300 Subject: [PATCH] Voice Note Locking. Limit of 60 minutes, after which it's cancelled. --- res/drawable/ic_chevron_up.xml | 9 + res/drawable/ic_lock.xml | 9 + res/layout/conversation_input_panel.xml | 69 +------ res/layout/microphone_recorder_view.xml | 63 ++++++ res/layout/recording_layout.xml | 74 +++++++ res/values/dimens.xml | 2 + res/values/strings.xml | 4 +- .../securesms/components/InputPanel.java | 105 ++++++---- .../components/MicrophoneRecorderView.java | 191 ++++++++++++------ .../securesms/components/SendButton.java | 2 + .../conversation/ConversationActivity.java | 19 ++ 11 files changed, 378 insertions(+), 169 deletions(-) create mode 100644 res/drawable/ic_chevron_up.xml create mode 100644 res/drawable/ic_lock.xml create mode 100644 res/layout/microphone_recorder_view.xml create mode 100644 res/layout/recording_layout.xml 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(); + } + } + + public boolean isRecordingLocked() { + return state == State.RUNNING_LOCKED; + } - if (listener != null) listener.onRecordCanceled(this.floatingRecordButton.lastPositionX); + 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; - - float offset = getOffset(x); + void moveTo(float x, float y) { + lastOffsetX = getXOffset(x); + lastOffsetY = getYOffset(y); - Animation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, offset, - Animation.ABSOLUTE, offset, - Animation.RELATIVE_TO_SELF, -.25f, - Animation.RELATIVE_TO_SELF, -.25f); + if (Math.abs(lastOffsetX) > Math.abs(lastOffsetY)) { + lastOffsetY = 0; + } else { + lastOffsetX = 0; + } - 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);