diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java index 141a6ad487719..0e9119a4332be 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationController.java @@ -20,9 +20,6 @@ import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_ 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; @@ -32,17 +29,12 @@ 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. + * Controller class that manages the creation of the {@link android.app.Presentation} object used + * to show content on the rear facing display. This controller notifies the session callback with + * {@link androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus} values when the + * feature is active, or when the feature has been ended. */ -class RearDisplayPresentationController implements DeviceStateRequest.Callback { - - private static final String TAG = "RearDisplayPresentationController"; +class RearDisplayPresentationController { // Original context that requested to enable rear display presentation mode @NonNull @@ -51,8 +43,6 @@ class RearDisplayPresentationController implements DeviceStateRequest.Callback { private final Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> mStateConsumer; @Nullable private ExtensionWindowAreaPresentation mExtensionWindowAreaPresentation; - @NonNull - private final DisplayManager mDisplayManager; /** * Creates the RearDisplayPresentationController @@ -71,25 +61,15 @@ class RearDisplayPresentationController implements DeviceStateRequest.Callback { 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; - } - + public void startSession(@NonNull Display rearDisplay) { mExtensionWindowAreaPresentation = - new RearDisplayPresentation(mContext, rearDisplays[0], mStateConsumer); + new RearDisplayPresentation(mContext, rearDisplay, mStateConsumer); mStateConsumer.accept(SESSION_STATE_ACTIVE); } - @Override - public void onRequestCanceled(@NonNull DeviceStateRequest request) { + public void endSession() { mStateConsumer.accept(SESSION_STATE_INACTIVE); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java new file mode 100644 index 0000000000000..ea33426d0bdf1 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentationRequestCallback.java @@ -0,0 +1,115 @@ +/* + * 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.content.Context; +import android.hardware.devicestate.DeviceStateRequest; +import android.hardware.display.DisplayManager; +import android.view.Display; + +import androidx.annotation.NonNull; + +/** + * Callback class to be notified of updates to a {@link DeviceStateRequest} for the rear display + * presentation state. This class notifies the {@link RearDisplayPresentationController} when the + * device is ready to enable the rear display presentation feature. + */ +public class RearDisplayPresentationRequestCallback implements DeviceStateRequest.Callback { + + private static final String TAG = RearDisplayPresentationRequestCallback.class.getSimpleName(); + + @NonNull + private final DisplayManager mDisplayManager; + @NonNull + private final DisplayManager.DisplayListener mRearDisplayListener = new RearDisplayListener(); + @NonNull + private final RearDisplayPresentationController mRearDisplayPresentationController; + private boolean mWaitingForRearDisplay = false; + + public RearDisplayPresentationRequestCallback(@NonNull Context context, + @NonNull RearDisplayPresentationController rearDisplayPresentationController) { + mDisplayManager = context.getSystemService(DisplayManager.class); + mDisplayManager.registerDisplayListener(mRearDisplayListener, + context.getMainThreadHandler(), DisplayManager.EVENT_FLAG_DISPLAY_ADDED + | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED); + + mRearDisplayPresentationController = rearDisplayPresentationController; + } + + @Override + public void onRequestActivated(@NonNull DeviceStateRequest request) { + Display[] rearDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR); + if (rearDisplays.length == 0) { + // No rear facing display found, marking waiting for display flag as true. + mWaitingForRearDisplay = true; + return; + } + mDisplayManager.unregisterDisplayListener(mRearDisplayListener); + mRearDisplayPresentationController.startSession(rearDisplays[0]); + } + + @Override + public void onRequestCanceled(@NonNull DeviceStateRequest request) { + mDisplayManager.unregisterDisplayListener(mRearDisplayListener); + mRearDisplayPresentationController.endSession(); + } + + /** + * {@link DisplayManager.DisplayListener} to be used if a rear facing {@link Display} isn't + * available synchronously when the device is entered into the rear display presentation state. + * A rear facing {@link Display} is a {@link Display} that is categorized as + * {@link DisplayManager#DISPLAY_CATEGORY_REAR}. This can occur if {@link DisplayManager} is + * still in the process of configuring itself for this state when + * {@link DeviceStateRequest.Callback#onRequestActivated} is called. + * + * The {@link DisplayManager.DisplayListener} removes itself when a rear facing display is + * found. + */ + private class RearDisplayListener implements DisplayManager.DisplayListener { + @Override + public void onDisplayAdded(int displayId) { + Display display = mDisplayManager.getDisplay(displayId); + if (mWaitingForRearDisplay && (display.getFlags() & Display.FLAG_REAR) != 0) { + startRearDisplayPresentation(display); + } + } + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override + public void onDisplayChanged(int displayId) { + Display display = mDisplayManager.getDisplay(displayId); + if (mWaitingForRearDisplay && (display.getFlags() & Display.FLAG_REAR) != 0) { + startRearDisplayPresentation(display); + } + } + + /** + * Starts a new {@link RearDisplayPresentation} with the updated {@link Display} with a + * category of {@link DisplayManager#DISPLAY_CATEGORY_REAR}. + */ + private void startRearDisplayPresentation(Display rearDisplay) { + // We have been notified of a change to a rear display, we can unregister the + // callback and stop waiting for a display + mDisplayManager.unregisterDisplayListener(this); + mWaitingForRearDisplay = false; + + mRearDisplayPresentationController.startSession(rearDisplay); + } + } +} 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 b374ae35f57f8..658d92cc74896 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -337,13 +337,15 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, consumer.accept(stateStatus); } }); - + RearDisplayPresentationRequestCallback deviceStateCallback = + new RearDisplayPresentationRequestCallback(activity, + mRearDisplayPresentationController); DeviceStateRequest concurrentDisplayStateRequest = DeviceStateRequest.newBuilder( mConcurrentDisplayState).build(); mDeviceStateManager.requestState( concurrentDisplayStateRequest, mExecutor, - mRearDisplayPresentationController + deviceStateCallback ); } }