InteractiveWatchFaceWcsClient.kt
/*
* Copyright 2020 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
*
* http://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 androidx.wear.watchface.client
import android.graphics.Bitmap
import android.os.IBinder
import android.support.wearable.watchface.SharedMemoryImage
import androidx.annotation.IntRange
import androidx.annotation.Px
import androidx.annotation.RequiresApi
import androidx.wear.complications.data.ComplicationData
import androidx.wear.watchface.RenderParameters
import androidx.wear.watchface.control.IInteractiveWatchFaceWCS
import androidx.wear.watchface.control.data.WatchfaceScreenshotParams
import androidx.wear.watchface.data.ComplicationBoundsType
import androidx.wear.watchface.data.IdAndComplicationDataWireFormat
import androidx.wear.watchface.style.UserStyle
import androidx.wear.watchface.style.UserStyleSchema
import androidx.wear.watchface.style.data.UserStyleWireFormat
/**
* Controls a stateful remote interactive watch face with an interface tailored for WCS the
* WearOS system server responsible for watch face management. Typically this will be used for
* the current active watch face.
*
* Note clients should call [close] when finished.
*/
public interface InteractiveWatchFaceWcsClient : AutoCloseable {
public companion object {
/**
* Constructs an [InteractiveWatchFaceWcsClient] from the [IBinder] returned by [asBinder].
*/
@JvmStatic
public fun createFromBinder(binder: IBinder): InteractiveWatchFaceWcsClient =
InteractiveWatchFaceWcsClientImpl(binder)
}
/**
* Sends new ComplicationData to the watch face. Note this doesn't have to be a full update,
* it's possible to update just one complication at a time, but doing so may result in a less
* visually clean transition.
*/
public fun updateComplicationData(idToComplicationData: Map<Int, ComplicationData>)
/**
* Requests for a WebP compressed shared memory backed [Bitmap] containing a screenshot of
* the watch face with the given settings.
*
* @param renderParameters The [RenderParameters] to draw with.
* @param compressionQuality The WebP compression quality, 100 = loss less.
* @param calendarTimeMillis The UTC time in milliseconds since the epoch to render with.
* @param userStyle Optional [UserStyle] to render with, if null the current style is used.
* @param idAndComplicationData Map of complication ids to [ComplicationData] to render with, or
* if null then the existing complication data if any is used.
* @return A WebP compressed shared memory backed [Bitmap] containing a screenshot of the watch
* face with the given settings.
*/
@RequiresApi(27)
public fun takeWatchFaceScreenshot(
renderParameters: RenderParameters,
@IntRange(from = 0, to = 100)
compressionQuality: Int,
calendarTimeMillis: Long,
userStyle: UserStyle?,
idAndComplicationData: Map<Int, ComplicationData>?
): Bitmap
/** The UTC reference preview time for this watch face in milliseconds since the epoch. */
public val previewReferenceTimeMillis: Long
/**
* Sets the watch face's current [UserStyle]. Note this may alter [complicationState].
*/
public fun setUserStyle(userStyle: UserStyle)
/**
* Sets the watch face's current UserStyle represented as a Map<String, String>. This can be
* helpful to avoid having to construct a [UserStyle] which requires the [UserStyleSchema]
* which is an additional IPC. Note this may alter [complicationState].
*/
public fun setUserStyle(userStyle: Map<String, String>)
/** Returns the ID of this watch face instance. */
public val instanceId: String
/** The watch face's [UserStyleSchema]. */
public val userStyleSchema: UserStyleSchema
/**
* Map of complication ids to [ComplicationState] for each complication slot. Note
* this can change, typically in response to styling.
*/
public val complicationState: Map<Int, ComplicationState>
/** Returns the associated [IBinder]. Allows this interface to be passed over AIDL. */
public fun asBinder(): IBinder
/** Returns the ID of the complication at the given coordinates or `null` if there isn't one.*/
@SuppressWarnings("AutoBoxing")
public fun getComplicationIdAt(@Px x: Int, @Px y: Int): Int? =
complicationState.asSequence().firstOrNull {
it.value.isEnabled && when (it.value.boundsType) {
ComplicationBoundsType.ROUND_RECT -> it.value.bounds.contains(x, y)
ComplicationBoundsType.BACKGROUND -> false
ComplicationBoundsType.EDGE -> false
else -> false
}
}?.key
/**
* Requests the specified complication is highlighted for a short period to bring attention to
* it.
*/
public fun bringAttentionToComplication(complicationId: Int)
}
/** Controls a stateful remote interactive watch face with an interface tailored for WCS. */
internal class InteractiveWatchFaceWcsClientImpl internal constructor(
private val iInteractiveWatchFaceWcs: IInteractiveWatchFaceWCS
) : InteractiveWatchFaceWcsClient {
constructor(binder: IBinder) : this(IInteractiveWatchFaceWCS.Stub.asInterface(binder))
override fun updateComplicationData(idToComplicationData: Map<Int, ComplicationData>) {
iInteractiveWatchFaceWcs.updateComplicationData(
idToComplicationData.map {
IdAndComplicationDataWireFormat(it.key, it.value.asWireComplicationData())
}
)
}
@RequiresApi(27)
override fun takeWatchFaceScreenshot(
renderParameters: RenderParameters,
@IntRange(from = 0, to = 100)
compressionQuality: Int,
calendarTimeMillis: Long,
userStyle: UserStyle?,
idAndComplicationData: Map<Int, ComplicationData>?
): Bitmap = SharedMemoryImage.ashmemCompressedImageBundleToBitmap(
iInteractiveWatchFaceWcs.takeWatchFaceScreenshot(
WatchfaceScreenshotParams(
renderParameters.toWireFormat(),
compressionQuality,
calendarTimeMillis,
userStyle?.toWireFormat(),
idAndComplicationData?.map {
IdAndComplicationDataWireFormat(
it.key,
it.value.asWireComplicationData()
)
}
)
)
)
override val previewReferenceTimeMillis: Long
get() = iInteractiveWatchFaceWcs.previewReferenceTimeMillis
override fun setUserStyle(userStyle: UserStyle) {
iInteractiveWatchFaceWcs.setCurrentUserStyle(userStyle.toWireFormat())
}
override fun setUserStyle(userStyle: Map<String, String>) {
iInteractiveWatchFaceWcs.setCurrentUserStyle(UserStyleWireFormat(userStyle))
}
override val instanceId: String
get() = iInteractiveWatchFaceWcs.instanceId
override val userStyleSchema: UserStyleSchema
get() = UserStyleSchema(iInteractiveWatchFaceWcs.userStyleSchema)
override val complicationState: Map<Int, ComplicationState>
get() = iInteractiveWatchFaceWcs.complicationDetails.associateBy(
{ it.id },
{ ComplicationState(it.complicationState) }
)
override fun close() {
iInteractiveWatchFaceWcs.release()
}
override fun asBinder(): IBinder = iInteractiveWatchFaceWcs.asBinder()
override fun bringAttentionToComplication(complicationId: Int) {
iInteractiveWatchFaceWcs.bringAttentionToComplication(complicationId)
}
}