[go: nahoru, domu]

blob: 4825dc00e0177a51968f1891c7afff49210465e2 [file] [log] [blame]
/*
* 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.app.Activity
import android.content.Context
import androidx.core.util.Consumer
import java.util.concurrent.Executor
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
/**
* A singleton controller class that gets information about the currently active activity
* splits and provides interaction points to customize the splits and form new
* splits.
*
* A split is a pair of containers that host activities in the same or different
* processes, combined under the same parent window of the hosting task.
*
* A pair of activities can be put into a split by providing a static or runtime
* split rule and then launching the activities in the same task using
* [Activity.startActivity()][android.app.Activity.startActivity].
*/
class SplitController private constructor(applicationContext: Context) {
private val embeddingBackend: EmbeddingBackend = ExtensionEmbeddingBackend
.getInstance(applicationContext)
// TODO(b/258356512): Make this method a flow API
/**
* Registers a listener for updates about the active split state(s) that this
* activity is part of. An activity can be in zero, one or more active splits.
* More than one active split is possible if an activity created multiple
* containers to side, stacked on top of each other. Or it can be in two
* different splits at the same time - in a secondary container for one (it was
* launched to the side) and in the primary for another (it launched another
* activity to the side). The reported splits in the list are ordered from
* bottom to top by their z-order, more recent splits appearing later.
* Guaranteed to be called at least once to report the most recent state.
*
* @param activity only split that this [Activity] is part of will be reported.
* @param executor when there is an update to the active split state(s), the [consumer] will be
* invoked on this [Executor].
* @param consumer [Consumer] that will be invoked on the [executor] when there is an update to
* the active split state(s).
*/
fun addSplitListener(
activity: Activity,
executor: Executor,
consumer: Consumer<List<SplitInfo>>
) {
embeddingBackend.addSplitListenerForActivity(activity, executor, consumer)
}
/**
* Unregisters a listener that was previously registered via [addSplitListener].
*
* @param consumer the previously registered [Consumer] to unregister.
*/
fun removeSplitListener(
consumer: Consumer<List<SplitInfo>>
) {
embeddingBackend.removeSplitListenerForActivity(consumer)
}
/**
* Indicates whether split functionality is supported on the device. Note
* that devices might not enable splits in all states or conditions. For
* example, a foldable device with multiple screens can choose to collapse
* splits when apps run on the device's small display, but enable splits
* when apps run on the device's large display. In cases like this,
* `isSplitSupported` always returns `true`, and if the split is collapsed,
* activities are launched on top, following the non-activity embedding
* model.
*/
fun isSplitSupported(): Boolean {
return embeddingBackend.isSplitSupported()
}
companion object {
@Volatile
private var globalInstance: SplitController? = null
private val globalLock = ReentrantLock()
internal const val sDebug = false
/**
* Obtains the singleton instance of [SplitController].
*
* @param context the [Context] to initialize the controller with
*/
@JvmStatic
fun getInstance(context: Context): SplitController {
if (globalInstance == null) {
globalLock.withLock {
if (globalInstance == null) {
globalInstance = SplitController(context.applicationContext)
}
}
}
return globalInstance!!
}
}
}