diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 92a9e566982cd..1b946d7b14b39 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6207,6 +6207,18 @@
different from the home screen wallpaper. -->
false
+
+ -1
+
+
+
+
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f460219aaffc4..1d7bf89a29759 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4907,6 +4907,8 @@
+
+
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java
new file mode 100644
index 0000000000000..1ff169433b9d4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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.extensions.area;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.view.Display;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.core.util.function.Consumer;
+
+/**
+ * {@link Presentation} object that is used to present extra content
+ * on the rear facing display when in a rear display presentation feature.
+ */
+class RearDisplayPresentation extends Presentation implements ExtensionWindowAreaPresentation {
+
+ @NonNull
+ private final Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> mStateConsumer;
+
+ RearDisplayPresentation(@NonNull Context outerContext, @NonNull Display display,
+ @NonNull Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> stateConsumer) {
+ super(outerContext, display);
+ mStateConsumer = stateConsumer;
+ }
+
+ /**
+ * {@code mStateConsumer} is notified that their content is now visible when the
+ * {@link Presentation} object is started. There is no comparable callback for
+ * {@link WindowAreaComponent#SESSION_STATE_INVISIBLE} in {@link #onStop()} due to the
+ * timing of when a {@link android.hardware.devicestate.DeviceStateRequest} is cancelled
+ * ending rear display presentation mode happening before the {@link Presentation} is stopped.
+ */
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mStateConsumer.accept(WindowAreaComponent.SESSION_STATE_VISIBLE);
+ }
+
+ @NonNull
+ @Override
+ public Context getPresentationContext() {
+ return getContext();
+ }
+
+ @Override
+ public void setPresentationView(View view) {
+ setContentView(view);
+ if (!isShowing()) {
+ show();
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java
new file mode 100644
index 0000000000000..141a6ad487719
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 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.extensions.area;
+
+import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_ACTIVE;
+import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_INACTIVE;
+
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.hardware.display.DisplayManager;
+import android.util.Log;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.window.extensions.core.util.function.Consumer;
+
+import java.util.Objects;
+
+/**
+ * Controller class that keeps track of the status of the device state request
+ * to enable the rear display presentation feature. This controller notifies the session callback
+ * when the state request is active, and notifies the callback when the request is canceled.
+ *
+ * Clients are notified via {@link Consumer} provided with
+ * {@link androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus} values to signify
+ * when the request becomes active and cancelled.
+ */
+class RearDisplayPresentationController implements DeviceStateRequest.Callback {
+
+ private static final String TAG = "RearDisplayPresentationController";
+
+ // Original context that requested to enable rear display presentation mode
+ @NonNull
+ private final Context mContext;
+ @NonNull
+ private final Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> mStateConsumer;
+ @Nullable
+ private ExtensionWindowAreaPresentation mExtensionWindowAreaPresentation;
+ @NonNull
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * Creates the RearDisplayPresentationController
+ * @param context Originating {@link android.content.Context} that is initiating the rear
+ * display presentation session.
+ * @param stateConsumer {@link Consumer} that will be notified that the session is active when
+ * the device state request is active and the session has been created. If the device
+ * state request is cancelled, the callback will be notified that the session has been
+ * ended. This could occur through a call to cancel the feature or if the device is
+ * manipulated in a way that cancels any device state override.
+ */
+ RearDisplayPresentationController(@NonNull Context context,
+ @NonNull Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> stateConsumer) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(stateConsumer);
+
+ mContext = context;
+ mStateConsumer = stateConsumer;
+ mDisplayManager = context.getSystemService(DisplayManager.class);
+ }
+
+ @Override
+ public void onRequestActivated(@NonNull DeviceStateRequest request) {
+ Display[] rearDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
+ if (rearDisplays.length == 0) {
+ mStateConsumer.accept(SESSION_STATE_INACTIVE);
+ Log.e(TAG, "Rear display list should not be empty");
+ return;
+ }
+
+ mExtensionWindowAreaPresentation =
+ new RearDisplayPresentation(mContext, rearDisplays[0], mStateConsumer);
+ mStateConsumer.accept(SESSION_STATE_ACTIVE);
+ }
+
+ @Override
+ public void onRequestCanceled(@NonNull DeviceStateRequest request) {
+ mStateConsumer.accept(SESSION_STATE_INACTIVE);
+ }
+
+ @Nullable
+ public ExtensionWindowAreaPresentation getWindowAreaPresentation() {
+ return mExtensionWindowAreaPresentation;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationStatus.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationStatus.java
new file mode 100644
index 0000000000000..0b1423ae48c02
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationStatus.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 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.extensions.area;
+
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Class that provides information around the current status of a window area feature. Contains
+ * the current {@link WindowAreaComponent.WindowAreaStatus} value corresponding to the
+ * rear display presentation feature, as well as the {@link DisplayMetrics} for the rear facing
+ * display.
+ */
+class RearDisplayPresentationStatus implements ExtensionWindowAreaStatus {
+
+ @WindowAreaComponent.WindowAreaStatus
+ private final int mWindowAreaStatus;
+
+ @NonNull
+ private final DisplayMetrics mDisplayMetrics;
+
+ RearDisplayPresentationStatus(@WindowAreaComponent.WindowAreaStatus int status,
+ @NonNull DisplayMetrics displayMetrics) {
+ mWindowAreaStatus = status;
+ mDisplayMetrics = displayMetrics;
+ }
+
+ /**
+ * Returns the {@link androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus}
+ * value that relates to the current status of a feature.
+ */
+ @Override
+ @WindowAreaComponent.WindowAreaStatus
+ public int getWindowAreaStatus() {
+ return mWindowAreaStatus;
+ }
+
+ /**
+ * Returns the {@link DisplayMetrics} that corresponds to the window area that a feature
+ * interacts with. This is converted to size class information provided to developers.
+ */
+ @Override
+ @NonNull
+ public DisplayMetrics getWindowAreaDisplayMetrics() {
+ return mDisplayMetrics;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 20602a1b290e1..3c0c2a10ba683 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -22,7 +22,12 @@ import android.app.Activity;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateRequest;
+import android.hardware.display.DisplayManager;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayAddress;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -30,6 +35,7 @@ import androidx.window.extensions.core.util.function.Consumer;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import java.util.concurrent.Executor;
@@ -47,61 +53,102 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
private final Object mLock = new Object();
+ @NonNull
private final DeviceStateManager mDeviceStateManager;
+ @NonNull
+ private final DisplayManager mDisplayManager;
+ @NonNull
private final Executor mExecutor;
@GuardedBy("mLock")
private final ArraySet> mRearDisplayStatusListeners = new ArraySet<>();
+ @GuardedBy("mLock")
+ private final ArraySet>
+ mRearDisplayPresentationStatusListeners = new ArraySet<>();
private final int mRearDisplayState;
+ private final int mConcurrentDisplayState;
+ @NonNull
+ private final int[] mFoldedDeviceStates;
+ @NonNull
+ private long mRearDisplayAddress = 0;
@WindowAreaSessionState
private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
@GuardedBy("mLock")
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
@GuardedBy("mLock")
- private int mCurrentDeviceBaseState = INVALID_DEVICE_STATE;
+ private int[] mCurrentSupportedDeviceStates;
+
@GuardedBy("mLock")
- private DeviceStateRequest mDeviceStateRequest;
+ private DeviceStateRequest mRearDisplayStateRequest;
+ @GuardedBy("mLock")
+ private RearDisplayPresentationController mRearDisplayPresentationController;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private DisplayMetrics mRearDisplayMetrics;
+
+ @WindowAreaSessionState
+ @GuardedBy("mLock")
+ private int mLastReportedRearDisplayPresentationStatus;
public WindowAreaComponentImpl(@NonNull Context context) {
mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+ mDisplayManager = context.getSystemService(DisplayManager.class);
mExecutor = context.getMainExecutor();
+ mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedStates();
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ R.array.config_foldedDeviceStates);
+
// TODO(b/236022708) Move rear display state to device state config file
mRearDisplayState = context.getResources().getInteger(
R.integer.config_deviceStateRearDisplay);
+ mConcurrentDisplayState = context.getResources().getInteger(
+ R.integer.config_deviceStateConcurrentRearDisplay);
+
mDeviceStateManager.registerCallback(mExecutor, this);
+ if (mConcurrentDisplayState != INVALID_DEVICE_STATE) {
+ mRearDisplayAddress = Long.parseLong(context.getResources().getString(
+ R.string.config_rearDisplayPhysicalAddress));
+ }
}
/**
* 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
+ * extensions, the result of the listener will be posted on the executor
* provided by the developer at the initial call site.
*
- * Depending on the initial state of the device, we will return either
+ * Rear display mode moves the calling application to the display on the device that is
+ * facing the same direction as the rear cameras. This would be the cover display on a fold-in
+ * style device when the device is opened.
+ *
+ * Depending on the initial state of the device, the {@link Consumer} will receive 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_
+ * state respectively. When the rear display feature is triggered, the status is updated 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.
+ * 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.
*/
+ @Override
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 current device state is still invalid, the initial value has not been provided.
if (mCurrentDeviceState == INVALID_DEVICE_STATE) {
return;
}
- consumer.accept(getCurrentStatus());
+ consumer.accept(getCurrentRearDisplayModeStatus());
}
}
@@ -109,6 +156,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
* Removes a listener no longer interested in receiving updates.
* @param consumer no longer interested in receiving updates to RearDisplayStatus
*/
+ @Override
public void removeRearDisplayStatusListener(
@NonNull Consumer<@WindowAreaStatus Integer> consumer) {
synchronized (mLock) {
@@ -119,13 +167,17 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
/**
* 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
+ * extensions, the result of the listener will be posted 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}
+ * Rear display mode moves the calling application to the display on the device that is
+ * facing the same direction as the rear cameras. This would be the cover display on a fold-in
+ * style device when the device is opened.
+ *
+ * When rear display mode is enabled, a request is made 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
+ * mode. When the {@link DeviceStateRequest} is activated, the provided {@link Consumer} is
+ * notified that the session is active by receiving
* {@link WindowAreaComponent#SESSION_STATE_ACTIVE}.
*
* @param activity to provide updates to the client on
@@ -133,19 +185,20 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
* @param rearDisplaySessionCallback to provide updates to the client on
* the status of the Session
*/
+ @Override
public void startRearDisplaySession(@NonNull Activity activity,
@NonNull Consumer<@WindowAreaSessionState Integer> rearDisplaySessionCallback) {
synchronized (mLock) {
- if (mDeviceStateRequest != null) {
+ if (mRearDisplayStateRequest != 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();
+ mRearDisplayStateRequest = DeviceStateRequest.newBuilder(mRearDisplayState).build();
mDeviceStateManager.requestState(
- mDeviceStateRequest,
+ mRearDisplayStateRequest,
mExecutor,
- new DeviceStateRequestCallbackAdapter(rearDisplaySessionCallback)
+ new RearDisplayStateRequestCallbackAdapter(rearDisplaySessionCallback)
);
}
}
@@ -153,13 +206,14 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
/**
* 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.
+ * extensions, the result of the listener will be posted on the executor
+ * provided by the developer at the initial call site.
*/
+ @Override
public void endRearDisplaySession() {
synchronized (mLock) {
- if (mDeviceStateRequest != null || isRearDisplayActive()) {
- mDeviceStateRequest = null;
+ if (mRearDisplayStateRequest != null || isRearDisplayActive()) {
+ mRearDisplayStateRequest = null;
mDeviceStateManager.cancelStateRequest();
} else {
throw new IllegalStateException(
@@ -168,13 +222,176 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
}
}
+ /**
+ * Adds a listener interested in receiving updates on the RearDisplayPresentationStatus
+ * of the device. Because this is being called from the OEM provided
+ * extensions, the result of the listener will be posted on the executor
+ * provided by the developer at the initial call site.
+ *
+ * Rear display presentation mode is a feature where an {@link Activity} can present
+ * additional content on a device with a second display that is facing the same direction
+ * as the rear camera (i.e. the cover display on a fold-in style device). The calling
+ * {@link Activity} does not move, whereas in rear display mode it does.
+ *
+ * This listener receives a {@link Pair} with the first item being the
+ * {@link WindowAreaComponent.WindowAreaStatus} that corresponds to the current status of the
+ * feature, and the second being the {@link DisplayMetrics} of the display that would be
+ * presented to when the feature is active.
+ *
+ * Depending on the initial state of the device, the {@link Consumer} will receive either
+ * {@link WindowAreaComponent#STATUS_AVAILABLE} or
+ * {@link WindowAreaComponent#STATUS_UNAVAILABLE} for the status value of the {@link Pair} if
+ * the feature is supported or not in that state respectively. Rear display presentation mode is
+ * currently not supported when the device is folded. When the rear display presentation feature
+ * is triggered, the status is updated 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 presentation mode.
+ */
@Override
- public void onBaseStateChanged(int state) {
+ public void addRearDisplayPresentationStatusListener(
+ @NonNull Consumer consumer) {
synchronized (mLock) {
- mCurrentDeviceBaseState = state;
- if (state == mCurrentDeviceState) {
- updateStatusConsumers(getCurrentStatus());
+ mRearDisplayPresentationStatusListeners.add(consumer);
+
+ // If current device state is still invalid, the initial value has not been provided
+ if (mCurrentDeviceState == INVALID_DEVICE_STATE) {
+ return;
}
+ @WindowAreaStatus int currentStatus = getCurrentRearDisplayPresentationModeStatus();
+ consumer.accept(
+ new RearDisplayPresentationStatus(currentStatus, getRearDisplayMetrics()));
+ }
+ }
+
+ /**
+ * Removes a listener no longer interested in receiving updates.
+ * @param consumer no longer interested in receiving updates to RearDisplayPresentationStatus
+ */
+ @Override
+ public void removeRearDisplayPresentationStatusListener(
+ @NonNull Consumer consumer) {
+ synchronized (mLock) {
+ mRearDisplayPresentationStatusListeners.remove(consumer);
+ }
+ }
+
+ /**
+ * Creates and starts a rear display presentation session and sends state updates to the
+ * consumer provided. This consumer will receive a constant represented by
+ * {@link WindowAreaSessionState} to represent the state of the current rear display
+ * session. It will be translated to a more friendly interface in the library.
+ *
+ * Because this is being called from the OEM provided extensions, the library
+ * will post the result of the listener on the executor provided by the developer.
+ *
+ * Rear display presentation mode refers to a feature where an {@link Activity} can present
+ * additional content on a device with a second display that is facing the same direction
+ * as the rear camera (i.e. the cover display on a fold-in style device). The calling
+ * {@link Activity} stays on the user-facing display.
+ *
+ * @param activity that the OEM implementation will use as a base
+ * context and to identify the source display area of the request.
+ * The reference to the activity instance must not be stored in the OEM
+ * implementation to prevent memory leaks.
+ * @param consumer to provide updates to the client on the status of the session
+ * @throws UnsupportedOperationException if this method is called when rear display presentation
+ * mode is not available. This could be to an incompatible device state or when
+ * another process is currently in this mode.
+ */
+ @Override
+ public void startRearDisplayPresentationSession(@NonNull Activity activity,
+ @NonNull Consumer<@WindowAreaSessionState Integer> consumer) {
+ synchronized (mLock) {
+ if (mRearDisplayPresentationController != null) {
+ // Rear display presentation session is already active
+ throw new IllegalStateException(
+ "Unable to start new rear display presentation session as one is already "
+ + "active");
+ }
+ if (getCurrentRearDisplayPresentationModeStatus()
+ != WindowAreaComponent.STATUS_AVAILABLE) {
+ throw new IllegalStateException(
+ "Unable to start new rear display presentation session as the feature is "
+ + "is not currently available");
+ }
+
+ mRearDisplayPresentationController = new RearDisplayPresentationController(activity,
+ stateStatus -> {
+ synchronized (mLock) {
+ if (stateStatus == SESSION_STATE_INACTIVE) {
+ // If the last reported session status was VISIBLE
+ // then the INVISIBLE state should be dispatched before INACTIVE
+ // due to not having a good mechanism to know when
+ // the content is no longer visible before it's fully removed
+ if (getLastReportedRearDisplayPresentationStatus()
+ == SESSION_STATE_VISIBLE) {
+ consumer.accept(SESSION_STATE_INVISIBLE);
+ }
+ mRearDisplayPresentationController = null;
+ }
+ mLastReportedRearDisplayPresentationStatus = stateStatus;
+ consumer.accept(stateStatus);
+ }
+ });
+
+ DeviceStateRequest concurrentDisplayStateRequest = DeviceStateRequest.newBuilder(
+ mConcurrentDisplayState).build();
+ mDeviceStateManager.requestState(
+ concurrentDisplayStateRequest,
+ mExecutor,
+ mRearDisplayPresentationController
+ );
+ }
+ }
+
+ /**
+ * Ends the current rear display presentation session and provides updates to the
+ * callback provided. When this is ended, the presented content from the calling
+ * {@link Activity} will also be removed from the rear facing display.
+ * Because this is being called from the OEM provided extensions, the result of the listener
+ * will be posted on the executor provided by the developer at the initial call site.
+ *
+ * Cancelling the {@link DeviceStateRequest} and exiting the rear display presentation state,
+ * will remove the presentation window from the cover display as the cover display is no longer
+ * enabled.
+ */
+ @Override
+ public void endRearDisplayPresentationSession() {
+ synchronized (mLock) {
+ if (mRearDisplayPresentationController != null) {
+ mDeviceStateManager.cancelStateRequest();
+ } else {
+ throw new IllegalStateException(
+ "Unable to cancel a rear display presentation session as there is no "
+ + "active session");
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public ExtensionWindowAreaPresentation getRearDisplayPresentation() {
+ synchronized (mLock) {
+ ExtensionWindowAreaPresentation presentation = null;
+ if (mRearDisplayPresentationController != null) {
+ presentation = mRearDisplayPresentationController.getWindowAreaPresentation();
+ }
+ return presentation;
+ }
+ }
+
+ @Override
+ public void onSupportedStatesChanged(int[] supportedStates) {
+ synchronized (mLock) {
+ mCurrentSupportedDeviceStates = supportedStates;
+ updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
+ updateRearDisplayPresentationStatusListeners(
+ getCurrentRearDisplayPresentationModeStatus());
}
}
@@ -182,34 +399,17 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
public void onStateChanged(int state) {
synchronized (mLock) {
mCurrentDeviceState = state;
- updateStatusConsumers(getCurrentStatus());
+ updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
+ updateRearDisplayPresentationStatusListeners(
+ getCurrentRearDisplayPresentationModeStatus());
}
}
- @Override
- public void addRearDisplayPresentationStatusListener(
- @NonNull Consumer consumer) {}
-
- @Override
- public void removeRearDisplayPresentationStatusListener(
- @NonNull Consumer consumer) {}
-
- @Override
- public void startRearDisplayPresentationSession(@NonNull Activity activity,
- @NonNull Consumer<@WindowAreaSessionState Integer> consumer) {}
-
- @Override
- public void endRearDisplayPresentationSession() {}
-
- @Override
- @Nullable
- public ExtensionWindowAreaPresentation getRearDisplayPresentation() {
- return null;
- }
@GuardedBy("mLock")
- private int getCurrentStatus() {
+ private int getCurrentRearDisplayModeStatus() {
if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
+ || !ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)
|| isRearDisplayActive()) {
return WindowAreaComponent.STATUS_UNAVAILABLE;
}
@@ -218,19 +418,17 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
/**
* 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
+ * if the current device state is that which corresponds to {@code mRearDisplayState}.
+ *
+ * @return {@code true} if the device is in rear display state {@code false} if not
*/
@GuardedBy("mLock")
private boolean isRearDisplayActive() {
- return (mCurrentDeviceState != mCurrentDeviceBaseState) && (mCurrentDeviceState
- == mRearDisplayState);
+ return mCurrentDeviceState == mRearDisplayState;
}
@GuardedBy("mLock")
- private void updateStatusConsumers(@WindowAreaStatus int windowAreaStatus) {
+ private void updateRearDisplayStatusListeners(@WindowAreaStatus int windowAreaStatus) {
synchronized (mLock) {
for (int i = 0; i < mRearDisplayStatusListeners.size(); i++) {
mRearDisplayStatusListeners.valueAt(i).accept(windowAreaStatus);
@@ -238,26 +436,92 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
}
}
+ @GuardedBy("mLock")
+ private int getCurrentRearDisplayPresentationModeStatus() {
+ if (mCurrentDeviceState == mConcurrentDisplayState
+ || !ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState)
+ || isDeviceFolded()) {
+ return WindowAreaComponent.STATUS_UNAVAILABLE;
+ }
+ return WindowAreaComponent.STATUS_AVAILABLE;
+ }
+
+ @GuardedBy("mLock")
+ private boolean isDeviceFolded() {
+ return ArrayUtils.contains(mFoldedDeviceStates, mCurrentDeviceState);
+ }
+
+ @GuardedBy("mLock")
+ private void updateRearDisplayPresentationStatusListeners(
+ @WindowAreaStatus int windowAreaStatus) {
+ RearDisplayPresentationStatus consumerValue = new RearDisplayPresentationStatus(
+ windowAreaStatus, getRearDisplayMetrics());
+ synchronized (mLock) {
+ for (int i = 0; i < mRearDisplayPresentationStatusListeners.size(); i++) {
+ mRearDisplayPresentationStatusListeners.valueAt(i).accept(consumerValue);
+ }
+ }
+ }
+
+ /**
+ * Returns the{@link DisplayMetrics} associated with the rear facing display. If the rear facing
+ * display was not found in the display list, but we have already computed the
+ * {@link DisplayMetrics} for that display, we return the cached value.
+ *
+ * TODO(b/267563768): Update with guidance from Display team for missing displays.
+ *
+ * @throws IllegalArgumentException if the display is not found and there is no cached
+ * {@link DisplayMetrics} for this display.
+ */
+ @GuardedBy("mLock")
+ private DisplayMetrics getRearDisplayMetrics() {
+ Display[] displays = mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+ for (int i = 0; i < displays.length; i++) {
+ DisplayAddress.Physical address =
+ (DisplayAddress.Physical) displays[i].getAddress();
+ if (mRearDisplayAddress == address.getPhysicalDisplayId()) {
+ if (mRearDisplayMetrics == null) {
+ mRearDisplayMetrics = new DisplayMetrics();
+ }
+ displays[i].getRealMetrics(mRearDisplayMetrics);
+ return mRearDisplayMetrics;
+ }
+ }
+ if (mRearDisplayMetrics != null) {
+ return mRearDisplayMetrics;
+ } else {
+ throw new IllegalArgumentException(
+ "No display found with the provided display address");
+ }
+ }
+
+ @GuardedBy("mLock")
+ @WindowAreaSessionState
+ private int getLastReportedRearDisplayPresentationStatus() {
+ return mLastReportedRearDisplayPresentationStatus;
+ }
+
/**
* 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 class RearDisplayStateRequestCallbackAdapter implements DeviceStateRequest.Callback {
private final Consumer mRearDisplaySessionCallback;
- DeviceStateRequestCallbackAdapter(@NonNull Consumer callback) {
+ RearDisplayStateRequestCallbackAdapter(@NonNull Consumer callback) {
mRearDisplaySessionCallback = callback;
}
@Override
public void onRequestActivated(@NonNull DeviceStateRequest request) {
synchronized (mLock) {
- if (request.equals(mDeviceStateRequest)) {
+ if (request.equals(mRearDisplayStateRequest)) {
mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_ACTIVE;
mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
- updateStatusConsumers(getCurrentStatus());
+ updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
}
}
}
@@ -265,12 +529,12 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
@Override
public void onRequestCanceled(DeviceStateRequest request) {
synchronized (mLock) {
- if (request.equals(mDeviceStateRequest)) {
- mDeviceStateRequest = null;
+ if (request.equals(mRearDisplayStateRequest)) {
+ mRearDisplayStateRequest = null;
}
mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
- updateStatusConsumers(getCurrentStatus());
+ updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
}
}
}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 378ad811bd22a..9596c2233ecc7 100644
Binary files a/libs/WindowManager/Jetpack/window-extensions-release.aar and b/libs/WindowManager/Jetpack/window-extensions-release.aar differ