refactor: replace Service-based sensor collection with direct Fragment sensor registration
Build APK / build (push) Failing after 4s
Build APK / build (push) Failing after 4s
Root cause analysis: the persistent crash on startup was caused by the Service + startForeground lifecycle complexity: 1. BIND_AUTO_CREATE creates Service in bound-only state 2. startForeground requires a started state (Android 12+) 3. Multiple permission checks and lifecycle edge cases Fix: Removed DataCollectionService dependency from DashboardFragment. The Fragment now registers SensorManager directly in onResume and unregisters in onPause. This: - Eliminates all Service lifecycle issues - No foreground notification required - No binding/startService ordering problems - Simpler and more reliable lifecycle management - Recording still uses DataRepository for file I/O Also added global uncaught exception handler in SensToolsApp that writes crash stack traces to Downloads/sensTools_crash.txt.
This commit is contained in:
@@ -1,16 +1,42 @@
|
|||||||
package com.senstools
|
package com.senstools
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.os.Environment
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileWriter
|
||||||
|
|
||||||
class SensToolsApp : Application() {
|
class SensToolsApp : Application() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
|
// Global crash handler - writes to Downloads/sensTools_crash.txt
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
||||||
|
try {
|
||||||
|
val dir = Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS
|
||||||
|
)
|
||||||
|
dir.mkdirs()
|
||||||
|
val crashFile = File(dir, "sensTools_crash.txt")
|
||||||
|
FileWriter(crashFile, false).use { writer ->
|
||||||
|
writer.appendLine("=== CRASH ===")
|
||||||
|
writer.appendLine("Time: ${System.currentTimeMillis()}")
|
||||||
|
writer.appendLine("Thread: ${thread.name}")
|
||||||
|
writer.appendLine("Exception: ${throwable.javaClass.name}: ${throwable.message}")
|
||||||
|
throwable.stackTrace?.forEach { writer.appendLine(" at $it") }
|
||||||
|
writer.appendLine()
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
|
// Also call the default handler so the app still crashes normally
|
||||||
|
prevHandler?.uncaughtException(thread, throwable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var instance: SensToolsApp
|
lateinit var instance: SensToolsApp
|
||||||
private set
|
private set
|
||||||
|
private val prevHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+194
-152
@@ -1,88 +1,68 @@
|
|||||||
package com.senstools.presentation.ui.dashboard
|
package com.senstools.presentation.ui.dashboard
|
||||||
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.hardware.Sensor
|
||||||
import android.content.ServiceConnection
|
import android.hardware.SensorEvent
|
||||||
|
import android.hardware.SensorEventListener
|
||||||
|
import android.hardware.SensorManager
|
||||||
|
import android.location.Location
|
||||||
|
import android.location.LocationListener
|
||||||
|
import android.location.LocationManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import com.senstools.data.datasource.DataCollectionService
|
|
||||||
import com.senstools.databinding.FragmentDashboardBinding
|
import com.senstools.databinding.FragmentDashboardBinding
|
||||||
import com.senstools.domain.ekf.EKFAttitude
|
import com.senstools.domain.ekf.EKFAttitude
|
||||||
|
import com.senstools.domain.model.AttitudeData
|
||||||
|
import com.senstools.domain.model.GPSData
|
||||||
|
import com.senstools.domain.model.SensorData
|
||||||
import com.senstools.domain.repository.DataRepository
|
import com.senstools.domain.repository.DataRepository
|
||||||
import com.senstools.presentation.ui.charts.AccelChartManager
|
import com.senstools.presentation.ui.charts.AccelChartManager
|
||||||
import com.senstools.presentation.ui.charts.GyroChartManager
|
import com.senstools.presentation.ui.charts.GyroChartManager
|
||||||
import com.senstools.presentation.ui.charts.MagnetometerBarManager
|
import com.senstools.presentation.ui.charts.MagnetometerBarManager
|
||||||
import com.senstools.presentation.viewmodel.DashboardViewModel
|
import com.senstools.presentation.viewmodel.DashboardViewModel
|
||||||
import kotlinx.coroutines.launch
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class DashboardFragment : Fragment() {
|
class DashboardFragment : Fragment(), SensorEventListener, LocationListener {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val TAG = "DashboardFragment"
|
|
||||||
}
|
|
||||||
|
|
||||||
private var _binding: FragmentDashboardBinding? = null
|
private var _binding: FragmentDashboardBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val viewModel: DashboardViewModel by viewModels()
|
private val viewModel: DashboardViewModel by viewModels()
|
||||||
|
|
||||||
private var collectionService: DataCollectionService? = null
|
private var sensorManager: SensorManager? = null
|
||||||
private var serviceBound = false
|
private var locationManager: LocationManager? = null
|
||||||
|
|
||||||
|
// Sensor data holders
|
||||||
|
private var lastAccel = floatArrayOf(0f, 0f, 0f)
|
||||||
|
private var lastGyro = floatArrayOf(0f, 0f, 0f)
|
||||||
|
private var lastMag = floatArrayOf(0f, 0f, 0f)
|
||||||
|
private var lastPressureAlt = 0f
|
||||||
|
private var lastTemp = 0f
|
||||||
|
private var lastHumidity = 0f
|
||||||
|
private var lastTimestamp = 0L
|
||||||
|
|
||||||
private var recordingFile: java.io.File? = null
|
private var recordingFile: java.io.File? = null
|
||||||
|
private var isRecording = false
|
||||||
|
|
||||||
private lateinit var repository: DataRepository
|
private lateinit var repository: DataRepository
|
||||||
private lateinit var ekfAttitude: EKFAttitude
|
private lateinit var ekfAttitude: EKFAttitude
|
||||||
|
|
||||||
// Chart managers
|
|
||||||
private lateinit var accelChartManager: AccelChartManager
|
private lateinit var accelChartManager: AccelChartManager
|
||||||
private lateinit var gyroChartManager: GyroChartManager
|
private lateinit var gyroChartManager: GyroChartManager
|
||||||
private lateinit var magnetometerManager: MagnetometerBarManager
|
private lateinit var magnetometerManager: MagnetometerBarManager
|
||||||
|
|
||||||
private var ekfInitialized = false
|
private var ekfInitialized = false
|
||||||
|
private var sensorsRegistered = false
|
||||||
|
|
||||||
private val connection = object : ServiceConnection {
|
private val timeFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
|
||||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||||
Log.d(TAG, "onServiceConnected")
|
|
||||||
val binder = service as DataCollectionService.LocalBinder
|
|
||||||
collectionService = binder.getService()
|
|
||||||
serviceBound = true
|
|
||||||
|
|
||||||
// Start the service so startForeground is legal on Android 12+
|
|
||||||
try {
|
|
||||||
val ctx = requireContext().applicationContext
|
|
||||||
ctx.startService(Intent(ctx, DataCollectionService::class.java))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "startService failed, trying direct collection", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start collection
|
|
||||||
try {
|
|
||||||
collectionService?.startCollection()
|
|
||||||
Log.d(TAG, "startCollection OK")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "startCollection failed", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Observe service flows
|
|
||||||
observeServiceData()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceDisconnected(name: ComponentName?) {
|
|
||||||
collectionService = null
|
|
||||||
serviceBound = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
@@ -99,27 +79,24 @@ class DashboardFragment : Fragment() {
|
|||||||
repository = DataRepository(requireContext())
|
repository = DataRepository(requireContext())
|
||||||
ekfAttitude = EKFAttitude()
|
ekfAttitude = EKFAttitude()
|
||||||
|
|
||||||
|
sensorManager = requireContext().getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
|
locationManager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
|
||||||
setupCharts()
|
setupCharts()
|
||||||
setupObservers()
|
setupObservers()
|
||||||
setupListeners()
|
setupListeners()
|
||||||
bindService()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (!serviceBound) {
|
registerSensors()
|
||||||
bindService()
|
registerLocation()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
if (serviceBound) {
|
unregisterSensors()
|
||||||
try {
|
unregisterLocation()
|
||||||
requireContext().unbindService(connection)
|
|
||||||
} catch (_: Exception) { }
|
|
||||||
serviceBound = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupCharts() {
|
private fun setupCharts() {
|
||||||
@@ -128,74 +105,155 @@ class DashboardFragment : Fragment() {
|
|||||||
magnetometerManager = MagnetometerBarManager(binding.chartMagnetometer)
|
magnetometerManager = MagnetometerBarManager(binding.chartMagnetometer)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindService() {
|
private fun registerSensors() {
|
||||||
|
if (sensorsRegistered) return
|
||||||
|
val sm = sensorManager ?: return
|
||||||
|
|
||||||
|
sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.let {
|
||||||
|
sm.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME)
|
||||||
|
}
|
||||||
|
sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE)?.let {
|
||||||
|
sm.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME)
|
||||||
|
}
|
||||||
|
sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.let {
|
||||||
|
sm.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME)
|
||||||
|
}
|
||||||
|
sm.getDefaultSensor(Sensor.TYPE_PRESSURE)?.let {
|
||||||
|
sm.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME)
|
||||||
|
}
|
||||||
|
sm.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE)?.let {
|
||||||
|
sm.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME)
|
||||||
|
}
|
||||||
|
sm.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY)?.let {
|
||||||
|
sm.registerListener(this, it, SensorManager.SENSOR_DELAY_GAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
sensorsRegistered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterSensors() {
|
||||||
|
sensorManager?.unregisterListener(this)
|
||||||
|
sensorsRegistered = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerLocation() {
|
||||||
|
val lm = locationManager ?: return
|
||||||
try {
|
try {
|
||||||
val intent = Intent(requireContext(), DataCollectionService::class.java)
|
if (requireContext().checkSelfPermission(
|
||||||
requireContext().bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
android.Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
} catch (e: Exception) {
|
) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||||
Log.e(TAG, "bindService failed", e)
|
) {
|
||||||
|
lm.requestLocationUpdates(
|
||||||
|
LocationManager.GPS_PROVIDER,
|
||||||
|
200L, 0f, this, null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterLocation() {
|
||||||
|
try {
|
||||||
|
locationManager?.removeUpdates(this)
|
||||||
|
} catch (_: Exception) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// SensorEventListener
|
||||||
|
override fun onSensorChanged(event: SensorEvent) {
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
lastTimestamp = timestamp
|
||||||
|
|
||||||
|
when (event.sensor.type) {
|
||||||
|
Sensor.TYPE_ACCELEROMETER -> lastAccel = event.values.clone()
|
||||||
|
Sensor.TYPE_GYROSCOPE -> lastGyro = event.values.clone()
|
||||||
|
Sensor.TYPE_MAGNETIC_FIELD -> lastMag = event.values.clone()
|
||||||
|
Sensor.TYPE_PRESSURE -> {
|
||||||
|
lastPressureAlt = SensorManager.getAltitude(
|
||||||
|
SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
|
||||||
|
event.values[0]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Sensor.TYPE_AMBIENT_TEMPERATURE -> lastTemp = event.values[0]
|
||||||
|
Sensor.TYPE_RELATIVE_HUMIDITY -> lastHumidity = event.values[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
val sensorData = SensorData(
|
||||||
|
timestamp = timestamp,
|
||||||
|
accelerometer = lastAccel.clone(),
|
||||||
|
gyroscope = lastGyro.clone(),
|
||||||
|
magnetometer = lastMag.clone(),
|
||||||
|
pressureAltitude = lastPressureAlt,
|
||||||
|
temperature = lastTemp,
|
||||||
|
humidity = lastHumidity,
|
||||||
|
time = timeFormat.format(Date(timestamp)),
|
||||||
|
date = dateFormat.format(Date(timestamp))
|
||||||
|
)
|
||||||
|
viewModel.updateSensorData(sensorData)
|
||||||
|
|
||||||
|
updateEKF(sensorData)
|
||||||
|
updateCharts(sensorData)
|
||||||
|
|
||||||
|
if (isRecording && recordingFile != null) {
|
||||||
|
repository.appendSensorData(sensorData, recordingFile!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeServiceData() {
|
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { }
|
||||||
val service = collectionService ?: return
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
// LocationListener
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
override fun onLocationChanged(location: Location) {
|
||||||
launch {
|
val now = System.currentTimeMillis()
|
||||||
service.currentGPSData.collect { gps ->
|
val gps = GPSData(
|
||||||
viewModel.updateGPSData(gps)
|
timestamp = now,
|
||||||
}
|
longitude = location.longitude,
|
||||||
}
|
latitude = location.latitude,
|
||||||
launch {
|
altitude = location.altitude,
|
||||||
service.currentSensorData.collect { sensor ->
|
speed = location.speed,
|
||||||
viewModel.updateSensorData(sensor)
|
bearing = location.bearing,
|
||||||
|
satellites = 0,
|
||||||
|
time = timeFormat.format(Date(now)),
|
||||||
|
date = dateFormat.format(Date(now))
|
||||||
|
)
|
||||||
|
viewModel.updateGPSData(gps)
|
||||||
|
|
||||||
if (!ekfInitialized) {
|
if (isRecording && recordingFile != null) {
|
||||||
ekfAttitude.initializeFromSensors(
|
repository.appendGPSData(gps, recordingFile!!)
|
||||||
sensor.accelerometer,
|
|
||||||
sensor.magnetometer
|
|
||||||
)
|
|
||||||
ekfInitialized = true
|
|
||||||
}
|
|
||||||
|
|
||||||
ekfAttitude.predict(
|
|
||||||
sensor.gyroscope,
|
|
||||||
sensor.timestamp
|
|
||||||
)
|
|
||||||
ekfAttitude.update(
|
|
||||||
sensor.accelerometer,
|
|
||||||
sensor.magnetometer
|
|
||||||
)
|
|
||||||
|
|
||||||
val attitude = ekfAttitude.getAttitude()
|
|
||||||
viewModel.updateAttitudeData(attitude)
|
|
||||||
|
|
||||||
accelChartManager.addData(
|
|
||||||
sensor.accelerometer[0],
|
|
||||||
sensor.accelerometer[1],
|
|
||||||
sensor.accelerometer[2]
|
|
||||||
)
|
|
||||||
gyroChartManager.addData(
|
|
||||||
sensor.gyroscope[0],
|
|
||||||
sensor.gyroscope[1],
|
|
||||||
sensor.gyroscope[2]
|
|
||||||
)
|
|
||||||
magnetometerManager.updateData(
|
|
||||||
sensor.magnetometer[0],
|
|
||||||
sensor.magnetometer[1],
|
|
||||||
sensor.magnetometer[2]
|
|
||||||
)
|
|
||||||
|
|
||||||
if (viewModel.isRecording.value == true && recordingFile != null) {
|
|
||||||
repository.appendSensorData(sensor, recordingFile!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onProviderEnabled(provider: String) { }
|
||||||
|
override fun onProviderDisabled(provider: String) { }
|
||||||
|
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { }
|
||||||
|
|
||||||
|
private fun updateEKF(sensor: SensorData) {
|
||||||
|
if (!ekfInitialized) {
|
||||||
|
ekfAttitude.initializeFromSensors(sensor.accelerometer, sensor.magnetometer)
|
||||||
|
ekfInitialized = true
|
||||||
|
} else {
|
||||||
|
ekfAttitude.predict(sensor.gyroscope, sensor.timestamp)
|
||||||
|
ekfAttitude.update(sensor.accelerometer, sensor.magnetometer)
|
||||||
|
}
|
||||||
|
val attitude = ekfAttitude.getAttitude()
|
||||||
|
viewModel.updateAttitudeData(attitude)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCharts(sensor: SensorData) {
|
||||||
|
accelChartManager.addData(
|
||||||
|
sensor.accelerometer[0],
|
||||||
|
sensor.accelerometer[1],
|
||||||
|
sensor.accelerometer[2]
|
||||||
|
)
|
||||||
|
gyroChartManager.addData(
|
||||||
|
sensor.gyroscope[0],
|
||||||
|
sensor.gyroscope[1],
|
||||||
|
sensor.gyroscope[2]
|
||||||
|
)
|
||||||
|
magnetometerManager.updateData(
|
||||||
|
sensor.magnetometer[0],
|
||||||
|
sensor.magnetometer[1],
|
||||||
|
sensor.magnetometer[2]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
viewModel.gpsData.observe(viewLifecycleOwner) { gps ->
|
viewModel.gpsData.observe(viewLifecycleOwner) { gps ->
|
||||||
binding.apply {
|
binding.apply {
|
||||||
@@ -205,10 +263,6 @@ class DashboardFragment : Fragment() {
|
|||||||
tvSpeed.text = String.format("%.2f m/s", gps.speed)
|
tvSpeed.text = String.format("%.2f m/s", gps.speed)
|
||||||
tvBearing.text = String.format("%.2f°", gps.bearing)
|
tvBearing.text = String.format("%.2f°", gps.bearing)
|
||||||
tvSatellites.text = gps.satellites.toString()
|
tvSatellites.text = gps.satellites.toString()
|
||||||
|
|
||||||
if (viewModel.isRecording.value == true && recordingFile != null) {
|
|
||||||
repository.appendGPSData(gps, recordingFile!!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,45 +291,33 @@ class DashboardFragment : Fragment() {
|
|||||||
tvYaw.text = String.format("%.2f°", attitude.yaw)
|
tvYaw.text = String.format("%.2f°", attitude.yaw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.isRecording.observe(viewLifecycleOwner) { isRecording ->
|
|
||||||
binding.apply {
|
|
||||||
btnRecord.isEnabled = !isRecording
|
|
||||||
btnStopRecording.isEnabled = isRecording
|
|
||||||
tvRecordingStatus.text = if (isRecording) "Recording..." else "Idle"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
binding.btnRecord.setOnClickListener { startRecording() }
|
binding.btnRecord.setOnClickListener {
|
||||||
binding.btnStopRecording.setOnClickListener { stopRecording() }
|
isRecording = true
|
||||||
}
|
|
||||||
|
|
||||||
private fun startRecording() {
|
|
||||||
if (collectionService != null) {
|
|
||||||
viewModel.startRecording()
|
viewModel.startRecording()
|
||||||
recordingFile = repository.createRecordingFile()
|
recordingFile = repository.createRecordingFile()
|
||||||
|
binding.btnRecord.isEnabled = false
|
||||||
|
binding.btnStopRecording.isEnabled = true
|
||||||
|
binding.tvRecordingStatus.text = "Recording..."
|
||||||
Toast.makeText(requireContext(), "Recording started", Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), "Recording started", Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
|
||||||
Toast.makeText(requireContext(), "Service not ready", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
}
|
binding.btnStopRecording.setOnClickListener {
|
||||||
|
isRecording = false
|
||||||
private fun stopRecording() {
|
viewModel.stopRecording()
|
||||||
viewModel.stopRecording()
|
recordingFile = null
|
||||||
recordingFile = null
|
binding.btnRecord.isEnabled = true
|
||||||
Toast.makeText(requireContext(), "Recording saved", Toast.LENGTH_SHORT).show()
|
binding.btnStopRecording.isEnabled = false
|
||||||
|
binding.tvRecordingStatus.text = "Idle"
|
||||||
|
Toast.makeText(requireContext(), "Recording saved", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
unregisterSensors()
|
||||||
if (serviceBound) {
|
unregisterLocation()
|
||||||
try {
|
|
||||||
requireContext().unbindService(connection)
|
|
||||||
} catch (_: Exception) { }
|
|
||||||
serviceBound = false
|
|
||||||
}
|
|
||||||
_binding = null
|
_binding = null
|
||||||
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user