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