diff --git a/wear/build.gradle b/wear/build.gradle index bb1104bd2e..8be2eec29f 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -53,7 +53,7 @@ android { defaultConfig { applicationId "info.nightscout.androidaps" minSdkVersion 23 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 2 versionName "1.0.3" buildConfigField "String", "BUILDVERSION", generateGitBuild() @@ -115,6 +115,12 @@ dependencies { implementation(name: 'wearpreferenceactivity-0.5.0', ext: 'aar') implementation('com.github.lecho:hellocharts-library:1.5.8@aar') + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0-RC' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0-Rc' + implementation 'androidx.core:core-ktx:1.7.0' + implementation "androidx.wear.tiles:tiles:1.0.0" + + implementation "androidx.core:core-ktx:$coreVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" testImplementation "junit:junit:$junit_version" diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index eb12cc4f02..7d14940599 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -8,6 +9,7 @@ + + + + + + + + + diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt new file mode 100644 index 0000000000..25bd71255e --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/tile/ActionSource.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.tile + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interaction.actions.BolusActivity +import info.nightscout.androidaps.interaction.actions.ECarbActivity +import info.nightscout.androidaps.interaction.actions.TempTargetActivity +import info.nightscout.androidaps.interaction.actions.WizardActivity + +data class Action( + val id: Long, + @StringRes val nameRes: Int, + val activityClass: String, + @DrawableRes val iconRes: Int, +) + +object ActionSource { + + fun getActions(): List { + return listOf( + Action( + id = 0, + nameRes = R.string.menu_wizard, + iconRes = R.drawable.ic_calculator_green, + activityClass = WizardActivity::class.java.getName(), + ), + Action( + id = 1, + nameRes = R.string.action_bolus, + iconRes = R.drawable.ic_bolus_carbs, + activityClass = BolusActivity::class.java.getName(), + ), + Action( + id = 2, + nameRes = R.string.action_carbs, + iconRes = R.drawable.ic_carbs_orange, + activityClass = ECarbActivity::class.java.getName(), + ), + Action( + id = 3, + nameRes = R.string.menu_tempt, + iconRes = R.drawable.ic_temptarget_flat, + activityClass = TempTargetActivity::class.java.getName(), + ) + ) + } +} diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/ActionsTileService.kt b/wear/src/main/java/info/nightscout/androidaps/tile/ActionsTileService.kt new file mode 100644 index 0000000000..89771d2754 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/tile/ActionsTileService.kt @@ -0,0 +1,172 @@ +package info.nightscout.androidaps.tile + +import androidx.core.content.ContextCompat + +import androidx.wear.tiles.ActionBuilders +import androidx.wear.tiles.ColorBuilders.argb +import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters +import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.LayoutElementBuilders.Box +import androidx.wear.tiles.LayoutElementBuilders.Column +import androidx.wear.tiles.LayoutElementBuilders.FontStyles +import androidx.wear.tiles.LayoutElementBuilders.Image +import androidx.wear.tiles.LayoutElementBuilders.Layout +import androidx.wear.tiles.LayoutElementBuilders.LayoutElement +import androidx.wear.tiles.LayoutElementBuilders.Row +import androidx.wear.tiles.LayoutElementBuilders.Spacer +import androidx.wear.tiles.LayoutElementBuilders.Text +import androidx.wear.tiles.ModifiersBuilders.Background +import androidx.wear.tiles.ModifiersBuilders.Clickable +import androidx.wear.tiles.ModifiersBuilders.Corner +import androidx.wear.tiles.ModifiersBuilders.Modifiers +import androidx.wear.tiles.ModifiersBuilders.Semantics +import androidx.wear.tiles.RequestBuilders +import androidx.wear.tiles.RequestBuilders.ResourcesRequest +import androidx.wear.tiles.ResourceBuilders.AndroidImageResourceByResId +import androidx.wear.tiles.ResourceBuilders.ImageResource +import androidx.wear.tiles.ResourceBuilders.Resources +import androidx.wear.tiles.TileBuilders.Tile +import androidx.wear.tiles.TileService +import androidx.wear.tiles.TimelineBuilders.Timeline +import androidx.wear.tiles.TimelineBuilders.TimelineEntry + +import com.google.common.util.concurrent.ListenableFuture + +import info.nightscout.androidaps.R + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.guava.future + +private const val RESOURCES_VERSION = "1" +private const val ID_IC_ACTION_PREFIX = "ic_action_" +private const val CIRCLE_SIZE = 75f +private val ICON_SIZE = dp(25f) +private val SPACING_ACTIONS = dp(3f) +private const val BUTTON_COLOR = R.color.gray_850 + +class ActionsTileService : TileService() { + + private val serviceJob = Job() + private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) + + override fun onTileRequest( + requestParams: RequestBuilders.TileRequest + ): ListenableFuture = serviceScope.future { + val actions = ActionSource.getActions().take(4) + Tile.Builder() + .setResourcesVersion(RESOURCES_VERSION) + .setTimeline( + Timeline.Builder().addTimelineEntry( + TimelineEntry.Builder().setLayout( + Layout.Builder().setRoot(layout(actions, requestParams.deviceParameters!!)).build() + ).build() + ).build() + ) + .build() + } + + override fun onResourcesRequest( + requestParams: ResourcesRequest + ): ListenableFuture = serviceScope.future { + val actions = ActionSource.getActions().take(4) + Resources.Builder() + .setVersion(RESOURCES_VERSION) + .apply { + actions.mapNotNull { action -> + addIdToImageMapping( + ID_IC_ACTION_PREFIX + action.id, + ImageResource.Builder() + .setAndroidResourceByResId( + AndroidImageResourceByResId.Builder() + .setResourceId(action.iconRes) + .build() + ) + .build() + ) + } + } + .build() + } + + private fun layout(actions: List, deviceParameters: DeviceParameters): LayoutElement = + Column.Builder() + .addContent( + Row.Builder() + .addContent(action(actions[0], deviceParameters)) + .addContent(Spacer.Builder().setWidth(SPACING_ACTIONS).build()) + .addContent(action(actions[1], deviceParameters)) + .build() + ) + .addContent(Spacer.Builder().setHeight(SPACING_ACTIONS).build()) + .addContent( + Row.Builder() + .addContent(action(actions[2], deviceParameters)) + .addContent(Spacer.Builder().setWidth(SPACING_ACTIONS).build()) + .addContent(action(actions[3], deviceParameters)) + .build() + ) + .build() + + private fun action(action: Action, deviceParameters: DeviceParameters) = Box.Builder() + .setWidth(dp(CIRCLE_SIZE)) + .setHeight(dp(CIRCLE_SIZE)) + .setModifiers( + Modifiers.Builder() + .setBackground( + Background.Builder() + .setColor( + argb(ContextCompat.getColor(baseContext, BUTTON_COLOR)) + ) + .setCorner( + Corner.Builder().setRadius(dp(CIRCLE_SIZE / 2)).build() + ) + .build() + ) + .setSemantics( + Semantics.Builder() + .setContentDescription(resources.getString(action.nameRes)) + .build() + ) + .setClickable( + Clickable.Builder() + .setOnClick( + ActionBuilders.LaunchAction.Builder() + .setAndroidActivity( + ActionBuilders.AndroidActivity.Builder() + .setClassName(action.activityClass) + .setPackageName(this.packageName) + .build() + ) + .build() + ) + .build() + ) + .build() + ) + .addContent( + Column.Builder() + .addContent( + Image.Builder() + .setWidth(ICON_SIZE) + .setHeight(ICON_SIZE) + .setResourceId(ID_IC_ACTION_PREFIX + action.id) + .build() + ).addContent( + Text.Builder() + .setText(resources.getString(action.nameRes)) + .setFontStyle( + FontStyles + .button(deviceParameters) + .setColor( + argb(ContextCompat.getColor(baseContext, R.color.white)) + ) + .build() + ) + .build() + ).build() + ) + .build() + +} diff --git a/wear/src/main/res/drawable-notround-nodpi/action_tile_preview.png b/wear/src/main/res/drawable-notround-nodpi/action_tile_preview.png new file mode 100644 index 0000000000..5cd8cc62f1 Binary files /dev/null and b/wear/src/main/res/drawable-notround-nodpi/action_tile_preview.png differ diff --git a/wear/src/main/res/drawable-round-nodpi/action_tile_preview.png b/wear/src/main/res/drawable-round-nodpi/action_tile_preview.png new file mode 100644 index 0000000000..5cd8cc62f1 Binary files /dev/null and b/wear/src/main/res/drawable-round-nodpi/action_tile_preview.png differ diff --git a/wear/src/main/res/drawable/ic_bolus_carbs.xml b/wear/src/main/res/drawable/ic_bolus_carbs.xml new file mode 100644 index 0000000000..7d076a8014 --- /dev/null +++ b/wear/src/main/res/drawable/ic_bolus_carbs.xml @@ -0,0 +1,11 @@ + + + + diff --git a/wear/src/main/res/drawable/ic_calculator_green.xml b/wear/src/main/res/drawable/ic_calculator_green.xml new file mode 100644 index 0000000000..81d189a8ab --- /dev/null +++ b/wear/src/main/res/drawable/ic_calculator_green.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/wear/src/main/res/drawable/ic_carbs_orange.xml b/wear/src/main/res/drawable/ic_carbs_orange.xml new file mode 100644 index 0000000000..53bb025c54 --- /dev/null +++ b/wear/src/main/res/drawable/ic_carbs_orange.xml @@ -0,0 +1,11 @@ + + + diff --git a/wear/src/main/res/drawable/ic_temptarget_flat.xml b/wear/src/main/res/drawable/ic_temptarget_flat.xml new file mode 100644 index 0000000000..c38ef9770f --- /dev/null +++ b/wear/src/main/res/drawable/ic_temptarget_flat.xml @@ -0,0 +1,12 @@ + + + + diff --git a/wear/src/main/res/values/colors.xml b/wear/src/main/res/values/colors.xml index 0bd7113370..7269a61922 100644 --- a/wear/src/main/res/values/colors.xml +++ b/wear/src/main/res/values/colors.xml @@ -46,6 +46,10 @@ #CF8BFE #FBC02D + + #00FF00 + #77dd77 + @@ -157,13 +161,16 @@ #3E2723 - + #FAFAFA #F5F5F5 #E0E0E0 #9E9E9E - #9E9E9E + #9E9E9E + #616161 + #424242 + #313131 #212121 #333333