1 /* 2 * Copyright (C) 2020 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.sidecar; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 21 import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; 22 import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; 23 24 import android.app.Activity; 25 import android.app.ActivityThread; 26 import android.content.Context; 27 import android.graphics.Rect; 28 import android.os.IBinder; 29 import android.util.Log; 30 31 import androidx.annotation.NonNull; 32 import androidx.window.common.DeviceStateManagerPostureProducer; 33 import androidx.window.common.DisplayFeature; 34 import androidx.window.common.ResourceConfigDisplayFeatureProducer; 35 import androidx.window.common.SettingsDevicePostureProducer; 36 import androidx.window.common.SettingsDisplayFeatureProducer; 37 import androidx.window.util.DataProducer; 38 import androidx.window.util.PriorityDataProducer; 39 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Optional; 44 45 /** 46 * Reference implementation of androidx.window.sidecar OEM interface for use with 47 * WindowManager Jetpack. 48 */ 49 class SampleSidecarImpl extends StubSidecar { 50 private static final String TAG = "SampleSidecar"; 51 private static final boolean DEBUG = false; 52 53 private final SettingsDevicePostureProducer mSettingsDevicePostureProducer; 54 private final DataProducer<Integer> mDevicePostureProducer; 55 56 private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; 57 private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer; 58 SampleSidecarImpl(Context context)59 SampleSidecarImpl(Context context) { 60 mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); 61 mDevicePostureProducer = new PriorityDataProducer<>(List.of( 62 mSettingsDevicePostureProducer, 63 new DeviceStateManagerPostureProducer(context) 64 )); 65 66 mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context); 67 mDisplayFeatureProducer = new PriorityDataProducer<>(List.of( 68 mSettingsDisplayFeatureProducer, 69 new ResourceConfigDisplayFeatureProducer(context) 70 )); 71 72 mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged); 73 mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); 74 } 75 onDevicePostureChanged()76 private void onDevicePostureChanged() { 77 updateDeviceState(getDeviceState()); 78 } 79 onDisplayFeaturesChanged()80 private void onDisplayFeaturesChanged() { 81 for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { 82 SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); 83 updateWindowLayout(windowToken, newLayout); 84 } 85 } 86 87 @NonNull 88 @Override getDeviceState()89 public SidecarDeviceState getDeviceState() { 90 Optional<Integer> posture = mDevicePostureProducer.getData(); 91 92 SidecarDeviceState deviceState = new SidecarDeviceState(); 93 deviceState.posture = posture.orElse(deviceStateFromFeature()); 94 return deviceState; 95 } 96 deviceStateFromFeature()97 private int deviceStateFromFeature() { 98 List<DisplayFeature> storedFeatures = mDisplayFeatureProducer.getData() 99 .orElse(Collections.emptyList()); 100 for (int i = 0; i < storedFeatures.size(); i++) { 101 DisplayFeature feature = storedFeatures.get(i); 102 final int state = feature.getState() == null ? -1 : feature.getState(); 103 if (DEBUG && feature.getState() == null) { 104 Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature); 105 } 106 107 switch (state) { 108 case DisplayFeature.COMMON_STATE_FLAT: 109 return SidecarDeviceState.POSTURE_OPENED; 110 case DisplayFeature.COMMON_STATE_HALF_OPENED: 111 return SidecarDeviceState.POSTURE_HALF_OPENED; 112 } 113 } 114 return SidecarDeviceState.POSTURE_UNKNOWN; 115 } 116 117 @NonNull 118 @Override getWindowLayoutInfo(@onNull IBinder windowToken)119 public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { 120 Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); 121 SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo(); 122 if (activity == null) { 123 return windowLayoutInfo; 124 } 125 windowLayoutInfo.displayFeatures = getDisplayFeatures(activity); 126 return windowLayoutInfo; 127 } 128 getDisplayFeatures(@onNull Activity activity)129 private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) { 130 List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>(); 131 int displayId = activity.getDisplay().getDisplayId(); 132 if (displayId != DEFAULT_DISPLAY) { 133 Log.w(TAG, "This sample doesn't support display features on secondary displays"); 134 return features; 135 } 136 137 if (activity.isInMultiWindowMode()) { 138 // It is recommended not to report any display features in multi-window mode, since it 139 // won't be possible to synchronize the display feature positions with window movement. 140 return features; 141 } 142 143 Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData(); 144 if (storedFeatures.isPresent()) { 145 for (DisplayFeature baseFeature : storedFeatures.get()) { 146 SidecarDisplayFeature feature = new SidecarDisplayFeature(); 147 Rect featureRect = baseFeature.getRect(); 148 rotateRectToDisplayRotation(displayId, featureRect); 149 transformToWindowSpaceRect(activity, featureRect); 150 feature.setRect(featureRect); 151 feature.setType(baseFeature.getType()); 152 features.add(feature); 153 } 154 } 155 return features; 156 } 157 158 @Override onListenersChanged()159 protected void onListenersChanged() { 160 if (hasListeners()) { 161 mSettingsDevicePostureProducer.registerObserversIfNeeded(); 162 mSettingsDisplayFeatureProducer.registerObserversIfNeeded(); 163 } else { 164 mSettingsDevicePostureProducer.unregisterObserversIfNeeded(); 165 mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded(); 166 } 167 } 168 } 169