WatchFaceEditorContract.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.
 */

package androidx.wear.watchface.editor

import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.support.wearable.watchface.Constants
import androidx.activity.result.contract.ActivityResultContract
import androidx.versionedparcelable.ParcelUtils
import androidx.wear.watchface.style.UserStyle
import androidx.wear.watchface.style.data.UserStyleWireFormat

/**
 * The request sent by [WatchFaceEditorContract.createIntent]. The editing session's result should
 * be reported via [Activity.setWatchRequestResult].
 */
public class EditorRequest(
    /** The [ComponentName] on the watch face being edited. */
    public val watchFaceComponentName: ComponentName,

    /** A unique ID for the instance of the watch face being edited. */
    public val watchFaceInstanceId: String,

    /** The initial [UserStyle], only required for a headless [EditorSession]. */
    public val initialUserStyle: Map<String, String>?
) {
    public companion object {
        /**
         * Returns an [EditorRequest] saved to a [Intent] by [WatchFaceEditorContract.createIntent]
         * if there is one or `null` otherwise. Intended for use by the watch face editor activity.
         */
        @JvmStatic
        public fun createFromIntent(intent: Intent): EditorRequest? {
            val componentName =
                intent.getParcelableExtra<ComponentName>(COMPONENT_NAME_KEY)
                    ?: intent.getParcelableExtra(Constants.EXTRA_WATCH_FACE_COMPONENT)
            val instanceId = intent.getStringExtra(INSTANCE_ID)!!
            val userStyleKey = intent.getStringArrayExtra(USER_STYLE_KEYS)
            val userStyleValue = intent.getStringArrayExtra(USER_STYLE_VALUES)
            return componentName?.let {
                if (userStyleKey != null && userStyleValue != null &&
                    userStyleKey.size == userStyleValue.size
                ) {
                    EditorRequest(
                        componentName,
                        instanceId,
                        HashMap<String, String>().apply {
                            for (i in userStyleKey.indices) {
                                put(userStyleKey[i], userStyleValue[i])
                            }
                        }
                    )
                } else {
                    EditorRequest(componentName, instanceId, null)
                }
            }
        }

        internal const val COMPONENT_NAME_KEY: String = "COMPONENT_NAME_KEY"
        internal const val INSTANCE_ID: String = "INSTANCE_ID"
        internal const val USER_STYLE_KEYS: String = "USER_STYLE_KEYS"
        internal const val USER_STYLE_VALUES: String = "USER_STYLE_VALUES"
    }
}

/**
 * The result for a successful [EditorRequest], to be returned via [Activity.setWatchRequestResult].
 */
public class EditorResult(
    /** The updated style, see [UserStyle]. */
    public val userStyle: Map<String, String>
) {
    internal companion object {
        internal const val USER_STYLE_KEY: String = "USER_STYLE_KEY"
    }
}

/** An [ActivityResultContract] for invoking a watch face editor. */
public open class WatchFaceEditorContract : ActivityResultContract<EditorRequest, EditorResult>() {

    public companion object {
        public const val ACTION_WATCH_FACE_EDITOR: String =
            "androidx.wear.watchface.editor.action.WATCH_FACE_EDITOR"
    }

    override fun createIntent(context: Context, input: EditorRequest): Intent =
        Intent(ACTION_WATCH_FACE_EDITOR).apply {
            putExtra(EditorRequest.COMPONENT_NAME_KEY, input.watchFaceComponentName)
            putExtra(EditorRequest.INSTANCE_ID, input.watchFaceInstanceId)
            input.initialUserStyle?.let {
                putExtra(EditorRequest.USER_STYLE_KEYS, it.keys.toTypedArray())
                putExtra(EditorRequest.USER_STYLE_VALUES, it.values.toTypedArray())
            }
        }

    override fun parseResult(resultCode: Int, intent: Intent?): EditorResult {
        val extras = intent!!.extras!!
        extras.classLoader = this::class.java.classLoader
        return EditorResult(
            ParcelUtils.fromParcelable<UserStyleWireFormat>(
                extras.getParcelable(EditorResult.USER_STYLE_KEY)!!
            ).mUserStyle
        )
    }
}