WatchFaceControlService.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.control

import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
import androidx.wear.watchface.WatchFaceService
import androidx.wear.watchface.control.data.HeadlessWatchFaceInstanceParams
import androidx.wear.watchface.control.data.WallpaperInteractiveWatchFaceInstanceParams
import androidx.wear.watchface.runOnHandler

/**
 * A service for creating and controlling watch face instances.
 *
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@RequiresApi(27)
public class WatchFaceControlService : Service() {
    private val watchFaceInstanceServiceStub =
        IWatchFaceInstanceServiceStub(this, Handler(Looper.getMainLooper()))

    /** @hide */
    public companion object {
        public const val ACTION_WATCHFACE_CONTROL_SERVICE: String =
            "com.google.android.wearable.action.WATCH_FACE_CONTROL"
    }

    override fun onBind(intent: Intent?): IBinder? =
        if (ACTION_WATCHFACE_CONTROL_SERVICE == intent?.action) {
            watchFaceInstanceServiceStub
        } else {
            null
        }

    // Required for testing
    public fun setContext(context: Context) {
        attachBaseContext(context)
    }
}

@RequiresApi(27)
private class IWatchFaceInstanceServiceStub(
    private val context: Context,
    private val uiThreadHandler: Handler
) : IWatchFaceControlService.Stub() {
    override fun getApiVersion() = IWatchFaceControlService.API_VERSION

    override fun getInteractiveWatchFaceInstanceSysUI(instanceId: String) =
        uiThreadHandler.runOnHandler {
            InteractiveInstanceManager.getAndRetainInstance(instanceId)?.createSysUiApi()
        }

    override fun createHeadlessWatchFaceInstance(
        params: HeadlessWatchFaceInstanceParams
    ): IHeadlessWatchFace? =
        uiThreadHandler.runOnHandler {
            createEngine(params.watchFaceName, context)?.createHeadlessInstance(params)
        }

    private fun createEngine(
        watchFaceName: ComponentName,
        context: Context
    ): WatchFaceService.EngineWrapper? {
        // Attempt to construct the class for the specified watchFaceName, failing if it either
        // doesn't exist or isn't a [WatchFaceService].
        val watchFaceServiceClass = Class.forName(watchFaceName.className) ?: return null
        if (!WatchFaceService::class.java.isAssignableFrom(WatchFaceService::class.java)) {
            return null
        }
        val watchFaceService =
            watchFaceServiceClass.getConstructor().newInstance() as WatchFaceService
        watchFaceService.setContext(context)
        return watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
    }

    override fun getOrCreateInteractiveWatchFaceWCS(
        params: WallpaperInteractiveWatchFaceInstanceParams,
        callback: IPendingInteractiveWatchFaceWCS
    ) = InteractiveInstanceManager
        .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
            InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
                params,
                callback
            )
        )
}