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 import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; 21 import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; 22 23 import android.app.Activity; 24 import android.app.ActivityThread; 25 import android.app.Application; 26 import android.content.Context; 27 import android.graphics.Rect; 28 import android.os.Bundle; 29 import android.os.IBinder; 30 31 import androidx.annotation.NonNull; 32 import androidx.window.common.CommonFoldingFeature; 33 import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; 34 import androidx.window.common.EmptyLifecycleCallbacksAdapter; 35 import androidx.window.common.RawFoldingFeatureProducer; 36 import androidx.window.util.BaseDataProducer; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.List; 41 42 /** 43 * Reference implementation of androidx.window.sidecar OEM interface for use with 44 * WindowManager Jetpack. 45 */ 46 class SampleSidecarImpl extends StubSidecar { 47 private List<CommonFoldingFeature> mStoredFeatures = new ArrayList<>(); 48 SampleSidecarImpl(Context context)49 SampleSidecarImpl(Context context) { 50 ((Application) context.getApplicationContext()) 51 .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); 52 BaseDataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context); 53 BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer = 54 new DeviceStateManagerFoldingFeatureProducer(context, 55 settingsFeatureProducer); 56 57 foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); 58 } 59 setStoredFeatures(List<CommonFoldingFeature> storedFeatures)60 private void setStoredFeatures(List<CommonFoldingFeature> storedFeatures) { 61 mStoredFeatures = storedFeatures; 62 } 63 onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures)64 private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) { 65 setStoredFeatures(storedFeatures); 66 updateDeviceState(getDeviceState()); 67 for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { 68 SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); 69 updateWindowLayout(windowToken, newLayout); 70 } 71 } 72 73 @NonNull 74 @Override getDeviceState()75 public SidecarDeviceState getDeviceState() { 76 SidecarDeviceState deviceState = new SidecarDeviceState(); 77 deviceState.posture = deviceStateFromFeature(); 78 return deviceState; 79 } 80 deviceStateFromFeature()81 private int deviceStateFromFeature() { 82 for (int i = 0; i < mStoredFeatures.size(); i++) { 83 CommonFoldingFeature feature = mStoredFeatures.get(i); 84 final int state = feature.getState(); 85 switch (state) { 86 case CommonFoldingFeature.COMMON_STATE_FLAT: 87 return SidecarDeviceState.POSTURE_OPENED; 88 case CommonFoldingFeature.COMMON_STATE_HALF_OPENED: 89 return SidecarDeviceState.POSTURE_HALF_OPENED; 90 case CommonFoldingFeature.COMMON_STATE_UNKNOWN: 91 return SidecarDeviceState.POSTURE_UNKNOWN; 92 } 93 } 94 return SidecarDeviceState.POSTURE_UNKNOWN; 95 } 96 97 @NonNull 98 @Override getWindowLayoutInfo(@onNull IBinder windowToken)99 public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) { 100 Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken); 101 SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo(); 102 if (activity == null) { 103 return windowLayoutInfo; 104 } 105 windowLayoutInfo.displayFeatures = getDisplayFeatures(activity); 106 return windowLayoutInfo; 107 } 108 getDisplayFeatures(@onNull Activity activity)109 private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) { 110 int displayId = activity.getDisplay().getDisplayId(); 111 if (displayId != DEFAULT_DISPLAY) { 112 return Collections.emptyList(); 113 } 114 115 if (activity.isInMultiWindowMode()) { 116 // It is recommended not to report any display features in multi-window mode, since it 117 // won't be possible to synchronize the display feature positions with window movement. 118 return Collections.emptyList(); 119 } 120 121 List<SidecarDisplayFeature> features = new ArrayList<>(); 122 final int rotation = activity.getResources().getConfiguration().windowConfiguration 123 .getDisplayRotation(); 124 for (CommonFoldingFeature baseFeature : mStoredFeatures) { 125 SidecarDisplayFeature feature = new SidecarDisplayFeature(); 126 Rect featureRect = baseFeature.getRect(); 127 rotateRectToDisplayRotation(displayId, rotation, featureRect); 128 transformToWindowSpaceRect(activity, featureRect); 129 feature.setRect(featureRect); 130 feature.setType(baseFeature.getType()); 131 features.add(feature); 132 } 133 return Collections.unmodifiableList(features); 134 } 135 136 @Override onListenersChanged()137 protected void onListenersChanged() { 138 if (hasListeners()) { 139 onDisplayFeaturesChanged(mStoredFeatures); 140 } 141 } 142 143 private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter { 144 @Override onActivityCreated(Activity activity, Bundle savedInstanceState)145 public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 146 super.onActivityCreated(activity, savedInstanceState); 147 onDisplayFeaturesChangedForActivity(activity); 148 } 149 150 @Override onActivityConfigurationChanged(Activity activity)151 public void onActivityConfigurationChanged(Activity activity) { 152 super.onActivityConfigurationChanged(activity); 153 onDisplayFeaturesChangedForActivity(activity); 154 } 155 onDisplayFeaturesChangedForActivity(@onNull Activity activity)156 private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) { 157 IBinder token = activity.getWindow().getAttributes().token; 158 if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) { 159 onDisplayFeaturesChanged(mStoredFeatures); 160 } 161 } 162 } 163 } 164