UserStyleCategory.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.style
import android.graphics.drawable.Icon
import androidx.annotation.RestrictTo
import androidx.wear.watchface.style.data.BooleanUserStyleCategoryWireFormat
import androidx.wear.watchface.style.data.ComplicationsUserStyleCategoryWireFormat
import androidx.wear.watchface.style.data.DoubleRangeUserStyleCategoryWireFormat
import androidx.wear.watchface.style.data.ListUserStyleCategoryWireFormat
import androidx.wear.watchface.style.data.LongRangeUserStyleCategoryWireFormat
import androidx.wear.watchface.style.data.UserStyleCategoryWireFormat
/**
* Watch faces often have user configurable styles. The definition of what is a style is left up
* to the watch face but it typically incorporates a variety of categories such as: color,
* visual theme for watch hands, font, tick shape, complications, audio elements, etc...
*/
public abstract class UserStyleCategory(
/** Identifier for the element, must be unique. */
public val id: String,
/** Localized human readable name for the element, used in the userStyle selection UI. */
public val displayName: CharSequence,
/** Localized description string displayed under the displayName. */
public val description: CharSequence,
/** Icon for use in the style selection UI. */
public val icon: Icon?,
/**
* List of options for this UserStyleCategory. Depending on the type of UserStyleCategory this
* may be an exhaustive list, or just examples to populate a ListView in case the
* UserStyleCategory isn't supported by the UI (e.g. a new WatchFace with an old Companion).
*/
public val options: List<Option>,
/**
* The default option index, used if nothing has been selected within the [options] list.
*/
public val defaultOptionIndex: Int,
/**
* Used by the style configuration UI. Describes which rendering layers this style affects.
*/
public val affectsLayers: Collection<Layer>
) {
public companion object {
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public fun createFromWireFormat(
wireFormat: UserStyleCategoryWireFormat
): UserStyleCategory = when (wireFormat) {
is BooleanUserStyleCategoryWireFormat -> BooleanUserStyleCategory(wireFormat)
is ComplicationsUserStyleCategoryWireFormat ->
ComplicationsUserStyleCategory(wireFormat)
is DoubleRangeUserStyleCategoryWireFormat -> DoubleRangeUserStyleCategory(wireFormat)
is ListUserStyleCategoryWireFormat -> ListUserStyleCategory(wireFormat)
is LongRangeUserStyleCategoryWireFormat -> LongRangeUserStyleCategory(wireFormat)
else -> throw IllegalArgumentException(
"Unknown StyleCategoryWireFormat " + wireFormat::javaClass.name
)
}
}
init {
require(defaultOptionIndex >= 0 && defaultOptionIndex < options.size) {
"defaultOptionIndex must be in the range [0 .. options.size)"
}
}
internal fun getCategoryOptionForId(id: String?) =
if (id == null) {
options[defaultOptionIndex]
} else {
getOptionForId(id)
}
internal constructor(wireFormat: UserStyleCategoryWireFormat) : this(
wireFormat.mId,
wireFormat.mDisplayName,
wireFormat.mDescription,
wireFormat.mIcon,
wireFormat.mOptions.map { Option.createFromWireFormat(it) },
wireFormat.mDefaultOptionIndex,
wireFormat.mAffectsLayers.map { Layer.values()[it] }
)
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public abstract fun toWireFormat(): UserStyleCategoryWireFormat
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public fun getWireFormatOptionsList(): List<UserStyleCategoryWireFormat.OptionWireFormat> =
options.map { it.toWireFormat() }
/** Returns the default for when the user hasn't selected an option. */
public fun getDefaultOption(): Option = options[defaultOptionIndex]
/**
* Represents a choice within a style category.
*
* @property id Machine readable identifier for the style setting.
*/
public abstract class Option(
/** Identifier for the option, must be unique within the UserStyleCategory. */
public val id: String
) {
public companion object {
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public fun createFromWireFormat(
wireFormat: UserStyleCategoryWireFormat.OptionWireFormat
): Option =
when (wireFormat) {
is BooleanUserStyleCategoryWireFormat.BooleanOptionWireFormat ->
BooleanUserStyleCategory.BooleanOption(wireFormat)
is ComplicationsUserStyleCategoryWireFormat.ComplicationsOptionWireFormat ->
ComplicationsUserStyleCategory.ComplicationsOption(wireFormat)
is DoubleRangeUserStyleCategoryWireFormat.DoubleRangeOptionWireFormat ->
DoubleRangeUserStyleCategory.DoubleRangeOption(wireFormat)
is ListUserStyleCategoryWireFormat.ListOptionWireFormat ->
ListUserStyleCategory.ListOption(wireFormat)
is LongRangeUserStyleCategoryWireFormat.LongRangeOptionWireFormat ->
LongRangeUserStyleCategory.LongRangeOption(wireFormat)
else -> throw IllegalArgumentException(
"Unknown StyleCategoryWireFormat.OptionWireFormat " +
wireFormat::javaClass.name
)
}
}
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public abstract fun toWireFormat(): UserStyleCategoryWireFormat.OptionWireFormat
}
/**
* Translates an option name into an option. This will need to be overridden for userStyle
* categories that can't sensibly be fully enumerated (e.g. a full 24-bit color picker).
*
* @param optionId The ID of the option
* @return An [Option] corresponding to the name. This could either be one of the
* options from userStyleCategories or a newly constructed Option depending on the nature
* of the UserStyleCategory. If optionName is unrecognized then the default value for the
* category should be returned.
*/
public open fun getOptionForId(optionId: String): Option =
options.find { it.id == optionId } ?: options[defaultOptionIndex]
}