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