Skip to content

Commit

Permalink
Merge of master and dev
Browse files Browse the repository at this point in the history
Signed-off-by: Víctor Domínguez <viconel27@gmail.com>
  • Loading branch information
VicDominguez committed May 5, 2024
2 parents 55c2494 + 5a0d09a commit 5e62029
Show file tree
Hide file tree
Showing 18 changed files with 164 additions and 167 deletions.
51 changes: 25 additions & 26 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.dagger.hilt.android'
id 'com.google.devtools.ksp' version '1.8.0-1.0.8'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
id 'kotlin-parcelize'
}

android {
compileSdk 33
compileSdk 34

defaultConfig {
applicationId "es.upm.bienestaremocional"
minSdk 28 //Health connect requires sdk >=27
targetSdk 33
targetSdk 34
versionCode 1
versionName "1.3.6"
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -67,29 +67,29 @@ kotlin {

dependencies {

implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation "androidx.core:core-ktx:$android_core_ktx_version"
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "com.google.android.material:material:$material_base_version"
implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
testImplementation "junit:junit:$junit_version"
androidTestImplementation "androidx.test.ext:junit:$junit_extension_version"
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"

// Jetpack compose
// Kotlin extensions for 'lifecycle' artifact
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Compose integration with Lifecycle
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.1'
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
// Compose integration with Lifecycle ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"

implementation 'androidx.activity:activity-compose:1.7.2'
implementation "androidx.activity:activity-compose:$activity_compose_version"

implementation "androidx.compose.ui:ui:$compose_ui_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"

// Material design
implementation "androidx.compose.material:material:1.4.3"
implementation "androidx.compose.material:material:$material_compose_version"
implementation "androidx.compose.material3:material3:$material3_version"

// Compose
Expand All @@ -108,25 +108,25 @@ dependencies {
implementation "com.google.accompanist:accompanist-pager-indicators:$accompanist_version"

// Lottie
implementation 'com.airbnb.android:lottie-compose:5.2.0'
implementation "com.airbnb.android:lottie-compose:$lottie_version"

// Window size
implementation "androidx.window:window:1.1.0"
implementation "androidx.window:window:$window_version"

// Preferences
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "androidx.datastore:datastore-preferences:$datastore_preferences_version"

// Settings
implementation "com.github.alorma:compose-settings-ui-m3:0.22.0"
implementation "com.github.alorma:compose-settings-ui-m3:$compose_settings_version"

// Navigation
implementation "androidx.navigation:navigation-compose:$navigation_compose_version"

// Health Connect. Version historic: https://developer.android.com/jetpack/androidx/releases/health-connect
implementation 'androidx.health.connect:connect-client:1.0.0-alpha11'
implementation "androidx.health.connect:connect-client:$health_connect_version"

// Language
implementation "com.github.YarikSOffice:lingver:1.3.0"
implementation "com.github.YarikSOffice:lingver:$lingver_version"

// Navigation
implementation "io.github.raamcosta.compose-destinations:core:$compose_destination_version"
Expand Down Expand Up @@ -170,7 +170,6 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:$work_version"

//Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.5.0"

implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
}
18 changes: 13 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,21 @@
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>

<!-- Required to specify which Health Connect permissions the app can request -->
<meta-data
android:name="health_permissions"
android:resource="@array/health_permissions"/>

</activity>

<!-- For versions starting Android 14, create an activity alias to show the rationale
of Health Connect permissions once users click the privacy policy link. -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>

<!-- Work manager provider -->

<provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ class MainApplication : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory

override fun getWorkManagerConfiguration() =
Configuration.Builder()
/**
* The [Configuration] used to initialize WorkManager
*/
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ enum class HealthConnectAvailability {

companion object {
fun getAvailability(context: Context): HealthConnectAvailability {
return when (HealthConnectClient.sdkStatus(context)) {
return when (HealthConnectClient.getSdkStatus(context)) {
HealthConnectClient.SDK_UNAVAILABLE -> NOT_SUPPORTED
HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED -> NOT_INSTALLED
HealthConnectClient.SDK_AVAILABLE -> INSTALLED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.aggregate.AggregationResult
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.records.SleepSessionRecord
import androidx.health.connect.client.records.SleepStageRecord
import androidx.health.connect.client.records.SleepSessionRecord.Stage
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.request.ReadRecordsRequest
import androidx.health.connect.client.time.TimeRangeFilter
Expand All @@ -25,16 +25,15 @@ class Sleep @Inject constructor(
) : HealthConnectSource<SleepSessionData>(healthConnectClient) {

override val readPermissions = setOf(
HealthPermission.getReadPermission(SleepSessionRecord::class),
HealthPermission.getReadPermission(SleepStageRecord::class)
HealthPermission.getReadPermission(SleepSessionRecord::class)
)

/**
* Reads sleep sessions for the previous seven days (from yesterday) to show a week's worth of
* sleep data.
*
* In addition to reading [SleepSessionRecord]s, for each session, the duration is calculated to
* demonstrate aggregation, and the underlying [SleepStageRecord] data is also read.
* demonstrate aggregation, and the underlying [Stage] data is also read.
*/
override suspend fun readSource(startTime: Instant, endTime: Instant): List<SleepSessionData> {
val sessions = mutableListOf<SleepSessionData>()
Expand Down Expand Up @@ -65,13 +64,6 @@ class Sleep @Inject constructor(
Log.e(logTag, "Exception during sleep aggregate:", e)
}

val stagesRequest = ReadRecordsRequest(
recordType = SleepStageRecord::class,
timeRangeFilter = sessionTimeFilter
)

val stagesResponse = healthConnectClient.readRecords(stagesRequest)

sessions.add(
SleepSessionData(
uid = session.metadata.id,
Expand All @@ -82,7 +74,7 @@ class Sleep @Inject constructor(
endTime = session.endTime,
endZoneOffset = session.endZoneOffset,
duration = aggregateResponse?.get(SleepSessionRecord.SLEEP_DURATION_TOTAL),
stages = stagesResponse.records
stages = session.stages
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package es.upm.bienestaremocional.data.healthconnect.types

import androidx.health.connect.client.records.Record
import androidx.health.connect.client.records.SleepSessionRecord
import androidx.health.connect.client.records.SleepStageRecord
import androidx.health.connect.client.records.SleepSessionRecord.Stage
import androidx.health.connect.client.records.metadata.Metadata
import java.time.Duration
import java.time.Instant
Expand All @@ -20,6 +20,6 @@ data class SleepSessionData(
val endTime: Instant,
val endZoneOffset: ZoneOffset?,
val duration: Duration?,
val stages: List<SleepStageRecord> = listOf(),
val stages: List<Stage> = listOf(),
override val metadata: Metadata = Metadata()
) : Record
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
package es.upm.bienestaremocional.data.remote.senders

import androidx.health.connect.client.records.SleepStageRecord
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_AWAKE
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_DEEP
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_LIGHT
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_OUT_OF_BED
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_REM
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_SLEEPING
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_UNKNOWN
import androidx.health.connect.client.records.SleepSessionRecord.Stage
import es.upm.bienestaremocional.utils.obtainTimestamp

/**
* Object used to send [SleepStageRecord], usually through JSON files.
* Object used to send [Stage], usually through JSON files.
*/
data class SleepStageSender(
val startTime: Long,
val endTime: Long,
val stage: String
) {
companion object {
fun SleepStageRecord.toSender(): SleepStageSender {
val startTime = obtainTimestamp(startTime, startZoneOffset)
val endTime = obtainTimestamp(endTime, endZoneOffset)
fun Stage.toSender(): SleepStageSender {
val startTime = obtainTimestamp(startTime, null)
val endTime = obtainTimestamp(endTime, null)
val stage = when (stage) {
SleepStageRecord.STAGE_TYPE_UNKNOWN -> "unknown"
SleepStageRecord.STAGE_TYPE_AWAKE -> "awake"
SleepStageRecord.STAGE_TYPE_SLEEPING -> "sleeping"
SleepStageRecord.STAGE_TYPE_OUT_OF_BED -> "out_of_bed"
SleepStageRecord.STAGE_TYPE_LIGHT -> "light"
SleepStageRecord.STAGE_TYPE_DEEP -> "deep"
SleepStageRecord.STAGE_TYPE_REM -> "rem"
STAGE_TYPE_UNKNOWN -> "unknown"
STAGE_TYPE_AWAKE -> "awake"
STAGE_TYPE_SLEEPING -> "sleeping"
STAGE_TYPE_OUT_OF_BED -> "out_of_bed"
STAGE_TYPE_LIGHT -> "light"
STAGE_TYPE_DEEP -> "deep"
STAGE_TYPE_REM -> "rem"
else -> "unknown"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import es.upm.bienestaremocional.utils.formatHoursMinutes
import es.upm.bienestaremocional.utils.generateInterval
import es.upm.bienestaremocional.utils.linspace
import java.time.Duration
import java.time.Instant
import kotlin.random.Random

/**
Expand Down Expand Up @@ -61,10 +60,9 @@ fun generateHeartRateDummyData(): HeartRateRecord {
val (init, end) = generateInterval()

val numberSamples = 5
val samples = linspace(init.toInstant().epochSecond, end.toInstant().epochSecond, numberSamples)
.map { instant ->
val samples = linspace(init, end, numberSamples).map {
HeartRateRecord.Sample(
Instant.ofEpochSecond(instant),
it.toInstant(),
Random.nextLong(60, 190)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.health.connect.client.records.SleepStageRecord
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_AWAKE
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_DEEP
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_LIGHT
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_OUT_OF_BED
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_REM
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_SLEEPING
import androidx.health.connect.client.records.SleepStageRecord.Companion.STAGE_TYPE_UNKNOWN
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_AWAKE
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_DEEP
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_LIGHT
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_OUT_OF_BED
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_REM
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_SLEEPING
import androidx.health.connect.client.records.SleepSessionRecord.Companion.STAGE_TYPE_UNKNOWN
import androidx.health.connect.client.records.SleepSessionRecord.Stage
import es.upm.bienestaremocional.R
import es.upm.bienestaremocional.data.healthconnect.types.SleepSessionData
import es.upm.bienestaremocional.ui.component.BasicCard
Expand All @@ -33,6 +33,8 @@ import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import kotlin.random.Random

private const val SLEEP_STAGE_TYPES = 8

/**
* Displays [SleepSessionData]
* @param widthSize: [WindowWidthSizeClass] to modify the component according to the screen
Expand Down Expand Up @@ -69,15 +71,15 @@ fun SleepSessionData.Display(widthSize: WindowWidthSizeClass) {
}

@Composable
fun SleepStageRecord.Display() {
fun Stage.Display() {
Row(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp)
)
{
val intervalLabel = formatDisplayTimeStartEnd(
startTime, startZoneOffset, endTime, endZoneOffset
startTime, null, endTime, null
)

Text(
Expand All @@ -94,7 +96,7 @@ fun SleepStageRecord.Display() {
}

@Composable
fun SleepStageRecord.decode(): String =
fun Stage.decode(): String =
when (stage) {
STAGE_TYPE_UNKNOWN -> stringResource(R.string.unknown)
STAGE_TYPE_AWAKE -> stringResource(R.string.awake)
Expand All @@ -110,25 +112,23 @@ fun SleepStageRecord.decode(): String =
/**
* Generates a random sleep stage for the purpose of populating data.
*/
private fun randomSleepStage() = Random.nextInt(7)
private fun randomSleepStage() = Random.nextInt(SLEEP_STAGE_TYPES)

/**
* Creates a random list of sleep stages that spans the specified [start] to [end] time.
*/
private fun generateSleepStages(start: ZonedDateTime, end: ZonedDateTime):
List<SleepStageRecord> {
val sleepStages = mutableListOf<SleepStageRecord>()
List<Stage> {
val sleepStages = mutableListOf<Stage>()
var stageStart = start
while (stageStart < end) {
val stageEnd = stageStart.plusMinutes(Random.nextLong(30, 120))
val checkedEnd = if (stageEnd > end) end else stageEnd
sleepStages.add(
SleepStageRecord(
Stage(
stage = randomSleepStage(),
startTime = stageStart.toInstant(),
startZoneOffset = stageStart.offset,
endTime = checkedEnd.toInstant(),
endZoneOffset = checkedEnd.offset
)
)
stageStart = checkedEnd
Expand All @@ -145,7 +145,7 @@ private fun generateDummyData(): SleepSessionData {
"Restful sleep"
)

val (bedtime, wakeUp) = generateInterval(upperBound = 14)
val (bedtime, wakeUp) = generateInterval(offsetDays = 1)
val sleepStages = generateSleepStages(bedtime, wakeUp)
return SleepSessionData(
uid = "",
Expand Down
Loading

0 comments on commit 5e62029

Please sign in to comment.