Fix composer voice memo cancellation due to focus loss.

This commit is contained in:
Nicholas 2023-02-02 16:11:59 -05:00 committed by Nicholas Tinsley
parent 63a153571d
commit d33aa247db
5 changed files with 54 additions and 33 deletions

View file

@ -7,6 +7,7 @@ import android.os.Build;
import android.os.ParcelFileDescriptor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.logging.Log;
@ -27,6 +28,7 @@ public class AudioRecorder {
private static final ExecutorService executor = SignalExecutors.newCachedSingleThreadExecutor("signal-AudioRecorder");
private final Context context;
private final AudioRecordingHandler uiHandler;
private final AudioRecorderFocusManager audioFocusManager;
private Recorder recorder;
@ -34,12 +36,28 @@ public class AudioRecorder {
private SingleSubject<VoiceNoteDraft> recordingSubject;
public AudioRecorder(@NonNull Context context) {
this.context = context;
audioFocusManager = AudioRecorderFocusManager.create(context, focusChange -> {
Log.i(TAG, "Audio focus change " + focusChange + " stopping recording");
stopRecording();
});
public AudioRecorder(@NonNull Context context, @Nullable AudioRecordingHandler uiHandler) {
this.context = context;
this.uiHandler = uiHandler;
AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener;
if (this.uiHandler != null) {
onAudioFocusChangeListener = focusChange -> {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
Log.i(TAG, "Audio focus change " + focusChange + " stopping recording");
this.uiHandler.onRecordCanceled(false);
}
};
} else {
onAudioFocusChangeListener = focusChange -> {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
Log.i(TAG, "Audio focus change " + focusChange + " stopping recording");
stopRecording();
}
};
}
audioFocusManager = AudioRecorderFocusManager.create(context, onAudioFocusChangeListener);
}
public @NonNull Single<VoiceNoteDraft> startRecording() {
@ -59,7 +77,7 @@ public class AudioRecorder {
.forData(new ParcelFileDescriptor.AutoCloseInputStream(fds[0]), 0)
.withMimeType(MediaUtil.AUDIO_AAC)
.createForDraftAttachmentAsync(context, () -> Log.i(TAG, "Write successful."), e -> Log.w(TAG, "Error during recording", e));
recorder = Build.VERSION.SDK_INT >= 26 ? new MediaRecorderWrapper() : new AudioCodec();
recorder = Build.VERSION.SDK_INT >= 26 ? new MediaRecorderWrapper() : new AudioCodec();
int focusResult = audioFocusManager.requestAudioFocus();
if (focusResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.w(TAG, "Could not gain audio focus. Received result code " + focusResult);

View file

@ -0,0 +1,10 @@
package org.thoughtcrime.securesms.audio
interface AudioRecordingHandler {
fun onRecordPressed()
fun onRecordReleased()
fun onRecordCanceled(byUser: Boolean)
fun onRecordLocked()
fun onRecordMoved(offsetX: Float, absoluteX: Float)
fun onRecordPermissionRequired()
}

View file

@ -33,6 +33,7 @@ import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
import org.thoughtcrime.securesms.audio.AudioRecordingHandler;
import org.thoughtcrime.securesms.components.emoji.EmojiEventListener;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
@ -63,7 +64,7 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class InputPanel extends LinearLayout
implements MicrophoneRecorderView.Listener,
implements AudioRecordingHandler,
KeyboardAwareLinearLayout.OnKeyboardShownListener,
EmojiEventListener,
ConversationStickerSuggestionAdapter.EventListener
@ -137,7 +138,7 @@ public class InputPanel extends LinearLayout
this.voiceNoteDraftView = findViewById(R.id.voice_note_draft_view);
this.slideToCancel = new SlideToCancel(findViewById(R.id.slide_to_cancel));
this.microphoneRecorderView = findViewById(R.id.recorder_view);
this.microphoneRecorderView.setListener(this);
this.microphoneRecorderView.setHandler(this);
this.recordTime = new RecordTime(findViewById(R.id.record_time),
findViewById(R.id.microphone),
TimeUnit.HOURS.toSeconds(1),

View file

@ -21,6 +21,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.audio.AudioRecordingHandler;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.ViewUtil;
@ -34,10 +35,10 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
public static final int ANIMATION_DURATION = 200;
private FloatingRecordButton floatingRecordButton;
private LockDropTarget lockDropTarget;
private @Nullable Listener listener;
private @NonNull State state = State.NOT_RUNNING;
private FloatingRecordButton floatingRecordButton;
private LockDropTarget lockDropTarget;
private @Nullable AudioRecordingHandler handler;
private @NonNull State state = State.NOT_RUNNING;
public MicrophoneRecorderView(Context context) {
super(context);
@ -63,8 +64,8 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
state = State.NOT_RUNNING;
hideUi();
if (listener != null) {
listener.onRecordCanceled(byUser);
if (handler != null) {
handler.onRecordCanceled(byUser);
}
}
}
@ -78,7 +79,7 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
state = State.RUNNING_LOCKED;
hideUi();
if (listener != null) listener.onRecordLocked();
if (handler != null) handler.onRecordLocked();
}
}
@ -87,7 +88,7 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
state = State.NOT_RUNNING;
hideUi();
if (listener != null) listener.onRecordReleased();
if (handler != null) handler.onRecordReleased();
}
}
@ -101,12 +102,12 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!Permissions.hasAll(getContext(), Manifest.permission.RECORD_AUDIO)) {
if (listener != null) listener.onRecordPermissionRequired();
if (handler != null) handler.onRecordPermissionRequired();
} else if (state == State.NOT_RUNNING) {
state = State.RUNNING_HELD;
floatingRecordButton.display(event.getX(), event.getY());
lockDropTarget.display();
if (listener != null) listener.onRecordPressed();
if (handler != null) handler.onRecordPressed();
}
break;
case MotionEvent.ACTION_CANCEL:
@ -114,13 +115,13 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
if (this.state == State.RUNNING_HELD) {
state = State.NOT_RUNNING;
hideUi();
if (listener != null) listener.onRecordReleased();
if (handler != null) handler.onRecordReleased();
}
break;
case MotionEvent.ACTION_MOVE:
if (this.state == State.RUNNING_HELD) {
this.floatingRecordButton.moveTo(event.getX(), event.getY());
if (listener != null) listener.onRecordMoved(floatingRecordButton.lastOffsetX, event.getRawX());
if (handler != null) handler.onRecordMoved(floatingRecordButton.lastOffsetX, event.getRawX());
int dimensionPixelSize = getResources().getDimensionPixelSize(R.dimen.recording_voice_lock_target);
if (floatingRecordButton.lastOffsetY <= dimensionPixelSize) {
@ -133,17 +134,8 @@ public final class MicrophoneRecorderView extends FrameLayout implements View.On
return false;
}
public void setListener(@Nullable Listener listener) {
this.listener = listener;
}
public interface Listener {
void onRecordPressed();
void onRecordReleased();
void onRecordCanceled(boolean byUser);
void onRecordLocked();
void onRecordMoved(float offsetX, float absoluteX);
void onRecordPermissionRequired();
public void setHandler(@Nullable AudioRecordingHandler handler) {
this.handler = handler;
}
private static class FloatingRecordButton {

View file

@ -2071,7 +2071,7 @@ public class ConversationParentFragment extends Fragment
inputPanel.setMediaListener(this);
attachmentManager = new AttachmentManager(requireContext(), view, this);
audioRecorder = new AudioRecorder(requireContext());
audioRecorder = new AudioRecorder(requireContext(), inputPanel);
typingTextWatcher = new ComposeTextWatcher();
SendButtonListener sendButtonListener = new SendButtonListener();