diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f07c92..1e41bc2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,13 @@
-Last updated: 2023-11-25
+Last updated: 2024-03-25
+
+- v3.0.2-alpha
+ - Use single animator to help avoid data races/bugs
+
+- v3.0.1-alpha
+ - Use LruCache instead of BitmapCache
+
+- v3.0.0-alpha
+ - Dynamic layouts
- v2.12.1
- Remove heartrate complication prefix style
diff --git a/app/build.gradle b/app/build.gradle
index 818d6dd..2a02e41 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -27,8 +27,8 @@ android {
applicationId "dev.rdnt.m8face"
minSdk 28
targetSdk 33
- versionCode 56
- versionName '2.12.1'
+ versionCode 62
+ versionName '3.0.5'
}
buildFeatures {
@@ -51,6 +51,7 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
versionNameSuffix '-preview'
+ signingConfig signingConfigs.debug
}
release {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4ef19fa..20859cf 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-
-
+ xmlns:tools="http://schemas.android.com/tools">
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
diff --git a/app/src/main/java/dev/rdnt/m8face/BitmapCache.kt b/app/src/main/java/dev/rdnt/m8face/BitmapCache.kt
new file mode 100644
index 0000000..624f911
--- /dev/null
+++ b/app/src/main/java/dev/rdnt/m8face/BitmapCache.kt
@@ -0,0 +1,68 @@
+package dev.rdnt.m8face
+
+import android.graphics.Bitmap
+import android.util.Log
+
+//const val HOURS_BITMAP_KEY = "hours"
+//const val MINUTES_BITMAP_KEY = "minutes"
+//const val SECONDS_BITMAP_KEY = "seconds"
+//const val AMPM_BITMAP_KEY = "ampm"
+//
+//const val TIME_BITMAP_KEY = "time"
+//const val AUX_BITMAP_KEY = "aux"
+
+class BitmapCacheOld {
+ private val entries: MutableMap = mutableMapOf()
+
+ override fun toString(): String {
+ //loop entries
+ var result = ""
+ entries.forEach { (k, v) -> result += "{$k: $v} " }
+ return "(entries= $result)"
+ }
+
+ fun get(k: String, h: String): Bitmap? {
+ return entries[k]?.get(h)
+ }
+
+ fun set(k: String, h: String, b: Bitmap?) {
+ val entry = entries.getOrPut(k) { BitmapCacheEntry() }
+ entry.set(h, b)
+ }
+
+ fun renders(k: String): Int {
+ return entries[k]?.renders ?: 1
+ }
+
+ fun loads(k: String): Int {
+ return entries[k]?.loads ?: 1
+ }
+}
+
+class BitmapCacheEntry {
+ var renders: Int = 1
+ var loads: Int = 1
+ private var hash: String = ""
+ private var bitmap: Bitmap? = null
+
+ override fun toString(): String {
+ return "(renders=$renders, loads=$loads)"
+ }
+
+ fun get(h: String): Bitmap? {
+ loads++
+
+ if (bitmap != null && h == hash) {
+ return bitmap
+ }
+
+ return null
+ }
+
+ fun set(h: String, b: Bitmap?) {
+ renders++
+
+ hash = h
+ bitmap = b
+ }
+}
diff --git a/app/src/main/java/dev/rdnt/m8face/WatchCanvasRenderer.kt b/app/src/main/java/dev/rdnt/m8face/WatchCanvasRenderer.kt
index 3099c7e..a136fe2 100644
--- a/app/src/main/java/dev/rdnt/m8face/WatchCanvasRenderer.kt
+++ b/app/src/main/java/dev/rdnt/m8face/WatchCanvasRenderer.kt
@@ -16,22 +16,33 @@
package dev.rdnt.m8face
import android.animation.AnimatorSet
-import android.animation.Keyframe
import android.animation.ObjectAnimator
-import android.animation.PropertyValuesHolder
import android.content.Context
-import android.graphics.*
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
import android.util.FloatProperty
+import android.util.Log
+import android.util.LruCache
import android.view.SurfaceHolder
import android.view.animation.AnimationUtils
import androidx.annotation.Keep
-import androidx.core.content.ContextCompat.getDrawable
+import androidx.compose.runtime.saveable.autoSaver
+import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.toBitmap
+import androidx.core.graphics.set
import androidx.core.graphics.withRotation
import androidx.core.graphics.withScale
+import androidx.core.graphics.withTranslation
import androidx.wear.watchface.ComplicationSlotsManager
-import androidx.wear.watchface.DrawMode
import androidx.wear.watchface.Renderer
import androidx.wear.watchface.WatchState
import androidx.wear.watchface.style.CurrentUserStyleRepository
@@ -40,24 +51,34 @@ import androidx.wear.watchface.style.UserStyleSetting
import androidx.wear.watchface.style.WatchFaceLayer
import dev.rdnt.m8face.data.watchface.AmbientStyle
import dev.rdnt.m8face.data.watchface.ColorStyle
+import dev.rdnt.m8face.data.watchface.LayoutStyle
import dev.rdnt.m8face.data.watchface.SecondsStyle
import dev.rdnt.m8face.data.watchface.WatchFaceColorPalette.Companion.convertToWatchFaceColorPalette
import dev.rdnt.m8face.data.watchface.WatchFaceData
import dev.rdnt.m8face.utils.AMBIENT_STYLE_SETTING
-import dev.rdnt.m8face.utils.BIG_AMBIENT_SETTING
import dev.rdnt.m8face.utils.COLOR_STYLE_SETTING
import dev.rdnt.m8face.utils.HorizontalComplication
+import dev.rdnt.m8face.utils.HorizontalTextComplication
+import dev.rdnt.m8face.utils.LAYOUT_STYLE_SETTING
import dev.rdnt.m8face.utils.MILITARY_TIME_SETTING
import dev.rdnt.m8face.utils.SECONDS_STYLE_SETTING
import dev.rdnt.m8face.utils.VerticalComplication
import java.time.Duration
+import java.time.Instant
import java.time.ZonedDateTime
+import java.time.temporal.ChronoUnit
+import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sqrt
-import kotlinx.coroutines.*
+import kotlin.system.measureNanoTime
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
-// Default for how long each frame is displayed at expected frame rate.
-private const val DEFAULT_INTERACTIVE_DRAW_MODE_UPDATE_DELAY_MILLIS: Long = 16
+private const val debug = false
+private const val debugTiming = false
/**
* Renders watch face via data in Room database. Also, updates watch face state based on setting
@@ -76,7 +97,7 @@ class WatchCanvasRenderer(
currentUserStyleRepository,
watchState,
canvasType,
- DEFAULT_INTERACTIVE_DRAW_MODE_UPDATE_DELAY_MILLIS,
+ 60000,
clearWithBackgroundTintBeforeRenderingHighlightLayer = false
) {
class AnalogSharedAssets : SharedAssets {
@@ -84,11 +105,12 @@ class WatchCanvasRenderer(
}
}
- private val scope: CoroutineScope =
- CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+ // https://stackoverflow.com/a/32948752
+ private val ambientTransitionMs = context.resources.getInteger(android.R.integer.config_longAnimTime).toLong()
- private val coroutineScope: CoroutineScope =
- CoroutineScope(Dispatchers.Main.immediate)
+ private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+
+ private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main.immediate)
// Represents all data needed to render the watch face. All value defaults are constants. Only
// three values are changeable by the user (color scheme, ticks being rendered, and length of
@@ -99,7 +121,7 @@ class WatchCanvasRenderer(
ambientStyle = AmbientStyle.OUTLINE,
secondsStyle = SecondsStyle.NONE,
militaryTime = true,
- bigAmbient = false,
+ layoutStyle = LayoutStyle.INFO1,
)
// Converts resource ids into Colors and ComplicationDrawable.
@@ -127,15 +149,18 @@ class WatchCanvasRenderer(
}
private val hourPaint = Paint().apply {
+ this.isSubpixelText = true
isAntiAlias = true // make sure text is not anti-aliased even with this on
typeface = context.resources.getFont(R.font.m8stealth57)
- textSize = 112f / 14f * 14f // TODO: 98f/112f
+// textSize = 112f / 14f * 14f // TODO: 98f/112f
+ textSize = 8f
color = watchFaceColors.primaryColor
}
private val ambientHourPaint = Paint().apply {
isAntiAlias = true
- val big = watchFaceData.bigAmbient
+// val big = watchFaceData.bigAmbient
+ var big = false
if (watchFaceData.ambientStyle.id == AmbientStyle.OUTLINE.id) {
typeface = context.resources.getFont(R.font.m8stealth57thin)
@@ -166,13 +191,32 @@ class WatchCanvasRenderer(
private val minutePaint = Paint().apply {
isAntiAlias = true // make sure text is not anti-aliased even with this on
typeface = context.resources.getFont(R.font.m8stealth57)
- textSize = 112f / 14f * 14f // TODO: 98f/112F
+// textSize = 112f / 14f * 14f // TODO: 98f/112F
+ textSize = 8f
color = watchFaceColors.secondaryColor
}
+ private val secondPaint = Paint().apply {
+ isAntiAlias = true // make sure text is not anti-aliased even with this on
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ color = watchFaceColors.tertiaryColor
+ textSize = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> 56f
+ else -> 48f
+ }
+ }
+
+ private val ampmPaint = Paint().apply {
+ isAntiAlias = true // make sure text is not anti-aliased even with this on
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ textSize = 112f / 14f * 5f
+ color = watchFaceColors.tertiaryColor
+ }
+
private val ambientMinutePaint = Paint().apply {
isAntiAlias = true
- val big = watchFaceData.bigAmbient
+// val big = watchFaceData.bigAmbient
+ var big = false
if (watchFaceData.ambientStyle.id == AmbientStyle.OUTLINE.id) {
typeface = context.resources.getFont(R.font.m8stealth57thin)
@@ -220,90 +264,75 @@ class WatchCanvasRenderer(
private var is24Format: Boolean = watchFaceData.militaryTime
- private val ambientTransitionMs = 1000L
private var drawProperties = DrawProperties()
+ private var isHeadless = false
+ private var isAmbient = false
- private val ambientExitAnimator =
- AnimatorSet().apply {
- val linearOutSlow =
- AnimationUtils.loadInterpolator(
- context,
- android.R.interpolator.linear_out_slow_in
- )
- play(
- ObjectAnimator.ofFloat(
- drawProperties,
- DrawProperties.TIME_SCALE,
- drawProperties.timeScale,
- 1.0f
- ).apply {
- duration = ambientTransitionMs
- interpolator = linearOutSlow
- setAutoCancel(false)
- },
+ private val ambientAnimator =
+ ObjectAnimator.ofFloat(drawProperties, DrawProperties.TIME_SCALE, 0f, 1f).apply {
+ interpolator = AnimationUtils.loadInterpolator(
+ context,
+ android.R.interpolator.accelerate_decelerate
)
+ duration = ambientTransitionMs
+ setAutoCancel(true)
}
- private val ambientEnterAnimator =
- AnimatorSet().apply {
- val linearOutSlow =
- AnimationUtils.loadInterpolator(
- context,
- android.R.interpolator.linear_out_slow_in
- )
-
- val keyframes = arrayOf(
- Keyframe.ofFloat(0f, drawProperties.timeScale),
- Keyframe.ofFloat(0.9f, 0f),
- Keyframe.ofFloat(1f, 0f)
- )
+ private lateinit var state: UserStyle
- val propertyValuesHolder = PropertyValuesHolder.ofKeyframe(
- DrawProperties.TIME_SCALE,
- *keyframes
- )
+ private var bitmapsInitialized: Boolean = false
+ private var bitmapsScale: Float = 0f
- play(
- ObjectAnimator.ofPropertyValuesHolder(drawProperties, propertyValuesHolder).apply {
- duration = ambientTransitionMs * 5 / 9
- interpolator = linearOutSlow
- setAutoCancel(false)
- },
- )
- }
+ override suspend fun init() {
+ super.init()
+ }
init {
scope.launch {
currentUserStyleRepository.userStyle.collect { userStyle ->
updateWatchFaceData(userStyle)
+ state = userStyle
}
}
coroutineScope.launch {
- watchState.isAmbient.collect { isAmbient ->
- if (isAmbient!!) { // you call this readable? come on
- ambientExitAnimator.cancel()
- drawProperties.timeScale = 0f
+ watchState.isAmbient.collect { ambient ->
+ isHeadless = watchState.isHeadless
+ isAmbient = ambient!!
+
+ if (!watchState.isHeadless) {
+ if (isAmbient) {
+ ambientAnimator.removeAllListeners()
+ ambientAnimator.cancel()
+ drawProperties.timeScale = 0f
+ interactiveDrawModeUpdateDelayMillis = interactiveFrameDelay
+ } else {
+ interactiveDrawModeUpdateDelayMillis = 16
+
+ ambientAnimator.removeAllListeners()
+ ambientAnimator.cancel()
+ ambientAnimator.doOnEnd { interactiveDrawModeUpdateDelayMillis = interactiveFrameDelay }
+ ambientAnimator.start()
+ }
} else {
- ambientExitAnimator.setupStartValues()
- ambientExitAnimator.start()
+ drawProperties.timeScale = 1f
}
}
}
}
- override suspend fun createSharedAssets(): AnalogSharedAssets {
- return AnalogSharedAssets()
+ private val interactiveFrameDelay: Long
+ get() = when (watchFaceData.secondsStyle.id) {
+ SecondsStyle.NONE.id -> if (shouldDrawSeconds) 1000 else 60000
+ SecondsStyle.DASHES.id -> 16
+ SecondsStyle.DOTS.id -> 16
+ else -> 1000
}
- private fun updateRefreshRate() {
- interactiveDrawModeUpdateDelayMillis = when {
- animating() -> 16 // 60 fps cause animating
- watchFaceData.secondsStyle.id == SecondsStyle.NONE.id -> 60000 // update once a second
- watchFaceData.secondsStyle.id == SecondsStyle.DASHES.id -> 16 // 60 fps
- watchFaceData.secondsStyle.id == SecondsStyle.DOTS.id -> 16 // 60 fps
- else -> 60000 // safe default
- }
+ private val memoryCache: LruCache = LruCache(485)
+
+ override suspend fun createSharedAssets(): AnalogSharedAssets {
+ return AnalogSharedAssets()
}
/*
@@ -318,9 +347,19 @@ class WatchCanvasRenderer(
// Loops through user style and applies new values to watchFaceData.
for (options in userStyle) {
when (options.key.id.toString()) {
+ LAYOUT_STYLE_SETTING -> {
+ val listOption =
+ options.value as UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
+
+ newWatchFaceData = newWatchFaceData.copy(
+ layoutStyle = LayoutStyle.getLayoutStyleConfig(
+ listOption.id.toString()
+ ),
+ )
+ }
+
COLOR_STYLE_SETTING -> {
- val listOption = options.value as
- UserStyleSetting.ListUserStyleSetting.ListOption
+ val listOption = options.value as UserStyleSetting.ListUserStyleSetting.ListOption
newWatchFaceData = newWatchFaceData.copy(
colorStyle = ColorStyle.getColorStyleConfig(
@@ -330,8 +369,7 @@ class WatchCanvasRenderer(
}
AMBIENT_STYLE_SETTING -> {
- val listOption = options.value as
- UserStyleSetting.ListUserStyleSetting.ListOption
+ val listOption = options.value as UserStyleSetting.ListUserStyleSetting.ListOption
newWatchFaceData = newWatchFaceData.copy(
ambientStyle = AmbientStyle.getAmbientStyleConfig(
@@ -341,8 +379,7 @@ class WatchCanvasRenderer(
}
SECONDS_STYLE_SETTING -> {
- val listOption = options.value as
- UserStyleSetting.ListUserStyleSetting.ListOption
+ val listOption = options.value as UserStyleSetting.ListUserStyleSetting.ListOption
newWatchFaceData = newWatchFaceData.copy(
secondsStyle = SecondsStyle.getSecondsStyleConfig(
@@ -352,22 +389,12 @@ class WatchCanvasRenderer(
}
MILITARY_TIME_SETTING -> {
- val booleanValue = options.value as
- UserStyleSetting.BooleanUserStyleSetting.BooleanOption
+ val booleanValue = options.value as UserStyleSetting.BooleanUserStyleSetting.BooleanOption
newWatchFaceData = newWatchFaceData.copy(
militaryTime = booleanValue.value,
)
}
-
- BIG_AMBIENT_SETTING -> {
- val booleanValue = options.value as
- UserStyleSetting.BooleanUserStyleSetting.BooleanOption
-
- newWatchFaceData = newWatchFaceData.copy(
- bigAmbient = booleanValue.value,
- )
- }
}
}
@@ -390,6 +417,14 @@ class WatchCanvasRenderer(
minutePaint.color = watchFaceColors.secondaryColor
ambientMinutePaint.color = watchFaceColors.secondaryColor
+ secondPaint.color = watchFaceColors.tertiaryColor
+ secondPaint.textSize = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> 56f
+ else -> 48f
+ }
+
+ ampmPaint.color = watchFaceColors.tertiaryColor
+
batteryPaint.color = watchFaceColors.tertiaryColor
batteryIconPaint.color = watchFaceColors.tertiaryColor
@@ -397,7 +432,11 @@ class WatchCanvasRenderer(
ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57thin)
ambientHourPaint.textSize = 8F
- if (watchFaceData.bigAmbient) {
+// if (watchFaceData.bigAmbient) {
+// ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57thinbig)
+// ambientHourPaint.textSize = 8F
+// }
+ if (false) {
ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57thinbig)
ambientHourPaint.textSize = 8F
}
@@ -405,34 +444,52 @@ class WatchCanvasRenderer(
ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57thin)
ambientMinutePaint.textSize = 8F
- if (watchFaceData.bigAmbient) {
+// if (watchFaceData.bigAmbient) {
+// ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57thinbig)
+// ambientMinutePaint.textSize = 8F
+// }
+ if (false) {
ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57thinbig)
ambientMinutePaint.textSize = 8F
}
} else if (watchFaceData.ambientStyle.id == AmbientStyle.BOLD_OUTLINE.id) {
ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57thick)
ambientHourPaint.textSize = 8F
- if (watchFaceData.bigAmbient) {
+// if (watchFaceData.bigAmbient) {
+// ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57thickbig)
+// ambientHourPaint.textSize = 8F
+// }
+ if (false) {
ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57thickbig)
ambientHourPaint.textSize = 8F
}
ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57thick)
ambientMinutePaint.textSize = 8F
- if (watchFaceData.bigAmbient) {
+// if (watchFaceData.bigAmbient) {
+// ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57thickbig)
+// ambientMinutePaint.textSize = 8F
+// }
+ if (false) {
ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57thickbig)
ambientMinutePaint.textSize = 8F
}
} else if (watchFaceData.ambientStyle.id == AmbientStyle.FILLED.id) {
ambientHourPaint.typeface = context.resources.getFont(R.font.m8stealth57)
ambientHourPaint.textSize = 112F / 14f * 16f
- if (watchFaceData.bigAmbient) {
+// if (watchFaceData.bigAmbient) {
+// ambientHourPaint.textSize = 112F / 14f * 18f
+// }
+ if (false) {
ambientHourPaint.textSize = 112F / 14f * 18f
}
ambientMinutePaint.typeface = context.resources.getFont(R.font.m8stealth57)
ambientMinutePaint.textSize = 112F / 14f * 16f
- if (watchFaceData.bigAmbient) {
+// if (watchFaceData.bigAmbient) {
+// ambientMinutePaint.textSize = 112F / 14f * 18f
+// }
+ if (false) {
ambientMinutePaint.textSize = 112F / 14f * 18f
}
@@ -440,6 +497,10 @@ class WatchCanvasRenderer(
is24Format = watchFaceData.militaryTime
+ if (!ambientAnimator.isRunning) {
+ interactiveDrawModeUpdateDelayMillis = interactiveFrameDelay
+ }
+
// TODO: update colors for all elements here
// Applies the user chosen complication color scheme changes. ComplicationDrawables for
@@ -454,12 +515,341 @@ class WatchCanvasRenderer(
is HorizontalComplication -> (complication.renderer as HorizontalComplication).tertiaryColor =
watchFaceColors.tertiaryColor
+ is HorizontalTextComplication -> (complication.renderer as HorizontalTextComplication).tertiaryColor =
+ watchFaceColors.tertiaryColor
+
else -> {}
}
}
}
}
+ private fun preloadBitmaps(bounds: Rect, scale: Float) {
+ Log.d("WatchCanvasRenderer", "preloadBitmaps($scale)")
+ bitmapsScale = scale
+
+ val compBmp = Bitmap.createBitmap(
+ bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888
+ )
+ memoryCache.put("comp", compBmp)
+
+ val secsBmp = Bitmap.createBitmap(
+ bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888
+ )
+ memoryCache.put("secs", secsBmp)
+
+ val canvas = Canvas()
+ preloadHourBitmaps(canvas, scale)
+ preloadHourBitmapsOutline(canvas, scale)
+ preloadHourBitmapsOutlineBig(canvas, scale)
+ preloadHourBitmapsBoldOutline(canvas, scale)
+ preloadHourBitmapsBoldOutlineBig(canvas, scale)
+ preloadMinuteBitmaps(canvas, scale)
+ preloadMinuteBitmapsOutline(canvas, scale)
+ preloadMinuteBitmapsOutlineBig(canvas, scale)
+ preloadMinuteBitmapsBoldOutline(canvas, scale)
+ preloadMinuteBitmapsBoldOutlineBig(canvas, scale)
+ preloadSecondBitmaps(canvas, scale)
+ preloadAmPmBitmaps(canvas, scale)
+ }
+
+ private val hourPaintNormal2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.primaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ textSize = 144f
+ }
+
+ private val hourPaint2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.primaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thin)
+ textSize = 8f
+ }
+
+ private val hourPaintBig2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.primaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thinbig)
+ textSize = 8f
+ }
+
+ private val hourPaintBold2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.primaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thick)
+ textSize = 8f
+ }
+
+ private val hourPaintBoldBig2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.primaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thickbig)
+ textSize = 8f
+ }
+
+ private val minutePaintNormal2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.secondaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ textSize = 144f
+ }
+
+ private val minutePaint2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.secondaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thin)
+ textSize = 8f
+ }
+
+ private val minutePaintBig2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.secondaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thinbig)
+ textSize = 8f
+ }
+
+ private val minutePaintBold2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.secondaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thick)
+ textSize = 8f
+ }
+
+ private val minutePaintBoldBig2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.secondaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57thickbig)
+ textSize = 8f
+ }
+
+ private val secondPaintNormal2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.tertiaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ textSize = 56f
+ }
+
+ private val ampmPaint2: Paint
+ get() = Paint(hourPaint).apply {
+ isAntiAlias = false
+
+ color = watchFaceColors.tertiaryColor
+
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ textSize = 40f
+ }
+
+ private fun preloadBitmap(tmpCanvas: Canvas, time: String, paint: Paint): Bitmap {
+ var textBounds = Rect()
+ paint.getTextBounds(time, 0, time.length, textBounds)
+ textBounds = Rect(0, 0, textBounds.width(), textBounds.height())
+
+ val bmp = Bitmap.createBitmap(
+ textBounds.width(), textBounds.height(), Bitmap.Config.ARGB_8888
+ )
+
+ tmpCanvas.setBitmap(bmp)
+
+ tmpCanvas.drawText(
+ time,
+ 0f,
+ textBounds.height().toFloat(),
+ paint,
+ )
+
+ tmpCanvas.setBitmap(null)
+
+ return bmp.asShared()
+ }
+
+ private fun preloadHourBitmaps(canvas: Canvas, scale: Float) {
+ for (i in 0..23) {
+ val hour = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, hour, hourPaintNormal2.apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "hour_normal_$hour"
+
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadHourBitmapsOutline(canvas: Canvas, scale: Float) {
+ for (i in 0..23) {
+ val hour = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, hour, Paint(hourPaint2).apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "hour_outline_$hour"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadHourBitmapsOutlineBig(canvas: Canvas, scale: Float) {
+ for (i in 0..23) {
+ val hour = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, hour, Paint(hourPaintBig2).apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "hour_big_outline_$hour"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadHourBitmapsBoldOutline(canvas: Canvas, scale: Float) {
+ for (i in 0..23) {
+ val hour = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, hour, Paint(hourPaintBold2).apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "hour_bold_outline_$hour"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadHourBitmapsBoldOutlineBig(canvas: Canvas, scale: Float) {
+ for (i in 0..23) {
+ val hour = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, hour, Paint(hourPaintBoldBig2).apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "hour_big_bold_outline_$hour"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadMinuteBitmaps(canvas: Canvas, scale: Float) {
+ for (i in 0..59) {
+ val minute = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, minute, minutePaintNormal2.apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "minute_normal_$minute"
+
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadMinuteBitmapsOutline(canvas: Canvas, scale: Float) {
+ for (i in 0..59) {
+ val minute = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, minute, minutePaint2.apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "minute_outline_$minute"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadMinuteBitmapsOutlineBig(canvas: Canvas, scale: Float) {
+ for (i in 0..59) {
+ val minute = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, minute, minutePaintBig2.apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "minute_big_outline_$minute"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadMinuteBitmapsBoldOutline(canvas: Canvas, scale: Float) {
+ for (i in 0..59) {
+ val minute = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, minute, minutePaintBold2.apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "minute_bold_outline_$minute"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadMinuteBitmapsBoldOutlineBig(canvas: Canvas, scale: Float) {
+ for (i in 0..59) {
+ val minute = i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, minute, minutePaintBoldBig2.apply {
+ textSize *= scale
+ })
+
+ val cacheKey = "minute_big_bold_outline_$minute"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadSecondBitmaps(canvas: Canvas, scale: Float) {
+ for (i in 0..60) {
+ val second = if (i == 60) "M8" else i.toString().padStart(2, '0')
+
+ val bmp = preloadBitmap(canvas, second, secondPaintNormal2.apply {
+ textSize *= scale
+ })
+ val cacheKey = "second_normal_$second"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
+ private fun preloadAmPmBitmaps(canvas: Canvas, scale: Float) {
+ for (text in arrayOf("AM", "PM")) {
+ val bmp = preloadBitmap(canvas, text, ampmPaint2.apply {
+ textSize *= scale
+ })
+ val cacheKey = "ampm_$text"
+ memoryCache.put(cacheKey, bmp)
+ }
+ }
+
override fun onDestroy() {
// Log.d(TAG, "onDestroy()")
scope.cancel("WatchCanvasRenderer scope clear() request")
@@ -467,10 +857,7 @@ class WatchCanvasRenderer(
}
override fun renderHighlightLayer(
- canvas: Canvas,
- bounds: Rect,
- zonedDateTime: ZonedDateTime,
- sharedAssets: AnalogSharedAssets
+ canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime, sharedAssets: AnalogSharedAssets
) {
canvas.drawColor(renderParameters.highlightLayer!!.backgroundTint)
@@ -481,199 +868,378 @@ class WatchCanvasRenderer(
}
}
- override fun render(
- canvas: Canvas,
- bounds: Rect,
- zonedDateTime: ZonedDateTime,
- sharedAssets: AnalogSharedAssets,
- ) {
- updateRefreshRate()
-
- canvas.drawColor(Color.parseColor("#ff000000"))
+ private val scale
+ get() = if (!isHeadless) drawProperties.timeScale else 1f
- if (renderParameters.watchFaceLayers.contains(WatchFaceLayer.BASE)) {
- var hour: Int
- if (is24Format) {
- hour = zonedDateTime.hour
- } else {
- hour = zonedDateTime.hour % 12
- if (hour == 0) {
- hour = 12
- }
- }
-
- if (drawProperties.timeScale == 0f) {
- var hourOffsetX = 0f
- var hourOffsetY = 0f
- var minuteOffsetX = 0f
- var minuteOffsetY = 0f
+ fun interpolate(start: Float, end: Float): Float {
+ return start + easeInOutCubic(scale) * (end - start)
+ }
- when (watchFaceData.ambientStyle.id) {
- AmbientStyle.OUTLINE.id -> {
- if (watchFaceData.bigAmbient) {
- hourOffsetX = -99f
- hourOffsetY = -9f
- minuteOffsetX = -99f
- minuteOffsetY = 135f
+ val timeScale: Float
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.INFO1.id, LayoutStyle.INFO2.id, LayoutStyle.INFO3.id, LayoutStyle.INFO4.id, LayoutStyle.SPORT.id -> {
+ when(watchFaceData.ambientStyle) {
+ AmbientStyle.OUTLINE, AmbientStyle.BOLD_OUTLINE -> {
+ if (isAmbient) {
+ 18f / 18f
} else {
- hourOffsetX = -88f
- hourOffsetY = -8f
- minuteOffsetX = -88f
- minuteOffsetY = 120f
+ interpolate(16f / 18f, 14f / 18f)
}
}
-
- AmbientStyle.BOLD_OUTLINE.id -> {
- if (watchFaceData.bigAmbient) {
- hourOffsetX = -99f
- hourOffsetY = -9f
- minuteOffsetX = -99f
- minuteOffsetY = 135f
+ AmbientStyle.BIG_OUTLINE, AmbientStyle.BIG_BOLD_OUTLINE, AmbientStyle.BIG_FILLED -> {
+ if (isAmbient) {
+ 18f / 18f
} else {
- hourOffsetX = -88f
- hourOffsetY = -8f
- minuteOffsetX = -88f
- minuteOffsetY = 120f
+ interpolate(18f / 18f, 14f / 18f)
}
}
+ AmbientStyle.FILLED -> {
+ if (isAmbient) {
+ 16f / 18f
+ } else {
+ interpolate(16f / 18f, 14f / 18f)
+ }
+ }
+ AmbientStyle.DETAILED -> 14f / 18f
+ }
+ }
- AmbientStyle.FILLED.id -> {
- if (watchFaceData.bigAmbient) {
- hourOffsetX = -99f
- hourOffsetY = -9f
- minuteOffsetX = -99f
- minuteOffsetY = 135f
+ LayoutStyle.FOCUS.id -> {
+ when(watchFaceData.ambientStyle) {
+ AmbientStyle.OUTLINE, AmbientStyle.BOLD_OUTLINE -> {
+ if (isAmbient) {
+ 18f / 18f
} else {
- hourOffsetX = -88f
- hourOffsetY = -8f
- minuteOffsetX = -88f
- minuteOffsetY = 120f
+ 16f / 18f
}
}
+ AmbientStyle.BIG_OUTLINE, AmbientStyle.BIG_BOLD_OUTLINE, AmbientStyle.BIG_FILLED -> {
+ if (isAmbient) {
+ 18f / 18f
+ } else {
+ interpolate(18f / 18f, 16f / 18f)
+ }
+ }
+ AmbientStyle.FILLED-> 16f / 18f
+ AmbientStyle.DETAILED-> 16f / 18f
}
+ }
- drawTime(canvas, bounds, hour, ambientHourPaint, hourOffsetX, hourOffsetY, 0f)
- drawTime(
- canvas,
- bounds,
- zonedDateTime.minute,
- ambientMinutePaint,
- minuteOffsetX,
- minuteOffsetY,
- 0f
- )
- } else {
- when (watchFaceData.secondsStyle.id) {
- SecondsStyle.NONE.id -> {
+ else -> 18f / 18f
+ }
+ val complicationsScale: Float
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.FOCUS.id -> {
+ when(watchFaceData.ambientStyle) {
+ AmbientStyle.BIG_OUTLINE, AmbientStyle.BIG_BOLD_OUTLINE, AmbientStyle.BIG_FILLED -> {
+ interpolate(18f / 16f, 16f / 16f)
+// interpolate(17f / 16f, 16f / 16f)
}
- SecondsStyle.DASHES.id -> {
- drawDashes(canvas, bounds, zonedDateTime)
+ else -> 16f / 16f
+ }
+ }
+
+ else -> {
+ when(watchFaceData.ambientStyle) {
+ AmbientStyle.BIG_OUTLINE, AmbientStyle.BIG_BOLD_OUTLINE, AmbientStyle.BIG_FILLED -> {
+ interpolate(18f / 14f, 14f / 14f)
+// interpolate(16f / 14f, 14f / 14f)
}
- SecondsStyle.DOTS.id -> {
- drawDots(canvas, bounds, zonedDateTime)
+ AmbientStyle.DETAILED -> 14f / 14f
+
+ else -> {
+ interpolate(16f / 14f, 14f / 14f)
+// interpolate(15f / 14f, 14f / 14f)
}
}
+ }
+ }
- val scaleOffset = if (this.watchFaceData.bigAmbient) {
- 18f / 14f - 1f
+ val hourOffsetY: Float
+ get() = when (watchFaceData.ambientStyle) {
+ AmbientStyle.OUTLINE, AmbientStyle.BOLD_OUTLINE -> {
+ if (isAmbient) {
+ -64f
} else {
- 16f / 14f - 1f
+ -72f
}
+ }
- drawTime(canvas, bounds, hour, hourPaint, -77f, -7f, scaleOffset) // Rect(0, 0, 152, 14))
- drawTime(
- canvas,
- bounds,
- zonedDateTime.minute,
- minutePaint,
- -77f,
- 105f,
- scaleOffset
- )//Rect(0, 0, 152, -210))
+ else -> -72f
+ }
+
+ val minuteOffsetY: Float
+ get() = when (watchFaceData.ambientStyle) {
+ AmbientStyle.OUTLINE, AmbientStyle.BOLD_OUTLINE -> {
+ if (isAmbient) {
+ 64f
+ } else {
+ 72f
+ }
}
+ else -> 72f
}
- if (renderParameters.watchFaceLayers.contains(WatchFaceLayer.COMPLICATIONS) &&
- drawProperties.timeScale != 0f
- ) {
- drawComplications(canvas, zonedDateTime)
+ val shouldDrawSeconds: Boolean
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.INFO1.id, LayoutStyle.INFO3.id, LayoutStyle.SPORT.id -> true
+ else -> false
}
- }
- override fun shouldAnimate(): Boolean {
- return ambientEnterAnimator.isRunning || super.shouldAnimate()
- }
+ val secondsOffsetX: Float
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> 94f
+ else -> 129f
+ }
- private fun animating(): Boolean {
- return ambientEnterAnimator.isRunning || ambientExitAnimator.isRunning
- }
+ val secondsOffsetY: Float
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> -32f
+ else -> 0f
+ }
- // ----- All drawing functions -----
- private fun drawComplications(canvas: Canvas, zonedDateTime: ZonedDateTime) {
- val opacity = this.easeInOutCirc(drawProperties.timeScale)
+ val shouldDrawAmPm: Boolean
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> true
+ else -> false
+ }
- for ((_, complication) in complicationSlotsManager.complicationSlots) {
- if (complication.enabled) {
- when (complication.renderer) {
- is VerticalComplication -> (complication.renderer as VerticalComplication).opacity =
- opacity
+ val timeTextStyle: String
+ get() = when(isAmbient) {
+ true -> {
+ when (watchFaceData.ambientStyle.id) {
+ AmbientStyle.OUTLINE.id -> "outline"
+ AmbientStyle.BIG_OUTLINE.id -> "big_outline"
+ AmbientStyle.BOLD_OUTLINE.id -> "bold_outline"
+ AmbientStyle.BIG_BOLD_OUTLINE.id -> "big_bold_outline"
+ else -> "normal"
+ }
+ }
+ false -> "normal"
+ }
- is HorizontalComplication -> (complication.renderer as HorizontalComplication).opacity =
- opacity
+ val timeOffsetX: Float
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> {
+ when(watchFaceData.ambientStyle) {
+ AmbientStyle.DETAILED -> -45f
+ else -> interpolate(0f, -45f)
+ }
+ }
+ else -> 0f
+ }
- else -> {}
+ val complicationsOffsetX: Float
+ get() = when (watchFaceData.layoutStyle.id) {
+ LayoutStyle.SPORT.id -> {
+ when(watchFaceData.ambientStyle) {
+ AmbientStyle.DETAILED -> 0f
+ else -> interpolate(35f, 0f)
}
+ }
+ else -> 0f
+ }
- complication.render(canvas, zonedDateTime, renderParameters)
+ fun getHour(zonedDateTime: ZonedDateTime): Int {
+ if (is24Format) {
+ return zonedDateTime.hour
+ } else {
+ val hour = zonedDateTime.hour % 12
+ if (hour == 0) {
+ return 12
}
+ return hour
}
}
- private fun drawTime(
+ fun getAmPm(zonedDateTime: ZonedDateTime): String {
+ return if (zonedDateTime.hour < 12) {
+ "am"
+ } else {
+ "pm"
+ }
+ }
+
+ override fun render(
canvas: Canvas,
bounds: Rect,
- time: Int,
- paint: Paint,
- offsetX: Float,
- offsetY: Float,
- scaleOffset: Float
+ zonedDateTime: ZonedDateTime,
+ sharedAssets: AnalogSharedAssets,
) {
- // dx:70 dy:98
- val p = Paint(paint)
- p.textSize = p.textSize / 384F * bounds.width()
+ canvas.drawColor(Color.parseColor("#ff000000"))
- var scale = 1f
+// if (!this::state.isInitialized) {
+// return
+// }
- p.isAntiAlias = true
+ // all calculations are done with 384x384 resolution in mind
+ val renderScale = min(bounds.width(), bounds.height()).toFloat() / 384.0f
- scale += (1f - this.easeInOutCirc(drawProperties.timeScale)) * scaleOffset
+ if (!bitmapsInitialized || bitmapsScale != renderScale) {
+ preloadBitmaps(bounds, renderScale)
+ bitmapsInitialized = true
+ }
- canvas.withScale(scale, scale, bounds.exactCenterX(), bounds.exactCenterY()) {
- canvas.drawText(
- time.toString().padStart(2, '0'),
- bounds.exactCenterX() + offsetX / 384F * bounds.width().toFloat(),
- bounds.exactCenterY() + offsetY / 384F * bounds.height().toFloat(),
- p
- )
+ val took = measureNanoTime {
+ canvas.withScale(
+ timeScale,
+ timeScale,
+ bounds.exactCenterX(),
+ bounds.exactCenterY()
+ ) {
+
+ canvas.withTranslation(timeOffsetX * renderScale, 0f) {
+ if (renderParameters.watchFaceLayers.contains(WatchFaceLayer.BASE)) {
+
+ val opacity = interpolate(1f, 1f)
+
+ val hourText = getHour(zonedDateTime).toString().padStart(2, '0')
+ val hourBmp = memoryCache.get("hour_${timeTextStyle}_$hourText")
+
+ canvas.drawBitmap(
+ hourBmp,
+ bounds.exactCenterX() - hourBmp.width / 2f,
+ bounds.exactCenterY() - hourBmp.height / 2f + hourOffsetY * renderScale,
+ Paint().apply { alpha = (opacity * 255).toInt() },
+ )
+
+ val minuteText = zonedDateTime.minute.toString().padStart(2, '0')
+ val minuteBmp = memoryCache.get("minute_${timeTextStyle}_$minuteText")
+
+ canvas.drawBitmap(
+ minuteBmp,
+ bounds.exactCenterX() - minuteBmp.width / 2f,
+ bounds.exactCenterY() - minuteBmp.height / 2f + minuteOffsetY * renderScale,
+ Paint().apply { alpha = (opacity * 255).toInt() },
+ )
+ }
+ }
+ }
+
+ val shouldDrawComplications = renderParameters.watchFaceLayers.contains(WatchFaceLayer.COMPLICATIONS) &&
+ (!isAmbient || watchFaceData.ambientStyle == AmbientStyle.DETAILED)
+ val shouldDrawSecondsAmPm = renderParameters.watchFaceLayers.contains(WatchFaceLayer.BASE)
+
+ if (shouldDrawComplications || shouldDrawSecondsAmPm) {
+ val compBmp = memoryCache.get("comp")
+ compBmp.eraseColor(Color.TRANSPARENT)
+ val compCanvas = Canvas(compBmp)
+
+ val opacity = if (watchFaceData.ambientStyle == AmbientStyle.DETAILED) interpolate(1f, 1f) else interpolate(0f, 1f)
+
+ canvas.withScale(complicationsScale, complicationsScale, bounds.exactCenterX(), bounds.exactCenterY()) {
+ canvas.withTranslation(complicationsOffsetX, 0f) {
+
+ if (shouldDrawSecondsAmPm) {
+ if (shouldDrawSeconds) {
+ val secondText = if (watchFaceData.ambientStyle == AmbientStyle.DETAILED && isAmbient) "M8" else zonedDateTime.second.toString().padStart(2, '0')
+ val cacheKey = "second_normal_$secondText"
+
+ val secondBmp = memoryCache.get(cacheKey)
+
+ val offsetX = secondsOffsetX * renderScale
+ val offsetY = secondsOffsetY * renderScale
+
+ compCanvas.drawBitmap(
+ secondBmp,
+ bounds.exactCenterX() - secondBmp.width / 2 + offsetX,
+ bounds.exactCenterY() - secondBmp.height / 2 + offsetY,
+ Paint(),
+ )
+
+ }
+
+ if (shouldDrawAmPm) {
+ val ampmText = getAmPm(zonedDateTime).uppercase()
+ val cacheKey = "ampm_$ampmText"
+
+ val ampmBmp = memoryCache.get(cacheKey)
+
+ val offsetX = 83f * renderScale
+ val offsetY = 24f * renderScale
+
+ compCanvas.drawBitmap(
+ ampmBmp,
+ bounds.exactCenterX() - ampmBmp.width / 2 + offsetX,
+ bounds.exactCenterY() - ampmBmp.height / 2 + offsetY,
+ Paint(),
+ )
+
+ }
+ }
+
+ if (shouldDrawComplications) {
+ drawComplications(compCanvas, zonedDateTime)
+ }
+
+ canvas.drawBitmap(
+ compBmp,
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ Paint().apply { alpha = (opacity * 255).toInt() },
+ )
+
+ }
+ }
+ }
+
+ if (watchFaceData.secondsStyle != SecondsStyle.NONE) {
+ val secsBmp = memoryCache.get("secs")
+ secsBmp.eraseColor(Color.TRANSPARENT)
+ val secsCanvas = Canvas(secsBmp)
+
+ val opacity = if (watchFaceData.ambientStyle == AmbientStyle.DETAILED) interpolate(1f, 1f) else interpolate(0f, 1f)
+
+ when (watchFaceData.secondsStyle.id) {
+ SecondsStyle.DASHES.id -> {
+ drawDashes(secsCanvas, bounds, zonedDateTime)
+ }
+
+ SecondsStyle.DOTS.id -> {
+ drawDots(secsCanvas, bounds, zonedDateTime)
+ }
+ }
+
+ canvas.drawBitmap(
+ secsBmp,
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ Paint().apply { alpha = (opacity * 255).toInt() },
+ )
+ }
+ }
+
+ if (debugTiming) {
+ Log.d("WatchCanvasRenderer.timing", "${took.toFloat() / 1000000.0}ms")
+ }
+ }
+
+ override fun shouldAnimate(): Boolean {
+ return super.shouldAnimate() || ambientAnimator.isRunning
+ }
+
+ // ----- All drawing functions -----
+ private fun drawComplications(canvas: Canvas, zonedDateTime: ZonedDateTime) {
+ for ((_, complication) in complicationSlotsManager.complicationSlots) {
+ if (complication.enabled) {
+ complication.render(canvas, zonedDateTime, renderParameters)
+ }
}
}
private fun drawDashes(
- canvas: Canvas,
- bounds: Rect,
- zonedDateTime: ZonedDateTime
+ canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime
) {
// Retrieve current time to calculate location/rotation of watch arms.
val nanoOfDate = zonedDateTime.toLocalTime().toNanoOfDay()
val secondsPerSecondHandRotation = Duration.ofMinutes(1).seconds
val secondsRotation =
- (nanoOfDate.toFloat() / 1000000000F).rem(secondsPerSecondHandRotation) * 360.0f /
- secondsPerSecondHandRotation
+ (nanoOfDate.toFloat() / 1000000000F).rem(secondsPerSecondHandRotation) * 360.0f / secondsPerSecondHandRotation
val secondHandSize = 12f * this.easeInOutCirc(this.drawProperties.timeScale)
@@ -681,19 +1247,19 @@ class WatchCanvasRenderer(
secondHandPaint.alpha =
(this.easeInOutCirc(this.drawProperties.timeScale) * secondHandPaint.alpha).toInt()
- val transitionOffset: Float = this.easeInOutCirc(1 - this.drawProperties.timeScale) * 16f
-
- canvas.withRotation(secondsRotation, bounds.exactCenterX(), bounds.exactCenterY()) {
- canvas.drawRect(
- RectF(
- (bounds.width() / 2f - 1.25f / 384F * bounds.width()),
- transitionOffset / 384F * bounds.width(),
- (bounds.width() / 2f + 1.25f / 384F * bounds.width()),
- (secondHandSize + transitionOffset * 2) / 384F * bounds.width(),
- ),
- secondHandPaint,
- )
- }
+ val transitionOffset: Float = this.easeInOutCirc(1 - this.drawProperties.timeScale) * -16f * 0
+
+// canvas.withRotation(secondsRotation, bounds.exactCenterX(), bounds.exactCenterY()) {
+// canvas.drawRect(
+// RectF(
+// (bounds.width() / 2f - 1.25f / 384F * bounds.width()),
+// transitionOffset / 384F * bounds.width(),
+// (bounds.width() / 2f + 1.25f / 384F * bounds.width()),
+// (secondHandSize + transitionOffset * 2) / 384F * bounds.width(),
+// ),
+// secondHandPaint,
+// )
+// }
val maxAlpha = 255f
@@ -709,18 +1275,18 @@ class WatchCanvasRenderer(
if (i.mod(90f) == 0f) { // cardinals
color = watchFaceColors.primaryColor
- maxSize = 18f
- weight = 1.75f
+ maxSize = 12f
+ weight = 2f
minAlpha = maxAlpha / 4f
} else if (i.mod(30f) == 0f) { // intermediates
color = watchFaceColors.secondaryColor
- maxSize = 14f
- weight = 1.75f
+ maxSize = 10f
+ weight = 2f
minAlpha = maxAlpha / 4f
} else {
color = watchFaceColors.tertiaryColor
- maxSize = 10f
- weight = 1.5f
+ maxSize = 8f
+ weight = 1.75f
minAlpha = maxAlpha / 8f
}
@@ -791,8 +1357,7 @@ class WatchCanvasRenderer(
transitionOffset / 384F * bounds.width(),
bounds.exactCenterX() + weight / 384F * bounds.width(),
(size + transitionOffset * 2) / 384F * bounds.width(),
- ),
- outerElementPaint
+ ), outerElementPaint
)
}
@@ -801,17 +1366,14 @@ class WatchCanvasRenderer(
}
private fun drawDots(
- canvas: Canvas,
- bounds: Rect,
- zonedDateTime: ZonedDateTime
+ canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime
) {
// Retrieve current time to calculate location/rotation of watch arms.
val nanoOfDate = zonedDateTime.toLocalTime().toNanoOfDay()
val secondsPerSecondHandRotation = Duration.ofMinutes(1).seconds
val secondsRotation =
- (nanoOfDate.toFloat() / 1000000000F).rem(secondsPerSecondHandRotation) * 360.0f /
- secondsPerSecondHandRotation
+ (nanoOfDate.toFloat() / 1000000000F).rem(secondsPerSecondHandRotation) * 360.0f / secondsPerSecondHandRotation
val div = secondsRotation.div(6f).toInt()
val mod = secondsRotation.mod(6f)
@@ -820,20 +1382,20 @@ class WatchCanvasRenderer(
var centerY = 8f * this.easeInOutCirc(this.drawProperties.timeScale)
val secondHandSize = 3.5f * this.easeInOutCirc(this.drawProperties.timeScale)
- val transitionOffset = this.easeInOutCirc(1 - this.drawProperties.timeScale) * 24f
+ val transitionOffset = this.easeInOutCirc(1 - this.drawProperties.timeScale) * 24f * 0
val secondHandPaint = Paint(this.secondHandPaint)
secondHandPaint.alpha =
(this.easeInOutCirc(this.drawProperties.timeScale) * secondHandPaint.alpha).toInt()
- canvas.withRotation(rotation, bounds.exactCenterX(), bounds.exactCenterY()) {
- canvas.drawCircle(
- bounds.centerX().toFloat(),
- (centerY + transitionOffset),
- secondHandSize / 384F * bounds.width(),
- secondHandPaint
- )
- }
+// canvas.withRotation(rotation, bounds.exactCenterX(), bounds.exactCenterY()) {
+// canvas.drawCircle(
+// bounds.centerX().toFloat(),
+// (centerY + transitionOffset),
+// secondHandSize / 384F * bounds.width(),
+// secondHandPaint
+// )
+// }
val maxAlpha = 255f
@@ -950,20 +1512,30 @@ class WatchCanvasRenderer(
}
}
+ private fun easeInOutCubic(x: Float): Float {
+ return if (x < 0.5f) {
+ 4 * x * x * x
+ } else {
+ 1 - (-2f * x + 2f).pow(3f) / 2
+ }
+ }
+
private class DrawProperties(
- var timeScale: Float = 0f
+ var timeScale: Float = 1f
) {
companion object {
- val TIME_SCALE =
- object : FloatProperty("timeScale") {
- override fun setValue(obj: DrawProperties, value: Float) {
- obj.timeScale = value
+ val TIME_SCALE = object : FloatProperty("timeScale") {
+ override fun setValue(obj: DrawProperties, value: Float) {
+ if (value.isNaN()) {
+ throw IllegalArgumentException("timeScale cannot be NaN")
}
+ obj.timeScale = value
+ }
- override fun get(obj: DrawProperties): Float {
- return obj.timeScale
- }
+ override fun get(obj: DrawProperties): Float {
+ return obj.timeScale
}
+ }
}
}
@@ -971,3 +1543,4 @@ class WatchCanvasRenderer(
private const val TAG = "WatchCanvasRenderer"
}
}
+
diff --git a/app/src/main/java/dev/rdnt/m8face/data/watchface/AmbientStyle.kt b/app/src/main/java/dev/rdnt/m8face/data/watchface/AmbientStyle.kt
index 2f269bc..011ac68 100644
--- a/app/src/main/java/dev/rdnt/m8face/data/watchface/AmbientStyle.kt
+++ b/app/src/main/java/dev/rdnt/m8face/data/watchface/AmbientStyle.kt
@@ -31,25 +31,56 @@ enum class AmbientStyle(
OUTLINE(
id = "outline",
nameResourceId = R.string.outline_ambient_style_name,
- iconResourceId = R.drawable.outline_style_icon,
+// iconResourceId = R.drawable.outline_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
+ ),
+ BIG_OUTLINE(
+ id = "big_outline",
+ nameResourceId = R.string.big_outline_ambient_style_name, // TODO
+// iconResourceId = R.drawable.outline_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
),
BOLD_OUTLINE(
id = "bold_outline",
nameResourceId = R.string.bold_outline_ambient_style_name,
- iconResourceId = R.drawable.bold_outline_style_icon,
+// iconResourceId = R.drawable.bold_outline_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
+ ),
+ BIG_BOLD_OUTLINE(
+ id = "big_bold_outline",
+ nameResourceId = R.string.big_bold_outline_ambient_style_name, // TODO
+// iconResourceId = R.drawable.bold_outline_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
),
FILLED(
id = "filled",
nameResourceId = R.string.filled_ambient_style_name,
- iconResourceId = R.drawable.filled_style_icon,
+// iconResourceId = R.drawable.filled_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
+ ),
+ BIG_FILLED(
+ id = "big_filled",
+ nameResourceId = R.string.big_filled_ambient_style_name,
+// iconResourceId = R.drawable.filled_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
+ ),
+ DETAILED(
+ id = "detailed",
+ nameResourceId = R.string.detailed_ambient_style_name, // TODO
+// iconResourceId = R.drawable.filled_style_icon,
+ iconResourceId = R.drawable.mauve_style_icon, // TODO @rdnt fix icon
);
companion object {
fun getAmbientStyleConfig(id: String): AmbientStyle {
return when (id) {
OUTLINE.id -> OUTLINE
+ BIG_OUTLINE.id -> BIG_OUTLINE
BOLD_OUTLINE.id -> BOLD_OUTLINE
+ BIG_BOLD_OUTLINE.id -> BIG_BOLD_OUTLINE
FILLED.id -> FILLED
+ BIG_FILLED.id -> BIG_FILLED
+ DETAILED.id -> DETAILED
else -> OUTLINE
}
}
diff --git a/app/src/main/java/dev/rdnt/m8face/data/watchface/LayoutStyle.kt b/app/src/main/java/dev/rdnt/m8face/data/watchface/LayoutStyle.kt
new file mode 100644
index 0000000..ee07967
--- /dev/null
+++ b/app/src/main/java/dev/rdnt/m8face/data/watchface/LayoutStyle.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dev.rdnt.m8face.data.watchface
+
+import android.content.Context
+import android.graphics.drawable.Icon
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.wear.watchface.style.UserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting
+import dev.rdnt.m8face.R
+
+enum class LayoutStyle(
+ val id: String,
+ @StringRes val nameResourceId: Int,
+ @DrawableRes val iconResourceId: Int,
+) {
+ // TODO: @rdnt use SVGs or compress the pngs to conserve wire memory for both
+ // these and other styles (so that we don't exceed transfer limitations of
+ // watchface config over BLE)
+ INFO1(
+ id = "info1",
+ nameResourceId = R.string.info1_layout_style_name,
+ iconResourceId = R.drawable.steel_style_icon, // TODO @rdnt fix icon
+ ),
+ INFO2(
+ id = "info2",
+ nameResourceId = R.string.info2_layout_style_name,
+ iconResourceId = R.drawable.steel_style_icon, // TODO @rdnt fix icon
+ ),
+ INFO3(
+ id = "info3",
+ nameResourceId = R.string.info3_layout_style_name,
+ iconResourceId = R.drawable.steel_style_icon, // TODO @rdnt fix icon
+ ),
+ INFO4(
+ id = "info4",
+ nameResourceId = R.string.info4_layout_style_name,
+ iconResourceId = R.drawable.steel_style_icon, // TODO @rdnt fix icon
+ ),
+ SPORT(
+ id = "sport",
+ nameResourceId = R.string.sport_layout_style_name,
+ iconResourceId = R.drawable.steel_style_icon, // TODO @rdnt fix icon
+ ),
+ FOCUS(
+ id = "focus",
+ nameResourceId = R.string.focus_layout_style_name,
+ iconResourceId = R.drawable.steel_style_icon, // TODO @rdnt fix icon
+ );
+
+ companion object {
+ fun getLayoutStyleConfig(id: String): LayoutStyle {
+ return when (id) {
+ INFO1.id -> INFO1
+ INFO2.id -> INFO2
+ INFO3.id -> INFO3
+ INFO4.id -> INFO4
+ SPORT.id -> SPORT
+ FOCUS.id -> FOCUS
+ else -> INFO1
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/dev/rdnt/m8face/data/watchface/WatchFaceData.kt b/app/src/main/java/dev/rdnt/m8face/data/watchface/WatchFaceData.kt
index 62efb91..bc761aa 100644
--- a/app/src/main/java/dev/rdnt/m8face/data/watchface/WatchFaceData.kt
+++ b/app/src/main/java/dev/rdnt/m8face/data/watchface/WatchFaceData.kt
@@ -19,9 +19,9 @@ package dev.rdnt.m8face.data.watchface
* Represents all data needed to render an analog watch face.
*/
data class WatchFaceData(
+ val layoutStyle: LayoutStyle = LayoutStyle.INFO1,
val colorStyle: ColorStyle = ColorStyle.LAST_DANCE,
val ambientStyle: AmbientStyle = AmbientStyle.OUTLINE,
val secondsStyle: SecondsStyle = SecondsStyle.NONE,
val militaryTime: Boolean = true,
- val bigAmbient: Boolean = true,
)
diff --git a/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigActivity.kt b/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigActivity.kt
index 0a03f78..bd5e941 100644
--- a/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigActivity.kt
+++ b/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigActivity.kt
@@ -22,6 +22,7 @@
package dev.rdnt.m8face.editor
+import android.graphics.RectF
import android.os.Bundle
import android.util.Log
import android.view.HapticFeedbackConstants.KEYBOARD_TAP
@@ -29,15 +30,43 @@ import android.view.HapticFeedbackConstants.LONG_PRESS
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.core.LinearEasing
-import androidx.compose.foundation.*
-import androidx.compose.foundation.gestures.*
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PagerDefaults
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.RippleTheme
-import androidx.compose.runtime.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -49,8 +78,6 @@ import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.Black
-import androidx.compose.ui.graphics.Color.Companion.Blue
-import androidx.compose.ui.graphics.Color.Companion.Red
import androidx.compose.ui.graphics.Color.Companion.Transparent
import androidx.compose.ui.graphics.Color.Companion.White
import androidx.compose.ui.graphics.ColorFilter
@@ -73,17 +100,24 @@ import androidx.compose.ui.zIndex
import androidx.core.graphics.ColorUtils
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
-import androidx.wear.compose.material.*
-import androidx.wear.compose.material.ButtonDefaults.outlinedButtonBorder
-import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults
import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
-import androidx.wear.compose.foundation.lazy.AutoCenteringParams
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.PagerDefaults
-import androidx.compose.foundation.pager.PagerState
-import androidx.compose.foundation.pager.rememberPagerState
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.ButtonDefaults.outlinedButtonBorder
+import androidx.wear.compose.material.HorizontalPageIndicator
+import androidx.wear.compose.material.OutlinedButton
+import androidx.wear.compose.material.PageIndicatorState
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.PositionIndicatorAlignment
+import androidx.wear.compose.material.PositionIndicatorState
+import androidx.wear.compose.material.PositionIndicatorVisibility
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.Switch
+import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.ToggleChip
+import androidx.wear.compose.material.ToggleChipDefaults
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.rotaryinput.rotaryWithSnap
import com.google.android.horologist.compose.rotaryinput.toRotaryScrollAdapter
@@ -91,19 +125,10 @@ import dev.chrisbanes.snapper.ExperimentalSnapperApi
import dev.rdnt.m8face.R
import dev.rdnt.m8face.data.watchface.AmbientStyle
import dev.rdnt.m8face.data.watchface.ColorStyle
+import dev.rdnt.m8face.data.watchface.LayoutStyle
import dev.rdnt.m8face.data.watchface.SecondsStyle
import dev.rdnt.m8face.theme.WearAppTheme
-import dev.rdnt.m8face.utils.BOTTOM_COMPLICATION_ID
-import dev.rdnt.m8face.utils.HORIZONTAL_COMPLICATION_HEIGHT
-import dev.rdnt.m8face.utils.HORIZONTAL_COMPLICATION_LEFT_BOUND
-import dev.rdnt.m8face.utils.HORIZONTAL_COMPLICATION_OFFSET
-import dev.rdnt.m8face.utils.HORIZONTAL_COMPLICATION_RIGHT_BOUND
-import dev.rdnt.m8face.utils.LEFT_COMPLICATION_ID
-import dev.rdnt.m8face.utils.RIGHT_COMPLICATION_ID
-import dev.rdnt.m8face.utils.TOP_COMPLICATION_ID
-import dev.rdnt.m8face.utils.VERTICAL_COMPLICATION_OFFSET
-import dev.rdnt.m8face.utils.VERTICAL_COMPLICATION_TOP_BOUND
-import dev.rdnt.m8face.utils.VERTICAL_COMPLICATION_WIDTH
+import dev.rdnt.m8face.utils.*
import kotlinx.coroutines.launch
@Preview()
@@ -152,9 +177,9 @@ fun ScrollableColumn(
}
Scaffold(
- Modifier
- .onPreRotaryScrollEvent { false }
- .fillMaxSize(),
+ Modifier
+ .onPreRotaryScrollEvent { false }
+ .fillMaxSize(),
positionIndicator = {
PositionIndicator(
state = state,
@@ -171,69 +196,69 @@ fun ScrollableColumn(
Box(
modifier = Modifier
- .fillMaxSize()
- .scrollable(
- orientation = Orientation.Vertical,
- state = rememberScrollableState { delta ->
- val value = position.value - delta
-
-
- if (value < 0f) {
- if (position.value != 0f) {
- view.performHapticFeedback(LONG_PRESS)
- }
-
- position.value = 0f
- } else if (value > maxValue) {
- if (position.value != maxValue - 1) {
- view.performHapticFeedback(LONG_PRESS)
+ .fillMaxSize()
+ .scrollable(
+ orientation = Orientation.Vertical,
+ state = rememberScrollableState { delta ->
+ val value = position.value - delta
+
+
+ if (value < 0f) {
+ if (position.value != 0f) {
+ view.performHapticFeedback(LONG_PRESS)
+ }
+
+ position.value = 0f
+ } else if (value > maxValue) {
+ if (position.value != maxValue - 1) {
+ view.performHapticFeedback(LONG_PRESS)
+ }
+
+ position.value = maxValue - 1
+ } else {
+ position.value = value
+ val selected = (value / threshold)
+ .toInt()
+ .coerceAtMost(items - 1)
+ if (selected != selectedItem.value) {
+ selectedItem.value = selected
+ view.performHapticFeedback(KEYBOARD_TAP)
+ }
+ }
+
+ delta
}
-
- position.value = maxValue - 1
- } else {
- position.value = value
- val selected = (value / threshold)
- .toInt()
- .coerceAtMost(items - 1)
- if (selected != selectedItem.value) {
- selectedItem.value = selected
- view.performHapticFeedback(KEYBOARD_TAP)
+ )
+ .onRotaryScrollEvent {
+ val value = position.value + it.verticalScrollPixels
+
+ if (value < 0f) {
+ if (position.value != 0f) {
+ view.performHapticFeedback(LONG_PRESS)
+ }
+
+ position.value = 0f
+ } else if (value > maxValue) {
+ if (position.value != maxValue - 1) {
+ view.performHapticFeedback(LONG_PRESS)
+ }
+
+ position.value = maxValue - 1
+ } else {
+ position.value = value
+ val selected = (value / threshold)
+ .toInt()
+ .coerceAtMost(items - 1)
+ if (selected != selectedItem.value) {
+ selectedItem.value = selected
+ view.performHapticFeedback(KEYBOARD_TAP)
+ }
}
- }
- delta
- }
- )
- .onRotaryScrollEvent {
- val value = position.value + it.verticalScrollPixels
-
- if (value < 0f) {
- if (position.value != 0f) {
- view.performHapticFeedback(LONG_PRESS)
- }
-
- position.value = 0f
- } else if (value > maxValue) {
- if (position.value != maxValue - 1) {
- view.performHapticFeedback(LONG_PRESS)
- }
-
- position.value = maxValue - 1
- } else {
- position.value = value
- val selected = (value / threshold)
- .toInt()
- .coerceAtMost(items - 1)
- if (selected != selectedItem.value) {
- selectedItem.value = selected
- view.performHapticFeedback(KEYBOARD_TAP)
- }
+ true
}
-
- true
- }
- .focusRequester(focusRequester)
- .focusable(),
+ .focusRequester(focusRequester)
+ .focusable(),
)
}
@@ -251,6 +276,10 @@ class WatchFaceConfigActivity : ComponentActivity() {
)
}
+ private val layoutStyles by lazy {
+ enumValues()
+ }
+
private val colorStyles by lazy {
enumValues()
}
@@ -268,6 +297,7 @@ class WatchFaceConfigActivity : ComponentActivity() {
lifecycleScope.launch {
stateHolder
+ layoutStyles
colorStyles
ambientStyles
secondsStyles
@@ -276,6 +306,7 @@ class WatchFaceConfigActivity : ComponentActivity() {
setContent {
WatchfaceConfigApp(
stateHolder,
+ layoutStyles,
colorStyles,
ambientStyles,
secondsStyles,
@@ -288,6 +319,7 @@ class WatchFaceConfigActivity : ComponentActivity() {
@Composable
fun WatchfaceConfigApp(
stateHolder: WatchFaceConfigStateHolder,
+ layoutStyles: Array,
colorStyles: Array,
ambientStyles: Array,
secondsStyles: Array,
@@ -306,6 +338,8 @@ fun WatchfaceConfigApp(
when (val state = uiState) {
is WatchFaceConfigStateHolder.EditWatchFaceUiState.Success -> {
val bitmap = state.userStylesAndPreview.previewImage.asImageBitmap()
+ val layoutIndex =
+ layoutStyles.indexOfFirst { it.id == state.userStylesAndPreview.layoutStyleId }
val colorIndex =
colorStyles.indexOfFirst { it.id == state.userStylesAndPreview.colorStyleId }
val ambientStyleIndex =
@@ -313,35 +347,35 @@ fun WatchfaceConfigApp(
val secondsStyleIndex =
secondsStyles.indexOfFirst { it.id == state.userStylesAndPreview.secondsStyleId }
val militaryTimeEnabled = state.userStylesAndPreview.militaryTime
- val bigAmbientEnabled = state.userStylesAndPreview.bigAmbient
Box(
- Modifier
- .fillMaxSize()
- .zIndex(1f)
- .background(Black)
+ Modifier
+ .fillMaxSize()
+ .zIndex(1f)
+ .background(Black)
) {
ConfigScaffold(
stateHolder,
+ layoutStyles,
colorStyles,
ambientStyles,
secondsStyles,
bitmap,
+ layoutIndex,
colorIndex,
ambientStyleIndex,
secondsStyleIndex,
militaryTimeEnabled,
- bigAmbientEnabled,
)
}
}
else -> {
Box(
- Modifier
- .fillMaxSize()
- .zIndex(2f)
- .background(Black)
+ Modifier
+ .fillMaxSize()
+ .zIndex(2f)
+ .background(Black)
) {
SplashScreen(screenIsRound)
}
@@ -355,22 +389,25 @@ fun WatchfaceConfigApp(
@Composable
fun ConfigScaffold(
stateHolder: WatchFaceConfigStateHolder,
+ layoutStyles: Array,
colorStyles: Array,
ambientStyles: Array,
secondsStyles: Array,
bitmap: ImageBitmap,
+ layoutIndex: Int,
colorIndex: Int,
ambientStyleIndex: Int,
secondsStyleIndex: Int,
militaryTimeEnabled: Boolean,
- bigAmbientEnabled: Boolean,
) {
Log.d(
"Editor",
- "ConfigScaffold($colorIndex, $ambientStyleIndex, $militaryTimeEnabled, $bigAmbientEnabled)"
+ "ConfigScaffold($layoutIndex, $colorIndex, $ambientStyleIndex, $militaryTimeEnabled)"
)
- val pagerState = rememberPagerState { 5 }
+ val pagerState = rememberPagerState(
+ pageCount = { 6 }
+ )
Scaffold(
positionIndicator = {
@@ -387,113 +424,116 @@ fun ConfigScaffold(
HorizontalPageIndicator(
pageIndicatorState = pageIndicatorState,
- Modifier
- .padding(4.dp)
- .zIndex(4f)
+ Modifier
+ .padding(4.dp)
+ .zIndex(4f)
)
},
modifier = Modifier
- .onPreRotaryScrollEvent { false }
- .fillMaxSize()
- .zIndex(1f)
+ .onPreRotaryScrollEvent { false }
+ .fillMaxSize()
+ .zIndex(1f)
) {
val focusRequester0 = remember { FocusRequester() }
val focusRequester1 = remember { FocusRequester() }
val focusRequester2 = remember { FocusRequester() }
+ val focusRequester3 = remember { FocusRequester() }
HorizontalPager(
flingBehavior = PagerDefaults.flingBehavior(state = pagerState),
state = pagerState,
modifier = Modifier
- .onPreRotaryScrollEvent { pagerState.currentPage != 0 && pagerState.currentPage != 1 && pagerState.currentPage != 2 }
- .zIndex(3f), // don't ask
+ .onPreRotaryScrollEvent { pagerState.currentPage != 0 && pagerState.currentPage != 1 && pagerState.currentPage != 2 && pagerState.currentPage != 3 }
+ .zIndex(3f), // don't ask
key = {
it
}
) { page ->
when (page) {
- 0 -> ColorStyleSelect(focusRequester0, stateHolder, colorStyles, colorIndex)
- 1 -> SecondsStyleSelect(focusRequester1, stateHolder, secondsStyles, secondsStyleIndex)
- 2 -> AmbientStyleSelect(focusRequester2, stateHolder, ambientStyles, ambientStyleIndex)
- 3 -> ComplicationPicker(stateHolder)
- 4 -> Options(stateHolder, militaryTimeEnabled, bigAmbientEnabled)
+ 0 -> LayoutStyleSelect(focusRequester0, stateHolder, layoutStyles, layoutIndex)
+ 1 -> ColorStyleSelect(focusRequester1, stateHolder, colorStyles, colorIndex)
+ 2 -> SecondsStyleSelect(focusRequester2, stateHolder, secondsStyles, secondsStyleIndex)
+ 3 -> AmbientStyleSelect(focusRequester3, stateHolder, ambientStyles, ambientStyleIndex)
+ 4 -> ComplicationPicker(stateHolder, layoutIndex)
+ 5 -> Options(stateHolder, militaryTimeEnabled)
}
}
- if (pagerState.currentPage == 2) { // special background for third page (ambient style)
+ if (pagerState.currentPage == 3) { // special background for third page (ambient style)
var id = 0
val current =
ambientStyles.indexOfFirst { it.id == (stateHolder.uiState.value as WatchFaceConfigStateHolder.EditWatchFaceUiState.Success).userStylesAndPreview.ambientStyleId }
- if (current == 0) {
- if (!militaryTimeEnabled && !bigAmbientEnabled) {
- id = R.drawable.preview_ambient_outline
- } else if (militaryTimeEnabled && !bigAmbientEnabled) {
- id = R.drawable.preview_ambient_outline_military
- } else if (!militaryTimeEnabled && bigAmbientEnabled) {
- id = R.drawable.preview_ambient_outline_big
- } else if (militaryTimeEnabled && bigAmbientEnabled) {
- id = R.drawable.preview_ambient_outline_military_big
- }
- } else if (current == 1) {
- if (!militaryTimeEnabled && !bigAmbientEnabled) {
- id = R.drawable.preview_ambient_bold
- } else if (militaryTimeEnabled && !bigAmbientEnabled) {
- id = R.drawable.preview_ambient_bold_military
- } else if (!militaryTimeEnabled && bigAmbientEnabled) {
- id = R.drawable.preview_ambient_bold_big
- } else if (militaryTimeEnabled && bigAmbientEnabled) {
- id = R.drawable.preview_ambient_bold_military_big
- }
- } else if (current == 2) {
- if (!militaryTimeEnabled && !bigAmbientEnabled) {
- id = R.drawable.preview_ambient_filled
- } else if (militaryTimeEnabled && !bigAmbientEnabled) {
- id = R.drawable.preview_ambient_filled_military
- } else if (!militaryTimeEnabled && bigAmbientEnabled) {
- id = R.drawable.preview_ambient_filled_big
- } else if (militaryTimeEnabled && bigAmbientEnabled) {
- id = R.drawable.preview_ambient_filled_military_big
- }
- }
-
- Image(
- painterResource(id = id),
- contentDescription = "Preview",
- colorFilter = ColorFilter.tint(
- colorResource(id = colorStyles[colorIndex].primaryColorId),
- BlendMode.Darken
- ),
- contentScale = ContentScale.Crop,
- modifier = Modifier
- .fillMaxSize()
- .zIndex(1f)
- .clip(TopHalfRectShape)
- .scale(1f)
- )
- Image(
- painterResource(id = id),
- contentDescription = "Preview",
- colorFilter = ColorFilter.tint(
- colorResource(id = colorStyles[colorIndex].secondaryColorId),
- BlendMode.Darken
- ),
- contentScale = ContentScale.Crop,
- modifier = Modifier
- .fillMaxSize()
- .zIndex(1f)
- .clip(BottomHalfRectShape)
- .scale(1f)
- )
+ // TODO: @rdnt fix
+ Preview(bitmap)
+// if (current == 0) {
+// if (!militaryTimeEnabled && !bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_outline
+// } else if (militaryTimeEnabled && !bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_outline_military
+// } else if (!militaryTimeEnabled && bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_outline_big
+// } else if (militaryTimeEnabled && bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_outline_military_big
+// }
+// } else if (current == 1) {
+// if (!militaryTimeEnabled && !bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_bold
+// } else if (militaryTimeEnabled && !bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_bold_military
+// } else if (!militaryTimeEnabled && bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_bold_big
+// } else if (militaryTimeEnabled && bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_bold_military_big
+// }
+// } else if (current == 2) {
+// if (!militaryTimeEnabled && !bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_filled
+// } else if (militaryTimeEnabled && !bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_filled_military
+// } else if (!militaryTimeEnabled && bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_filled_big
+// } else if (militaryTimeEnabled && bigAmbientEnabled) {
+// id = R.drawable.preview_ambient_filled_military_big
+// }
+// }
+// Image(
+// painterResource(id = id),
+// contentDescription = "Preview",
+// colorFilter = ColorFilter.tint(
+// colorResource(id = colorStyles[colorIndex].primaryColorId),
+// BlendMode.Darken
+// ),
+// contentScale = ContentScale.Crop,
+// modifier = Modifier
+// .fillMaxSize()
+// .zIndex(1f)
+// .clip(TopHalfRectShape)
+// .scale(1f)
+// )
+// Image(
+// painterResource(id = id),
+// contentDescription = "Preview",
+// colorFilter = ColorFilter.tint(
+// colorResource(id = colorStyles[colorIndex].secondaryColorId),
+// BlendMode.Darken
+// ),
+// contentScale = ContentScale.Crop,
+// modifier = Modifier
+// .fillMaxSize()
+// .zIndex(1f)
+// .clip(BottomHalfRectShape)
+// .scale(1f)
+// )
} else {
Preview(bitmap)
}
- Overlay(pagerState)
+// Overlay(pagerState) // TODO fix
LaunchedEffect(pagerState.currentPage) {
Log.d("Editor", "LaunchedEffect(${pagerState.currentPage})")
@@ -504,6 +544,8 @@ fun ConfigScaffold(
focusRequester1.requestFocus()
} else if (pagerState.currentPage == 2) {
focusRequester2.requestFocus()
+ } else if (pagerState.currentPage == 3) {
+ focusRequester3.requestFocus()
}
}
}
@@ -522,18 +564,18 @@ fun Overlay(
}
Box(
modifier = Modifier
- .fillMaxSize()
- .alpha(opacity * 0.75f)
- .background(
- brush = Brush.verticalGradient(
- startY = 192f,
- colors = listOf(
- Color(0x00000000),
- Color(0xFF000000),
- )
+ .fillMaxSize()
+ .alpha(opacity * 0.75f)
+ .background(
+ brush = Brush.verticalGradient(
+ startY = 192f,
+ colors = listOf(
+ Color(0x00000000),
+ Color(0xFF000000),
+ )
+ )
)
- )
- .zIndex(2f),
+ .zIndex(2f),
)
}
@@ -562,63 +604,63 @@ fun Overlay(
)
Box(
- Modifier
- .zIndex(2f)
- .fillMaxSize()
- .border(10.dp, Color(ringColor), CircleShape)
+ Modifier
+ .zIndex(2f)
+ .fillMaxSize()
+ .border(10.dp, Color(ringColor), CircleShape)
)
Column(
- Modifier
- .fillMaxSize()
- .padding(10.dp)
- .clip(CircleShape)
- .zIndex(2f)
+ Modifier
+ .fillMaxSize()
+ .padding(10.dp)
+ .clip(CircleShape)
+ .zIndex(2f)
) {
Box(
- Modifier
- .weight(HORIZONTAL_COMPLICATION_OFFSET + HORIZONTAL_COMPLICATION_HEIGHT)
- .fillMaxWidth()
- .alpha(opacity2 * 0.75f)
- .background(Black)
+ Modifier
+ .weight(HORIZONTAL_COMPLICATION_OFFSET + HORIZONTAL_COMPLICATION_HEIGHT)
+ .fillMaxWidth()
+ .alpha(opacity2 * 0.75f)
+ .background(Black)
)
Row(
- Modifier
- .weight(1f - HORIZONTAL_COMPLICATION_OFFSET * 2 - HORIZONTAL_COMPLICATION_HEIGHT * 2 + extend)
- .fillMaxWidth()
+ Modifier
+ .weight(1f - HORIZONTAL_COMPLICATION_OFFSET * 2 - HORIZONTAL_COMPLICATION_HEIGHT * 2 + extend)
+ .fillMaxWidth()
) {
Box(
- Modifier
- .weight(VERTICAL_COMPLICATION_OFFSET + VERTICAL_COMPLICATION_WIDTH)
- .fillMaxHeight()
- .alpha(opacity2 * 0.75f)
- .background(Black)
+ Modifier
+ .weight(VERTICAL_COMPLICATION_OFFSET + VERTICAL_COMPLICATION_WIDTH)
+ .fillMaxHeight()
+ .alpha(opacity2 * 0.75f)
+ .background(Black)
)
Box(
- Modifier
- .weight(1f - VERTICAL_COMPLICATION_OFFSET * 2 - VERTICAL_COMPLICATION_WIDTH * 2 + extend)
- .fillMaxHeight()
- .alpha(opacity * 0.75f)
- .background(Black)
+ Modifier
+ .weight(1f - VERTICAL_COMPLICATION_OFFSET * 2 - VERTICAL_COMPLICATION_WIDTH * 2 + extend)
+ .fillMaxHeight()
+ .alpha(opacity * 0.75f)
+ .background(Black)
)
Box(
- Modifier
- .weight(VERTICAL_COMPLICATION_OFFSET + VERTICAL_COMPLICATION_WIDTH)
- .fillMaxHeight()
- .alpha(opacity2 * 0.75f)
- .background(Black)
+ Modifier
+ .weight(VERTICAL_COMPLICATION_OFFSET + VERTICAL_COMPLICATION_WIDTH)
+ .fillMaxHeight()
+ .alpha(opacity2 * 0.75f)
+ .background(Black)
)
}
Box(
- Modifier
- .weight(HORIZONTAL_COMPLICATION_OFFSET + HORIZONTAL_COMPLICATION_HEIGHT)
- .fillMaxWidth()
- .alpha(opacity2 * 0.75f)
- .background(Black)
+ Modifier
+ .weight(HORIZONTAL_COMPLICATION_OFFSET + HORIZONTAL_COMPLICATION_HEIGHT)
+ .fillMaxWidth()
+ .alpha(opacity2 * 0.75f)
+ .background(Black)
)
}
}
@@ -628,7 +670,6 @@ fun Overlay(
fun Options(
stateHolder: WatchFaceConfigStateHolder,
militaryTime: Boolean,
- bigAmbient: Boolean,
) {
Box(
Modifier
@@ -641,9 +682,9 @@ fun Options(
) {
ToggleChip(
modifier = Modifier
- .fillMaxWidth()
- .padding(12.dp, 0.dp)
- .height((40.dp)),
+ .fillMaxWidth()
+ .padding(12.dp, 0.dp)
+ .height((40.dp)),
checked = militaryTime,
colors = ToggleChipDefaults.toggleChipColors(
checkedStartBackgroundColor = Transparent,
@@ -663,7 +704,7 @@ fun Options(
label = {
Text(
text = "Military Time",
- fontSize = 14.sp,
+ fontSize = 12.sp,
fontWeight = Medium,
fontFamily = Default,
color = White
@@ -671,37 +712,6 @@ fun Options(
},
)
- ToggleChip(
- modifier = Modifier
- .fillMaxWidth()
- .padding(12.dp, 0.dp)
- .height((40.dp)),
- checked = bigAmbient,
- colors = ToggleChipDefaults.toggleChipColors(
- checkedStartBackgroundColor = Transparent,
- checkedEndBackgroundColor = Transparent,
- uncheckedStartBackgroundColor = Transparent,
- uncheckedEndBackgroundColor = Transparent,
- ),
- onCheckedChange = {
- stateHolder.setBigAmbient(it)
- },
- toggleControl = {
- Switch(
- modifier = Modifier.padding(0.dp),
- checked = bigAmbient,
- )
- },
- label = {
- Text(
- text = "Big Ambient",
- fontSize = 14.sp,
- fontWeight = Medium,
- fontFamily = Default,
- color = White
- )
- },
- )
}
}
}
@@ -750,8 +760,8 @@ fun Preview(
bitmap = bitmap,
contentDescription = "Preview",
modifier = Modifier
- .fillMaxSize()
- .zIndex(1f)
+ .fillMaxSize()
+ .zIndex(1f)
)
}
@@ -788,6 +798,41 @@ fun ColorStyleSelect(
}
+}
+
+@Composable
+fun LayoutStyleSelect(
+ focusRequester: FocusRequester,
+ stateHolder: WatchFaceConfigStateHolder,
+ layoutStyles: Array,
+ layoutIndex: Int,
+) {
+ Log.d("Editor", "LayoutStyleSelect($layoutIndex)")
+
+ val layoutIdsSize = remember { layoutStyles.size }
+
+ Box(
+ Modifier
+ .fillMaxSize()
+ ) {
+ ScrollableColumn(
+ focusRequester,
+ layoutIdsSize,
+ 100f,
+ layoutIndex,
+ ) { itemIndex ->
+ val current =
+ layoutStyles.indexOfFirst { it.id == (stateHolder.uiState.value as WatchFaceConfigStateHolder.EditWatchFaceUiState.Success).userStylesAndPreview.colorStyleId }
+ if (current != itemIndex) {
+ Log.d("Editor", "setLayoutStyle(${layoutStyles[itemIndex].id})")
+ stateHolder.setLayoutStyle(layoutStyles[itemIndex].id)
+ }
+ }
+
+ ColorName(stringResource(layoutStyles[layoutIndex].nameResourceId))
+ }
+
+
}
@Composable
@@ -910,10 +955,10 @@ fun ColorPicker(
alignment = Alignment.CenterVertically
),
modifier = Modifier
- .fillMaxSize()
- .focusable()
- .focusRequester(focusRequester)
- .rotaryWithSnap(adapter, focusRequester),
+ .fillMaxSize()
+ .focusable()
+ .focusRequester(focusRequester)
+ .rotaryWithSnap(adapter, focusRequester),
state = state,
autoCentering = AutoCenteringParams(itemIndex = 0, itemOffset = 0),
scalingParams = ScalingLazyColumnDefaults.scalingParams(
@@ -939,136 +984,387 @@ fun ColorPicker(
@Composable
fun ComplicationPicker(
stateHolder: WatchFaceConfigStateHolder,
+ layoutIndex: Int,
) {
Log.d("Editor", "ComplicationPicker()")
- Box (
+ Box(
Modifier
.fillMaxSize()
) {
- Column(
- Modifier
- .fillMaxSize()
- ) {
- Row(
- modifier = Modifier
- .weight(HORIZONTAL_COMPLICATION_OFFSET, true)
- ) {}
- Row(
- modifier = Modifier
- .weight(HORIZONTAL_COMPLICATION_HEIGHT, true)
- ) {
- Box(
- Modifier
- .weight(HORIZONTAL_COMPLICATION_LEFT_BOUND, true)
- .fillMaxHeight()
+
+ when (layoutIndex) {
+ 0, 1, 2, 3 -> {
+ ComplicationButton(
+ stateHolder,
+ HOUR_COMPLICATION_ID,
+ RectF(
+ HOUR_COMPLICATION_LEFT_BOUND + 6f / 384f,
+ HOUR_COMPLICATION_TOP_BOUND + 6f / 384f,
+ HOUR_COMPLICATION_RIGHT_BOUND - 6f / 384f,
+ HOUR_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
+ ),
)
- OutlinedButton(
- onClick = { stateHolder.setComplication(TOP_COMPLICATION_ID) },
- border = outlinedButtonBorder(
- Color(0xFF5c6063),
- borderWidth = 2.dp
+ ComplicationButton(
+ stateHolder,
+ MINUTE_COMPLICATION_ID,
+ RectF(
+ MINUTE_COMPLICATION_LEFT_BOUND + 6f / 384f,
+ MINUTE_COMPLICATION_TOP_BOUND + 6f / 384f,
+ MINUTE_COMPLICATION_RIGHT_BOUND - 6f / 384f,
+ MINUTE_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
),
- modifier = Modifier
- .weight(HORIZONTAL_COMPLICATION_RIGHT_BOUND - HORIZONTAL_COMPLICATION_LEFT_BOUND, true)
- .fillMaxHeight()
- ) {}
- Box(
- Modifier
- .weight(HORIZONTAL_COMPLICATION_LEFT_BOUND, true)
- .fillMaxHeight()
)
}
- Box(
- modifier = Modifier
- .weight(1f - HORIZONTAL_COMPLICATION_OFFSET * 2 - HORIZONTAL_COMPLICATION_HEIGHT * 2, true)
- )
- Row(
- modifier = Modifier
- .weight(HORIZONTAL_COMPLICATION_HEIGHT, true)
- ) {
- Box(
- Modifier
- .weight(HORIZONTAL_COMPLICATION_LEFT_BOUND, true)
- .fillMaxHeight()
+ 4 -> {
+ ComplicationButton(
+ stateHolder,
+ HOUR_COMPLICATION_ID,
+ RectF(
+ HOUR_SPORT_COMPLICATION_LEFT_BOUND + 6f / 384f,
+ HOUR_SPORT_COMPLICATION_TOP_BOUND + 6f / 384f,
+ HOUR_SPORT_COMPLICATION_RIGHT_BOUND - 6f / 384f,
+ HOUR_SPORT_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
+ ),
)
- OutlinedButton(
- onClick = { stateHolder.setComplication(BOTTOM_COMPLICATION_ID) },
- border = outlinedButtonBorder(
- Color(0xFF5c6063),
- borderWidth = 2.dp
+ ComplicationButton(
+ stateHolder,
+ MINUTE_COMPLICATION_ID,
+ RectF(
+ MINUTE_SPORT_COMPLICATION_LEFT_BOUND,
+ MINUTE_SPORT_COMPLICATION_TOP_BOUND + 6f / 384f,
+ MINUTE_SPORT_COMPLICATION_RIGHT_BOUND - 6f / 384f,
+ MINUTE_SPORT_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
+ ),
+ )
+ }
+ 5 -> {
+ ComplicationButton(
+ stateHolder,
+ HOUR_COMPLICATION_ID,
+ RectF(
+ HOUR_FOCUS_COMPLICATION_LEFT_BOUND + 6f / 384f,
+ HOUR_FOCUS_COMPLICATION_TOP_BOUND + 6f / 384f,
+ HOUR_FOCUS_COMPLICATION_RIGHT_BOUND - 6f / 384f,
+ HOUR_FOCUS_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
+ ),
+ )
+ ComplicationButton(
+ stateHolder,
+ MINUTE_COMPLICATION_ID,
+ RectF(
+ MINUTE_FOCUS_COMPLICATION_LEFT_BOUND + 6f / 384f,
+ MINUTE_FOCUS_COMPLICATION_TOP_BOUND + 6f / 384f,
+ MINUTE_FOCUS_COMPLICATION_RIGHT_BOUND - 6f / 384f,
+ MINUTE_FOCUS_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
),
- modifier = Modifier
- .weight(HORIZONTAL_COMPLICATION_RIGHT_BOUND - HORIZONTAL_COMPLICATION_LEFT_BOUND, true)
- .fillMaxHeight()
- ) {}
- Box(
- Modifier
- .weight(HORIZONTAL_COMPLICATION_LEFT_BOUND, true)
- .fillMaxHeight()
)
}
- Row(
- modifier = Modifier
- .weight(HORIZONTAL_COMPLICATION_OFFSET, true)
- ) {}
}
- Column(
- Modifier
- .fillMaxSize()
- ) {
- Box(
- modifier = Modifier
- .weight(VERTICAL_COMPLICATION_TOP_BOUND, true)
- )
+ when (layoutIndex) {
+ 0 -> {
+ ComplicationButton(
+ stateHolder,
+ TOP_COMPLICATION_ID,
+ RectF(
+ TOP_COMPLICATION_LEFT_BOUND,
+ TOP_COMPLICATION_TOP_BOUND,
+ TOP_COMPLICATION_RIGHT_BOUND,
+ TOP_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
- Row(
- modifier = Modifier
- .weight(1f - VERTICAL_COMPLICATION_TOP_BOUND * 2, true)
- ) {
- Box(
- Modifier
- .weight(VERTICAL_COMPLICATION_OFFSET, true)
- .fillMaxHeight()
+ ComplicationButton(
+ stateHolder,
+ BOTTOM_COMPLICATION_ID,
+ RectF(
+ BOTTOM_COMPLICATION_LEFT_BOUND,
+ BOTTOM_COMPLICATION_TOP_BOUND,
+ BOTTOM_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_COMPLICATION_BOTTOM_BOUND,
+ ),
)
- OutlinedButton(
- onClick = { stateHolder.setComplication(LEFT_COMPLICATION_ID) },
- border = outlinedButtonBorder(
- Color(0xFF5c6063),
- borderWidth = 2.dp
+
+ ComplicationButton(
+ stateHolder,
+ LEFT_COMPLICATION_ID,
+ RectF(
+ LEFT_COMPLICATION_LEFT_BOUND,
+ VERTICAL_COMPLICATION_TOP_BOUND,
+ LEFT_COMPLICATION_RIGHT_BOUND,
+ VERTICAL_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+ }
+ 1 -> {
+ ComplicationButton(
+ stateHolder,
+ TOP_COMPLICATION_ID,
+ RectF(
+ TOP_COMPLICATION_LEFT_BOUND,
+ TOP_COMPLICATION_TOP_BOUND,
+ TOP_COMPLICATION_RIGHT_BOUND,
+ TOP_COMPLICATION_BOTTOM_BOUND,
),
- modifier = Modifier
- .weight(VERTICAL_COMPLICATION_WIDTH, true)
- .fillMaxHeight(),
- ) {}
- Box(
- Modifier
- .weight(1f - VERTICAL_COMPLICATION_WIDTH * 2 - VERTICAL_COMPLICATION_OFFSET * 2, true)
- .fillMaxHeight()
)
- OutlinedButton(
- onClick = { stateHolder.setComplication(RIGHT_COMPLICATION_ID) },
+
+ ComplicationButton(
+ stateHolder,
+ BOTTOM_COMPLICATION_ID,
+ RectF(
+ BOTTOM_COMPLICATION_LEFT_BOUND,
+ BOTTOM_COMPLICATION_TOP_BOUND,
+ BOTTOM_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ LEFT_COMPLICATION_ID,
+ RectF(
+ LEFT_COMPLICATION_LEFT_BOUND,
+ VERTICAL_COMPLICATION_TOP_BOUND,
+ LEFT_COMPLICATION_RIGHT_BOUND,
+ VERTICAL_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ RIGHT_COMPLICATION_ID,
+ RectF(
+ RIGHT_COMPLICATION_LEFT_BOUND,
+ VERTICAL_COMPLICATION_TOP_BOUND,
+ RIGHT_COMPLICATION_RIGHT_BOUND,
+ VERTICAL_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+ }
+
+ 2 -> {
+ ComplicationButton(
+ stateHolder,
+ TOP_COMPLICATION_ID,
+ RectF(
+ TOP_COMPLICATION_LEFT_BOUND,
+ TOP_COMPLICATION_TOP_BOUND,
+ TOP_COMPLICATION_RIGHT_BOUND,
+ TOP_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ BOTTOM_COMPLICATION_ID,
+ RectF(
+ BOTTOM_COMPLICATION_LEFT_BOUND,
+ BOTTOM_COMPLICATION_TOP_BOUND,
+ BOTTOM_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ COMPLICATIONS_TOP_LEFT_COMPLICATION_ID,
+ RectF(
+ TOP_LEFT_COMPLICATION_LEFT_BOUND - 3f / 384f,
+ TOP_LEFT_COMPLICATION_TOP_BOUND - 6f / 384f,
+ TOP_LEFT_COMPLICATION_RIGHT_BOUND + 3f / 384f,
+ TOP_LEFT_COMPLICATION_BOTTOM_BOUND + 6f / 384f,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID,
+ RectF(
+ BOTTOM_LEFT_COMPLICATION_LEFT_BOUND - 3f / 384f,
+ BOTTOM_LEFT_COMPLICATION_TOP_BOUND - 6f / 384f,
+ BOTTOM_LEFT_COMPLICATION_RIGHT_BOUND + 3f / 384f,
+ BOTTOM_LEFT_COMPLICATION_BOTTOM_BOUND + 6f / 384f,
+ ),
+ )
+ }
+
+ 3-> {
+ ComplicationButton(
+ stateHolder,
+ TOP_COMPLICATION_ID,
+ RectF(
+ TOP_COMPLICATION_LEFT_BOUND,
+ TOP_COMPLICATION_TOP_BOUND,
+ TOP_COMPLICATION_RIGHT_BOUND,
+ TOP_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ BOTTOM_COMPLICATION_ID,
+ RectF(
+ BOTTOM_COMPLICATION_LEFT_BOUND,
+ BOTTOM_COMPLICATION_TOP_BOUND,
+ BOTTOM_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ COMPLICATIONS_TOP_LEFT_COMPLICATION_ID,
+ RectF(
+ TOP_LEFT_COMPLICATION_LEFT_BOUND - 3f / 384f,
+ TOP_LEFT_COMPLICATION_TOP_BOUND - 6f / 384f,
+ TOP_LEFT_COMPLICATION_RIGHT_BOUND + 3f / 384f,
+ TOP_LEFT_COMPLICATION_BOTTOM_BOUND + 6f / 384f,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID,
+ RectF(
+ BOTTOM_LEFT_COMPLICATION_LEFT_BOUND - 3f / 384f,
+ BOTTOM_LEFT_COMPLICATION_TOP_BOUND - 6f / 384f,
+ BOTTOM_LEFT_COMPLICATION_RIGHT_BOUND + 3f / 384f,
+ BOTTOM_LEFT_COMPLICATION_BOTTOM_BOUND + 6f / 384f,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID,
+ RectF(
+ TOP_RIGHT_COMPLICATION_LEFT_BOUND - 3f / 384f,
+ TOP_RIGHT_COMPLICATION_TOP_BOUND - 6f / 384f,
+ TOP_RIGHT_COMPLICATION_RIGHT_BOUND + 3f / 384f,
+ TOP_RIGHT_COMPLICATION_BOTTOM_BOUND + 6f / 384f,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID,
+ RectF(
+ BOTTOM_RIGHT_COMPLICATION_LEFT_BOUND - 3f / 384f,
+ BOTTOM_RIGHT_COMPLICATION_TOP_BOUND - 6f / 384f,
+ BOTTOM_RIGHT_COMPLICATION_RIGHT_BOUND + 3f / 384f,
+ BOTTOM_RIGHT_COMPLICATION_BOTTOM_BOUND + 6f / 384f,
+ ),
+ )
+ }
+
+ 4 -> {
+ ComplicationButton(
+ stateHolder,
+ TOP_COMPLICATION_ID,
+ RectF(
+ TOP_COMPLICATION_LEFT_BOUND,
+ TOP_COMPLICATION_TOP_BOUND,
+ TOP_COMPLICATION_RIGHT_BOUND,
+ TOP_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ BOTTOM_COMPLICATION_ID,
+ RectF(
+ BOTTOM_COMPLICATION_LEFT_BOUND,
+ BOTTOM_COMPLICATION_TOP_BOUND,
+ BOTTOM_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ RIGHT_TEXT_COMPLICATION_ID,
+ RectF(
+ RIGHT_TEXT_COMPLICATION_LEFT_BOUND,
+ RIGHT_TEXT_COMPLICATION_TOP_BOUND,
+ RIGHT_TEXT_COMPLICATION_RIGHT_BOUND,
+ RIGHT_TEXT_COMPLICATION_BOTTOM_BOUND,
+ ),
+ )
+ }
+
+ 5 -> {
+ ComplicationButton(
+ stateHolder,
+ FOCUS_LEFT_ICON_COMPLICATION_ID,
+ RectF(
+ LEFT_ICON_COMPLICATION_LEFT_BOUND - 9f / 384f,
+ LEFT_ICON_COMPLICATION_TOP_BOUND + 6f / 384f,
+ LEFT_ICON_COMPLICATION_RIGHT_BOUND + 9f / 384f,
+ LEFT_ICON_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
+ ),
+ )
+
+ ComplicationButton(
+ stateHolder,
+ FOCUS_RIGHT_ICON_COMPLICATION_ID,
+ RectF(
+ RIGHT_ICON_COMPLICATION_LEFT_BOUND - 9f / 384f,
+ RIGHT_ICON_COMPLICATION_TOP_BOUND + 6f / 384f,
+ RIGHT_ICON_COMPLICATION_RIGHT_BOUND + 9f / 384f,
+ RIGHT_ICON_COMPLICATION_BOTTOM_BOUND - 6f / 384f,
+ ),
+ )
+ }
+ }
+
+
+ }
+}
+
+@Composable
+fun ComplicationButton(
+ stateHolder: WatchFaceConfigStateHolder,
+ id: Int,
+ bounds: RectF,
+// left: Float, top: Float, right: Float, bottom: Float
+) {
+// var left= bounds.left - 6f / 384f
+// var right= bounds.right + 6f / 384f
+// var top= bounds.top - 6f / 384f
+// var bottom= bounds.bottom + 6f / 384f
+
+ var left= bounds.left
+ var right= bounds.right
+ var top= bounds.top
+ var bottom= bounds.bottom
+
+// Log.d("Editor", "ComplicationButton(${left}, ${top}, ${right}, ${bottom})")
+
+ Row(Modifier.fillMaxSize()) {
+ Box(Modifier.weight(left, true))
+ Column(
+ Modifier
+ .fillMaxSize()
+ .weight(right - left, true)
+ ) {
+ Box(Modifier.weight(top, true))
+
+ OutlinedButton(
+ onClick = { stateHolder.setComplication(id) },
border = outlinedButtonBorder(
Color(0xFF5c6063),
borderWidth = 2.dp
),
+// shape = RoundedCornerShape(16.dp),
modifier = Modifier
- .weight(VERTICAL_COMPLICATION_WIDTH, true)
- .fillMaxHeight(),
+ .weight(bottom - top, true)
+// .background(Color.Blue)
+ .fillMaxSize(),
) {}
- Box(
- Modifier
- .weight(VERTICAL_COMPLICATION_OFFSET, true)
- .fillMaxHeight()
- )
- }
- Box(
- modifier = Modifier
- .weight(VERTICAL_COMPLICATION_TOP_BOUND, true)
- )
+ Box(Modifier.weight(1f - bottom, true))
}
+ Box(Modifier.weight(1f - right, true))
}
}
@@ -1087,11 +1383,11 @@ fun ColorName(name: String) {
text = name,
textAlign = TextAlign.Center,
modifier = Modifier
- .weight(4f)
- .height(20.dp)
- .clip(RoundedCornerShape(10.dp))
- .background(Color(0xFF202124))
- .padding(12.dp, 1.dp),
+ .weight(4f)
+ .height(20.dp)
+ .clip(RoundedCornerShape(10.dp))
+ .background(Color(0xFF202124))
+ .padding(12.dp, 1.dp),
fontSize = 12.sp,
fontWeight = Medium,
fontFamily = Default,
@@ -1126,10 +1422,10 @@ fun Label(label: String) {
text = label,
textAlign = TextAlign.Center,
modifier = Modifier
- .weight(4f)
- .clip(RoundedCornerShape(10.dp))
- .background(Color(0xFF000000))
- .padding(12.dp, 1.dp),
+ .weight(4f)
+ .clip(RoundedCornerShape(10.dp))
+ .background(Color(0xFF000000))
+ .padding(12.dp, 1.dp),
fontSize = 12.sp,
fontWeight = Medium,
fontFamily = Default,
@@ -1147,10 +1443,10 @@ fun ColorItem() {
Box(
modifier = Modifier
- .padding(0.dp, 0.dp)
- .height(48.dp)
- .width(0.dp)
- .alpha(0.0F)
+ .padding(0.dp, 0.dp)
+ .height(48.dp)
+ .width(0.dp)
+ .alpha(0.0F)
)
}
@@ -1160,12 +1456,12 @@ fun DebugColorItem(colorId: Int?) {
Box(
modifier = Modifier
- .padding(0.dp, 0.dp)
- .height(48.dp)
- .fillMaxWidth()
- .alpha(0.0F)
+ .padding(0.dp, 0.dp)
+ .height(48.dp)
+ .fillMaxWidth()
+ .alpha(0.0F)
// .alpha(0.2F)
- .background(colorId?.let { colorResource(colorId) } ?: Black)
+ .background(colorId?.let { colorResource(colorId) } ?: Black)
)
}
diff --git a/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigStateHolder.kt b/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigStateHolder.kt
index 580185d..31ec2f7 100644
--- a/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigStateHolder.kt
+++ b/app/src/main/java/dev/rdnt/m8face/editor/WatchFaceConfigStateHolder.kt
@@ -60,11 +60,11 @@ class WatchFaceConfigStateHolder(
private lateinit var editorSession: EditorSession
// Keys from Watch Face Data Structure
+ private lateinit var layoutStyleKey: UserStyleSetting.ComplicationSlotsUserStyleSetting
private lateinit var colorStyleKey: UserStyleSetting.ListUserStyleSetting
private lateinit var ambientStyleKey: UserStyleSetting.ListUserStyleSetting
private lateinit var secondsStyleKey: UserStyleSetting.ListUserStyleSetting
private lateinit var militaryTimeKey: UserStyleSetting.BooleanUserStyleSetting
- private lateinit var bigAmbientKey: UserStyleSetting.BooleanUserStyleSetting
val uiState: StateFlow =
flow {
@@ -99,6 +99,10 @@ class WatchFaceConfigStateHolder(
// Loops through user styles and retrieves user editable styles.
for (setting in userStyleSchema.userStyleSettings) {
when (setting.id.toString()) {
+ LAYOUT_STYLE_SETTING -> {
+ layoutStyleKey = setting as UserStyleSetting.ComplicationSlotsUserStyleSetting
+ }
+
COLOR_STYLE_SETTING -> {
colorStyleKey = setting as UserStyleSetting.ListUserStyleSetting
}
@@ -114,10 +118,6 @@ class WatchFaceConfigStateHolder(
MILITARY_TIME_SETTING -> {
militaryTimeKey = setting as UserStyleSetting.BooleanUserStyleSetting
}
-
- BIG_AMBIENT_SETTING -> {
- bigAmbientKey = setting as UserStyleSetting.BooleanUserStyleSetting
- }
}
}
}
@@ -132,6 +132,8 @@ class WatchFaceConfigStateHolder(
Log.d(TAG, "createWatchFacePreview()")
+ // actual watch uses this date and not 09:30:36. changed to 22 instead of 10
+ // for military time to be visible
val instant = LocalDateTime.parse("2020-10-10T22:09:36")
.atZone(ZoneId.of("UTC"))
.toInstant()
@@ -145,6 +147,9 @@ class WatchFaceConfigStateHolder(
complicationsPreviewData
)
+ val layoutStyle =
+ userStyle[layoutStyleKey] as UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
+
val colorStyle =
userStyle[colorStyleKey] as UserStyleSetting.ListUserStyleSetting.ListOption
@@ -157,26 +162,23 @@ class WatchFaceConfigStateHolder(
val militaryTime =
userStyle[militaryTimeKey] as UserStyleSetting.BooleanUserStyleSetting.BooleanOption
- val bigAmbient =
- userStyle[bigAmbientKey] as UserStyleSetting.BooleanUserStyleSetting.BooleanOption
-
return UserStylesAndPreview(
+ layoutStyleId = layoutStyle.id.toString(),
colorStyleId = colorStyle.id.toString(),
ambientStyleId = ambientStyle.id.toString(),
secondsStyleId = secondsStyle.id.toString(),
militaryTime = militaryTime.value,
- bigAmbient = bigAmbient.value,
previewImage = bitmap,
)
}
- fun setComplication(complicationLocation: Int) {
+ fun setComplication(complicationId: Int) {
if (launchInProgress) {
return
}
launchInProgress = true
- val complicationSlotId = when (complicationLocation) {
+ val complicationSlotId = when (complicationId) {
LEFT_COMPLICATION_ID -> {
LEFT_COMPLICATION_ID
}
@@ -193,6 +195,42 @@ class WatchFaceConfigStateHolder(
BOTTOM_COMPLICATION_ID
}
+ COMPLICATIONS_TOP_LEFT_COMPLICATION_ID -> {
+ COMPLICATIONS_TOP_LEFT_COMPLICATION_ID
+ }
+
+ COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID -> {
+ COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID
+ }
+
+ COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID -> {
+ COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID
+ }
+
+ COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID -> {
+ COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID
+ }
+
+ FOCUS_LEFT_ICON_COMPLICATION_ID -> {
+ FOCUS_LEFT_ICON_COMPLICATION_ID
+ }
+
+ FOCUS_RIGHT_ICON_COMPLICATION_ID -> {
+ FOCUS_RIGHT_ICON_COMPLICATION_ID
+ }
+
+ RIGHT_TEXT_COMPLICATION_ID -> {
+ RIGHT_TEXT_COMPLICATION_ID
+ }
+
+ HOUR_COMPLICATION_ID -> {
+ HOUR_COMPLICATION_ID
+ }
+
+ MINUTE_COMPLICATION_ID -> {
+ MINUTE_COMPLICATION_ID
+ }
+
else -> {
launchInProgress = false
return
@@ -208,6 +246,28 @@ class WatchFaceConfigStateHolder(
)
}
+ fun setLayoutStyle(layoutStyleId: String) {
+ val userStyleSettingList = editorSession.userStyleSchema.userStyleSettings
+ // TODO @rdnt editorSession.userStyleSchema.rootUserStyleSettings
+
+ // Loops over all UserStyleSettings (basically the keys in the map) to find the setting for
+ // the color style (which contains all the possible options for that style setting).
+ for (userStyleSetting in userStyleSettingList) {
+ if (userStyleSetting.id == UserStyleSetting.Id(LAYOUT_STYLE_SETTING)) {
+ val layoutUserStyleSetting =
+ userStyleSetting as UserStyleSetting.ComplicationSlotsUserStyleSetting
+
+ // Loops over the UserStyleSetting.Option colors (all possible values for the key)
+ // to find the matching option, and if it exists, sets it as the color style.
+ for (layoutOptions in layoutUserStyleSetting.options) {
+ if (layoutOptions.id.toString() == layoutStyleId) {
+ setUserStyleOption(layoutStyleKey, layoutOptions)
+ }
+ }
+ }
+ }
+ }
+
fun setColorStyle(colorStyleId: String) {
val userStyleSettingList = editorSession.userStyleSchema.userStyleSettings
@@ -278,13 +338,6 @@ class WatchFaceConfigStateHolder(
)
}
- fun setBigAmbient(enabled: Boolean) {
- setUserStyleOption(
- bigAmbientKey,
- UserStyleSetting.BooleanUserStyleSetting.BooleanOption.from(enabled)
- )
- }
-
// Saves User Style Option change back to the back to the EditorSession.
// Note: The UI widgets in the Activity that can trigger this method (through the 'set' methods)
// will only be enabled after the EditorSession has been initialized.
@@ -311,12 +364,12 @@ class WatchFaceConfigStateHolder(
}
data class UserStylesAndPreview(
+ val layoutStyleId: String,
val colorStyleId: String,
val ambientStyleId: String,
val secondsStyleId: String,
val militaryTime: Boolean,
- val bigAmbient: Boolean,
- val previewImage: Bitmap
+ val previewImage: Bitmap,
)
companion object {
diff --git a/app/src/main/java/dev/rdnt/m8face/utils/ComplicationUtils.kt b/app/src/main/java/dev/rdnt/m8face/utils/ComplicationUtils.kt
index af24df4..bf097f5 100644
--- a/app/src/main/java/dev/rdnt/m8face/utils/ComplicationUtils.kt
+++ b/app/src/main/java/dev/rdnt/m8face/utils/ComplicationUtils.kt
@@ -19,33 +19,20 @@ import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
import android.graphics.Rect
import android.graphics.RectF
import android.util.Log
-import androidx.annotation.Keep
-import androidx.core.content.ContextCompat
-import androidx.core.graphics.drawable.toBitmap
-import androidx.wear.watchface.CanvasComplication
+import android.util.LruCache
import androidx.wear.watchface.CanvasComplicationFactory
import androidx.wear.watchface.ComplicationSlot
import androidx.wear.watchface.ComplicationSlotsManager
-import androidx.wear.watchface.RenderParameters
import androidx.wear.watchface.complications.ComplicationSlotBounds
import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
import androidx.wear.watchface.complications.SystemDataSources
import androidx.wear.watchface.complications.data.ComplicationData
import androidx.wear.watchface.complications.data.ComplicationType
-import androidx.wear.watchface.complications.data.MonochromaticImageComplicationData
-import androidx.wear.watchface.complications.data.NoDataComplicationData
-import androidx.wear.watchface.complications.data.ShortTextComplicationData
-import androidx.wear.watchface.complications.data.SmallImageComplicationData
import androidx.wear.watchface.style.CurrentUserStyleRepository
import dev.rdnt.m8face.R
-import java.time.Instant
-import java.time.ZonedDateTime
// Information needed for complications.
// Creates bounds for the locations of both right and left complications. (This is the
@@ -55,37 +42,45 @@ const val VERTICAL_COMPLICATION_TOP_BOUND = 0.328125f // 126px / 384
const val VERTICAL_COMPLICATION_BOTTOM_BOUND = 1f - VERTICAL_COMPLICATION_TOP_BOUND
// offset: 18px, width: 78px (canvas 384x384)
-const val VERTICAL_COMPLICATION_OFFSET = 24f / 384f
+const val VERTICAL_COMPLICATION_OFFSET = 21f / 384f
const val VERTICAL_COMPLICATION_WIDTH = 78f / 384f
//const val VERTICAL_COMPLICATION_HEIGHT = VERTICAL_COMPLICATION_BOTTOM_BOUND - VERTICAL_COMPLICATION_TOP_BOUND
// 0.03125
-private const val LEFT_COMPLICATION_LEFT_BOUND = VERTICAL_COMPLICATION_OFFSET
-private const val LEFT_COMPLICATION_RIGHT_BOUND =
+ const val LEFT_COMPLICATION_LEFT_BOUND = VERTICAL_COMPLICATION_OFFSET
+ const val LEFT_COMPLICATION_RIGHT_BOUND =
VERTICAL_COMPLICATION_OFFSET + VERTICAL_COMPLICATION_WIDTH
+const val LEFT_COMPLICATION_TOP_BOUND = VERTICAL_COMPLICATION_TOP_BOUND
+const val LEFT_COMPLICATION_BOTTOM_BOUND = VERTICAL_COMPLICATION_BOTTOM_BOUND
-private const val RIGHT_COMPLICATION_LEFT_BOUND =
+ const val RIGHT_COMPLICATION_LEFT_BOUND =
1f - VERTICAL_COMPLICATION_OFFSET - VERTICAL_COMPLICATION_WIDTH
-private const val RIGHT_COMPLICATION_RIGHT_BOUND = 1f - VERTICAL_COMPLICATION_OFFSET
+ const val RIGHT_COMPLICATION_RIGHT_BOUND = 1f - VERTICAL_COMPLICATION_OFFSET
+const val RIGHT_COMPLICATION_TOP_BOUND = VERTICAL_COMPLICATION_TOP_BOUND
+const val RIGHT_COMPLICATION_BOTTOM_BOUND = VERTICAL_COMPLICATION_BOTTOM_BOUND
// Both left and right complications use the same top and bottom bounds.
-const val HORIZONTAL_COMPLICATION_LEFT_BOUND = 102f / 384f
+const val HORIZONTAL_COMPLICATION_LEFT_BOUND = 99f / 384f
const val HORIZONTAL_COMPLICATION_RIGHT_BOUND = 1f - HORIZONTAL_COMPLICATION_LEFT_BOUND
// offset: 18px, height: 51px (canvas 384x384)
-const val HORIZONTAL_COMPLICATION_OFFSET = 24f / 384f
+const val HORIZONTAL_COMPLICATION_OFFSET = 27f / 384f
const val HORIZONTAL_COMPLICATION_HEIGHT = 48f / 384f
//const val HORIZONTAL_COMPLICATION_WIDTH = HORIZONTAL_COMPLICATION_RIGHT_BOUND - HORIZONTAL_COMPLICATION_LEFT_BOUND
-private const val TOP_COMPLICATION_TOP_BOUND = HORIZONTAL_COMPLICATION_OFFSET
-private const val TOP_COMPLICATION_BOTTOM_BOUND =
+ const val TOP_COMPLICATION_TOP_BOUND = HORIZONTAL_COMPLICATION_OFFSET
+ const val TOP_COMPLICATION_BOTTOM_BOUND =
HORIZONTAL_COMPLICATION_OFFSET + HORIZONTAL_COMPLICATION_HEIGHT
+const val TOP_COMPLICATION_LEFT_BOUND = HORIZONTAL_COMPLICATION_LEFT_BOUND
+const val TOP_COMPLICATION_RIGHT_BOUND = HORIZONTAL_COMPLICATION_RIGHT_BOUND
-private const val BOTTOM_COMPLICATION_TOP_BOUND =
+ const val BOTTOM_COMPLICATION_TOP_BOUND =
1f - HORIZONTAL_COMPLICATION_OFFSET - HORIZONTAL_COMPLICATION_HEIGHT
-private const val BOTTOM_COMPLICATION_BOTTOM_BOUND = 1f - HORIZONTAL_COMPLICATION_OFFSET
+ const val BOTTOM_COMPLICATION_BOTTOM_BOUND = 1f - HORIZONTAL_COMPLICATION_OFFSET
+const val BOTTOM_COMPLICATION_LEFT_BOUND = HORIZONTAL_COMPLICATION_LEFT_BOUND
+const val BOTTOM_COMPLICATION_RIGHT_BOUND = HORIZONTAL_COMPLICATION_RIGHT_BOUND
// Unique IDs for each complication. The settings activity that supports allowing users
// to select their complication data provider requires numbers to be >= 0.
@@ -93,285 +88,788 @@ internal const val LEFT_COMPLICATION_ID = 100
internal const val RIGHT_COMPLICATION_ID = 101
internal const val TOP_COMPLICATION_ID = 102
internal const val BOTTOM_COMPLICATION_ID = 103
+internal const val HOUR_COMPLICATION_ID = 104
+internal const val MINUTE_COMPLICATION_ID = 105
+
+internal const val COMPLICATIONS_TOP_LEFT_COMPLICATION_ID = 106
+internal const val COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID = 107
+internal const val COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID = 108
+internal const val COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID = 109
+internal const val COMPLICATIONS_TOP_COMPLICATION_ID = 110
+internal const val COMPLICATIONS_BOTTOM_COMPLICATION_ID = 111
+internal const val COMPLICATIONS_HOUR_COMPLICATION_ID = 112
+internal const val COMPLICATIONS_MINUTE_COMPLICATION_ID = 113
+
+internal const val FOCUS_LEFT_ICON_COMPLICATION_ID = 114
+internal const val FOCUS_RIGHT_ICON_COMPLICATION_ID = 115
+internal const val FOCUS_HOUR_COMPLICATION_ID = 116
+internal const val FOCUS_MINUTE_COMPLICATION_ID = 117
+
+internal const val RIGHT_TEXT_COMPLICATION_ID = 118
+internal const val SPORT_HOUR_COMPLICATION_ID = 119
+internal const val SPORT_MINUTE_COMPLICATION_ID = 120
+
+const val TOP_LEFT_COMPLICATION_LEFT_BOUND = 33f / 384f
+const val TOP_LEFT_COMPLICATION_TOP_BOUND = 93f / 384f
+const val TOP_LEFT_COMPLICATION_RIGHT_BOUND = 33f / 384f + 60f / 384f
+const val TOP_LEFT_COMPLICATION_BOTTOM_BOUND = 93f / 384f + 90f / 384f
+
+const val BOTTOM_LEFT_COMPLICATION_LEFT_BOUND = 33f / 384f
+const val BOTTOM_LEFT_COMPLICATION_TOP_BOUND = 201f / 384f
+const val BOTTOM_LEFT_COMPLICATION_RIGHT_BOUND = 33f / 384f + 60f / 384f
+const val BOTTOM_LEFT_COMPLICATION_BOTTOM_BOUND = 201f / 384f + 90f / 384f
+
+const val TOP_RIGHT_COMPLICATION_LEFT_BOUND = 285f / 384f
+const val TOP_RIGHT_COMPLICATION_TOP_BOUND = 93f / 384f
+const val TOP_RIGHT_COMPLICATION_RIGHT_BOUND = 285f / 384f + 60f / 384f
+const val TOP_RIGHT_COMPLICATION_BOTTOM_BOUND = 93f / 384f + 90f / 384f
+
+const val BOTTOM_RIGHT_COMPLICATION_LEFT_BOUND = 285f / 384f
+const val BOTTOM_RIGHT_COMPLICATION_TOP_BOUND = 201f / 384f
+const val BOTTOM_RIGHT_COMPLICATION_RIGHT_BOUND = 285f / 384f + 60f / 384f
+const val BOTTOM_RIGHT_COMPLICATION_BOTTOM_BOUND = 201f / 384f + 90f / 384f
+
+const val LEFT_ICON_COMPLICATION_LEFT_BOUND = 24f / 384f
+const val LEFT_ICON_COMPLICATION_TOP_BOUND = 126f / 384f
+const val LEFT_ICON_COMPLICATION_RIGHT_BOUND = 24f / 384f + 54f / 384f
+const val LEFT_ICON_COMPLICATION_BOTTOM_BOUND = 126f / 384f + 132f / 384f
+
+const val RIGHT_ICON_COMPLICATION_LEFT_BOUND = 306f / 384f
+const val RIGHT_ICON_COMPLICATION_TOP_BOUND = 126f / 384f
+const val RIGHT_ICON_COMPLICATION_RIGHT_BOUND = 306f / 384f + 54f / 384f
+const val RIGHT_ICON_COMPLICATION_BOTTOM_BOUND = 126f / 384f + 132f / 384f
+
+const val RIGHT_TEXT_COMPLICATION_LEFT_BOUND = 249f / 384f - 14f / 384f
+const val RIGHT_TEXT_COMPLICATION_TOP_BOUND = 246f / 384f - 14f / 384f + 2f / 384f
+const val RIGHT_TEXT_COMPLICATION_RIGHT_BOUND = 249f / 384f + 82f / 384f + 14f / 384f
+const val RIGHT_TEXT_COMPLICATION_BOTTOM_BOUND = 246f / 384f + 14f / 384f + 14f / 384f + 2f / 384f
+
+const val HOUR_COMPLICATION_LEFT_BOUND = 114f / 384f
+const val HOUR_COMPLICATION_TOP_BOUND = 87f / 384f
+const val HOUR_COMPLICATION_RIGHT_BOUND = 114f / 384f + 156f / 384f
+const val HOUR_COMPLICATION_BOTTOM_BOUND = 87f / 384f + 99f / 384f
+
+const val HOUR_SPORT_COMPLICATION_LEFT_BOUND = 81f / 384f
+const val HOUR_SPORT_COMPLICATION_TOP_BOUND = 87f / 384f
+const val HOUR_SPORT_COMPLICATION_RIGHT_BOUND = 81f / 384f + 156f / 384f
+const val HOUR_SPORT_COMPLICATION_BOTTOM_BOUND = 87f / 384f + 102f / 384f
+
+const val HOUR_FOCUS_COMPLICATION_LEFT_BOUND = 93f / 384f
+const val HOUR_FOCUS_COMPLICATION_TOP_BOUND = 57f / 384f
+const val HOUR_FOCUS_COMPLICATION_RIGHT_BOUND = 93f / 384f + 198f / 384f
+const val HOUR_FOCUS_COMPLICATION_BOTTOM_BOUND = 57f / 384f + 126f / 384f
+
+const val MINUTE_COMPLICATION_LEFT_BOUND = 114f / 384f
+const val MINUTE_COMPLICATION_TOP_BOUND = 198f / 384f
+const val MINUTE_COMPLICATION_RIGHT_BOUND = 114f / 384f + 156f / 384f
+const val MINUTE_COMPLICATION_BOTTOM_BOUND = 198f / 384f + 99f / 384f
+
+const val MINUTE_SPORT_COMPLICATION_LEFT_BOUND = 81f / 384f
+const val MINUTE_SPORT_COMPLICATION_TOP_BOUND = 198f / 384f
+const val MINUTE_SPORT_COMPLICATION_RIGHT_BOUND = 81f / 384f + 156f / 384f
+const val MINUTE_SPORT_COMPLICATION_BOTTOM_BOUND = 198f / 384f + 102f / 384f
+
+const val MINUTE_FOCUS_COMPLICATION_LEFT_BOUND = 93f / 384f
+const val MINUTE_FOCUS_COMPLICATION_TOP_BOUND = 201f / 384f
+const val MINUTE_FOCUS_COMPLICATION_RIGHT_BOUND = 93f / 384f + 198f / 384f
+const val MINUTE_FOCUS_COMPLICATION_BOTTOM_BOUND = 201f / 384f + 126f / 384f
+
+///**
+// * Represents the unique id associated with a complication and the complication types it supports.
+// */
+//sealed class ComplicationConfigDelete(
+// val id: Int,
+// val supportedTypes: List,
+// val renderBounds: RectF = RectF(),
+// val slotBounds: RectF = RectF(),
+// val nameResourceId: Int?,
+//) {
+// object Hour : ComplicationConfigDelete(
+// HOUR_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// slotBounds = RectF(
+// HOUR_COMPLICATION_LEFT_BOUND,
+// HOUR_COMPLICATION_TOP_BOUND,
+// HOUR_COMPLICATION_RIGHT_BOUND,
+// HOUR_COMPLICATION_BOTTOM_BOUND,
+// ),
+// renderBounds = RectF(
+// HOUR_COMPLICATION_LEFT_BOUND,
+// HOUR_COMPLICATION_TOP_BOUND,
+// HOUR_COMPLICATION_RIGHT_BOUND,
+// HOUR_COMPLICATION_BOTTOM_BOUND,
+// ),
+// nameResourceId = R.string.hour_complication_name,
+// )
+//
+// object Minute : ComplicationConfigDelete(
+// MINUTE_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// slotBounds = RectF(
+// MINUTE_COMPLICATION_LEFT_BOUND,
+// MINUTE_COMPLICATION_TOP_BOUND,
+// MINUTE_COMPLICATION_RIGHT_BOUND,
+// MINUTE_COMPLICATION_BOTTOM_BOUND,
+// ),
+// renderBounds = RectF(
+// MINUTE_COMPLICATION_LEFT_BOUND,
+// MINUTE_COMPLICATION_TOP_BOUND,
+// MINUTE_COMPLICATION_RIGHT_BOUND,
+// MINUTE_COMPLICATION_BOTTOM_BOUND,
+// ),
+// nameResourceId = R.string.minute_complication_name,
+// )
+//
+// object Left : ComplicationConfigDelete(
+// LEFT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// LEFT_COMPLICATION_LEFT_BOUND,
+// LEFT_COMPLICATION_TOP_BOUND,
+// LEFT_COMPLICATION_RIGHT_BOUND,
+// LEFT_COMPLICATION_BOTTOM_BOUND,
+// ),
+// slotBounds = RectF(
+// 0f / 384f,
+// 87f / 384f,
+// 0f / 384f + 108f / 384f,
+// 87f / 384f + 210f / 384f,
+// ),
+// nameResourceId = R.string.left_complication_name,
+// )
+//
+// object Right : ComplicationConfigDelete(
+// RIGHT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// RIGHT_COMPLICATION_LEFT_BOUND,
+// RIGHT_COMPLICATION_TOP_BOUND,
+// RIGHT_COMPLICATION_RIGHT_BOUND,
+// RIGHT_COMPLICATION_BOTTOM_BOUND,
+// ),
+// slotBounds = RectF(
+// 276f / 384f,
+// 87f / 384f,
+// 276f / 384f + 108f / 384f,
+// 87f / 384f + 210f / 384f
+// ),
+// nameResourceId = R.string.right_complication_name,
+// )
+//
+// object Top : ComplicationConfigDelete(
+// TOP_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ),
+// renderBounds = RectF(
+// TOP_COMPLICATION_LEFT_BOUND,
+// TOP_COMPLICATION_TOP_BOUND,
+// TOP_COMPLICATION_RIGHT_BOUND,
+// TOP_COMPLICATION_BOTTOM_BOUND,
+// ),
+// slotBounds = RectF(
+// 0f / 384f,
+// 0f / 384f,
+// 0f / 384f + 384f / 384f,
+// 0f / 384f + 81f / 384f,
+// ),
+// nameResourceId = R.string.top_complication_name,
+// )
+//
+// object Bottom : ComplicationConfigDelete(
+// BOTTOM_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ),
+// renderBounds = RectF(
+// BOTTOM_COMPLICATION_LEFT_BOUND,
+// BOTTOM_COMPLICATION_TOP_BOUND,
+// BOTTOM_COMPLICATION_RIGHT_BOUND,
+// BOTTOM_COMPLICATION_BOTTOM_BOUND,
+// ),
+// slotBounds = RectF(
+// 0f / 384f,
+// 303f / 384f,
+// 0f / 384f + 384f / 384f,
+// 303f / 384f + 81f / 384f,
+// ),
+// nameResourceId = R.string.bottom_complication_name,
+// )
+//
+// // ==========================================================================
+//
+// object TopLeft : ComplicationConfigDelete(
+// COMPLICATIONS_TOP_LEFT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// TOP_LEFT_COMPLICATION_LEFT_BOUND,
+// TOP_LEFT_COMPLICATION_TOP_BOUND-6f/384f,
+// TOP_LEFT_COMPLICATION_RIGHT_BOUND,
+// TOP_LEFT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+// ),
+// slotBounds = RectF(
+// 0f / 384f,
+// 87f / 384f,
+// 0f / 384f + 108f / 384f,
+// 87f / 384f + 102f / 384f,
+// ),
+// nameResourceId = R.string.top_left_complication_name,
+// )
+//
+// object BottomLeft : ComplicationConfigDelete(
+// COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// BOTTOM_LEFT_COMPLICATION_LEFT_BOUND,
+// BOTTOM_LEFT_COMPLICATION_TOP_BOUND-6f/384f,
+// BOTTOM_LEFT_COMPLICATION_RIGHT_BOUND,
+// BOTTOM_LEFT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+// ),
+// slotBounds = RectF(
+// 0f / 384f,
+// 195f / 384f,
+// 0f / 384f + 108f / 384f,
+// 195f / 384f + 102f / 384f,
+// ),
+// nameResourceId = R.string.bottom_left_complication_name,
+// )
+//
+// object TopRight : ComplicationConfigDelete(
+// COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// TOP_RIGHT_COMPLICATION_LEFT_BOUND,
+// TOP_RIGHT_COMPLICATION_TOP_BOUND-6f/384f,
+// TOP_RIGHT_COMPLICATION_RIGHT_BOUND,
+// TOP_RIGHT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+// ),
+// slotBounds = RectF(
+// 276f / 384f,
+// 87f / 384f,
+// 276f / 384f + 108f / 384f,
+// 87f / 384f + 102f / 384f,
+// ),
+// nameResourceId = R.string.top_right_complication_name,
+// )
+//
+// object BottomRight : ComplicationConfigDelete(
+// COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// BOTTOM_RIGHT_COMPLICATION_LEFT_BOUND,
+// BOTTOM_RIGHT_COMPLICATION_TOP_BOUND-6f/384f,
+// BOTTOM_RIGHT_COMPLICATION_RIGHT_BOUND,
+// BOTTOM_RIGHT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+// ),
+// slotBounds = RectF(
+// 276f / 384f,
+// 195f / 384f,
+// 276f / 384f + 108f / 384f,
+// 195f / 384f + 102f / 384f,
+// ),
+// nameResourceId = R.string.bottom_right_complication_name,
+// )
+//
+//// object ComplicationsTop : ComplicationConfigDelete(
+//// COMPLICATIONS_TOP_COMPLICATION_ID, listOf(
+//// ComplicationType.SHORT_TEXT,
+//// ),
+//// renderBounds = RectF(
+//// TOP_COMPLICATION_LEFT_BOUND,
+//// TOP_COMPLICATION_TOP_BOUND,
+//// TOP_COMPLICATION_RIGHT_BOUND,
+//// TOP_COMPLICATION_BOTTOM_BOUND,
+//// ),
+//// slotBounds = RectF(
+//// 0f / 384f,
+//// 0f / 384f,
+//// 0f / 384f + 384f / 384f,
+//// 0f / 384f + 81f / 384f,
+//// ),
+//// )
+//
+//// object ComplicationsBottom : ComplicationConfigDelete(
+//// COMPLICATIONS_BOTTOM_COMPLICATION_ID, listOf(
+//// ComplicationType.SHORT_TEXT,
+//// ),
+//// renderBounds = RectF(
+//// BOTTOM_COMPLICATION_LEFT_BOUND,
+//// BOTTOM_COMPLICATION_TOP_BOUND,
+//// BOTTOM_COMPLICATION_RIGHT_BOUND,
+//// BOTTOM_COMPLICATION_BOTTOM_BOUND,
+//// ),
+//// slotBounds = RectF(
+//// 0f / 384f,
+//// 303f / 384f,
+//// 0f / 384f + 384f / 384f,
+//// 303f / 384f + 81f / 384f,
+//// ),
+//// )
+//
+// // ==========================================================================
+//
+// object LeftIcon : ComplicationConfigDelete(
+// FOCUS_LEFT_ICON_COMPLICATION_ID, listOf(
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// LEFT_ICON_COMPLICATION_LEFT_BOUND,
+// LEFT_ICON_COMPLICATION_TOP_BOUND,
+// LEFT_ICON_COMPLICATION_RIGHT_BOUND,
+// LEFT_ICON_COMPLICATION_BOTTOM_BOUND
+// ),
+// slotBounds = RectF(
+// 0f / 384f,
+// 0f / 384f,
+// 0f / 384f + 93f / 384f,
+// 0f / 384f + 384f / 384f
+// ),
+// nameResourceId = R.string.left_complication_name,
+// )
+//
+// object RightIcon : ComplicationConfigDelete(
+// FOCUS_RIGHT_ICON_COMPLICATION_ID, listOf(
+// ComplicationType.MONOCHROMATIC_IMAGE,
+// ComplicationType.SMALL_IMAGE
+// ),
+// renderBounds = RectF(
+// RIGHT_ICON_COMPLICATION_LEFT_BOUND,
+// RIGHT_ICON_COMPLICATION_TOP_BOUND,
+// RIGHT_ICON_COMPLICATION_RIGHT_BOUND,
+// RIGHT_ICON_COMPLICATION_BOTTOM_BOUND
+// ),
+// slotBounds = RectF(
+// 291f / 384f,
+// 0f / 384f,
+// 291f / 384f + 93f / 384f,
+// 0f / 384f + 384f / 384f
+// ),
+// nameResourceId = R.string.right_complication_name,
+// )
+//
+// object Text : ComplicationConfigDelete(
+// RIGHT_TEXT_COMPLICATION_ID, listOf(
+// ComplicationType.SHORT_TEXT,
+// ),
+// renderBounds = RectF(
+// RIGHT_TEXT_COMPLICATION_LEFT_BOUND,
+// RIGHT_TEXT_COMPLICATION_TOP_BOUND,
+// RIGHT_TEXT_COMPLICATION_RIGHT_BOUND,
+// RIGHT_TEXT_COMPLICATION_BOTTOM_BOUND
+// ),
+// slotBounds = RectF(
+// RIGHT_TEXT_COMPLICATION_LEFT_BOUND,
+// RIGHT_TEXT_COMPLICATION_TOP_BOUND,
+// RIGHT_TEXT_COMPLICATION_RIGHT_BOUND,
+// RIGHT_TEXT_COMPLICATION_BOTTOM_BOUND
+// ),
+// nameResourceId = R.string.right_complication_name,
+// )
+//}
-/**
- * Represents the unique id associated with a complication and the complication types it supports.
- */
-sealed class ComplicationConfig(val id: Int, val supportedTypes: List) {
- object Left : ComplicationConfig(
- LEFT_COMPLICATION_ID,
- listOf(
+// Utility function that initializes default complication slots (left and right).
+fun createComplicationSlotManager(
+ context: Context,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+): ComplicationSlotsManager {
+ val verticalComplicationFactory = createVerticalComplicationFactory(context)
+ val horizontalComplicationFactory = createHorizontalComplicationFactory(context)
+ val invisibleComplicationFactory = createInvisibleComplicationFactory(context)
+ val horizontalTextComplicationFactory = createHorizontalTextComplicationFactory(context)
+
+ val hourComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = HOUR_COMPLICATION_ID,
+ canvasComplicationFactory = invisibleComplicationFactory,
+ supportedTypes = listOf(
ComplicationType.SHORT_TEXT,
-// ComplicationType.RANGED_VALUE,
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ HOUR_COMPLICATION_LEFT_BOUND,
+ HOUR_COMPLICATION_TOP_BOUND,
+ HOUR_COMPLICATION_RIGHT_BOUND,
+ HOUR_COMPLICATION_BOTTOM_BOUND,
+ ),
)
- )
-
- object Right : ComplicationConfig(
- RIGHT_COMPLICATION_ID,
- listOf(
+ ).setNameResourceId(R.string.hour_complication_name)
+ .setScreenReaderNameResourceId(R.string.hour_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val minuteComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = MINUTE_COMPLICATION_ID,
+ canvasComplicationFactory = invisibleComplicationFactory,
+ supportedTypes = listOf(
ComplicationType.SHORT_TEXT,
-// ComplicationType.RANGED_VALUE,
ComplicationType.MONOCHROMATIC_IMAGE,
ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ MINUTE_COMPLICATION_LEFT_BOUND,
+ MINUTE_COMPLICATION_TOP_BOUND,
+ MINUTE_COMPLICATION_RIGHT_BOUND,
+ MINUTE_COMPLICATION_BOTTOM_BOUND,
+ )
)
- )
-
- object Top : ComplicationConfig(
- TOP_COMPLICATION_ID,
- listOf(
- ComplicationType.SHORT_TEXT,
- )
- )
-
- object Bottom : ComplicationConfig(
- BOTTOM_COMPLICATION_ID,
- listOf(
+ ).setNameResourceId(R.string.minute_complication_name)
+ .setScreenReaderNameResourceId(R.string.minute_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val leftComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = LEFT_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
ComplicationType.SHORT_TEXT,
- )
- )
-}
-
-// Utility function that initializes default complication slots (left and right).
-fun createComplicationSlotManager(
- context: Context,
- currentUserStyleRepository: CurrentUserStyleRepository,
-): ComplicationSlotsManager {
-
- val customLeftComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
- id = ComplicationConfig.Left.id,
- canvasComplicationFactory = createVerticalComplicationFactory(context),
- supportedTypes = ComplicationConfig.Left.supportedTypes,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
-// SystemDataSources.DATA_SOURCE_DAY_OF_WEEK,
-// ComplicationType.SHORT_TEXT
- SystemDataSources.NO_DATA_SOURCE,
- ComplicationType.SHORT_TEXT
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
),
bounds = ComplicationSlotBounds(
RectF(
LEFT_COMPLICATION_LEFT_BOUND,
- VERTICAL_COMPLICATION_TOP_BOUND,
+ LEFT_COMPLICATION_TOP_BOUND,
LEFT_COMPLICATION_RIGHT_BOUND,
- VERTICAL_COMPLICATION_BOTTOM_BOUND
+ LEFT_COMPLICATION_BOTTOM_BOUND,
)
)
).setNameResourceId(R.string.left_complication_name)
- .setScreenReaderNameResourceId(R.string.left_complication_name).build()
-
- val customRightComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
- id = ComplicationConfig.Right.id,
- canvasComplicationFactory = createVerticalComplicationFactory(context),
- supportedTypes = ComplicationConfig.Right.supportedTypes,
+ .setScreenReaderNameResourceId(R.string.left_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val rightComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = RIGHT_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
-// SystemDataSources.DATA_SOURCE_DAY_OF_WEEK,
-// ComplicationType.SHORT_TEXT
- SystemDataSources.NO_DATA_SOURCE,
- ComplicationType.SHORT_TEXT
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
),
bounds = ComplicationSlotBounds(
RectF(
RIGHT_COMPLICATION_LEFT_BOUND,
- VERTICAL_COMPLICATION_TOP_BOUND,
+ RIGHT_COMPLICATION_TOP_BOUND,
RIGHT_COMPLICATION_RIGHT_BOUND,
- VERTICAL_COMPLICATION_BOTTOM_BOUND
+ RIGHT_COMPLICATION_BOTTOM_BOUND,
)
)
).setNameResourceId(R.string.right_complication_name)
- .setScreenReaderNameResourceId(R.string.right_complication_name).build()
-
- val customTopComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
- id = ComplicationConfig.Top.id,
- canvasComplicationFactory = createHorizontalComplicationFactory(context),
- supportedTypes = ComplicationConfig.Top.supportedTypes,
+ .setScreenReaderNameResourceId(R.string.right_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val topComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = TOP_COMPLICATION_ID,
+ canvasComplicationFactory = horizontalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ),
defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
- SystemDataSources.DATA_SOURCE_DATE,
- ComplicationType.SHORT_TEXT
-// SystemDataSources.NO_DATA_SOURCE,
-// ComplicationType.SHORT_TEXT
+ SystemDataSources.DATA_SOURCE_DATE, ComplicationType.SHORT_TEXT
),
bounds = ComplicationSlotBounds(
RectF(
- HORIZONTAL_COMPLICATION_LEFT_BOUND,
+ TOP_COMPLICATION_LEFT_BOUND,
TOP_COMPLICATION_TOP_BOUND,
- HORIZONTAL_COMPLICATION_RIGHT_BOUND,
- TOP_COMPLICATION_BOTTOM_BOUND
+ TOP_COMPLICATION_RIGHT_BOUND,
+ TOP_COMPLICATION_BOTTOM_BOUND,
)
)
).setNameResourceId(R.string.top_complication_name)
- .setScreenReaderNameResourceId(R.string.top_complication_name).build()
-
- val customBottomComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
- id = ComplicationConfig.Bottom.id,
- canvasComplicationFactory = createHorizontalComplicationFactory(context),
- supportedTypes = ComplicationConfig.Bottom.supportedTypes,
+ .setScreenReaderNameResourceId(R.string.top_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val bottomComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = BOTTOM_COMPLICATION_ID,
+ canvasComplicationFactory = horizontalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ),
defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
- SystemDataSources.DATA_SOURCE_WATCH_BATTERY,
- ComplicationType.SHORT_TEXT
+ SystemDataSources.DATA_SOURCE_WATCH_BATTERY, ComplicationType.SHORT_TEXT
),
bounds = ComplicationSlotBounds(
RectF(
- HORIZONTAL_COMPLICATION_LEFT_BOUND,
+ BOTTOM_COMPLICATION_LEFT_BOUND,
BOTTOM_COMPLICATION_TOP_BOUND,
- HORIZONTAL_COMPLICATION_RIGHT_BOUND,
- BOTTOM_COMPLICATION_BOTTOM_BOUND
+ BOTTOM_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_COMPLICATION_BOTTOM_BOUND,
)
)
).setNameResourceId(R.string.bottom_complication_name)
- .setScreenReaderNameResourceId(R.string.bottom_complication_name).build()
+ .setScreenReaderNameResourceId(R.string.bottom_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val topLeftComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = COMPLICATIONS_TOP_LEFT_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ TOP_LEFT_COMPLICATION_LEFT_BOUND,
+ TOP_LEFT_COMPLICATION_TOP_BOUND-6f/384f,
+ TOP_LEFT_COMPLICATION_RIGHT_BOUND,
+ TOP_LEFT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+ )
+ )
+ ).setNameResourceId(R.string.top_left_complication_name)
+ .setScreenReaderNameResourceId(R.string.top_left_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val bottomLeftComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ BOTTOM_LEFT_COMPLICATION_LEFT_BOUND,
+ BOTTOM_LEFT_COMPLICATION_TOP_BOUND-6f/384f,
+ BOTTOM_LEFT_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_LEFT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+ )
+ )
+ ).setNameResourceId(R.string.bottom_left_complication_name)
+ .setScreenReaderNameResourceId(R.string.bottom_left_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val topRightComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ TOP_RIGHT_COMPLICATION_LEFT_BOUND,
+ TOP_RIGHT_COMPLICATION_TOP_BOUND-6f/384f,
+ TOP_RIGHT_COMPLICATION_RIGHT_BOUND,
+ TOP_RIGHT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+ )
+ )
+ ).setNameResourceId(R.string.top_right_complication_name)
+ .setScreenReaderNameResourceId(R.string.top_right_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val bottomRightComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ BOTTOM_RIGHT_COMPLICATION_LEFT_BOUND,
+ BOTTOM_RIGHT_COMPLICATION_TOP_BOUND-6f/384f,
+ BOTTOM_RIGHT_COMPLICATION_RIGHT_BOUND,
+ BOTTOM_RIGHT_COMPLICATION_BOTTOM_BOUND+6f/384f,
+ )
+ )
+ ).setNameResourceId(R.string.bottom_right_complication_name)
+ .setScreenReaderNameResourceId(R.string.bottom_right_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val leftIconComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = FOCUS_LEFT_ICON_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.MONOCHROMATIC_IMAGE
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ LEFT_ICON_COMPLICATION_LEFT_BOUND,
+ LEFT_ICON_COMPLICATION_TOP_BOUND,
+ LEFT_ICON_COMPLICATION_RIGHT_BOUND,
+ LEFT_ICON_COMPLICATION_BOTTOM_BOUND
+ )
+ )
+ ).setNameResourceId(R.string.left_complication_name)
+ .setScreenReaderNameResourceId(R.string.left_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val rightIconComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = FOCUS_RIGHT_ICON_COMPLICATION_ID,
+ canvasComplicationFactory = verticalComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.MONOCHROMATIC_IMAGE
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ RIGHT_ICON_COMPLICATION_LEFT_BOUND,
+ RIGHT_ICON_COMPLICATION_TOP_BOUND,
+ RIGHT_ICON_COMPLICATION_RIGHT_BOUND,
+ RIGHT_ICON_COMPLICATION_BOTTOM_BOUND
+ )
+ )
+ ).setNameResourceId(R.string.right_complication_name)
+ .setScreenReaderNameResourceId(R.string.right_complication_name)
+ .setEnabled(false)
+ .build()
+
+ val textComplication = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ id = RIGHT_TEXT_COMPLICATION_ID,
+ canvasComplicationFactory = horizontalTextComplicationFactory,
+ supportedTypes = listOf(
+ ComplicationType.SHORT_TEXT,
+ ),
+ defaultDataSourcePolicy = DefaultComplicationDataSourcePolicy(
+ SystemDataSources.NO_DATA_SOURCE, ComplicationType.SHORT_TEXT
+ ),
+ bounds = ComplicationSlotBounds(
+ RectF(
+ RIGHT_TEXT_COMPLICATION_LEFT_BOUND,
+ RIGHT_TEXT_COMPLICATION_TOP_BOUND,
+ RIGHT_TEXT_COMPLICATION_RIGHT_BOUND,
+ RIGHT_TEXT_COMPLICATION_BOTTOM_BOUND
+ )
+ )
+ ).setNameResourceId(R.string.right_complication_name)
+ .setScreenReaderNameResourceId(R.string.right_complication_name)
+ .setEnabled(false)
+ .build()
return ComplicationSlotsManager(
listOf(
- customLeftComplication,
- customRightComplication,
- customTopComplication,
- customBottomComplication
- ),
- currentUserStyleRepository
- )
-}
+ hourComplication,
+ minuteComplication,
-class RectangleCanvasComplication(private val context: Context) : CanvasComplication {
- override fun render(
- canvas: Canvas,
- bounds: Rect,
- zonedDateTime: ZonedDateTime,
- renderParameters: RenderParameters,
- slotId: Int
- ) {
- val start = System.currentTimeMillis()
- Log.d("RectangleCanvasComplication", "render($slotId, ${_data.type}) -- start: ${start}ms")
-
- val dataSource = _data.dataSource
- val isBattery =
- dataSource?.className == "com.google.android.clockwork.sysui.experiences.complications.providers.BatteryProviderService"
-
- // debug
- val dp = Paint()
- dp.color = Color.parseColor("#444444")
-// canvas.drawRect(bounds, dp)
-
- var text: String
- var title: String? = null
- var icon: Bitmap? = null
- var iconRect = Rect(0, 0, 32, 32)
-
- when (_data.type) {
- ComplicationType.SHORT_TEXT -> {
- val dat = _data as ShortTextComplicationData
- text = dat.text.getTextAt(context.resources, Instant.now()).toString()
-
- if (dat.monochromaticImage != null) {
- val drawable = dat.monochromaticImage!!.image.loadDrawable(context)
- if (drawable != null) {
- icon = drawable.toBitmap(32, 32)
- }
- }
-
- if (dat.title != null) {
- title = dat.title!!.getTextAt(context.resources, Instant.now()).toString()
- }
-
- }
-
- else -> {
- Log.d("TIME", "start: ${start}ms, elapsed: ${System.currentTimeMillis() - start}ms")
- return
- }
- }
+ leftComplication,
+ rightComplication,
- if (isBattery) {
- val drawable = ContextCompat.getDrawable(context, R.drawable.battery_icon)!!
- icon = drawable.toBitmap(30, 15)
- iconRect = Rect(-1, 0, 29, 15)
- }
+ topComplication,
+ bottomComplication,
-// text = "1234567"
+ topLeftComplication,
+ bottomLeftComplication,
+ topRightComplication,
+ bottomRightComplication,
- val tp = Paint()
- tp.isAntiAlias = true
- tp.textSize = 24F / 384F * canvas.width
- tp.typeface = context.resources.getFont(R.font.m8stealth57)
- tp.textAlign = Paint.Align.CENTER
- tp.color = Color.parseColor("#8888bb")
+ leftIconComplication,
+ rightIconComplication,
- var offsetX =
- iconRect.width() / 2f + 6f // half icon width to the right plus 3f to the right for some spacing
- val offsetY = 10.5f
+ textComplication,
+ ), currentUserStyleRepository
+ )
+}
- var prefixLen = 0
- if (isBattery) {
- prefixLen = 3 - text.length
- text = text.padStart(3, ' ')
- }
+class ComplicationRenderer {
+ val bmpCache = LruCache(1)
+ val complCache = LruCache(1)
- val width = 15f * text.length + 3f * (text.length - 1)
+ fun reset() {
+ complCache.evictAll()
+ }
- if (title != null) {
- offsetX = 0f
- text = "$title $text"
+ inline fun render(
+ bounds: Rect,
+ data: ComplicationData,
+ renderer: (canvas: Canvas, bounds: Rect, data: T) -> Unit
+ ): Bitmap {
+ val cacheKey = "${bounds.hashCode()},${data.hashCode()}"
+
+ val cached = complCache.get(cacheKey)
+ if (cached != null) {
+ return cached
}
- tp.color = Color.parseColor("#8888bb")
-
- canvas.drawText(
- text.uppercase(),
- bounds.exactCenterX() + offsetX / 384F * canvas.width.toFloat(),
- bounds.exactCenterY() + offsetY / 384F * canvas.height.toFloat(),
- tp
- )
-
- if (isBattery) {
- val prefix = "".padStart(prefixLen, '0') + " ".repeat(3 - prefixLen)
-
- tp.color = Color.parseColor("#343434")
+ val bmpKey = "${bounds.hashCode()}"
- canvas.drawText(
- prefix,
- bounds.exactCenterX() + offsetX / 384F * canvas.width.toFloat(),
- bounds.exactCenterY() + offsetY / 384F * canvas.height.toFloat(),
- tp
+ var bitmap = bmpCache.get(bmpKey)
+ if (bitmap != null) {
+ bitmap.eraseColor(Color.TRANSPARENT)
+ } else {
+ bitmap = Bitmap.createBitmap(
+ bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888
)
+ bmpCache.put(bmpKey, bitmap)
}
- if (icon != null && title == null) {
+ val bitmapCanvas = Canvas(bitmap)
+ val rect = Rect(0, 0, bitmap.width, bitmap.height)
- val srcRect = iconRect
- val dstRect = RectF(
- bounds.exactCenterX() - iconRect.width() / 2f - width / 2 - 6f,
- bounds.exactCenterY() - iconRect.height() / 2f,
- bounds.exactCenterX() + iconRect.width() / 2f - width / 2 - 6f,
- bounds.exactCenterY() + iconRect.height() / 2f,
- )
-
- val iconPaint = Paint()
- iconPaint.isAntiAlias = false
- iconPaint.colorFilter =
- PorterDuffColorFilter(Color.parseColor("#8888bb"), PorterDuff.Mode.SRC_IN)
- canvas.drawBitmap(icon, srcRect, dstRect, iconPaint)
+ (data as? T)?.let {
+ renderer(bitmapCanvas, rect, it)
}
- Log.d("TIME", "start: ${start}ms, elapsed: ${System.currentTimeMillis() - start}ms")
- }
+ complCache.put(cacheKey, bitmap)
- override fun drawHighlight(
- canvas: Canvas,
- bounds: Rect,
- boundsType: Int,
- zonedDateTime: ZonedDateTime,
- color: Int
- ) {
+ return bitmap
}
- private var _data: ComplicationData = NoDataComplicationData()
-
- override fun getData(): ComplicationData = _data
-
- override fun loadData(
- complicationData: ComplicationData,
- loadDrawablesAsynchronous: Boolean
- ) {
- _data = complicationData
- }
}
diff --git a/app/src/main/java/dev/rdnt/m8face/utils/HorizontalComplication.kt b/app/src/main/java/dev/rdnt/m8face/utils/HorizontalComplication.kt
index a4d35b8..5b68862 100644
--- a/app/src/main/java/dev/rdnt/m8face/utils/HorizontalComplication.kt
+++ b/app/src/main/java/dev/rdnt/m8face/utils/HorizontalComplication.kt
@@ -1,21 +1,34 @@
package dev.rdnt.m8face.utils
import android.content.Context
-import android.graphics.*
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.Rect
+import android.graphics.RectF
import android.util.Log
-import androidx.core.content.ContextCompat
-import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.toBitmap
-import androidx.core.graphics.withRotation
import androidx.wear.watchface.CanvasComplication
import androidx.wear.watchface.CanvasComplicationFactory
import androidx.wear.watchface.RenderParameters
-import androidx.wear.watchface.complications.data.*
+import androidx.wear.watchface.complications.data.ComplicationData
+import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.data.NoDataComplicationData
+import androidx.wear.watchface.complications.data.ShortTextComplicationData
import dev.rdnt.m8face.R
import java.time.Instant
import java.time.ZonedDateTime
class HorizontalComplication(private val context: Context) : CanvasComplication {
+ private val renderer = ComplicationRenderer()
+
+ init {
+ Log.d("HorizontalComplication", "Constructor ran")
+ }
+
var tertiaryColor: Int = Color.parseColor("#8888bb")
set(tertiaryColor) {
field = tertiaryColor
@@ -24,21 +37,7 @@ class HorizontalComplication(private val context: Context) : CanvasComplication
iconPaint.colorFilter = PorterDuffColorFilter(tertiaryColor, PorterDuff.Mode.SRC_IN)
prefixPaint.color = tertiaryColor
prefixPaint.alpha = 100
- }
-
- var opacity: Float = 1f
- set(opacity) {
- field = opacity
-
- val color = ColorUtils.blendARGB(Color.TRANSPARENT, tertiaryColor, opacity)
-
- textPaint.color = color
- titlePaint.color = color
-
- iconPaint.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
-
- prefixPaint.color = color
- prefixPaint.alpha = 100
+ renderer.reset()
}
private val textPaint = Paint().apply {
@@ -76,116 +75,73 @@ class HorizontalComplication(private val context: Context) : CanvasComplication
) {
if (bounds.isEmpty) return
- when (data.type) {
+ val data = if (data.type == ComplicationType.NO_DATA) {
+ val placeholder = (data as NoDataComplicationData).placeholder
+ placeholder ?: data
+ } else {
+ data
+ }
+
+ val bitmap = when (data.type) {
ComplicationType.SHORT_TEXT -> {
- renderShortTextComplication(canvas, bounds, data as ShortTextComplicationData)
+ renderer.render(bounds, data, ::renderShortTextComplication)
}
else -> return
}
+
+ canvas.drawBitmap(
+ bitmap,
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ Paint(),
+ )
}
private fun renderShortTextComplication(
canvas: Canvas,
bounds: Rect,
- data: ShortTextComplicationData,
+ complData: ComplicationData
) {
+ val data = complData as ShortTextComplicationData
+
val now = Instant.now()
var text = data.text.getTextAt(context.resources, now).toString().uppercase()
- if (text == "--") {
- return
- }
-
- val isBattery =
- data.dataSource?.className == "com.google.android.clockwork.sysui.experiences.complications.providers.BatteryProviderService"
- val threeDigit = isBattery
+ val title = data.title?.getTextAt(context.resources, now)?.toString()?.uppercase()
+ if (title != null) {
+ text = "$text $title"
+ }
- var title: String? = null
var icon: Bitmap? = null
var iconBounds = Rect()
+ if (title == null) {
+ val bmpSize = (bounds.width().coerceAtMost(bounds.height()).toFloat() * 0.55f).toInt()
- if (isBattery) {
- val drawable = ContextCompat.getDrawable(context, R.drawable.battery_icon_32)!!
- icon = drawable.toBitmap(
- (32f / 48f * bounds.height()).toInt(),
- (32f / 48f * bounds.height()).toInt()
- )
- iconBounds =
- Rect(0, 0, (32f / 48f * bounds.height()).toInt(), (32f / 48f * bounds.height()).toInt())
- } else if (data.monochromaticImage != null) {
- val drawable = data.monochromaticImage!!.image.loadDrawable(context)
- if (drawable != null) {
- val size = (bounds.width().coerceAtMost(bounds.height()).toFloat() * 0.55f).toInt()
-
- icon = drawable.toBitmap(size, size)
- iconBounds = Rect(0, 0, size, size)
- }
- }
-
- var prefixLen = 0
-
- if (threeDigit) {
- prefixLen = 3 - text.length
- text = text.padStart(3, ' ')
- }
-
- if (data.title != null && !data.title!!.isPlaceholder()) {
- title = data.title!!.getTextAt(context.resources, now).toString().uppercase()
+ icon = data.monochromaticImage?.image?.loadDrawable(context)?.toBitmap(bmpSize, bmpSize)
+ iconBounds = Rect(0, 0, bmpSize, bmpSize)
}
- textPaint.textSize = 24F / 48f * bounds.height()
+ textPaint.textSize = 24F / 186f * canvas.width
val textBounds = Rect()
-
- if (threeDigit) {
- textPaint.getTextBounds("000", 0, 3, textBounds)
- } else {
- textPaint.getTextBounds(text, 0, text.length, textBounds)
- }
-
- val titleBounds = Rect()
-
- if (title != null) {
- titlePaint.textSize = textPaint.textSize
- titlePaint.getTextBounds(title, 0, title.length, titleBounds)
- }
+ textPaint.getTextBounds(text, 0, text.length, textBounds)
var iconOffsetX = 0f
- var titleOffsetX = 0f
var textOffsetX = 0f
- if (title != null) {
- val width = titleBounds.width() + textBounds.width()
-
- titleOffsetX = (width - titleBounds.width()).toFloat() / 2f
- textOffsetX = (width - textBounds.width()).toFloat() / 2f
-
- titleOffsetX += 6f / 156f * bounds.width()
- textOffsetX += 6f / 156f * bounds.width()
- } else if (icon != null) {
+ if (icon != null) {
val width = iconBounds.width() + textBounds.width()
iconOffsetX = (width - iconBounds.width()).toFloat() / 2f
textOffsetX = (width - textBounds.width()).toFloat() / 2f
- iconOffsetX += 9f / 156f * bounds.width()
- textOffsetX += 9f / 156f * bounds.width()
-
- if (isBattery) {
- iconOffsetX = iconOffsetX.toInt().toFloat()
- }
+ iconOffsetX += 9f / 186f * canvas.width
+ textOffsetX += 9f / 186f * canvas.width
}
- if (title != null) {
- canvas.drawText(
- title,
- bounds.exactCenterX() - titleBounds.width() / 2 - titleOffsetX,
- bounds.exactCenterY() + titleBounds.height() / 2,
- titlePaint
- )
- } else if (icon != null) {
+ if (title == null && icon != null) {
val dstRect = RectF(
bounds.exactCenterX() - iconBounds.width() / 2f - iconOffsetX,
bounds.exactCenterY() - iconBounds.height() / 2f,
@@ -196,22 +152,10 @@ class HorizontalComplication(private val context: Context) : CanvasComplication
canvas.drawBitmap(icon, iconBounds, dstRect, iconPaint)
}
- if (prefixLen > 0) {
- val prefix = "".padStart(prefixLen, '0')
- prefixPaint.textSize = textPaint.textSize
-
- canvas.drawText(
- prefix,
- bounds.exactCenterX() - textBounds.width() / 2 + textOffsetX,
- bounds.exactCenterY() + textBounds.height() / 2,
- prefixPaint
- )
- }
-
canvas.drawText(
text,
bounds.exactCenterX() - textBounds.width() / 2 + textOffsetX,
- bounds.exactCenterY() + textBounds.height() / 2,
+ bounds.exactCenterY() + textPaint.fontSpacing / 2,
textPaint
)
}
diff --git a/app/src/main/java/dev/rdnt/m8face/utils/HorizontalTextComplication.kt b/app/src/main/java/dev/rdnt/m8face/utils/HorizontalTextComplication.kt
new file mode 100644
index 0000000..cc24026
--- /dev/null
+++ b/app/src/main/java/dev/rdnt/m8face/utils/HorizontalTextComplication.kt
@@ -0,0 +1,185 @@
+package dev.rdnt.m8face.utils
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.Log
+import android.util.LruCache
+import androidx.core.content.ContextCompat
+import androidx.core.graphics.ColorUtils
+import androidx.core.graphics.drawable.toBitmap
+import androidx.core.graphics.toRectF
+import androidx.core.graphics.withScale
+import androidx.core.graphics.withTranslation
+import androidx.wear.watchface.CanvasComplication
+import androidx.wear.watchface.CanvasComplicationFactory
+import androidx.wear.watchface.RenderParameters
+import androidx.wear.watchface.complications.data.ComplicationData
+import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.data.NoDataComplicationData
+import androidx.wear.watchface.complications.data.ShortTextComplicationData
+import dev.rdnt.m8face.BitmapCacheEntry
+import dev.rdnt.m8face.R
+import java.time.Instant
+import java.time.ZonedDateTime
+
+private const val debug = false
+
+class HorizontalTextComplication(private val context: Context) : CanvasComplication {
+ private val memoryCache = LruCache(1)
+
+ var tertiaryColor: Int = Color.parseColor("#8888bb")
+ set(tertiaryColor) {
+ field = tertiaryColor
+ textPaint.color = tertiaryColor
+ }
+
+ private val textPaint = Paint().apply {
+ isAntiAlias = true
+ typeface = context.resources.getFont(R.font.m8stealth57)
+ textAlign = Paint.Align.LEFT
+ color = tertiaryColor
+ textSize = 112f / 14f / 7f * 14f
+ }
+
+ override fun render(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime,
+ renderParameters: RenderParameters,
+ slotId: Int
+ ) {
+ if (bounds.isEmpty) return
+
+ if (memoryCache.get("") == null) {
+ Log.d("@@@", "create bitmap")
+ val bitmap = Bitmap.createBitmap(
+ bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888
+ )
+ memoryCache.put("", bitmap)
+ }
+
+ return
+
+ val bitmap = when (data.type) {
+ ComplicationType.SHORT_TEXT -> {
+ drawShortTextComplication(bounds, data as ShortTextComplicationData)
+ }
+
+ ComplicationType.NO_DATA -> {
+ val placeholder = (data as NoDataComplicationData).placeholder
+ if (placeholder != null && placeholder.type == ComplicationType.SHORT_TEXT) {
+ drawShortTextComplication(bounds, placeholder as ShortTextComplicationData)
+ } else {
+ return
+ }
+ }
+
+ else -> return
+ }
+
+// renderDebug(canvas, bounds.toRectF())
+
+ canvas.drawBitmap(
+ bitmap,
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ Paint(),
+ )
+ }
+
+ private fun drawShortTextComplication(
+ bounds: Rect,
+ data: ShortTextComplicationData
+ ): Bitmap {
+ val cached = memoryCache.get("")
+ return cached
+ cached.eraseColor(Color.TRANSPARENT)
+ val bitmap = cached
+
+// val bitmap = Bitmap.createBitmap(
+// bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888
+// )
+ val bitmapCanvas = Canvas(bitmap)
+
+ val rect = Rect(0, 0, bitmap.width, bitmap.height)
+
+ renderShortTextComplication(bitmapCanvas, rect, data)
+
+ memoryCache.put("", bitmap)
+
+ return bitmap
+ }
+
+ private fun renderShortTextComplication(
+ canvas: Canvas,
+ bounds: Rect,
+ data: ShortTextComplicationData,
+ ) {
+ val now = Instant.now()
+
+ val text = data.text.getTextAt(context.resources, now).toString().uppercase()
+
+ val textBounds = Rect()
+ textPaint.getTextBounds(text, 0, text.length, textBounds)
+
+ canvas.drawText(
+ text,
+ bounds.left.toFloat() + 14f,
+ bounds.exactCenterY() + textBounds.height() / 2,
+ textPaint,
+ )
+ }
+
+ private fun renderDebug(canvas: Canvas, bounds: RectF) {
+ if (debug) {
+ canvas.drawRect(bounds, Paint().apply {
+ this.color = ColorUtils.blendARGB(Color.TRANSPARENT, Color.parseColor("#aa02d7f2"), 1f)
+ style = Paint.Style.STROKE
+ strokeWidth = 2f
+ })
+ val p2 = Paint()
+ p2.color = ColorUtils.blendARGB(Color.TRANSPARENT, Color.parseColor("#aa02d7f2"), 1f)
+ p2.typeface = context.resources.getFont(R.font.m8stealth57)
+ p2.textSize = 8f
+// canvas.drawText(
+// "r ${bitmapCache.loads} w ${bitmapCache.renders}",
+// bounds.left + 3f,
+// bounds.bottom - 3f,
+// p2,
+// )
+ }
+ }
+
+ override fun drawHighlight(
+ canvas: Canvas,
+ bounds: Rect,
+ boundsType: Int,
+ zonedDateTime: ZonedDateTime,
+ color: Int
+ ) {
+ // Rendering of highlights
+ }
+
+ private var data: ComplicationData = NoDataComplicationData()
+
+ override fun getData(): ComplicationData = data
+
+ override fun loadData(
+ complicationData: ComplicationData,
+ loadDrawablesAsynchronous: Boolean
+ ) {
+ data = complicationData
+// memoryCache.remove("")
+ }
+}
+
+fun createHorizontalTextComplicationFactory(context: Context) = CanvasComplicationFactory { _, _ ->
+ HorizontalTextComplication(context)
+}
diff --git a/app/src/main/java/dev/rdnt/m8face/utils/InvisibleComplication.kt b/app/src/main/java/dev/rdnt/m8face/utils/InvisibleComplication.kt
new file mode 100644
index 0000000..5a54a34
--- /dev/null
+++ b/app/src/main/java/dev/rdnt/m8face/utils/InvisibleComplication.kt
@@ -0,0 +1,58 @@
+package dev.rdnt.m8face.utils
+
+import android.content.Context
+import android.graphics.*
+import android.util.Log
+import androidx.core.content.ContextCompat
+import androidx.core.graphics.ColorUtils
+import androidx.core.graphics.drawable.toBitmap
+import androidx.core.graphics.withRotation
+import androidx.wear.watchface.CanvasComplication
+import androidx.wear.watchface.CanvasComplicationFactory
+import androidx.wear.watchface.RenderParameters
+import androidx.wear.watchface.complications.data.*
+import dev.rdnt.m8face.R
+import java.time.Instant
+import java.time.ZonedDateTime
+
+class InvisibleComplication(private val context: Context) : CanvasComplication {
+ override fun render(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime,
+ renderParameters: RenderParameters,
+ slotId: Int
+ ) {
+ if (bounds.isEmpty) return
+
+// // DEBUG
+// canvas.drawRect(bounds, Paint().apply {
+// color = Color.parseColor("#22ffffff")
+// })
+ }
+
+ override fun drawHighlight(
+ canvas: Canvas,
+ bounds: Rect,
+ boundsType: Int,
+ zonedDateTime: ZonedDateTime,
+ color: Int
+ ) {
+ // Rendering of highlights
+ }
+
+ private var data: ComplicationData = NoDataComplicationData()
+
+ override fun getData(): ComplicationData = data
+
+ override fun loadData(
+ complicationData: ComplicationData,
+ loadDrawablesAsynchronous: Boolean
+ ) {
+ data = complicationData
+ }
+}
+
+fun createInvisibleComplicationFactory(context: Context) = CanvasComplicationFactory { _, _ ->
+ InvisibleComplication(context)
+}
diff --git a/app/src/main/java/dev/rdnt/m8face/utils/UserStyleSchemaUtils.kt b/app/src/main/java/dev/rdnt/m8face/utils/UserStyleSchemaUtils.kt
index 01770a5..9ed8649 100644
--- a/app/src/main/java/dev/rdnt/m8face/utils/UserStyleSchemaUtils.kt
+++ b/app/src/main/java/dev/rdnt/m8face/utils/UserStyleSchemaUtils.kt
@@ -16,26 +16,33 @@
package dev.rdnt.m8face.utils
import android.content.Context
+import android.graphics.RectF
+import android.graphics.drawable.Icon
import android.text.format.DateFormat
+import androidx.wear.watchface.complications.ComplicationSlotBounds
+import androidx.wear.watchface.complications.data.ComplicationType
import androidx.wear.watchface.style.UserStyleSchema
import androidx.wear.watchface.style.UserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
import androidx.wear.watchface.style.WatchFaceLayer
import dev.rdnt.m8face.R
import dev.rdnt.m8face.data.watchface.AmbientStyle
import dev.rdnt.m8face.data.watchface.AmbientStyle.Companion.ambientStyleToListOption
import dev.rdnt.m8face.data.watchface.ColorStyle
import dev.rdnt.m8face.data.watchface.ColorStyle.Companion.colorStyleToListOption
+import dev.rdnt.m8face.data.watchface.LayoutStyle
import dev.rdnt.m8face.data.watchface.SecondsStyle
import dev.rdnt.m8face.data.watchface.SecondsStyle.Companion.secondsStyleToListOption
// Keys to matched content in the user style settings. We listen for changes to these
// values in the renderer and if new, we will update the database and update the watch face
// being rendered.
+const val LAYOUT_STYLE_SETTING = "layout_style_setting"
const val COLOR_STYLE_SETTING = "color_style_setting"
const val AMBIENT_STYLE_SETTING = "ambient_style_setting"
const val SECONDS_STYLE_SETTING = "seconds_style_setting"
const val MILITARY_TIME_SETTING = "military_time_setting"
-const val BIG_AMBIENT_SETTING = "big_ambient_setting"
/*
* Creates user styles in the settings activity associated with the watch face, so users can
@@ -45,6 +52,281 @@ const val BIG_AMBIENT_SETTING = "big_ambient_setting"
fun createUserStyleSchema(context: Context): UserStyleSchema {
// 1. Allows user to change the color styles of the watch face (if any are available).
+ // TODO: @rdnt fix icons, fix name resource IDs and screen reader name resource IDs
+
+ val info1 = ComplicationSlotsOption(
+ id = UserStyleSetting.Option.Id(LayoutStyle.INFO1.id),
+ resources = context.resources,
+ displayNameResourceId = R.string.info1_layout_style_name,
+ icon = Icon.createWithResource(context, R.drawable.aqua_style_icon),
+// screenReaderNameResourceId = R.string.sport_layout_style_name,
+ complicationSlotOverlays = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ HOUR_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ MINUTE_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ LEFT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ TOP_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ BOTTOM_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ )
+ )
+
+ val info2 = ComplicationSlotsOption(
+ id = UserStyleSetting.Option.Id(LayoutStyle.INFO2.id),
+ resources = context.resources,
+ displayNameResourceId = R.string.info2_layout_style_name,
+ icon = Icon.createWithResource(context, R.drawable.aqua_style_icon),
+// screenReaderNameResourceId = R.string.sport_layout_style_name,
+ complicationSlotOverlays = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ HOUR_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ MINUTE_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ LEFT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ RIGHT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ TOP_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ BOTTOM_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ )
+ )
+
+ val info3 = ComplicationSlotsOption(
+ id = UserStyleSetting.Option.Id(LayoutStyle.INFO3.id),
+ resources = context.resources,
+ displayNameResourceId = R.string.info3_layout_style_name,
+ icon = Icon.createWithResource(context, R.drawable.aqua_style_icon),
+// screenReaderNameResourceId = R.string.sport_layout_style_name,
+ complicationSlotOverlays = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ HOUR_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ MINUTE_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ TOP_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ BOTTOM_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ COMPLICATIONS_TOP_LEFT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ )
+ )
+
+ val info4 = ComplicationSlotsOption(
+ id = UserStyleSetting.Option.Id(LayoutStyle.INFO4.id),
+ resources = context.resources,
+ displayNameResourceId = R.string.info4_layout_style_name,
+ icon = Icon.createWithResource(context, R.drawable.aqua_style_icon),
+// screenReaderNameResourceId = R.string.sport_layout_style_name,
+ complicationSlotOverlays = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ HOUR_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ MINUTE_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ TOP_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ BOTTOM_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ COMPLICATIONS_TOP_LEFT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ COMPLICATIONS_BOTTOM_LEFT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ COMPLICATIONS_TOP_RIGHT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ COMPLICATIONS_BOTTOM_RIGHT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ )
+ )
+
+ val sport = ComplicationSlotsOption(
+ id = UserStyleSetting.Option.Id(LayoutStyle.SPORT.id),
+ resources = context.resources,
+ displayNameResourceId = R.string.sport_layout_style_name,
+ icon = Icon.createWithResource(context, R.drawable.aqua_style_icon),
+// screenReaderNameResourceId = R.string.sport_layout_style_name,
+ complicationSlotOverlays = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ HOUR_COMPLICATION_ID,
+ enabled = true,
+ complicationSlotBounds = ComplicationSlotBounds(RectF(
+ HOUR_SPORT_COMPLICATION_LEFT_BOUND,
+ HOUR_SPORT_COMPLICATION_TOP_BOUND,
+ HOUR_SPORT_COMPLICATION_RIGHT_BOUND,
+ HOUR_SPORT_COMPLICATION_BOTTOM_BOUND,
+ )),
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ MINUTE_COMPLICATION_ID,
+ enabled = true,
+ complicationSlotBounds = ComplicationSlotBounds(RectF(
+ MINUTE_SPORT_COMPLICATION_LEFT_BOUND,
+ MINUTE_SPORT_COMPLICATION_TOP_BOUND,
+ MINUTE_SPORT_COMPLICATION_RIGHT_BOUND,
+ MINUTE_SPORT_COMPLICATION_BOTTOM_BOUND,
+ )),
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ TOP_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ BOTTOM_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ RIGHT_TEXT_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ )
+ )
+
+ val focus = ComplicationSlotsOption(
+ id = UserStyleSetting.Option.Id(LayoutStyle.FOCUS.id),
+ resources = context.resources,
+ displayNameResourceId = R.string.focus_layout_style_name,
+ icon = Icon.createWithResource(context, R.drawable.aqua_style_icon),
+// screenReaderNameResourceId = R.string.sport_layout_style_name,
+ complicationSlotOverlays = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ HOUR_COMPLICATION_ID,
+ enabled = true,
+ complicationSlotBounds = ComplicationSlotBounds(RectF(
+ HOUR_FOCUS_COMPLICATION_LEFT_BOUND,
+ HOUR_FOCUS_COMPLICATION_TOP_BOUND,
+ HOUR_FOCUS_COMPLICATION_RIGHT_BOUND,
+ HOUR_FOCUS_COMPLICATION_BOTTOM_BOUND,
+ )),
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ MINUTE_COMPLICATION_ID,
+ enabled = true,
+ complicationSlotBounds = ComplicationSlotBounds(RectF(
+ MINUTE_FOCUS_COMPLICATION_LEFT_BOUND,
+ MINUTE_FOCUS_COMPLICATION_TOP_BOUND,
+ MINUTE_FOCUS_COMPLICATION_RIGHT_BOUND,
+ MINUTE_FOCUS_COMPLICATION_BOTTOM_BOUND,
+ )),
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ FOCUS_LEFT_ICON_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(
+ FOCUS_RIGHT_ICON_COMPLICATION_ID,
+ enabled = true,
+// nameResourceId = R.string.minute_complication_name,
+ ),
+ )
+ )
+
+ val layoutStyleSetting =
+ ComplicationSlotsUserStyleSetting(
+ id = UserStyleSetting.Id(LAYOUT_STYLE_SETTING),
+ resources = context.resources,
+ displayNameResourceId = R.string.layout_style_setting,
+ descriptionResourceId = R.string.layout_style_setting_description,
+ icon = Icon.createWithResource(context, R.drawable.mauve_style_icon), // TODO: @rdnt fix icon
+ complicationConfig = listOf(
+ info1,
+ info2,
+ info3,
+ info4,
+ sport,
+ focus,
+ ),
+ listOf(WatchFaceLayer.COMPLICATIONS),
+ defaultOption = info2,
+ )
+
val colorStyleSetting =
UserStyleSetting.ListUserStyleSetting(
UserStyleSetting.Id(COLOR_STYLE_SETTING),
@@ -92,24 +374,14 @@ fun createUserStyleSchema(context: Context): UserStyleSchema {
DateFormat.is24HourFormat(context), // default
)
- val bigAmbientSetting = UserStyleSetting.BooleanUserStyleSetting(
- UserStyleSetting.Id(BIG_AMBIENT_SETTING),
- context.resources,
- R.string.big_ambient_setting,
- R.string.big_ambient_setting_description,
- null,
- listOf(WatchFaceLayer.BASE),
- false,
- )
-
// 4. Create style settings to hold all options.
return UserStyleSchema(
listOf(
+ layoutStyleSetting,
colorStyleSetting,
ambientStyleSetting,
secondsStyleSetting,
militaryTimeSetting,
- bigAmbientSetting,
)
)
}
diff --git a/app/src/main/java/dev/rdnt/m8face/utils/VerticalComplication.kt b/app/src/main/java/dev/rdnt/m8face/utils/VerticalComplication.kt
index f0075b5..0e5449b 100644
--- a/app/src/main/java/dev/rdnt/m8face/utils/VerticalComplication.kt
+++ b/app/src/main/java/dev/rdnt/m8face/utils/VerticalComplication.kt
@@ -2,9 +2,7 @@ package dev.rdnt.m8face.utils
import android.content.Context
import android.graphics.*
-import android.util.Log
import androidx.core.content.ContextCompat
-import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.toBitmap
import androidx.wear.watchface.CanvasComplication
import androidx.wear.watchface.CanvasComplicationFactory
@@ -15,6 +13,8 @@ import java.time.Instant
import java.time.ZonedDateTime
class VerticalComplication(private val context: Context) : CanvasComplication {
+ private val renderer = ComplicationRenderer()
+
var tertiaryColor: Int = Color.parseColor("#8888bb")
set(tertiaryColor) {
field = tertiaryColor
@@ -23,25 +23,13 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
iconPaint.colorFilter = PorterDuffColorFilter(tertiaryColor, PorterDuff.Mode.SRC_IN)
prefixPaint.color = tertiaryColor
prefixPaint.alpha = 100
- }
-
- var opacity: Float = 1f
- set(opacity) {
- field = opacity
-
- val color = ColorUtils.blendARGB(Color.TRANSPARENT, tertiaryColor, opacity)
- textPaint.color = color
- titlePaint.color = color
-
- iconPaint.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
- imagePaint.alpha = (opacity * 255).toInt()
-
- prefixPaint.color = color
- prefixPaint.alpha = 100
+ renderer.reset()
}
private val textPaint = Paint().apply {
isAntiAlias = true
+ isDither = true
+ isFilterBitmap = true
typeface = context.resources.getFont(R.font.m8stealth57)
textAlign = Paint.Align.LEFT
color = tertiaryColor
@@ -77,25 +65,35 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
) {
if (bounds.isEmpty) return
- when (data.type) {
+ val data = if (data.type == ComplicationType.NO_DATA) {
+ val placeholder = (data as NoDataComplicationData).placeholder
+ placeholder ?: data
+ } else {
+ data
+ }
+
+ val bitmap = when (data.type) {
ComplicationType.SHORT_TEXT -> {
- renderShortTextComplication(canvas, bounds, data as ShortTextComplicationData)
+ renderer.render(bounds, data, ::renderShortTextComplication)
}
ComplicationType.MONOCHROMATIC_IMAGE -> {
- renderMonochromaticImageComplication(
- canvas,
- bounds,
- data as MonochromaticImageComplicationData
- )
+ renderer.render(bounds, data, ::renderMonochromaticImageComplication)
}
ComplicationType.SMALL_IMAGE -> {
- renderSmallImageComplication(canvas, bounds, data as SmallImageComplicationData)
+ renderer.render(bounds, data, ::renderSmallImageComplication)
}
else -> return
}
+
+ canvas.drawBitmap(
+ bitmap,
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ Paint(),
+ )
}
private fun renderShortTextComplication(
@@ -122,11 +120,11 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
if (isBattery) {
val drawable = ContextCompat.getDrawable(context, R.drawable.battery_icon_32)!!
icon = drawable.toBitmap(
- (32f / 78f * bounds.width()).toInt(),
- (32f / 78f * bounds.width()).toInt()
+ (32f).toInt(),
+ (32f).toInt()
)
iconBounds =
- Rect(0, 0, (32f / 78f * bounds.width()).toInt(), (32f / 78f * bounds.width()).toInt())
+ Rect(0, 0, (32f).toInt(), (32f).toInt())
} else if (data.monochromaticImage != null) {
val drawable = data.monochromaticImage!!.image.loadDrawable(context)
if (drawable != null) {
@@ -149,11 +147,11 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
}
if (text.length <= 3) {
- textPaint.textSize = 24F / 78F * bounds.width()
+ textPaint.textSize = 24F
} else if (text.length <= 6) {
- textPaint.textSize = 16F / 78F * bounds.width()
+ textPaint.textSize = 16F
} else {
- textPaint.textSize = 12F / 78F * bounds.width()
+ textPaint.textSize = 12F
}
val textBounds = Rect()
@@ -168,11 +166,11 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
if (title != null) {
if (title.length <= 3) {
- titlePaint.textSize = 24F / 78F * bounds.width()
+ titlePaint.textSize = 24F
} else if (title.length <= 6) {
- titlePaint.textSize = 16F / 78F * bounds.width()
+ titlePaint.textSize = 16F
} else {
- titlePaint.textSize = 12F / 78F * bounds.width()
+ titlePaint.textSize = 12F
}
titlePaint.getTextBounds(title, 0, title.length, titleBounds)
@@ -188,22 +186,28 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
iconOffsetY = (height - iconBounds.height()).toFloat() / 2f
textOffsetY = (height - textBounds.height()).toFloat() / 2f
- iconOffsetY += 9f / 132f * bounds.height()
+ iconOffsetY += 6f
if (isBattery) {
iconOffsetY = iconOffsetY.toInt().toFloat()
}
- textOffsetY += 9f / 132f * bounds.height()
+ textOffsetY += 6f
} else if (title != null) {
val height = titleBounds.height() + textBounds.height()
titleOffsetY = (height - titleBounds.height()).toFloat() / 2f
textOffsetY = (height - textBounds.height()).toFloat() / 2f
- titleOffsetY += 9f / 132f * bounds.height()
- textOffsetY += 9f / 132f * bounds.height()
+ titleOffsetY += 6f
+ textOffsetY += 6f
}
+
+
+
+
+
+
if (icon != null) {
val dstRect = RectF(
bounds.exactCenterX() - iconBounds.width() / 2,
@@ -240,6 +244,7 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
bounds.exactCenterY() + textBounds.height() / 2 + textOffsetY,
textPaint
)
+
}
private fun renderMonochromaticImageComplication(
@@ -252,7 +257,7 @@ class VerticalComplication(private val context: Context) : CanvasComplication {
val drawable = data.monochromaticImage.image.loadDrawable(context) ?: return
- val size = (bounds.width().coerceAtMost(bounds.height()).toFloat() * 0.8f).toInt()
+ val size = (bounds.width().coerceAtMost(bounds.height()).toFloat() * 0.75f).toInt()
icon = drawable.toBitmap(size, size)
iconBounds = Rect(0, 0, size, size)
diff --git a/app/src/main/res/drawable-nodpi/bold_outline_style_icon.png b/app/src/main/res/drawable-nodpi/bold_outline_style_icon.png
index d3ba120..b9c4542 100644
Binary files a/app/src/main/res/drawable-nodpi/bold_outline_style_icon.png and b/app/src/main/res/drawable-nodpi/bold_outline_style_icon.png differ
diff --git a/app/src/main/res/drawable-nodpi/watch_preview.png b/app/src/main/res/drawable-nodpi/watch_preview.png
index 9ed6b23..2ba7c71 100644
Binary files a/app/src/main/res/drawable-nodpi/watch_preview.png and b/app/src/main/res/drawable-nodpi/watch_preview.png differ
diff --git a/app/src/main/res/drawable-nodpi/watch_preview_subtle.png b/app/src/main/res/drawable-nodpi/watch_preview_subtle.png
index 98018a4..42d2e99 100644
Binary files a/app/src/main/res/drawable-nodpi/watch_preview_subtle.png and b/app/src/main/res/drawable-nodpi/watch_preview_subtle.png differ
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fed0f35..d9f9aa1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -14,16 +14,18 @@
limitations under the License.
-->
- m8face
+ M8
- m8face
+ M8
+ Layout Style
Color
Ambient Style
Seconds Indicator
+ Layout Style
Color scheme
Ambient mode style
Seconds indicator style
@@ -75,20 +77,41 @@
Snow
Onyx
- Outline
- Bold
- Filled
+ Outline I
+ Outline II
+ Bold I
+ Bold II
+ Filled I
+ Filled II
+ Detailed
None
Dashes
Dots
+ Hours
+ Minutes
+
Left
Right
+
Top
Bottom
+ Top Left
+ Bottom Left
+ Top Right
+ Bottom Right
+
+
+ Info I
+ Info II
+ Info III
+ Info IV
+ Focus
+ Sport
+
@@ -126,12 +149,16 @@
Hour Pips
Military Time
Big Ambient
+ Detailed Ambient
+ Debug
Whether to draw or not
Whether to use military instead of standard time
Use big time on ambient mode
+ Show complications on ambient mode
+ >.>
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 651071e..94174ba 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,12 +1,11 @@
[versions]
accompanist-pager = "0.32.0"
-activity-compose = "1.8.0"
-android-gradle-plugin = "8.1.2"
-androidx-activity = "1.8.0"
-androidx-lifecycle = "2.6.2"
-androidx-wear-watchface = "1.1.1"
-compiler = "1.5.3"
-compose-material = "1.2.0"
+activity-compose = "1.8.2"
+android-gradle-plugin = "8.3.1"
+androidx-activity = "1.8.2"
+androidx-lifecycle = "2.7.0"
+androidx-wear-watchface = "1.2.1"
+compiler = "1.5.11"
horologist-compose-layout = "0.5.7"
ktlint = "0.46.1"
org-jetbrains-kotlin = "1.9.10"
@@ -15,14 +14,14 @@ org-jetbrains-kotlinx = "1.7.3"
[libraries]
accompanist-pager = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanist-pager" }
accompanist-pager-indicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanist-pager" }
-android-material = "com.google.android.material:material:1.10.0"
+android-material = "com.google.android.material:material:1.11.0"
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
androidx-compose-material-iconsExtended = { module = "androidx.compose.material:material-icons-extended" }
androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
androidx-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" }
-androidx-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version = "1.3.0-alpha07" }
-androidx-compose-material = { module = "androidx.wear.compose:compose-material", version = "1.3.0-alpha07" }
+androidx-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version = "1.3.0" }
+androidx-compose-material = { module = "androidx.wear.compose:compose-material", version = "1.3.0" }
androidx-core-ktx = "androidx.core:core-ktx:1.12.0"
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 2848974..309b4e1 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/settings.gradle b/settings.gradle
index 4b87ba7..f299f1c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -33,4 +33,4 @@ dependencyResolutionManagement {
}
include ":app"
-rootProject.name = "m8face"
+rootProject.name = "m8"