1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.window.extensions.area;
18 
19 import android.content.Context;
20 import android.hardware.devicestate.DeviceStateRequest;
21 import android.hardware.display.DisplayManager;
22 import android.view.Display;
23 
24 import androidx.annotation.NonNull;
25 
26 /**
27  * Callback class to be notified of updates to a {@link DeviceStateRequest} for the rear display
28  * presentation state. This class notifies the {@link RearDisplayPresentationController} when the
29  * device is ready to enable the rear display presentation feature.
30  */
31 public class RearDisplayPresentationRequestCallback implements DeviceStateRequest.Callback {
32 
33     private static final String TAG = RearDisplayPresentationRequestCallback.class.getSimpleName();
34 
35     @NonNull
36     private final DisplayManager mDisplayManager;
37     @NonNull
38     private final DisplayManager.DisplayListener mRearDisplayListener = new RearDisplayListener();
39     @NonNull
40     private final RearDisplayPresentationController mRearDisplayPresentationController;
41     private boolean mWaitingForRearDisplay = false;
42 
RearDisplayPresentationRequestCallback(@onNull Context context, @NonNull RearDisplayPresentationController rearDisplayPresentationController)43     public RearDisplayPresentationRequestCallback(@NonNull Context context,
44             @NonNull RearDisplayPresentationController rearDisplayPresentationController) {
45         mDisplayManager = context.getSystemService(DisplayManager.class);
46         mDisplayManager.registerDisplayListener(mRearDisplayListener,
47                 context.getMainThreadHandler(), DisplayManager.EVENT_FLAG_DISPLAY_ADDED
48                         | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
49 
50         mRearDisplayPresentationController = rearDisplayPresentationController;
51     }
52 
53     @Override
onRequestActivated(@onNull DeviceStateRequest request)54     public void onRequestActivated(@NonNull DeviceStateRequest request) {
55         Display[] rearDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
56         if (rearDisplays.length == 0) {
57             // No rear facing display found, marking waiting for display flag as true.
58             mWaitingForRearDisplay = true;
59             return;
60         }
61         mDisplayManager.unregisterDisplayListener(mRearDisplayListener);
62         mRearDisplayPresentationController.startSession(rearDisplays[0]);
63     }
64 
65     @Override
onRequestCanceled(@onNull DeviceStateRequest request)66     public void onRequestCanceled(@NonNull DeviceStateRequest request) {
67         mDisplayManager.unregisterDisplayListener(mRearDisplayListener);
68         mRearDisplayPresentationController.endSession();
69     }
70 
71     /**
72      * {@link DisplayManager.DisplayListener} to be used if a rear facing {@link Display} isn't
73      * available synchronously when the device is entered into the rear display presentation state.
74      * A rear facing {@link Display} is a {@link Display} that is categorized as
75      * {@link DisplayManager#DISPLAY_CATEGORY_REAR}. This can occur if {@link DisplayManager} is
76      * still in the process of configuring itself for this state when
77      * {@link DeviceStateRequest.Callback#onRequestActivated} is called.
78      *
79      * The {@link DisplayManager.DisplayListener} removes itself when a rear facing display is
80      * found.
81      */
82     private class RearDisplayListener implements DisplayManager.DisplayListener {
83         @Override
onDisplayAdded(int displayId)84         public void onDisplayAdded(int displayId) {
85             Display display = mDisplayManager.getDisplay(displayId);
86             if (mWaitingForRearDisplay && (display.getFlags() & Display.FLAG_REAR) != 0) {
87                 startRearDisplayPresentation(display);
88             }
89         }
90 
91         @Override
onDisplayRemoved(int displayId)92         public void onDisplayRemoved(int displayId) {}
93 
94         @Override
onDisplayChanged(int displayId)95         public void onDisplayChanged(int displayId) {
96             Display display = mDisplayManager.getDisplay(displayId);
97             if (mWaitingForRearDisplay && (display.getFlags() & Display.FLAG_REAR) != 0) {
98                 startRearDisplayPresentation(display);
99             }
100         }
101 
102         /**
103          * Starts a new {@link RearDisplayPresentation} with the updated {@link Display} with a
104          * category of {@link DisplayManager#DISPLAY_CATEGORY_REAR}.
105          */
startRearDisplayPresentation(Display rearDisplay)106         private void startRearDisplayPresentation(Display rearDisplay) {
107             // We have been notified of a change to a rear display, we can unregister the
108             // callback and stop waiting for a display
109             mDisplayManager.unregisterDisplayListener(this);
110             mWaitingForRearDisplay = false;
111 
112             mRearDisplayPresentationController.startSession(rearDisplay);
113         }
114     }
115 }
116