ComplicationSlotBounds.kt
/*
* 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
*
* 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.
*/
/** Removes the KT class from the public API */
@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
package androidx.wear.watchface.complications
import android.content.res.Resources
import android.content.res.TypedArray
import android.content.res.XmlResourceParser
import android.graphics.RectF
import androidx.annotation.RestrictTo
import androidx.wear.watchface.complications.data.ComplicationType
import org.xmlpull.v1.XmlPullParser
/**
* ComplicationSlotBounds are defined by fractional screen space coordinates in unit-square [0..1].
* These bounds will be subsequently clamped to the unit square and converted to screen space
* coordinates. NB 0 and 1 are included in the unit square.
*
* One bound is expected per [ComplicationType] to allow [androidx.wear.watchface.ComplicationSlot]s
* to change shape depending on the type.
*/
public class ComplicationSlotBounds(
/** Per [ComplicationType] fractional unit-square screen space complication bounds. */
public val perComplicationTypeBounds: Map<ComplicationType, RectF>
) {
/**
* Constructs a ComplicationSlotBounds where all complication types have the same screen space
* unit-square bounds.
*/
public constructor(bounds: RectF) : this(ComplicationType.values().associateWith { bounds })
init {
require(perComplicationTypeBounds.size == ComplicationType.values().size) {
"ComplicationSlotBounds must contain entries for each ComplicationType"
}
for (type in ComplicationType.values()) {
require(perComplicationTypeBounds.containsKey(type)) {
"Missing bounds for $type"
}
}
}
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
companion object {
/**
* The [parser] should be inside a node with any number of ComplicationSlotBounds child
* nodes. No other child nodes are expected.
*/
fun inflate(resources: Resources, parser: XmlResourceParser): ComplicationSlotBounds? {
var type = 0
val outerDepth = parser.depth
val perComplicationTypeBounds by lazy { HashMap<ComplicationType, RectF>() }
do {
if (type == XmlPullParser.START_TAG) {
when (parser.name) {
"ComplicationSlotBounds" -> {
val attrs = resources.obtainAttributes(
parser,
R.styleable.ComplicationSlotBounds
)
val rect = RectF(
attrs.requireAndGet(R.styleable.ComplicationSlotBounds_left) {
"ComplicationSlotBounds must define 'left'"
},
attrs.requireAndGet(R.styleable.ComplicationSlotBounds_top) {
"ComplicationSlotBounds must define 'top'"
},
attrs.requireAndGet(R.styleable.ComplicationSlotBounds_right) {
"ComplicationSlotBounds must define 'right'"
},
attrs.requireAndGet(R.styleable.ComplicationSlotBounds_bottom) {
"ComplicationSlotBounds must define 'bottom'"
}
)
if (attrs.hasValue(
R.styleable.ComplicationSlotBounds_complicationType
)
) {
val complicationType = ComplicationType.fromWireType(
attrs.getInteger(
R.styleable.ComplicationSlotBounds_complicationType,
0
)
)
require(
!perComplicationTypeBounds.contains(complicationType)
) {
"Duplicate $complicationType"
}
perComplicationTypeBounds[complicationType] = rect
} else {
for (complicationType in ComplicationType.values()) {
require(
!perComplicationTypeBounds.contains(
complicationType
)
) {
"Duplicate $complicationType"
}
perComplicationTypeBounds[complicationType] = rect
}
}
attrs.recycle()
}
else -> throw IllegalArgumentException(
"Unexpected node ${parser.name} at line ${parser.lineNumber}"
)
}
}
type = parser.next()
} while (type != XmlPullParser.END_DOCUMENT && parser.depth > outerDepth)
return if (perComplicationTypeBounds.isEmpty()) {
null
} else {
ComplicationSlotBounds(perComplicationTypeBounds)
}
}
}
}
internal fun TypedArray.requireAndGet(resourceId: Int, produceError: () -> String): Float {
require(hasValue(resourceId), produceError)
return getFloat(resourceId, 0f)
}