diff --git a/gradle.properties b/gradle.properties index 34b5d40..8105770 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official group=io.rebble.libpebblecommon -version=0.0.25 -org.gradle.jvmargs=-Xms2G -Xmx2G \ No newline at end of file +version=0.0.26 +org.gradle.jvmargs=-Xms2G -Xmx2G diff --git a/src/androidMain/kotlin/util/Bitmap.kt b/src/androidMain/kotlin/util/Bitmap.kt new file mode 100644 index 0000000..32c4f55 --- /dev/null +++ b/src/androidMain/kotlin/util/Bitmap.kt @@ -0,0 +1,27 @@ +package io.rebble.libpebblecommon.util + +/** + * Convert android bitmap into common multiplatform bitmap. + * + * Only supported for [android.graphics.Bitmap.Config.ARGB_8888] formats + */ +actual class Bitmap(private val androidBitmap: android.graphics.Bitmap) { + init { + if (androidBitmap.config != android.graphics.Bitmap.Config.ARGB_8888) { + throw IllegalArgumentException("Only ARGB_8888 bitmaps are supported") + } + } + + actual val width: Int + get() = androidBitmap.width + actual val height: Int + get() = androidBitmap.height + + /** + * Return pixel at the specified position at in AARRGGBB format. + */ + actual fun getPixel(x: Int, y: Int): Int { + return androidBitmap.getPixel(x, y) + } + +} \ No newline at end of file diff --git a/src/commonMain/kotlin/io/rebble/libpebblecommon/packets/AppCustomization.kt b/src/commonMain/kotlin/io/rebble/libpebblecommon/packets/AppCustomization.kt new file mode 100644 index 0000000..506d0bc --- /dev/null +++ b/src/commonMain/kotlin/io/rebble/libpebblecommon/packets/AppCustomization.kt @@ -0,0 +1,91 @@ +package io.rebble.libpebblecommon.packets + +import io.rebble.libpebblecommon.protocolhelpers.PebblePacket +import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint +import io.rebble.libpebblecommon.structmapper.SBytes +import io.rebble.libpebblecommon.structmapper.SFixedString +import io.rebble.libpebblecommon.structmapper.SUByte +import io.rebble.libpebblecommon.structmapper.SUShort +import io.rebble.libpebblecommon.util.Bitmap +import io.rebble.libpebblecommon.util.DataBuffer + +class AppCustomizationSetStockAppTitleMessage( + appType: AppType, + newName: String +) : PebblePacket(ProtocolEndpoint.APP_CUSTOMIZE) { + val appType = SUByte(m, appType.value) + val name = SFixedString(m, 30, newName) +} + +class AppCustomizationSetStockAppIconMessage( + appType: AppType, + icon: Bitmap +) : PebblePacket(ProtocolEndpoint.APP_CUSTOMIZE) { + // First bit being set signifies that this is icon packet instead of name packet + val appType = SUByte(m, appType.value or 0b10000000u) + val bytesPerLine = SUShort(m, endianness = '<') + + /** + * No idea what flags are possible. Stock app always sends 4096 here. + */ + val flags = SUShort(m, 4096u, endianness = '<') + + /** + * Offset is not supported by app. Always 0. + */ + val originY = SUShort(m, 0u, endianness = '<') + val originX = SUShort(m, 0u, endianness = '<') + + val width = SUShort(m, endianness = '<') + val height = SUShort(m, endianness = '<') + + val imageData = SBytes(m) + + init { + val width = icon.width + val height = icon.height + + this.width.set(width.toUShort()) + this.height.set(height.toUShort()) + + val bytesPerLine = ((width + 31) / 32) * 4 + val totalBytes = bytesPerLine * height + + val dataBuffer = DataBuffer(totalBytes) + + for (y in 0 until height) { + for (lineIntIndex in 0 until bytesPerLine / 4) { + var currentInt = 0 + val startX = lineIntIndex * 32 + val pixelsToTraverse = 32.coerceAtMost(width - startX) + + for (innerX in 0 until pixelsToTraverse) { + val x = startX + innerX + + val pixelValue = icon.getPixel(x, y) + val valueWithoutAlpha = pixelValue and 0x00FFFFFF + + val pixelBit = if (valueWithoutAlpha > 0) { + 1 + } else { + 0 + } + + currentInt = currentInt or (pixelBit shl innerX) + } + + dataBuffer.putInt(currentInt) + } + } + + val imageBytes = dataBuffer.array() + imageData.set(imageBytes, imageBytes.size) + + this.bytesPerLine.set(bytesPerLine.toUShort()) + } +} + +enum class AppType(val value: UByte) { + SPORTS(0x00u), + GOLF(0x01u), +} diff --git a/src/commonMain/kotlin/io/rebble/libpebblecommon/services/SystemService.kt b/src/commonMain/kotlin/io/rebble/libpebblecommon/services/SystemService.kt index afb34f4..2d13d3a 100644 --- a/src/commonMain/kotlin/io/rebble/libpebblecommon/services/SystemService.kt +++ b/src/commonMain/kotlin/io/rebble/libpebblecommon/services/SystemService.kt @@ -2,8 +2,10 @@ package io.rebble.libpebblecommon.services import io.rebble.libpebblecommon.PacketPriority import io.rebble.libpebblecommon.ProtocolHandler -import io.rebble.libpebblecommon.getPlatform -import io.rebble.libpebblecommon.packets.* +import io.rebble.libpebblecommon.packets.PhoneAppVersion +import io.rebble.libpebblecommon.packets.SystemPacket +import io.rebble.libpebblecommon.packets.WatchFactoryData +import io.rebble.libpebblecommon.packets.WatchVersion import io.rebble.libpebblecommon.protocolhelpers.PebblePacket import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint import io.rebble.libpebblecommon.structmapper.SInt @@ -83,4 +85,4 @@ class SystemService(private val protocolHandler: ProtocolHandler) : ProtocolServ } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/io/rebble/libpebblecommon/services/appmessage/AppMessageService.kt b/src/commonMain/kotlin/io/rebble/libpebblecommon/services/appmessage/AppMessageService.kt index f370e54..814a48e 100644 --- a/src/commonMain/kotlin/io/rebble/libpebblecommon/services/appmessage/AppMessageService.kt +++ b/src/commonMain/kotlin/io/rebble/libpebblecommon/services/appmessage/AppMessageService.kt @@ -1,6 +1,8 @@ package io.rebble.libpebblecommon.services.appmessage import io.rebble.libpebblecommon.ProtocolHandler +import io.rebble.libpebblecommon.packets.AppCustomizationSetStockAppIconMessage +import io.rebble.libpebblecommon.packets.AppCustomizationSetStockAppTitleMessage import io.rebble.libpebblecommon.packets.AppMessage import io.rebble.libpebblecommon.protocolhelpers.PebblePacket import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint @@ -21,6 +23,14 @@ class AppMessageService(private val protocolHandler: ProtocolHandler) : Protocol protocolHandler.send(packet) } + suspend fun send(packet: AppCustomizationSetStockAppIconMessage) { + protocolHandler.send(packet) + } + + suspend fun send(packet: AppCustomizationSetStockAppTitleMessage) { + protocolHandler.send(packet) + } + fun receive(packet: PebblePacket) { if (packet !is AppMessage) { throw IllegalStateException("Received invalid packet type: $packet") diff --git a/src/commonMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt b/src/commonMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt new file mode 100644 index 0000000..2ba646b --- /dev/null +++ b/src/commonMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt @@ -0,0 +1,11 @@ +package io.rebble.libpebblecommon.util + +expect class Bitmap { + val width: Int + val height: Int + + /** + * Return pixel at the specified position at in AARRGGBB format. + */ + fun getPixel(x: Int, y: Int): Int +} \ No newline at end of file diff --git a/src/iosMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt b/src/iosMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt new file mode 100644 index 0000000..985f8ad --- /dev/null +++ b/src/iosMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt @@ -0,0 +1,15 @@ +package io.rebble.libpebblecommon.util + +actual class Bitmap { + actual val width: Int + get() = throw UnsupportedOperationException("Not supported on iOS yet") + actual val height: Int + get() = throw UnsupportedOperationException("Not supported on iOS yet") + + /** + * Return pixel at the specified position at in AARRGGBB format. + */ + actual fun getPixel(x: Int, y: Int): Int { + throw UnsupportedOperationException("Not supported on iOS yet") + } +} \ No newline at end of file diff --git a/src/jvmMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt b/src/jvmMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt new file mode 100644 index 0000000..d2ad47f --- /dev/null +++ b/src/jvmMain/kotlin/io/rebble/libpebblecommon/util/Bitmap.kt @@ -0,0 +1,15 @@ +package io.rebble.libpebblecommon.util + +actual class Bitmap { + actual val width: Int + get() = throw UnsupportedOperationException("Not supported on generic JVM") + actual val height: Int + get() = throw UnsupportedOperationException("Not supported on generic JVM") + + /** + * Return pixel at the specified position at in AARRGGBB format. + */ + actual fun getPixel(x: Int, y: Int): Int { + throw UnsupportedOperationException("Not supported on generic JVM") + } +} \ No newline at end of file