WindowAreaController.kt
/*
* Copyright 2023 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.area
import android.app.Activity
import android.os.Build
import android.util.Log
import androidx.annotation.RestrictTo
import androidx.window.core.BuildConfig
import androidx.window.core.ExperimentalWindowApi
import androidx.window.core.VerificationMode
import androidx.window.extensions.WindowExtensionsProvider
import androidx.window.extensions.area.WindowAreaComponent
import java.util.concurrent.Executor
import kotlinx.coroutines.flow.Flow
/**
* An interface to provide information about available window areas on the device and an option
* to use the rear display area of a foldable device, exclusively or concurrently with the internal
* display.
*
* @hide
*
*/
@ExperimentalWindowApi
interface WindowAreaController {
/**
* Provides information about the current state of the window area of the rear display on the
* device, if or when it is available. Rear Display mode can be invoked if the current status is
* [WindowAreaStatus.AVAILABLE].
*/
fun rearDisplayStatus(): Flow<WindowAreaStatus>
/**
* Starts Rear Display Mode and moves the provided activity to the rear side of the device in
* order to face the same direction as the primary device camera(s). When a rear display
* mode is started, the system will turn on the rear display of the device to show the content
* there, and can disable the internal display. The provided [Activity] is likely to get a
* configuration change or being relaunched due to the difference in the internal and rear
* display sizes on the device.
* <p>Only the top visible application can request and use this mode. The system can dismiss the
* mode if the user changes the device state.
* <p>This method can only be called if the feature is supported on the device and is reported
* as available in the current state through [rearDisplayStatus], otherwise it will
* throw an [Exception].
*/
fun rearDisplayMode(
activity: Activity,
executor: Executor,
windowAreaSessionCallback: WindowAreaSessionCallback
)
public companion object {
private val TAG = WindowAreaController::class.simpleName
private var decorator: WindowAreaControllerDecorator = EmptyDecorator
/**
* Provides an instance of [WindowAreaController].
*/
@JvmName("getOrCreate")
@JvmStatic
fun getOrCreate(): WindowAreaController {
var windowAreaComponentExtensions: WindowAreaComponent?
try {
// TODO(b/267972002): Introduce reflection guard for WindowAreaComponent
windowAreaComponentExtensions = WindowExtensionsProvider
.getWindowExtensions()
.windowAreaComponent
} catch (t: Throwable) {
if (BuildConfig.verificationMode == VerificationMode.STRICT) {
Log.d(TAG, "Failed to load WindowExtensions")
}
windowAreaComponentExtensions = null
}
val controller =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
windowAreaComponentExtensions == null) {
EmptyWindowAreaControllerImpl()
} else {
WindowAreaControllerImpl(windowAreaComponentExtensions)
}
return decorator.decorate(controller)
}
@JvmStatic
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun overrideDecorator(overridingDecorator: WindowAreaControllerDecorator) {
decorator = overridingDecorator
}
@JvmStatic
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun reset() {
decorator = EmptyDecorator
}
}
}
/**
* Decorator that allows us to provide different functionality
* in our window-testing artifact.
*/
@ExperimentalWindowApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
interface WindowAreaControllerDecorator {
/**
* Returns an instance of [WindowAreaController] associated to the [Activity]
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun decorate(controller: WindowAreaController): WindowAreaController
}
@ExperimentalWindowApi
private object EmptyDecorator : WindowAreaControllerDecorator {
override fun decorate(controller: WindowAreaController): WindowAreaController {
return controller
}
}