diff --git a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java index 231a8600d239851db4c35d447cb628b929e70d8d..a91c82002bc00bc9aac3c2aad714d5bb5bcc1918 100644 --- a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java @@ -9,39 +9,33 @@ import android.os.Bundle; import android.os.Environment; import android.os.FileObserver; import android.os.Handler; -import android.os.SystemClock; import android.util.Log; import android.view.View; import android.view.WindowManager; import android.widget.Toast; - -import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; - +import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableSet; - +import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; +import eu.siacs.conversations.databinding.ActivityRecordingBinding; +import eu.siacs.conversations.utils.TimeFrameUtils; import java.io.File; import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.Set; - -import eu.siacs.conversations.Config; -import eu.siacs.conversations.R; -import eu.siacs.conversations.databinding.ActivityRecordingBinding; -import eu.siacs.conversations.ui.util.SettingsUtils; -import eu.siacs.conversations.utils.TimeFrameUtils; public class RecordingActivity extends BaseActivity implements View.OnClickListener { private ActivityRecordingBinding binding; private MediaRecorder mRecorder; - private long mStartTime = 0; + private Stopwatch stopwatch; private final CountDownLatch outputFileWrittenLatch = new CountDownLatch(1); @@ -63,17 +57,48 @@ public class RecordingActivity extends BaseActivity implements View.OnClickListe protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_recording); + this.binding.timer.setOnClickListener( + v -> { + onPauseContinue(); + }); this.binding.cancelButton.setOnClickListener(this); this.binding.shareButton.setOnClickListener(this); this.setFinishOnTouchOutside(false); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + + private void onPauseContinue() { + final var recorder = this.mRecorder; + final var stopwatch = this.stopwatch; + if (recorder == null + || stopwatch == null + || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return; + } + if (stopwatch.isRunning()) { + try { + recorder.pause(); + stopwatch.stop(); + } catch (final IllegalStateException e) { + Log.d(Config.LOGTAG, "could not pause recording", e); + } + } else { + try { + recorder.resume(); + stopwatch.start(); + } catch (final IllegalStateException e) { + Log.d(Config.LOGTAG, "could not resume recording", e); + } + } + } + @Override public void onStart() { super.onStart(); if (!startRecording()) { this.binding.shareButton.setEnabled(false); - this.binding.timer.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyMedium); + this.binding.timer.setTextAppearance( + com.google.android.material.R.style.TextAppearance_Material3_BodyMedium); // TODO reset font family. make red? this.binding.timer.setText(R.string.unable_to_start_recording); } @@ -93,22 +118,31 @@ public class RecordingActivity extends BaseActivity implements View.OnClickListe private static final Set AAC_SENSITIVE_DEVICES = new ImmutableSet.Builder() - .add("FP4") // Fairphone 4 https://codeberg.org/monocles/monocles_chat/issues/133 - .add("ONEPLUS A6000") // OnePlus 6 https://github.com/iNPUTmice/Conversations/issues/4329 - .add("ONEPLUS A6003") // OnePlus 6 https://github.com/iNPUTmice/Conversations/issues/4329 - .add("ONEPLUS A6010") // OnePlus 6T https://codeberg.org/monocles/monocles_chat/issues/133 - .add("ONEPLUS A6013") // OnePlus 6T https://codeberg.org/monocles/monocles_chat/issues/133 - .add("Pixel 4a") // Pixel 4a https://github.com/iNPUTmice/Conversations/issues/4223 - .add("WP12 Pro") // Oukitel WP 12 Pro https://github.com/iNPUTmice/Conversations/issues/4223 - .add("Volla Phone X") // Volla Phone X https://github.com/iNPUTmice/Conversations/issues/4223 + .add("FP4") // Fairphone 4 + // https://codeberg.org/monocles/monocles_chat/issues/133 + .add("ONEPLUS A6000") // OnePlus 6 + // https://github.com/iNPUTmice/Conversations/issues/4329 + .add("ONEPLUS A6003") // OnePlus 6 + // https://github.com/iNPUTmice/Conversations/issues/4329 + .add("ONEPLUS A6010") // OnePlus 6T + // https://codeberg.org/monocles/monocles_chat/issues/133 + .add("ONEPLUS A6013") // OnePlus 6T + // https://codeberg.org/monocles/monocles_chat/issues/133 + .add("Pixel 4a") // Pixel 4a + // https://github.com/iNPUTmice/Conversations/issues/4223 + .add("WP12 Pro") // Oukitel WP 12 Pro + // https://github.com/iNPUTmice/Conversations/issues/4223 + .add("Volla Phone X") // Volla Phone X + // https://github.com/iNPUTmice/Conversations/issues/4223 .build(); private boolean startRecording() { mRecorder = new MediaRecorder(); + stopwatch = Stopwatch.createUnstarted(); try { mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); } catch (final RuntimeException e) { - Log.e(Config.LOGTAG,"could not set audio source", e); + Log.e(Config.LOGTAG, "could not set audio source", e); return false; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -123,8 +157,10 @@ public class RecordingActivity extends BaseActivity implements View.OnClickListe } else { outputFormat = MediaRecorder.OutputFormat.MPEG_4; mRecorder.setOutputFormat(outputFormat); - if (AAC_SENSITIVE_DEVICES.contains(Build.MODEL) && Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) { - // Changing these three settings for AAC sensitive devices for Android<=13 might lead to sporadically truncated (cut-off) voice messages. + if (AAC_SENSITIVE_DEVICES.contains(Build.MODEL) + && Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) { + // Changing these three settings for AAC sensitive devices for Android<=13 might + // lead to sporadically truncated (cut-off) voice messages. mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC); mRecorder.setAudioSamplingRate(24_000); mRecorder.setAudioEncodingBitRate(28_000); @@ -140,7 +176,7 @@ public class RecordingActivity extends BaseActivity implements View.OnClickListe try { mRecorder.prepare(); mRecorder.start(); - mStartTime = SystemClock.elapsedRealtime(); + stopwatch.start(); mHandler.postDelayed(mTickExecutor, 100); Log.d(Config.LOGTAG, "started recording to " + mOutputFile.getAbsolutePath()); return true; @@ -154,14 +190,17 @@ public class RecordingActivity extends BaseActivity implements View.OnClickListe try { mRecorder.stop(); mRecorder.release(); - } catch (Exception e) { + if (stopwatch.isRunning()) { + stopwatch.stop(); + } + } catch (final Exception e) { + Log.d(Config.LOGTAG, "could not save recording", e); if (saveFile) { Toast.makeText(this, R.string.unable_to_save_recording, Toast.LENGTH_SHORT).show(); return; } } finally { mRecorder = null; - mStartTime = 0; } if (!saveFile && mOutputFile != null) { if (mOutputFile.delete()) { @@ -256,24 +295,23 @@ public class RecordingActivity extends BaseActivity implements View.OnClickListe } private void tick() { - this.binding.timer.setText(TimeFrameUtils.formatTimePassed(mStartTime, true)); + this.binding.timer.setText( + TimeFrameUtils.formatElapsedTime(stopwatch.elapsed(TimeUnit.MILLISECONDS), true)); } @Override public void onClick(final View view) { - switch (view.getId()) { - case R.id.cancel_button: - mHandler.removeCallbacks(mTickExecutor); - stopRecording(false); - setResult(RESULT_CANCELED); - finish(); - break; - case R.id.share_button: - this.binding.shareButton.setEnabled(false); - this.binding.shareButton.setText(R.string.please_wait); - mHandler.removeCallbacks(mTickExecutor); - mHandler.postDelayed(() -> stopRecording(true), 500); - break; + if (view.getId() == R.id.cancel_button) { + mHandler.removeCallbacks(mTickExecutor); + stopRecording(false); + setResult(RESULT_CANCELED); + finish(); + } else if (view.getId() == R.id.share_button) { + this.binding.timer.setOnClickListener(null); + this.binding.shareButton.setEnabled(false); + this.binding.shareButton.setText(R.string.please_wait); + mHandler.removeCallbacks(mTickExecutor); + mHandler.postDelayed(() -> stopRecording(true), 500); } } }