Skip to content

Commit

Permalink
Added video capture timer and fixed crashing bug when device is rotat…
Browse files Browse the repository at this point in the history
…ed while recording video
  • Loading branch information
Senior Ngenge authored and Senior Ngenge committed Feb 14, 2025
1 parent 08a7917 commit 5d5da5e
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 24 deletions.
2 changes: 2 additions & 0 deletions android-libproofcam/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
android:name=".CameraActivity"
android:exported="true"
android:label="@string/app_name"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:windowSoftInputMode="adjustNothing"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class CameraFragment : BaseFragment<FragmentCameraBinding>(R.layout.fragment_cam
private lateinit var pinchToZoomDetector: ScaleGestureDetector
private lateinit var context: Context

private val lensViewModel: CameraLensViewModel by activityViewModels()
private val lensViewModel: CameraViewModel by activityViewModels()


// An instance for display manager to get display change callbacks
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.witness.proofmode.camera.fragments

import android.os.Handler
import android.os.Looper
import androidx.camera.core.CameraSelector
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.util.Locale

class CameraViewModel : ViewModel() {
var lensFacing: MutableLiveData<Int> = MutableLiveData(CameraSelector.LENS_FACING_BACK)
private set
private val handler = Handler(Looper.getMainLooper())
private lateinit var timerRunnable: Runnable
private var startTime: Long = 0

// LiveData to expose the elapsed time
private val _elapsedTime = MutableLiveData<String>()
val elapsedTime: LiveData<String> get() = _elapsedTime



fun toggleLensFacing() {
lensFacing.value = if (lensFacing.value == CameraSelector.LENS_FACING_BACK) {
CameraSelector.LENS_FACING_FRONT
} else {
CameraSelector.LENS_FACING_BACK
}
}

// Format the elapsed time
private fun formatElapsedTime(elapsedTime: Long): String {
val hours = (elapsedTime / 3600000).toInt()
val minutes = (elapsedTime % 3600000 / 60000).toInt()
val seconds = (elapsedTime % 60000 / 1000).toInt()

return if (hours > 0) {
String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds)
} else {
String.format(Locale.getDefault(),"%02d:%02d", minutes, seconds)
}
}

fun stopTimer() {
handler.removeCallbacks(timerRunnable)
_elapsedTime.value = "00:00:00" // Reset the timer
}
fun startTimer() {
startTime = System.currentTimeMillis()
timerRunnable = object : Runnable {
override fun run() {
val timeDelta = System.currentTimeMillis() - startTime
_elapsedTime.postValue(formatElapsedTime(timeDelta))
handler.postDelayed(this, 1000)

}
}
handler.post(timerRunnable)
}

override fun onCleared() {
handler.removeCallbacks(timerRunnable)
super.onCleared()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.database.Cursor
import android.hardware.display.DisplayManager
import android.net.Uri
import android.os.Build
Expand Down Expand Up @@ -58,7 +57,7 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video
private lateinit var tapDetector: GestureDetector
private lateinit var pinchToZoomDetector: ScaleGestureDetector
private lateinit var viewFinder: PreviewView
private val lensViewModel: CameraLensViewModel by activityViewModels()
private val cameraViewModel: CameraViewModel by activityViewModels()
private lateinit var context: Context

// An instance for display manager to get display change callbacks
Expand Down Expand Up @@ -183,6 +182,11 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video
tapDetector.onTouchEvent(motionEvent)
return@setOnTouchListener true
}

cameraViewModel.elapsedTime.observe(viewLifecycleOwner) { time->
binding.chipTimer?.text = time

}
}
}

Expand Down Expand Up @@ -223,7 +227,7 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video
* toggleButton() function is an Extension function made to animate button rotation
* */
private fun toggleCamera() = binding.btnSwitchCamera.toggleButton(
flag = lensViewModel.lensFacing.value == CameraSelector.LENS_FACING_BACK,
flag = cameraViewModel.lensFacing.value == CameraSelector.LENS_FACING_BACK,
rotationAngle = 180f,
firstIcon = R.drawable.ic_outline_camera_rear,
secondIcon = R.drawable.ic_outline_camera_front,
Expand All @@ -236,7 +240,7 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video
).show()
return@toggleButton
} else {
lensViewModel.toggleLensFacing()
cameraViewModel.toggleLensFacing()
startCamera()
}

Expand Down Expand Up @@ -282,7 +286,7 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video
// Bind all use cases to the camera with lifecycle
val selector = CameraSelector.Builder()
.requireLensFacing(
lensViewModel.lensFacing.value ?: CameraSelector.LENS_FACING_BACK
cameraViewModel.lensFacing.value ?: CameraSelector.LENS_FACING_BACK
)
.build()
camera = localCameraProvider.bindToLifecycle(
Expand Down Expand Up @@ -363,6 +367,8 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video

if (!isRecording) {
animateRecord.start()
binding.chipTimer?.visibility = View.VISIBLE
cameraViewModel.startTimer()
localVideoCapture.startRecording(
outputOptions, // the options needed for the final video
requireContext().mainExecutor(), // the executor, on which the task will run
Expand Down Expand Up @@ -422,6 +428,9 @@ class VideoFragment : BaseFragment<FragmentVideoBinding>(R.layout.fragment_video
} else {
animateRecord.cancel()
localVideoCapture.stopRecording()
cameraViewModel.stopTimer()
binding.chipTimer?.visibility = View.GONE

}
isRecording = !isRecording
}
Expand Down
14 changes: 14 additions & 0 deletions android-libproofcam/src/main/res/layout-land/fragment_video.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.4"
android:id="@+id/view"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
Expand All @@ -37,6 +38,19 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.chip.Chip
android:id="@+id/chipTimer"
android:elevation="@dimen/half_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content"
android:visibility="gone"
tools:visibility="visible"
app:chipBackgroundColor="@android:color/holo_red_light"
android:layout_marginBottom="@dimen/half_margin"
app:layout_constraintTop_toBottomOf="@+id/btnRecordVideo"
android:layout_height="wrap_content" />


<ImageButton
android:id="@+id/btnExit"
Expand Down
13 changes: 13 additions & 0 deletions android-libproofcam/src/main/res/layout/fragment_video.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.chip.Chip
android:id="@+id/chipTimer"
android:elevation="@dimen/half_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content"
android:visibility="gone"
app:chipBackgroundColor="@android:color/holo_red_light"
android:layout_marginBottom="@dimen/half_margin"
app:layout_constraintBottom_toTopOf="@id/view"
android:layout_height="wrap_content" />


<View
android:id="@+id/view"
android:layout_width="0dp"
Expand Down

0 comments on commit 5d5da5e

Please sign in to comment.