Merge changes from topic "rear-display-qpr" into tm-qpr-dev am: 51d289b772
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19519706 Change-Id: Ieb19afb7a38d0a7a79bfef2c1238f327e61fa1c0 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -5856,4 +5856,10 @@
|
|||||||
|
|
||||||
<!-- The number of tasks to scan to get the visibility of Home -->
|
<!-- The number of tasks to scan to get the visibility of Home -->
|
||||||
<integer name="config_maxScanTasksForHomeVisibility">10</integer>
|
<integer name="config_maxScanTasksForHomeVisibility">10</integer>
|
||||||
|
|
||||||
|
<!-- Device state that corresponds to rear display mode, feature provided
|
||||||
|
through Jetpack WindowManager
|
||||||
|
TODO(b/236022708) Move rear display state to device state config file
|
||||||
|
-->
|
||||||
|
<integer name="config_deviceStateRearDisplay">-1</integer>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -4811,6 +4811,7 @@
|
|||||||
<java-symbol type="drawable" name="ic_swap_horiz" />
|
<java-symbol type="drawable" name="ic_swap_horiz" />
|
||||||
<java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" />
|
<java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" />
|
||||||
<java-symbol type="array" name="config_serviceStateLocationAllowedPackages" />
|
<java-symbol type="array" name="config_serviceStateLocationAllowedPackages" />
|
||||||
|
<java-symbol type="integer" name="config_deviceStateRearDisplay"/>
|
||||||
|
|
||||||
<!-- For app language picker -->
|
<!-- For app language picker -->
|
||||||
<java-symbol type="string" name="system_locale_title" />
|
<java-symbol type="string" name="system_locale_title" />
|
||||||
|
|||||||
@@ -20,11 +20,14 @@ import android.app.ActivityThread;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.window.extensions.area.WindowAreaComponent;
|
||||||
|
import androidx.window.extensions.area.WindowAreaComponentImpl;
|
||||||
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
|
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
|
||||||
import androidx.window.extensions.embedding.SplitController;
|
import androidx.window.extensions.embedding.SplitController;
|
||||||
import androidx.window.extensions.layout.WindowLayoutComponent;
|
import androidx.window.extensions.layout.WindowLayoutComponent;
|
||||||
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reference implementation of {@link WindowExtensions} that implements the initial API version.
|
* The reference implementation of {@link WindowExtensions} that implements the initial API version.
|
||||||
*/
|
*/
|
||||||
@@ -33,10 +36,12 @@ public class WindowExtensionsImpl implements WindowExtensions {
|
|||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
private volatile WindowLayoutComponent mWindowLayoutComponent;
|
private volatile WindowLayoutComponent mWindowLayoutComponent;
|
||||||
private volatile SplitController mSplitController;
|
private volatile SplitController mSplitController;
|
||||||
|
private volatile WindowAreaComponent mWindowAreaComponent;
|
||||||
|
|
||||||
|
// TODO(b/241126279) Introduce constants to better version functionality
|
||||||
@Override
|
@Override
|
||||||
public int getVendorApiLevel() {
|
public int getVendorApiLevel() {
|
||||||
return 1;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,4 +80,23 @@ public class WindowExtensionsImpl implements WindowExtensions {
|
|||||||
}
|
}
|
||||||
return mSplitController;
|
return mSplitController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a reference implementation of {@link WindowAreaComponent} if available,
|
||||||
|
* {@code null} otherwise. The implementation must match the API level reported in
|
||||||
|
* {@link WindowExtensions#getWindowAreaComponent()}.
|
||||||
|
* @return {@link WindowAreaComponent} OEM implementation.
|
||||||
|
*/
|
||||||
|
public WindowAreaComponent getWindowAreaComponent() {
|
||||||
|
if (mWindowAreaComponent == null) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mWindowAreaComponent == null) {
|
||||||
|
Context context = ActivityThread.currentApplication();
|
||||||
|
mWindowAreaComponent =
|
||||||
|
new WindowAreaComponentImpl(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mWindowAreaComponent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.extensions.area;
|
||||||
|
|
||||||
|
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.devicestate.DeviceStateManager;
|
||||||
|
import android.hardware.devicestate.DeviceStateRequest;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference implementation of androidx.window.extensions.area OEM interface for use with
|
||||||
|
* WindowManager Jetpack.
|
||||||
|
*
|
||||||
|
* This component currently supports Rear Display mode with the ability to add and remove
|
||||||
|
* status listeners for this mode.
|
||||||
|
*
|
||||||
|
* The public methods in this class are thread-safe.
|
||||||
|
**/
|
||||||
|
public class WindowAreaComponentImpl implements WindowAreaComponent,
|
||||||
|
DeviceStateManager.DeviceStateCallback {
|
||||||
|
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
|
private final DeviceStateManager mDeviceStateManager;
|
||||||
|
private final Executor mExecutor;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private final ArraySet<Consumer<Integer>> mRearDisplayStatusListeners = new ArraySet<>();
|
||||||
|
private final int mRearDisplayState;
|
||||||
|
@WindowAreaSessionState
|
||||||
|
private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private int mCurrentDeviceBaseState = INVALID_DEVICE_STATE;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private DeviceStateRequest mDeviceStateRequest;
|
||||||
|
|
||||||
|
public WindowAreaComponentImpl(@NonNull Context context) {
|
||||||
|
mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
|
||||||
|
mExecutor = context.getMainExecutor();
|
||||||
|
|
||||||
|
// TODO(b/236022708) Move rear display state to device state config file
|
||||||
|
mRearDisplayState = context.getResources().getInteger(
|
||||||
|
R.integer.config_deviceStateRearDisplay);
|
||||||
|
|
||||||
|
mDeviceStateManager.registerCallback(mExecutor, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener interested in receiving updates on the RearDisplayStatus
|
||||||
|
* of the device. Because this is being called from the OEM provided
|
||||||
|
* extensions, we will post the result of the listener on the executor
|
||||||
|
* provided by the developer at the initial call site.
|
||||||
|
*
|
||||||
|
* Depending on the initial state of the device, we will return either
|
||||||
|
* {@link WindowAreaComponent#STATUS_AVAILABLE} or
|
||||||
|
* {@link WindowAreaComponent#STATUS_UNAVAILABLE} if the feature is supported or not in that
|
||||||
|
* state respectively. When the rear display feature is triggered, we update the status to be
|
||||||
|
* {@link WindowAreaComponent#STATUS_UNAVAILABLE}. TODO(b/240727590) Prefix with AREA_
|
||||||
|
*
|
||||||
|
* TODO(b/239833099) Add a STATUS_ACTIVE option to let apps know if a feature is currently
|
||||||
|
* enabled.
|
||||||
|
*
|
||||||
|
* @param consumer {@link Consumer} interested in receiving updates to the status of
|
||||||
|
* rear display mode.
|
||||||
|
*/
|
||||||
|
public void addRearDisplayStatusListener(
|
||||||
|
@NonNull Consumer<@WindowAreaStatus Integer> consumer) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mRearDisplayStatusListeners.add(consumer);
|
||||||
|
|
||||||
|
// If current device state is still invalid, we haven't gotten our initial value yet
|
||||||
|
if (mCurrentDeviceState == INVALID_DEVICE_STATE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
consumer.accept(getCurrentStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a listener no longer interested in receiving updates.
|
||||||
|
* @param consumer no longer interested in receiving updates to RearDisplayStatus
|
||||||
|
*/
|
||||||
|
public void removeRearDisplayStatusListener(
|
||||||
|
@NonNull Consumer<@WindowAreaStatus Integer> consumer) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mRearDisplayStatusListeners.remove(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and starts a rear display session and provides updates to the
|
||||||
|
* callback provided. Because this is being called from the OEM provided
|
||||||
|
* extensions, we will post the result of the listener on the executor
|
||||||
|
* provided by the developer at the initial call site.
|
||||||
|
*
|
||||||
|
* When we enable rear display mode, we submit a request to {@link DeviceStateManager}
|
||||||
|
* to override the device state to the state that corresponds to RearDisplay
|
||||||
|
* mode. When the {@link DeviceStateRequest} is activated, we let the
|
||||||
|
* consumer know that the session is active by sending
|
||||||
|
* {@link WindowAreaComponent#SESSION_STATE_ACTIVE}.
|
||||||
|
*
|
||||||
|
* @param activity to provide updates to the client on
|
||||||
|
* the status of the Session
|
||||||
|
* @param rearDisplaySessionCallback to provide updates to the client on
|
||||||
|
* the status of the Session
|
||||||
|
*/
|
||||||
|
public void startRearDisplaySession(@NonNull Activity activity,
|
||||||
|
@NonNull Consumer<@WindowAreaSessionState Integer> rearDisplaySessionCallback) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mDeviceStateRequest != null) {
|
||||||
|
// Rear display session is already active
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unable to start new rear display session as one is already active");
|
||||||
|
}
|
||||||
|
mDeviceStateRequest = DeviceStateRequest.newBuilder(mRearDisplayState).build();
|
||||||
|
mDeviceStateManager.requestState(
|
||||||
|
mDeviceStateRequest,
|
||||||
|
mExecutor,
|
||||||
|
new DeviceStateRequestCallbackAdapter(rearDisplaySessionCallback)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the current rear display session and provides updates to the
|
||||||
|
* callback provided. Because this is being called from the OEM provided
|
||||||
|
* extensions, we will post the result of the listener on the executor
|
||||||
|
* provided by the developer.
|
||||||
|
*/
|
||||||
|
public void endRearDisplaySession() {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mDeviceStateRequest != null || isRearDisplayActive()) {
|
||||||
|
mDeviceStateRequest = null;
|
||||||
|
mDeviceStateManager.cancelStateRequest();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unable to cancel a rear display session as there is no active session");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBaseStateChanged(int state) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCurrentDeviceBaseState = state;
|
||||||
|
if (state == mCurrentDeviceState) {
|
||||||
|
updateStatusConsumers(getCurrentStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStateChanged(int state) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCurrentDeviceState = state;
|
||||||
|
updateStatusConsumers(getCurrentStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private int getCurrentStatus() {
|
||||||
|
if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
|
||||||
|
|| isRearDisplayActive()) {
|
||||||
|
return WindowAreaComponent.STATUS_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
return WindowAreaComponent.STATUS_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to determine if a rear display session is currently active by checking
|
||||||
|
* if the current device configuration matches that of rear display. This would be true
|
||||||
|
* if there is a device override currently active (base state != current state) and the current
|
||||||
|
* state is that which corresponds to {@code mRearDisplayState}
|
||||||
|
* @return {@code true} if the device is in rear display mode and {@code false} if not
|
||||||
|
*/
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private boolean isRearDisplayActive() {
|
||||||
|
return (mCurrentDeviceState != mCurrentDeviceBaseState) && (mCurrentDeviceState
|
||||||
|
== mRearDisplayState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private void updateStatusConsumers(@WindowAreaStatus int windowAreaStatus) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
for (int i = 0; i < mRearDisplayStatusListeners.size(); i++) {
|
||||||
|
mRearDisplayStatusListeners.valueAt(i).accept(windowAreaStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for the {@link DeviceStateRequest} to be notified of when the request has been
|
||||||
|
* activated or cancelled. This callback provides information to the client library
|
||||||
|
* on the status of the RearDisplay session through {@code mRearDisplaySessionCallback}
|
||||||
|
*/
|
||||||
|
private class DeviceStateRequestCallbackAdapter implements DeviceStateRequest.Callback {
|
||||||
|
|
||||||
|
private final Consumer<Integer> mRearDisplaySessionCallback;
|
||||||
|
|
||||||
|
DeviceStateRequestCallbackAdapter(@NonNull Consumer<Integer> callback) {
|
||||||
|
mRearDisplaySessionCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestActivated(@NonNull DeviceStateRequest request) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (request.equals(mDeviceStateRequest)) {
|
||||||
|
mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_ACTIVE;
|
||||||
|
mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
|
||||||
|
updateStatusConsumers(getCurrentStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestCanceled(DeviceStateRequest request) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (request.equals(mDeviceStateRequest)) {
|
||||||
|
mDeviceStateRequest = null;
|
||||||
|
}
|
||||||
|
mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
|
||||||
|
mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
|
||||||
|
updateStatusConsumers(getCurrentStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user