• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.quickstep.util;
18 
19 import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN;
20 import static android.view.Surface.ROTATION_0;
21 import static android.view.Surface.ROTATION_180;
22 import static android.view.Surface.ROTATION_270;
23 import static android.view.Surface.ROTATION_90;
24 
25 import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
26 import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
27 import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
28 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
29 import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI;
30 import static com.android.quickstep.BaseActivityInterface.getTaskDimension;
31 
32 import static java.lang.annotation.RetentionPolicy.SOURCE;
33 
34 import android.content.Context;
35 import android.graphics.Matrix;
36 import android.graphics.Point;
37 import android.graphics.PointF;
38 import android.graphics.Rect;
39 import android.util.Log;
40 import android.view.MotionEvent;
41 import android.view.OrientationEventListener;
42 import android.view.Surface;
43 
44 import androidx.annotation.IntDef;
45 import androidx.annotation.NonNull;
46 
47 import com.android.launcher3.DeviceProfile;
48 import com.android.launcher3.Flags;
49 import com.android.launcher3.InvariantDeviceProfile;
50 import com.android.launcher3.LauncherPrefChangeListener;
51 import com.android.launcher3.LauncherPrefs;
52 import com.android.launcher3.testing.shared.TestProtocol;
53 import com.android.launcher3.touch.PagedOrientationHandler;
54 import com.android.launcher3.util.DisplayController;
55 import com.android.launcher3.util.SettingsCache;
56 import com.android.quickstep.BaseContainerInterface;
57 import com.android.quickstep.SystemUiProxy;
58 import com.android.quickstep.TaskAnimationManager;
59 import com.android.quickstep.fallback.window.RecentsDisplayModel;
60 import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
61 
62 import java.lang.annotation.Retention;
63 import java.util.function.IntConsumer;
64 
65 /**
66  * Container to hold orientation/rotation related information for Launcher.
67  * This is not meant to be an abstraction layer for applying different functionality between
68  * the different orientation/rotations. For that see {@link PagedOrientationHandler}
69  *
70  * This class has initial default state assuming the device and foreground app have
71  * no ({@link Surface#ROTATION_0} rotation.
72  */
73 public class RecentsOrientedState implements LauncherPrefChangeListener {
74 
75     private static final String TAG = "RecentsOrientedState";
76     private static final boolean DEBUG = false;
77 
78     @Retention(SOURCE)
79     @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
80     public @interface SurfaceRotation {}
81 
82     private RecentsPagedOrientationHandler mOrientationHandler =
83             RecentsPagedOrientationHandler.PORTRAIT;
84 
85     private @SurfaceRotation int mTouchRotation = ROTATION_0;
86     private @SurfaceRotation int mDisplayRotation = ROTATION_0;
87     private @SurfaceRotation int mRecentsActivityRotation = ROTATION_0;
88     private @SurfaceRotation int mRecentsRotation = ROTATION_0 - 1;
89 
90     // Launcher activity supports multiple orientation, but fallback activity does not
91     private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
92     // Multiple orientation is only supported if density is < 600
93     private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY = 1 << 1;
94     // Shared prefs for rotation, only if activity supports it
95     private static final int FLAG_HOME_ROTATION_ALLOWED_IN_PREFS = 1 << 2;
96     // If the user has enabled system rotation
97     private static final int FLAG_SYSTEM_ROTATION_ALLOWED = 1 << 3;
98     // Multiple orientation is not supported in multiwindow mode
99     private static final int FLAG_MULTIWINDOW_ROTATION_ALLOWED = 1 << 4;
100     // Whether to rotation sensor is supported on the device
101     private static final int FLAG_ROTATION_WATCHER_SUPPORTED = 1 << 5;
102     // Whether to enable rotation watcher when multi-rotation is supported
103     private static final int FLAG_ROTATION_WATCHER_ENABLED = 1 << 6;
104     // Enable home rotation for UI tests, ignoring home rotation value from prefs
105     private static final int FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING = 1 << 7;
106     // Whether the swipe gesture is running, so the recents would stay locked in the
107     // current orientation
108     private static final int FLAG_SWIPE_UP_NOT_RUNNING = 1 << 8;
109     // Ignore shared prefs for home rotation rotation, allowing it in if the activity supports it
110     private static final int FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF = 1 << 9;
111 
112     // Shared prefs for fixed 90 degree rotation, activities should rotate if they support it
113     private static final int FLAG_HOME_FIXED_LANDSCAPE_PREFS = 1 << 10;
114 
115     private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
116             FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
117                     | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
118 
119     // State for which rotation watcher will be enabled. We skip it when home rotation or
120     // multi-window is enabled as in that case, activity itself rotates.
121     private static final int VALUE_ROTATION_WATCHER_ENABLED =
122             MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
123                     | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED
124                     | FLAG_SWIPE_UP_NOT_RUNNING;
125 
126     private final Context mContext;
127     private final BaseContainerInterface mContainerInterface;
128     private final OrientationEventListener mOrientationListener;
129     private final SettingsCache mSettingsCache;
130     private final SettingsCache.OnChangeListener mRotationChangeListener =
131             isEnabled -> updateAutoRotateSetting();
132 
133     private final Matrix mTmpMatrix = new Matrix();
134 
135     private int mFlags;
136     private int mPreviousRotation = ROTATION_0;
137     private boolean mListenersInitialized = false;
138 
139     // Combined int which encodes the full state.
140     private int mStateId = 0;
141 
142     /**
143      * @param rotationChangeListener Callback for receiving rotation events when rotation watcher
144      *                              is enabled
145      * @see #setRotationWatcherEnabled(boolean)
146      */
RecentsOrientedState(Context context, BaseContainerInterface containerInterface, IntConsumer rotationChangeListener)147     public RecentsOrientedState(Context context, BaseContainerInterface containerInterface,
148             IntConsumer rotationChangeListener) {
149         mContext = context;
150         mContainerInterface = containerInterface;
151         mOrientationListener = new OrientationEventListener(context) {
152             @Override
153             public void onOrientationChanged(int degrees) {
154                 int newRotation = getRotationForUserDegreesRotated(degrees, mPreviousRotation);
155                 if (newRotation != mPreviousRotation) {
156                     mPreviousRotation = newRotation;
157                     rotationChangeListener.accept(newRotation);
158                 }
159             }
160         };
161 
162         mFlags = mContainerInterface.rotationSupportedByActivity
163                 ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
164 
165         mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
166         mSettingsCache = SettingsCache.INSTANCE.get(mContext);
167         initFlags();
168     }
169 
getContainerInterface()170     public BaseContainerInterface getContainerInterface() {
171         return mContainerInterface;
172     }
173 
174     /**
175      * Sets the device profile for the current state.
176      */
setDeviceProfile(DeviceProfile deviceProfile)177     public void setDeviceProfile(DeviceProfile deviceProfile) {
178         boolean oldMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
179         setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.isTablet);
180         if (mListenersInitialized) {
181             boolean newMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
182             // If isMultipleOrientationSupportedByDevice is changed, init or destroy listeners
183             // accordingly.
184             if (newMultipleOrientationsSupported != oldMultipleOrientationsSupported) {
185                 if (newMultipleOrientationsSupported) {
186                     initMultipleOrientationListeners();
187                 } else {
188                     destroyMultipleOrientationListeners();
189                 }
190             }
191         }
192     }
193 
194     /**
195      * Sets the rotation for the recents activity, which could affect the appearance of task view.
196      * @see #update(int, int)
197      */
setRecentsRotation(@urfaceRotation int recentsRotation)198     public boolean setRecentsRotation(@SurfaceRotation int recentsRotation) {
199         mRecentsRotation = recentsRotation;
200         return updateHandler();
201     }
202 
203     /**
204      * Sets if the host is in multi-window mode
205      */
setMultiWindowMode(boolean isMultiWindow)206     public void setMultiWindowMode(boolean isMultiWindow) {
207         setFlag(FLAG_MULTIWINDOW_ROTATION_ALLOWED, isMultiWindow);
208     }
209 
210     /**
211      * Sets if the swipe up gesture is currently running or not
212      */
setGestureActive(boolean isGestureActive)213     public boolean setGestureActive(boolean isGestureActive) {
214         return setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
215     }
216 
217     /**
218      * Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
219      * @param touchRotation The rotation the nav bar region that is touched is in
220      * @param displayRotation Rotation of the display/device
221      *
222      * @return true if there was any change in the internal state as a result of this call,
223      *         false otherwise
224      */
update( @urfaceRotation int touchRotation, @SurfaceRotation int displayRotation)225     public boolean update(
226             @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
227         mDisplayRotation = displayRotation;
228         mTouchRotation = touchRotation;
229         mPreviousRotation = touchRotation;
230         return updateHandler();
231     }
232 
updateHandler()233     private boolean updateHandler() {
234         mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
235         if (mRecentsActivityRotation == mTouchRotation || shouldUseRealOrientation()) {
236             mOrientationHandler = RecentsPagedOrientationHandler.PORTRAIT;
237         } else if (mTouchRotation == ROTATION_90) {
238             mOrientationHandler = RecentsPagedOrientationHandler.LANDSCAPE;
239         } else if (mTouchRotation == ROTATION_270) {
240             mOrientationHandler = RecentsPagedOrientationHandler.SEASCAPE;
241         } else {
242             mOrientationHandler = RecentsPagedOrientationHandler.PORTRAIT;
243         }
244         if (DEBUG) {
245             Log.d(TAG, "current RecentsOrientedState: " + this);
246         }
247 
248         int oldStateId = mStateId;
249         // Each SurfaceRotation value takes two bits
250         mStateId = (((((mFlags << 2)
251                 | mDisplayRotation) << 2)
252                 | mTouchRotation) << 3)
253                 | (mRecentsRotation < 0 ? 7 : mRecentsRotation);
254         return mStateId != oldStateId;
255     }
256 
shouldUseRealOrientation()257     private boolean shouldUseRealOrientation() {
258         return isRecentsActivityRotationAllowed() || isLauncherFixedLandscape();
259     }
260 
261     @SurfaceRotation
inferRecentsActivityRotation(@urfaceRotation int displayRotation)262     private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
263         if (shouldUseRealOrientation()) {
264             return mRecentsRotation < 0 ? displayRotation : mRecentsRotation;
265         } else {
266             return ROTATION_0;
267         }
268     }
269 
setFlag(int mask, boolean enabled)270     private boolean setFlag(int mask, boolean enabled) {
271         boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
272                 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
273                 && !isRecentsActivityRotationAllowed();
274         if (enabled) {
275             mFlags |= mask;
276         } else {
277             mFlags &= ~mask;
278         }
279 
280         boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation
281                 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
282                 && !isRecentsActivityRotationAllowed();
283         if (wasRotationEnabled != isRotationEnabled) {
284             UI_HELPER_EXECUTOR.execute(() -> {
285                 if (isRotationEnabled) {
286                     mOrientationListener.enable();
287                 } else {
288                     mOrientationListener.disable();
289                 }
290             });
291         }
292         return updateHandler();
293     }
294 
295     @Override
onPrefChanged(String s)296     public void onPrefChanged(String s) {
297         if (LauncherPrefs.ALLOW_ROTATION.getSharedPrefKey().equals(s)) {
298             updateHomeRotationSetting();
299         }
300         if (LauncherPrefs.FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(s)) {
301             updateFixedLandscapeSetting();
302         }
303     }
304 
updateAutoRotateSetting()305     private void updateAutoRotateSetting() {
306         setFlag(FLAG_SYSTEM_ROTATION_ALLOWED,
307                 mSettingsCache.getValue(ROTATION_SETTING_URI, 1));
308     }
309 
updateFixedLandscapeSetting()310     private void updateFixedLandscapeSetting() {
311         if (Flags.oneGridSpecs()) {
312             setFlag(
313                     FLAG_HOME_FIXED_LANDSCAPE_PREFS,
314                     LauncherPrefs.get(mContext).get(FIXED_LANDSCAPE_MODE)
315             );
316         }
317     }
318 
updateHomeRotationSetting()319     private void updateHomeRotationSetting() {
320         boolean homeRotationEnabled = LauncherPrefs.get(mContext).get(ALLOW_ROTATION);
321         setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled);
322         SystemUiProxy.INSTANCE.get(mContext).setHomeRotationEnabled(homeRotationEnabled);
323     }
324 
initFlags()325     private void initFlags() {
326         setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, mOrientationListener.canDetectOrientation());
327 
328         // initialize external flags
329         updateAutoRotateSetting();
330         updateHomeRotationSetting();
331         updateFixedLandscapeSetting();
332     }
333 
initMultipleOrientationListeners()334     private void initMultipleOrientationListeners() {
335         LauncherPrefs.get(mContext).addListener(this, ALLOW_ROTATION);
336         mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener);
337         updateAutoRotateSetting();
338     }
339 
destroyMultipleOrientationListeners()340     private void destroyMultipleOrientationListeners() {
341         LauncherPrefs.get(mContext).removeListener(this, ALLOW_ROTATION);
342         mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener);
343     }
344 
345     /**
346      * Initializes any system values and registers corresponding change listeners. It must be
347      * paired with {@link #destroyListeners()} call
348      */
initListeners()349     public void initListeners() {
350         mListenersInitialized = true;
351         if (isMultipleOrientationSupportedByDevice()) {
352             initMultipleOrientationListeners();
353         }
354         initFlags();
355     }
356 
357     /**
358      * Unregisters any previously registered listeners.
359      */
destroyListeners()360     public void destroyListeners() {
361         mListenersInitialized = false;
362         if (isMultipleOrientationSupportedByDevice()) {
363             destroyMultipleOrientationListeners();
364         }
365         setRotationWatcherEnabled(false);
366     }
367 
forceAllowRotationForTesting(boolean forceAllow)368     public void forceAllowRotationForTesting(boolean forceAllow) {
369         setFlag(FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING, forceAllow);
370     }
371 
372     @SurfaceRotation
getDisplayRotation()373     public int getDisplayRotation() {
374         if (TaskAnimationManager.SHELL_TRANSITIONS_ROTATION) {
375             // When shell transitions are enabled, both the display and activity rotations should
376             // be the same once the gesture starts
377             return mRecentsActivityRotation;
378         }
379         return mDisplayRotation;
380     }
381 
382     @SurfaceRotation
getTouchRotation()383     public int getTouchRotation() {
384         return mTouchRotation;
385     }
386 
387     @SurfaceRotation
getRecentsActivityRotation()388     public int getRecentsActivityRotation() {
389         return mRecentsActivityRotation;
390     }
391 
392     /**
393      * Returns an id that can be used to tracking internal changes
394      */
getStateId()395     public int getStateId() {
396         return mStateId;
397     }
398 
isMultipleOrientationSupportedByDevice()399     public boolean isMultipleOrientationSupportedByDevice() {
400         return (mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
401                 == MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
402     }
403 
ignoreAllowHomeRotationPreference()404     public void ignoreAllowHomeRotationPreference() {
405         setFlag(FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF, true);
406     }
407 
isLauncherFixedLandscape()408     public boolean isLauncherFixedLandscape() {
409         return (mFlags & FLAG_HOME_FIXED_LANDSCAPE_PREFS) == FLAG_HOME_FIXED_LANDSCAPE_PREFS;
410     }
411 
isRecentsActivityRotationAllowed()412     public boolean isRecentsActivityRotationAllowed() {
413         // Activity rotation is allowed if the multi-simulated-rotation is not supported
414         // (fallback recents or tablets) or activity rotation is enabled by various settings.
415         return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
416                 != MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
417                 || (mFlags & (FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF
418                 | FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
419                 | FLAG_MULTIWINDOW_ROTATION_ALLOWED
420                 | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
421     }
422 
423     /**
424      * Enables or disables the rotation watcher for listening to rotation callbacks
425      */
setRotationWatcherEnabled(boolean isEnabled)426     public void setRotationWatcherEnabled(boolean isEnabled) {
427         setFlag(FLAG_ROTATION_WATCHER_ENABLED, isEnabled);
428     }
429 
430     /**
431      * Returns the scale and pivot so that the provided taskRect can fit the provided full size
432      */
getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot)433     public float getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot) {
434         getTaskDimension(mContext, dp, outPivot);
435         float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height());
436         if (scale == 1) {
437             outPivot.set(taskView.centerX(), taskView.centerY());
438         } else {
439             float factor = scale / (scale - 1);
440             outPivot.set(taskView.left * factor, taskView.top * factor);
441         }
442         return scale;
443     }
444 
getOrientationHandler()445     public RecentsPagedOrientationHandler getOrientationHandler() {
446         return mOrientationHandler;
447     }
448 
449     /**
450      * For landscape, since the navbar is already in a vertical position, we don't have to do any
451      * rotations as the change in Y coordinate is what is read. We only flip the sign of the
452      * y coordinate to make it match existing behavior of swipe to the top to go previous
453      */
flipVertical(MotionEvent ev)454     public void flipVertical(MotionEvent ev) {
455         mTmpMatrix.setScale(1, -1);
456         ev.transform(mTmpMatrix);
457     }
458 
459     /**
460      * Creates a matrix to transform the given motion event specified by degrees.
461      * If inverse is {@code true}, the inverse of that matrix will be applied
462      */
transformEvent(float degrees, MotionEvent ev, boolean inverse)463     public void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
464         mTmpMatrix.setRotate(inverse ? -degrees : degrees);
465         ev.transform(mTmpMatrix);
466 
467         // TODO: Add scaling back in based on degrees
468         /*
469         if (getWidth() > 0 && getHeight() > 0) {
470             float scale = ((float) getWidth()) / getHeight();
471             transform.postScale(scale, 1 / scale);
472         }
473         */
474     }
475 
476     @SurfaceRotation
getRotationForUserDegreesRotated(float degrees, int currentRotation)477     public static int getRotationForUserDegreesRotated(float degrees, int currentRotation) {
478         if (degrees == ORIENTATION_UNKNOWN) {
479             return currentRotation;
480         }
481 
482         int threshold = 70;
483         switch (currentRotation) {
484             case ROTATION_0:
485                 if (degrees > 180 && degrees < (360 - threshold)) {
486                     return ROTATION_90;
487                 }
488                 if (degrees < 180 && degrees > threshold) {
489                     return ROTATION_270;
490                 }
491                 break;
492             case ROTATION_270:
493                 if (degrees < (90 - threshold) ||
494                         (degrees > (270 + threshold) && degrees < 360)) {
495                     return ROTATION_0;
496                 }
497                 if (degrees > (90 + threshold) && degrees < 180) {
498                     return ROTATION_180;
499                 }
500                 // flip from seascape to landscape
501                 if (degrees > (180 + threshold) && degrees < 360) {
502                     return ROTATION_90;
503                 }
504                 break;
505             case ROTATION_180:
506                 if (degrees < (180 - threshold)) {
507                     return ROTATION_270;
508                 }
509                 if (degrees > (180 + threshold)) {
510                     return ROTATION_90;
511                 }
512                 break;
513             case ROTATION_90:
514                 if (degrees < (270 - threshold) && degrees > 90) {
515                     return ROTATION_180;
516                 }
517                 if (degrees > (270 + threshold) && degrees < 360
518                         || (degrees >= 0 && degrees < threshold)) {
519                     return ROTATION_0;
520                 }
521                 // flip from landscape to seascape
522                 if (degrees > threshold && degrees < 180) {
523                     return ROTATION_270;
524                 }
525                 break;
526         }
527 
528         return currentRotation;
529     }
530 
isDisplayPhoneNatural()531     public boolean isDisplayPhoneNatural() {
532         return mDisplayRotation == Surface.ROTATION_0 || mDisplayRotation == Surface.ROTATION_180;
533     }
534 
535     /**
536      * Posts the transformation on the matrix representing the provided display rotation
537      */
postDisplayRotation(@urfaceRotation int displayRotation, float screenWidth, float screenHeight, Matrix out)538     public static void postDisplayRotation(@SurfaceRotation int displayRotation,
539             float screenWidth, float screenHeight, Matrix out) {
540         switch (displayRotation) {
541             case ROTATION_0:
542                 return;
543             case ROTATION_90:
544                 out.postRotate(270);
545                 out.postTranslate(0, screenWidth);
546                 break;
547             case ROTATION_180:
548                 out.postRotate(180);
549                 out.postTranslate(screenHeight, screenWidth);
550                 break;
551             case ROTATION_270:
552                 out.postRotate(90);
553                 out.postTranslate(screenHeight, 0);
554                 break;
555         }
556     }
557 
558     /**
559      * Contrary to {@link #postDisplayRotation}.
560      */
preDisplayRotation(@urfaceRotation int displayRotation, float screenWidth, float screenHeight, Matrix out)561     public static void preDisplayRotation(@SurfaceRotation int displayRotation,
562             float screenWidth, float screenHeight, Matrix out) {
563         switch (displayRotation) {
564             case ROTATION_0:
565                 return;
566             case ROTATION_90:
567                 out.postRotate(90);
568                 out.postTranslate(screenWidth, 0);
569                 break;
570             case ROTATION_180:
571                 out.postRotate(180);
572                 out.postTranslate(screenHeight, screenWidth);
573                 break;
574             case ROTATION_270:
575                 out.postRotate(270);
576                 out.postTranslate(0, screenHeight);
577                 break;
578         }
579     }
580 
581     @NonNull
582     @Override
toString()583     public String toString() {
584         boolean systemRotationOn = (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0;
585         return "["
586                 + "this=" + nameAndAddress(this)
587                 + " mOrientationHandler=" + nameAndAddress(mOrientationHandler)
588                 + " mDisplayRotation=" + mDisplayRotation
589                 + " mTouchRotation=" + mTouchRotation
590                 + " mRecentsActivityRotation=" + mRecentsActivityRotation
591                 + " mRecentsRotation=" + mRecentsRotation
592                 + " isRecentsActivityRotationAllowed=" + isRecentsActivityRotationAllowed()
593                 + " mSystemRotation=" + systemRotationOn
594                 + " mStateId=" + mStateId
595                 + " mFlags=" + mFlags
596                 + "]";
597     }
598 
599     /**
600      * Returns the device profile based on expected launcher rotation
601      */
getLauncherDeviceProfile(int displayId)602     public DeviceProfile getLauncherDeviceProfile(int displayId) {
603         if (enableOverviewOnConnectedDisplays()) {
604             return RecentsDisplayModel.getINSTANCE().get(mContext).getRecentsWindowManager(
605                     displayId).getDeviceProfile();
606         } else {
607             InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
608             Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
609 
610             int width, height;
611             if ((mRecentsActivityRotation == ROTATION_90
612                     || mRecentsActivityRotation == ROTATION_270)) {
613                 width = Math.max(currentSize.x, currentSize.y);
614                 height = Math.min(currentSize.x, currentSize.y);
615             } else {
616                 width = Math.min(currentSize.x, currentSize.y);
617                 height = Math.max(currentSize.x, currentSize.y);
618             }
619             return idp.getBestMatch(width, height, mRecentsActivityRotation);
620         }
621     }
622 
nameAndAddress(Object obj)623     private static String nameAndAddress(Object obj) {
624         return obj.getClass().getSimpleName() + "@" + obj.hashCode();
625     }
626 }
627