Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import android.app.Application
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Build
import com.motionapps.sensorbox.core.error.CompositeDiagnosticLogger
import com.motionapps.sensorbox.core.error.DiagnosticLogger
import com.motionapps.sensorbox.core.error.DiagnosticMetadata
import com.motionapps.sensorbox.core.error.DiagnosticsStore
import com.motionapps.sensorbox.core.error.FileDiagnostics
import com.motionapps.sensorbox.core.error.LogcatDiagnosticLogger
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -40,7 +42,8 @@ object DiagnosticsModule {
)

@Provides
fun provideDiagnosticLogger(diagnostics: FileDiagnostics): DiagnosticLogger = diagnostics
fun provideDiagnosticLogger(diagnostics: FileDiagnostics): DiagnosticLogger =
CompositeDiagnosticLogger(diagnostics, LogcatDiagnosticLogger())

@Provides
fun provideDiagnosticsStore(diagnostics: FileDiagnostics): DiagnosticsStore = diagnostics
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.motionapps.sensorbox.domain.measurement

import android.content.Context
import android.content.Intent
import com.motionapps.sensorbox.core.error.AppResult
import com.motionapps.sensorbox.core.storage.NativeDocumentStorage
import dagger.hilt.android.qualifiers.ApplicationContext
import com.motionapps.sensorbox.core.storage.DocumentStorage
import javax.inject.Inject

interface DocumentStorageGateway {
Expand All @@ -15,16 +13,10 @@ interface DocumentStorageGateway {
fun persist(resultIntent: Intent): AppResult<Unit>
}

class DocumentStorageUseCase @Inject constructor(@ApplicationContext private val context: Context) :
DocumentStorageGateway {
override fun hasStorage(): AppResult<Boolean> = NativeDocumentStorage.hasAppDirectory(context, APP_DIRECTORY)
class DocumentStorageUseCase @Inject constructor(private val storage: DocumentStorage) : DocumentStorageGateway {
override fun hasStorage(): AppResult<Boolean> = storage.hasConfiguredDirectory()

override fun displayPath(): AppResult<String?> = NativeDocumentStorage.displayPath(context, APP_DIRECTORY)
override fun displayPath(): AppResult<String?> = storage.displayPath()

override fun persist(resultIntent: Intent): AppResult<Unit> =
NativeDocumentStorage.persistRootAccess(context, resultIntent, APP_DIRECTORY)

private companion object {
const val APP_DIRECTORY = "SensorBox"
}
override fun persist(resultIntent: Intent): AppResult<Unit> = storage.persistRootAccess(resultIntent)
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package com.motionapps.sensorbox.domain.measurement

import com.motionapps.sensorbox.core.error.AppResult
import com.motionapps.sensorbox.core.time.EpochClock
import com.motionapps.sensorbox.domain.paired.PairedRecordingCoordinator
import com.motionapps.wearoslib.protocol.WearStopReason
import javax.inject.Inject

class MeasurementControlUseCase @Inject constructor(
private val pairedRecordingCoordinator: PairedRecordingCoordinator,
private val localController: PhoneRecordingController,
private val clock: EpochClock,
) {
suspend fun start(request: MeasurementRequest): AppResult<Unit> = pairedRecordingCoordinator.start(request)

suspend fun stop(): AppResult<Unit> = pairedRecordingCoordinator.stop(WearStopReason.USER_REQUEST)

fun annotate(text: String, timestampMillis: Long = System.currentTimeMillis()): AppResult<Unit> =
fun annotate(text: String, timestampMillis: Long = clock.nowMillis()): AppResult<Unit> =
localController.annotate(text, timestampMillis)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.motionapps.sensorbox.core.error.AppErrorCode
import com.motionapps.sensorbox.core.error.AppResult
import com.motionapps.sensorbox.core.error.appResult
import com.motionapps.sensorbox.core.error.flatMap
import com.motionapps.sensorbox.core.storage.NativeDocumentStorage
import com.motionapps.sensorbox.core.storage.DocumentStorage
import com.motionapps.sensorservices.intent.MeasurementIntentFactory
import com.motionapps.sensorservices.intent.MeasurementLaunchRequest
import com.motionapps.sensorservices.services.MeasurementService
Expand Down Expand Up @@ -38,12 +38,13 @@ interface PhoneRecordingController {
class AndroidPhoneRecordingController @Inject constructor(
@ApplicationContext private val context: Context,
private val intentFactory: MeasurementIntentFactory,
private val documentStorage: DocumentStorage,
) : PhoneRecordingController {
private val committedSessions = mutableSetOf<String>()

override suspend fun prepare(sessionId: String, request: MeasurementRequest): AppResult<PreparedPhoneRecording> =
NativeDocumentStorage
.hasAppDirectory(context, APP_DIRECTORY)
documentStorage
.hasConfiguredDirectory()
.flatMap { storageReady ->
if (!storageReady) {
AppResult.failure(AppError(AppErrorCode.STORAGE, "Prepare phone recording storage"))
Expand Down Expand Up @@ -121,7 +122,6 @@ class AndroidPhoneRecordingController @Inject constructor(
}

private companion object {
const val APP_DIRECTORY = "SensorBox"
val SENSOR_PERIODS = intArrayOf(
SensorManager.SENSOR_DELAY_FASTEST,
SensorManager.SENSOR_DELAY_GAME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.motionapps.sensorbox.core.error.AppResult
import com.motionapps.sensorbox.core.error.DiagnosticLogger
import com.motionapps.sensorbox.core.error.combineAppResults
import com.motionapps.sensorbox.core.error.toDiagnosticEvent
import com.motionapps.sensorbox.core.time.EpochClock
import com.motionapps.sensorbox.domain.measurement.MeasurementRequest
import com.motionapps.sensorbox.domain.measurement.PhoneRecordingController
import com.motionapps.sensorbox.domain.measurement.PreparedPhoneRecording
Expand Down Expand Up @@ -40,6 +41,7 @@ class PairedRecordingCoordinator @Inject constructor(
private val acknowledgementInbox: WearAcknowledgementInbox,
private val sessionIdFactory: RecordingSessionIdFactory,
private val diagnosticLogger: DiagnosticLogger,
private val clock: EpochClock,
) {
private val mutex = Mutex()
private val mutableSession = MutableStateFlow<PairedRecordingSession?>(null)
Expand Down Expand Up @@ -93,7 +95,7 @@ class PairedRecordingCoordinator @Inject constructor(

private suspend fun commitBoth(preparation: PairedPreparation, request: MeasurementRequest): AppResult<Unit> {
val sessionId = preparation.prepared.sessionId
val startAtEpochMillis = System.currentTimeMillis() + request.delaySeconds.coerceAtLeast(0) * 1_000L +
val startAtEpochMillis = clock.nowMillis() + request.delaySeconds.coerceAtLeast(0) * 1_000L +
if (preparation.controlsWear) PAIRED_START_LEAD_MILLIS else 0L
val local = localController.commit(preparation.prepared, startAtEpochMillis)
if (local is AppResult.Failure) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.motionapps.sensorbox.core.error.AppErrorCode
import com.motionapps.sensorbox.core.error.AppResult
import com.motionapps.sensorbox.core.error.appResult
import com.motionapps.sensorbox.core.error.flatMap
import com.motionapps.sensorbox.core.storage.NativeDocumentStorage
import com.motionapps.sensorbox.core.storage.DocumentStorage
import com.motionapps.wearoslib.files.WearFileMetadata
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
Expand All @@ -16,7 +16,10 @@ import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class WearFileDestination @Inject constructor(@ApplicationContext private val context: Context) {
class WearFileDestination @Inject constructor(
@ApplicationContext private val context: Context,
private val documentStorage: DocumentStorage,
) {
fun isReady(): AppResult<Boolean> = hasConfiguredDirectory().map { configured -> configured || isDebugBuild() }

fun copy(metadata: WearFileMetadata, input: InputStream): AppResult<Unit> {
Expand All @@ -36,10 +39,8 @@ class WearFileDestination @Inject constructor(@ApplicationContext private val co
measurementName: String,
fileName: String,
input: InputStream,
): AppResult<Unit> = NativeDocumentStorage.copyToMeasurement(
context = context,
): AppResult<Unit> = documentStorage.copyToMeasurement(
input = input,
appDirectoryName = APP_DIRECTORY,
measurementName = measurementName,
fileName = fileName,
mimeType = mimeType(fileName),
Expand All @@ -62,10 +63,7 @@ class WearFileDestination @Inject constructor(@ApplicationContext private val co
}
}

private fun hasConfiguredDirectory(): AppResult<Boolean> = NativeDocumentStorage.hasAppDirectory(
context,
APP_DIRECTORY,
)
private fun hasConfiguredDirectory(): AppResult<Boolean> = documentStorage.hasConfiguredDirectory()

private fun isDebugBuild(): Boolean = context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,18 @@ class MainViewModel @Inject constructor(
onSuccess = { preferences ->
val route = if (hasChosenInitialRoute) {
state.value.route
} else if (preferences.hasCompletedIntro && preferences.hasAcceptedPolicy) {
} else if (
preferences.onboarding.hasCompletedIntro &&
preferences.onboarding.hasAcceptedPolicy
) {
MainRoute.RECORD
} else {
MainRoute.ONBOARDING
}
hasChosenInitialRoute = true
mutableState.value = state.value.copy(
route = route,
keepScreenAwake = preferences.keepPhoneDisplayOn,
keepScreenAwake = preferences.display.keepPhoneDisplayOn,
hasLoadedPreferences = true,
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private fun MeasurementSetupContent(state: RecordingState, onIntent: (RecordingI
item { TimingSetup(state, onIntent) }
item { NotesAndAlarmsSetup(state, onIntent) }
item {
SamplingSetting(state.preferences.sensorSamplingPeriod) { index ->
SamplingSetting(state.preferences.recording.sensorSamplingPeriod) { index ->
onIntent(RecordingIntent.SetSamplingPeriod(index))
}
}
Expand Down Expand Up @@ -199,7 +199,7 @@ private fun BatterySetup(state: RecordingState, onIntent: (RecordingIntent) -> U
BooleanSetting(
title = stringResource(R.string.battery_guard),
description = stringResource(R.string.battery_guard_setup_description),
checked = state.preferences.restrictMeasurementOnLowBattery,
checked = state.preferences.recording.restrictMeasurementOnLowBattery,
) { onIntent(RecordingIntent.SetLowBatteryRestriction(it)) }
}

Expand All @@ -208,7 +208,7 @@ private fun WakeLockSetup(state: RecordingState, onIntent: (RecordingIntent) ->
BooleanSetting(
title = stringResource(R.string.keep_cpu_awake),
description = stringResource(R.string.keep_cpu_awake_setup_description),
checked = state.preferences.useWakeLock,
checked = state.preferences.recording.useWakeLock,
) { onIntent(RecordingIntent.SetWakeLock(it)) }
}

Expand All @@ -217,19 +217,19 @@ private fun KeepScreenAwakeSetup(state: RecordingState, onIntent: (RecordingInte
BooleanSetting(
title = stringResource(R.string.keep_screen_awake),
description = stringResource(R.string.keep_screen_awake_setup_description),
checked = state.preferences.keepPhoneDisplayOn,
checked = state.preferences.display.keepPhoneDisplayOn,
) { onIntent(RecordingIntent.SetKeepScreenAwake(it)) }
}

@Composable
private fun GpsIntervalSetup(state: RecordingState, onIntent: (RecordingIntent) -> Unit) {
StepSetting(
stringResource(R.string.gps_interval),
state.preferences.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
pluralStringResource(
R.plurals.seconds_count,
state.preferences.gpsIntervalSeconds,
state.preferences.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
),
1,
3_600,
Expand All @@ -242,11 +242,11 @@ private fun GpsIntervalSetup(state: RecordingState, onIntent: (RecordingIntent)
private fun GpsDistanceSetup(state: RecordingState, onIntent: (RecordingIntent) -> Unit) {
StepSetting(
stringResource(R.string.gps_minimum_distance),
state.preferences.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
pluralStringResource(
R.plurals.meters_count,
state.preferences.gpsMinDistanceMeters,
state.preferences.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
),
0,
10_000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,11 @@ class RecordingViewModel @Inject constructor(
private fun RecordingState.toMeasurementRequest() = MeasurementRequest(
sensorIds = selectedSensorIds,
includesGps = includesGps,
samplingPeriodIndex = preferences.sensorSamplingPeriod,
stopOnLowBattery = preferences.restrictMeasurementOnLowBattery,
useWakeLock = preferences.useWakeLock,
gpsIntervalSeconds = preferences.gpsIntervalSeconds,
gpsMinDistanceMeters = preferences.gpsMinDistanceMeters,
samplingPeriodIndex = preferences.recording.sensorSamplingPeriod,
stopOnLowBattery = preferences.recording.restrictMeasurementOnLowBattery,
useWakeLock = preferences.recording.useWakeLock,
gpsIntervalSeconds = preferences.recording.gpsIntervalSeconds,
gpsMinDistanceMeters = preferences.recording.gpsMinDistanceMeters,
wearSensorIds = selectedWearSensorIds,
wearIncludesGps = wearIncludesGps,
customName = customMeasurementName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ private fun GpsDetails(state: RecordingState) {
permissionRevision += 1
}
val details = rememberGpsDetails(
intervalSeconds = state.preferences.gpsIntervalSeconds,
minimumDistanceMeters = state.preferences.gpsMinDistanceMeters,
intervalSeconds = state.preferences.recording.gpsIntervalSeconds,
minimumDistanceMeters = state.preferences.recording.gpsMinDistanceMeters,
permissionRevision = permissionRevision,
)
val unavailableValue = stringResource(if (details.hasPermission) R.string.waiting else R.string.unavailable)
Expand Down Expand Up @@ -158,16 +158,16 @@ private fun GpsDetailRows(details: GpsDetailsState, state: RecordingState, unava
stringResource(R.string.detail_update_interval),
pluralStringResource(
R.plurals.seconds_count,
state.preferences.gpsIntervalSeconds,
state.preferences.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
),
)
DetailRow(
stringResource(R.string.detail_minimum_distance),
pluralStringResource(
R.plurals.meters_count,
state.preferences.gpsMinDistanceMeters,
state.preferences.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ private fun GpsPreview(state: RecordingState) {
permissionRevision += 1
}
val details = rememberGpsDetails(
intervalSeconds = state.preferences.gpsIntervalSeconds,
minimumDistanceMeters = state.preferences.gpsMinDistanceMeters,
intervalSeconds = state.preferences.recording.gpsIntervalSeconds,
minimumDistanceMeters = state.preferences.recording.gpsMinDistanceMeters,
permissionRevision = permissionRevision,
)
val unavailableValue = stringResource(if (details.hasPermission) R.string.waiting else R.string.unavailable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fun SettingsScreen(
SensorBoxTopAppBar(stringResource(R.string.measurement_settings), onBack)
}
item {
SamplingSetting(state.preferences.sensorSamplingPeriod) { index ->
SamplingSetting(state.preferences.recording.sensorSamplingPeriod) { index ->
onIntent(SettingsIntent.SetSamplingPeriod(index))
}
}
Expand Down Expand Up @@ -170,22 +170,22 @@ private fun GpsSettings(state: SettingsState, onIntent: (SettingsIntent) -> Unit
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
NumberPickerSetting(
stringResource(R.string.gps_interval),
state.preferences.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
pluralStringResource(
R.plurals.seconds_count,
state.preferences.gpsIntervalSeconds,
state.preferences.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
state.preferences.recording.gpsIntervalSeconds,
),
1,
3_600,
) { onIntent(SettingsIntent.SetGpsInterval(it)) }
NumberPickerSetting(
stringResource(R.string.gps_minimum_distance),
state.preferences.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
pluralStringResource(
R.plurals.meters_count,
state.preferences.gpsMinDistanceMeters,
state.preferences.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
state.preferences.recording.gpsMinDistanceMeters,
),
0,
10_000,
Expand Down Expand Up @@ -265,7 +265,7 @@ private fun BatteryGuardSetting(state: SettingsState, onIntent: (SettingsIntent)
BooleanSetting(
title = stringResource(R.string.battery_guard),
description = stringResource(R.string.battery_guard_settings_description),
checked = state.preferences.restrictMeasurementOnLowBattery,
checked = state.preferences.recording.restrictMeasurementOnLowBattery,
) { onIntent(SettingsIntent.SetLowBatteryRestriction(it)) }
}

Expand All @@ -274,7 +274,7 @@ private fun CpuWakeLockSetting(state: SettingsState, onIntent: (SettingsIntent)
BooleanSetting(
title = stringResource(R.string.keep_cpu_awake),
description = stringResource(R.string.keep_cpu_awake_settings_description),
checked = state.preferences.useWakeLock,
checked = state.preferences.recording.useWakeLock,
) { onIntent(SettingsIntent.SetWakeLock(it)) }
}

Expand All @@ -283,7 +283,7 @@ private fun ScreenAwakeSetting(state: SettingsState, onIntent: (SettingsIntent)
BooleanSetting(
title = stringResource(R.string.keep_screen_awake),
description = stringResource(R.string.keep_screen_awake_settings_description),
checked = state.preferences.keepPhoneDisplayOn,
checked = state.preferences.display.keepPhoneDisplayOn,
) { onIntent(SettingsIntent.SetKeepScreenAwake(it)) }
}

Expand Down
Loading