1 /** 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * ``` 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * ``` 10 * 11 * Unless required by applicable law or agreed to in writing, software distributed under the License 12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 * or implied. See the License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.healthconnect.testapps.toolbox.fieldviews 17 18 import android.annotation.SuppressLint 19 import android.content.Context 20 import android.health.connect.datatypes.CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample 21 import android.health.connect.datatypes.ExerciseLap 22 import android.health.connect.datatypes.ExerciseSegment 23 import android.health.connect.datatypes.ExerciseSegmentType 24 import android.health.connect.datatypes.HeartRateRecord.HeartRateSample 25 import android.health.connect.datatypes.PowerRecord.PowerRecordSample 26 import android.health.connect.datatypes.SleepSessionRecord 27 import android.health.connect.datatypes.SpeedRecord.SpeedRecordSample 28 import android.health.connect.datatypes.StepsCadenceRecord.StepsCadenceRecordSample 29 import android.health.connect.datatypes.units.Length 30 import android.health.connect.datatypes.units.Power 31 import android.health.connect.datatypes.units.Velocity 32 import android.widget.LinearLayout 33 import android.widget.TextView 34 import com.android.healthconnect.testapps.toolbox.Constants.INPUT_TYPE_DOUBLE 35 import com.android.healthconnect.testapps.toolbox.Constants.INPUT_TYPE_LONG 36 import com.android.healthconnect.testapps.toolbox.R 37 import com.android.healthconnect.testapps.toolbox.utils.GeneralUtils.Companion.getStaticFieldNamesAndValues 38 import com.google.android.material.floatingactionbutton.FloatingActionButton 39 import java.lang.reflect.ParameterizedType 40 import java.lang.reflect.Type 41 42 @SuppressLint("ViewConstructor") 43 class ListInputField(context: Context, fieldName: String, inputFieldType: ParameterizedType) : 44 InputFieldView(context) { 45 46 data class Row(val context: Context) { 47 val startTime = DateTimePicker(context, "Start Time", true) 48 val endTime = DateTimePicker(context, "End Time") 49 lateinit var dataPointField: InputFieldView 50 } 51 52 private var mLinearLayout: LinearLayout 53 private var mDataTypeClass: Type 54 private var mRowsData: ArrayList<Row> 55 56 init { 57 inflate(context, R.layout.fragment_list_input_view, this) 58 findViewById<TextView>(R.id.field_name).text = fieldName 59 mLinearLayout = findViewById(R.id.list_input_linear_layout) 60 mDataTypeClass = inputFieldType.actualTypeArguments[0] 61 mRowsData = ArrayList() 62 setupAddRowButtonListener() 63 } 64 setupAddRowButtonListenernull65 private fun setupAddRowButtonListener() { 66 val buttonView = findViewById<FloatingActionButton>(R.id.add_row) 67 68 buttonView.setOnClickListener { addRow() } 69 } 70 addRownull71 private fun addRow() { 72 val rowLayout = LinearLayout(context) 73 rowLayout.orientation = VERTICAL 74 75 val row = Row(context) 76 77 rowLayout.addView(row.startTime) 78 val dataPointField: InputFieldView = 79 when (mDataTypeClass) { 80 SpeedRecordSample::class.java -> { 81 EditableTextView(context, "Velocity", INPUT_TYPE_DOUBLE) 82 } 83 HeartRateSample::class.java -> { 84 EditableTextView(context, "Beats per minute", INPUT_TYPE_LONG) 85 } 86 PowerRecordSample::class.java -> { 87 EditableTextView(context, "Power", INPUT_TYPE_DOUBLE) 88 } 89 CyclingPedalingCadenceRecordSample::class.java -> { 90 EditableTextView(context, "Revolutions Per Minute", INPUT_TYPE_DOUBLE) 91 } 92 SleepSessionRecord.Stage::class.java -> { 93 rowLayout.addView(row.endTime) 94 EnumDropDown( 95 context, 96 "Sleep Stage", 97 getStaticFieldNamesAndValues(SleepSessionRecord.StageType::class)) 98 } 99 StepsCadenceRecordSample::class.java -> { 100 EditableTextView(context, "Steps Cadence", INPUT_TYPE_DOUBLE) 101 } 102 ExerciseSegment::class.java -> { 103 rowLayout.addView(row.endTime) 104 EnumDropDown( 105 context, 106 "Segment Type", 107 getStaticFieldNamesAndValues(ExerciseSegmentType::class)) 108 } 109 ExerciseLap::class.java -> { 110 rowLayout.addView(row.endTime) 111 EditableTextView(context, "Length", INPUT_TYPE_DOUBLE) 112 } 113 else -> { 114 return 115 } 116 } 117 row.dataPointField = dataPointField 118 rowLayout.addView(dataPointField) 119 mRowsData.add(row) 120 mLinearLayout.addView(rowLayout, 0) 121 } 122 getFieldValuenull123 override fun getFieldValue(): List<Any> { 124 val samples: ArrayList<Any> = ArrayList() 125 for (row in mRowsData) { 126 val dataPoint = row.dataPointField 127 val instant = row.startTime 128 val dataPointString = dataPoint.getFieldValue().toString() 129 when (mDataTypeClass) { 130 SpeedRecordSample::class.java -> { 131 samples.add( 132 SpeedRecordSample( 133 Velocity.fromMetersPerSecond(dataPointString.toDouble()), 134 instant.getFieldValue())) 135 } 136 HeartRateSample::class.java -> { 137 samples.add(HeartRateSample(dataPointString.toLong(), instant.getFieldValue())) 138 } 139 PowerRecordSample::class.java -> { 140 samples.add( 141 PowerRecordSample( 142 Power.fromWatts(dataPointString.toDouble()), instant.getFieldValue())) 143 } 144 CyclingPedalingCadenceRecordSample::class.java -> { 145 samples.add( 146 CyclingPedalingCadenceRecordSample( 147 dataPointString.toDouble(), instant.getFieldValue())) 148 } 149 StepsCadenceRecordSample::class.java -> { 150 samples.add( 151 StepsCadenceRecordSample( 152 dataPointString.toDouble(), instant.getFieldValue())) 153 } 154 SleepSessionRecord.Stage::class.java -> { 155 samples.add( 156 SleepSessionRecord.Stage( 157 instant.getFieldValue(), 158 row.endTime.getFieldValue(), 159 dataPointString.toInt())) 160 } 161 ExerciseSegment::class.java -> { 162 samples.add( 163 ExerciseSegment.Builder( 164 instant.getFieldValue(), 165 row.endTime.getFieldValue(), 166 dataPointString.toInt()) 167 .build()) 168 } 169 ExerciseLap::class.java -> { 170 samples.add( 171 ExerciseLap.Builder(instant.getFieldValue(), row.endTime.getFieldValue()) 172 .apply { 173 if (dataPointString.isNotEmpty()) { 174 setLength(Length.fromMeters(dataPointString.toDouble())) 175 } 176 } 177 .build()) 178 } 179 } 180 } 181 return samples 182 } 183 isEmptynull184 override fun isEmpty(): Boolean { 185 return getFieldValue().isEmpty() 186 } 187 } 188