EmbeddingCompat.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.window.embedding

import android.util.Log
import androidx.window.core.ExperimentalWindowApi
import androidx.window.embedding.EmbeddingInterfaceCompat.EmbeddingCallbackInterface
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.embedding.ActivityEmbeddingComponent
import androidx.window.extensions.embedding.SplitInfo
import java.util.function.Consumer
import androidx.window.extensions.embedding.EmbeddingRule as ExtensionsEmbeddingRule

/**
 * Adapter implementation for different historical versions of activity embedding OEM interface in
 * [ActivityEmbeddingComponent]. Only supports the single current version in this implementation.
 */
@ExperimentalWindowApi
internal class EmbeddingCompat constructor(
    private val embeddingExtension: ActivityEmbeddingComponent,
    private val adapter: EmbeddingAdapter
) : EmbeddingInterfaceCompat {
    constructor() : this(
        embeddingComponent(),
        EmbeddingAdapter()
    )

    override fun setSplitRules(rules: Set<EmbeddingRule>) {
        embeddingExtension.setEmbeddingRules(adapter.translate(rules))
    }

    override fun setEmbeddingCallback(embeddingCallback: EmbeddingCallbackInterface) {
        val translatingCallback = EmbeddingTranslatingCallback(embeddingCallback, adapter)
        embeddingExtension.setSplitInfoCallback(translatingCallback)
    }

    companion object {
        const val DEBUG = true
        private const val TAG = "EmbeddingCompat"

        fun getExtensionApiLevel(): Int? {
            return try {
                WindowExtensionsProvider.getWindowExtensions().vendorApiLevel
            } catch (e: NoClassDefFoundError) {
                if (DEBUG) {
                    Log.d(TAG, "Embedding extension version not found")
                }
                null
            } catch (e: UnsupportedOperationException) {
                if (DEBUG) {
                    Log.d(TAG, "Stub Extension")
                }
                null
            }
        }

        fun isEmbeddingAvailable(): Boolean {
            return try {
                WindowExtensionsProvider.getWindowExtensions().activityEmbeddingComponent != null
            } catch (e: NoClassDefFoundError) {
                if (DEBUG) {
                    Log.d(TAG, "Embedding extension version not found")
                }
                false
            } catch (e: UnsupportedOperationException) {
                if (DEBUG) {
                    Log.d(TAG, "Stub Extension")
                }
                false
            }
        }

        fun embeddingComponent(): ActivityEmbeddingComponent {
            return if (isEmbeddingAvailable()) {
                WindowExtensionsProvider.getWindowExtensions().getActivityEmbeddingComponent()
                    ?: EmptyEmbeddingComponent()
            } else {
                EmptyEmbeddingComponent()
            }
        }
    }
}

// Empty implementation of the embedding component to use when the device doesn't provide one and
// avoid null checks.
private class EmptyEmbeddingComponent : ActivityEmbeddingComponent {
    override fun setEmbeddingRules(splitRules: MutableSet<ExtensionsEmbeddingRule>) {
        // empty
    }

    override fun setSplitInfoCallback(consumer: Consumer<MutableList<SplitInfo>>) {
        // empty
    }
}