From 52c8dfc99880ad31a94e23b3377b6e72c77fb8ad Mon Sep 17 00:00:00 2001 From: Sagar Date: Fri, 31 Jan 2025 19:47:58 +0530 Subject: [PATCH] Support scheduled voice notes. Resolves #13957 --- .../securesms/audio/AudioRecordingHandler.kt | 1 + .../securesms/components/InputPanel.java | 15 ++++++++-- .../components/MicrophoneRecorderView.java | 11 +++++++ .../securesms/components/SendButton.kt | 12 ++------ .../conversation/v2/ConversationFragment.kt | 30 +++++++++++++------ .../v2/VoiceMessageRecordingDelegate.kt | 6 ++++ 6 files changed, 54 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecordingHandler.kt b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecordingHandler.kt index 6d99a0b3c4..f033abaea2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecordingHandler.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/AudioRecordingHandler.kt @@ -5,6 +5,7 @@ interface AudioRecordingHandler { fun onRecordReleased() fun onRecordCanceled(byUser: Boolean) fun onRecordLocked() + fun onRecordSaved() fun onRecordMoved(offsetX: Float, absoluteX: Float) fun onRecordPermissionRequired() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java index 8ca643e847..27f5006aa9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InputPanel.java @@ -62,7 +62,6 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.Quote; import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.keyboard.KeyboardPage; -import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; @@ -625,10 +624,21 @@ public class InputPanel extends ConstraintLayout if (listener != null) listener.onRecorderLocked(); } + @Override + public void onRecordSaved() { + Log.d(TAG, "Recording saved"); + onRecordHideEvent(); + if (listener != null) listener.onRecorderSaveDraft(); + } + public void onPause() { this.microphoneRecorderView.cancelAction(false); } + public void onSaveRecordDraft() { + this.microphoneRecorderView.saveAction(); + } + public @NonNull Observer getPlaybackStateObserver() { return voiceNoteDraftView.getPlaybackStateObserver(); } @@ -692,7 +702,7 @@ public class InputPanel extends ConstraintLayout return microphoneRecorderView.isRecordingLocked(); } - public void releaseRecordingLock() { + public void releaseRecordingLockAndSend() { microphoneRecorderView.unlockAction(); } @@ -794,6 +804,7 @@ public class InputPanel extends ConstraintLayout public interface Listener extends VoiceNoteDraftView.Listener { void onRecorderStarted(); void onRecorderLocked(); + void onRecorderSaveDraft(); void onRecorderFinished(); void onRecorderCanceled(boolean byUser); void onRecorderPermissionRequired(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java b/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java index 543acaa69f..67cf6fb5df 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/MicrophoneRecorderView.java @@ -70,6 +70,17 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On } } + public void saveAction() { + if (state != State.NOT_RUNNING) { + state = State.NOT_RUNNING; + hideUi(); + + if (handler != null) { + handler.onRecordSaved(); + } + } + } + public boolean isRecordingLocked() { return state == State.RUNNING_LOCKED; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.kt b/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.kt index 30de588f41..cd181b2e96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/SendButton.kt @@ -41,19 +41,11 @@ class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImage if (!isEnabled) { return false } - - val scheduleListener = scheduledSendListener - - return if (scheduleListener?.canSchedule() == true) { - scheduleListener.onSendScheduled() - true - } else { - false - } + scheduledSendListener?.onSendScheduled() + return true } interface ScheduledSendListener { fun onSendScheduled() - fun canSchedule(): Boolean } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 494855a77c..50ee9359e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -1840,7 +1840,8 @@ class ConversationFragment : slide: Slide? = null, contacts: List = emptyList(), quote: QuoteModel? = null, - clearCompose: Boolean = true + clearCompose: Boolean = true, + scheduledDate: Long = -1 ) { sendMessage( slideDeck = slide?.let { SlideDeck().apply { addSlide(slide) } }, @@ -1852,7 +1853,8 @@ class ConversationFragment : messageToEdit = null, quote = quote, linkPreviews = emptyList(), - bypassPreSendSafetyNumberCheck = true + bypassPreSendSafetyNumberCheck = true, + scheduledDate = scheduledDate ) } @@ -1892,19 +1894,24 @@ class ConversationFragment : } if (inputPanel.isRecordingInLockedMode) { - inputPanel.releaseRecordingLock() + inputPanel.releaseRecordingLockAndSend() return } if (slideDeck == null) { val voiceNote: DraftTable.Draft? = draftViewModel.voiceNoteDraft if (voiceNote != null) { - sendMessageWithoutComposeInput(slide = AudioSlide.createFromVoiceNoteDraft(voiceNote), clearCompose = true) + sendMessageWithoutComposeInput( + slide = AudioSlide.createFromVoiceNoteDraft(voiceNote), + quote = quote, + clearCompose = true, + scheduledDate = scheduledDate + ) return } } - if (body.isNullOrBlank() && slideDeck?.containsMediaSlide() != true && preUploadResults.isEmpty() && contacts.isEmpty()) { + if (body.isBlank() && slideDeck?.containsMediaSlide() != true && preUploadResults.isEmpty() && contacts.isEmpty()) { Log.i(TAG, "Unable to send due to empty message") toast(R.string.ConversationActivity_message_is_empty_exclamation) return @@ -3970,6 +3977,10 @@ class ConversationFragment : } override fun onSendScheduled() { + if (inputPanel.isRecordingInLockedMode) { + inputPanel.onSaveRecordDraft() + } + ScheduleMessageContextMenu.show(sendButton, (requireView() as ViewGroup)) { time -> if (time == -1L) { showSchedule(childFragmentManager) @@ -3978,10 +3989,6 @@ class ConversationFragment : } } } - - override fun canSchedule(): Boolean { - return !(inputPanel.isRecordingInLockedMode || draftViewModel.voiceNoteDraft != null) - } } private inner class ComposeTextEventsListener : @@ -4126,6 +4133,11 @@ class ConversationFragment : voiceMessageRecordingDelegate.onRecorderCanceled(byUser) } + override fun onRecorderSaveDraft() { + voiceMessageRecordingDelegate.onRecordSaveDraft() + inputPanel.voiceNoteDraft = draftViewModel.voiceNoteDraft + } + override fun onRecorderPermissionRequired() { Permissions .with(this@ConversationFragment) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt index 9c25359be5..c001ee56e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/VoiceMessageRecordingDelegate.kt @@ -69,6 +69,12 @@ class VoiceMessageRecordingDelegate( } } + fun onRecordSaveDraft() { + voiceRecorderWakeLock.release() + vibrateAndResetOrientation(50) + session?.saveDraft() + } + @Suppress("DEPRECATION") private fun vibrateAndResetOrientation(milliseconds: Long) { val activity = fragment.activity