Support scheduled voice notes.

Resolves #13957
This commit is contained in:
Sagar
2025-01-31 19:47:58 +05:30
committed by Michelle Tang
parent da5c8ff6ea
commit 52c8dfc998
6 changed files with 54 additions and 21 deletions

View File

@@ -5,6 +5,7 @@ interface AudioRecordingHandler {
fun onRecordReleased() fun onRecordReleased()
fun onRecordCanceled(byUser: Boolean) fun onRecordCanceled(byUser: Boolean)
fun onRecordLocked() fun onRecordLocked()
fun onRecordSaved()
fun onRecordMoved(offsetX: Float, absoluteX: Float) fun onRecordMoved(offsetX: Float, absoluteX: Float)
fun onRecordPermissionRequired() fun onRecordPermissionRequired()
} }

View File

@@ -62,7 +62,6 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.Quote; import org.thoughtcrime.securesms.database.model.Quote;
import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.keyboard.KeyboardPage; import org.thoughtcrime.securesms.keyboard.KeyboardPage;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.linkpreview.LinkPreview; import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
@@ -625,10 +624,21 @@ public class InputPanel extends ConstraintLayout
if (listener != null) listener.onRecorderLocked(); if (listener != null) listener.onRecorderLocked();
} }
@Override
public void onRecordSaved() {
Log.d(TAG, "Recording saved");
onRecordHideEvent();
if (listener != null) listener.onRecorderSaveDraft();
}
public void onPause() { public void onPause() {
this.microphoneRecorderView.cancelAction(false); this.microphoneRecorderView.cancelAction(false);
} }
public void onSaveRecordDraft() {
this.microphoneRecorderView.saveAction();
}
public @NonNull Observer<VoiceNotePlaybackState> getPlaybackStateObserver() { public @NonNull Observer<VoiceNotePlaybackState> getPlaybackStateObserver() {
return voiceNoteDraftView.getPlaybackStateObserver(); return voiceNoteDraftView.getPlaybackStateObserver();
} }
@@ -692,7 +702,7 @@ public class InputPanel extends ConstraintLayout
return microphoneRecorderView.isRecordingLocked(); return microphoneRecorderView.isRecordingLocked();
} }
public void releaseRecordingLock() { public void releaseRecordingLockAndSend() {
microphoneRecorderView.unlockAction(); microphoneRecorderView.unlockAction();
} }
@@ -794,6 +804,7 @@ public class InputPanel extends ConstraintLayout
public interface Listener extends VoiceNoteDraftView.Listener { public interface Listener extends VoiceNoteDraftView.Listener {
void onRecorderStarted(); void onRecorderStarted();
void onRecorderLocked(); void onRecorderLocked();
void onRecorderSaveDraft();
void onRecorderFinished(); void onRecorderFinished();
void onRecorderCanceled(boolean byUser); void onRecorderCanceled(boolean byUser);
void onRecorderPermissionRequired(); void onRecorderPermissionRequired();

View File

@@ -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() { public boolean isRecordingLocked() {
return state == State.RUNNING_LOCKED; return state == State.RUNNING_LOCKED;
} }

View File

@@ -41,19 +41,11 @@ class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImage
if (!isEnabled) { if (!isEnabled) {
return false return false
} }
scheduledSendListener?.onSendScheduled()
val scheduleListener = scheduledSendListener return true
return if (scheduleListener?.canSchedule() == true) {
scheduleListener.onSendScheduled()
true
} else {
false
}
} }
interface ScheduledSendListener { interface ScheduledSendListener {
fun onSendScheduled() fun onSendScheduled()
fun canSchedule(): Boolean
} }
} }

View File

@@ -1840,7 +1840,8 @@ class ConversationFragment :
slide: Slide? = null, slide: Slide? = null,
contacts: List<Contact> = emptyList(), contacts: List<Contact> = emptyList(),
quote: QuoteModel? = null, quote: QuoteModel? = null,
clearCompose: Boolean = true clearCompose: Boolean = true,
scheduledDate: Long = -1
) { ) {
sendMessage( sendMessage(
slideDeck = slide?.let { SlideDeck().apply { addSlide(slide) } }, slideDeck = slide?.let { SlideDeck().apply { addSlide(slide) } },
@@ -1852,7 +1853,8 @@ class ConversationFragment :
messageToEdit = null, messageToEdit = null,
quote = quote, quote = quote,
linkPreviews = emptyList(), linkPreviews = emptyList(),
bypassPreSendSafetyNumberCheck = true bypassPreSendSafetyNumberCheck = true,
scheduledDate = scheduledDate
) )
} }
@@ -1892,19 +1894,24 @@ class ConversationFragment :
} }
if (inputPanel.isRecordingInLockedMode) { if (inputPanel.isRecordingInLockedMode) {
inputPanel.releaseRecordingLock() inputPanel.releaseRecordingLockAndSend()
return return
} }
if (slideDeck == null) { if (slideDeck == null) {
val voiceNote: DraftTable.Draft? = draftViewModel.voiceNoteDraft val voiceNote: DraftTable.Draft? = draftViewModel.voiceNoteDraft
if (voiceNote != null) { if (voiceNote != null) {
sendMessageWithoutComposeInput(slide = AudioSlide.createFromVoiceNoteDraft(voiceNote), clearCompose = true) sendMessageWithoutComposeInput(
slide = AudioSlide.createFromVoiceNoteDraft(voiceNote),
quote = quote,
clearCompose = true,
scheduledDate = scheduledDate
)
return 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") Log.i(TAG, "Unable to send due to empty message")
toast(R.string.ConversationActivity_message_is_empty_exclamation) toast(R.string.ConversationActivity_message_is_empty_exclamation)
return return
@@ -3970,6 +3977,10 @@ class ConversationFragment :
} }
override fun onSendScheduled() { override fun onSendScheduled() {
if (inputPanel.isRecordingInLockedMode) {
inputPanel.onSaveRecordDraft()
}
ScheduleMessageContextMenu.show(sendButton, (requireView() as ViewGroup)) { time -> ScheduleMessageContextMenu.show(sendButton, (requireView() as ViewGroup)) { time ->
if (time == -1L) { if (time == -1L) {
showSchedule(childFragmentManager) showSchedule(childFragmentManager)
@@ -3978,10 +3989,6 @@ class ConversationFragment :
} }
} }
} }
override fun canSchedule(): Boolean {
return !(inputPanel.isRecordingInLockedMode || draftViewModel.voiceNoteDraft != null)
}
} }
private inner class ComposeTextEventsListener : private inner class ComposeTextEventsListener :
@@ -4126,6 +4133,11 @@ class ConversationFragment :
voiceMessageRecordingDelegate.onRecorderCanceled(byUser) voiceMessageRecordingDelegate.onRecorderCanceled(byUser)
} }
override fun onRecorderSaveDraft() {
voiceMessageRecordingDelegate.onRecordSaveDraft()
inputPanel.voiceNoteDraft = draftViewModel.voiceNoteDraft
}
override fun onRecorderPermissionRequired() { override fun onRecorderPermissionRequired() {
Permissions Permissions
.with(this@ConversationFragment) .with(this@ConversationFragment)

View File

@@ -69,6 +69,12 @@ class VoiceMessageRecordingDelegate(
} }
} }
fun onRecordSaveDraft() {
voiceRecorderWakeLock.release()
vibrateAndResetOrientation(50)
session?.saveDraft()
}
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private fun vibrateAndResetOrientation(milliseconds: Long) { private fun vibrateAndResetOrientation(milliseconds: Long) {
val activity = fragment.activity val activity = fragment.activity