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.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; 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.Utilities; 49 import com.android.launcher3.testing.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.views.TaskView; 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 105 private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE = 106 FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY 107 | FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY; 108 109 // State for which rotation watcher will be enabled. We skip it when home rotation or 110 // multi-window is enabled as in that case, activity itself rotates. 111 private static final int VALUE_ROTATION_WATCHER_ENABLED = 112 MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED 113 | FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED 114 | FLAG_SWIPE_UP_NOT_RUNNING; 115 116 private final Context mContext; 117 private final SharedPreferences mSharedPrefs; 118 private final OrientationEventListener mOrientationListener; 119 private final SettingsCache mSettingsCache; 120 private final SettingsCache.OnChangeListener mRotationChangeListener = 121 isEnabled -> updateAutoRotateSetting(); 122 123 private final Matrix mTmpMatrix = new Matrix(); 124 125 private int mFlags; 126 private int mPreviousRotation = ROTATION_0; 127 private boolean mListenersInitialized = false; 128 129 // Combined int which encodes the full state. 130 private int mStateId = 0; 131 132 /** 133 * @param rotationChangeListener Callback for receiving rotation events when rotation watcher 134 * is enabled 135 * @see #setRotationWatcherEnabled(boolean) 136 */ RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy, IntConsumer rotationChangeListener)137 public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy, 138 IntConsumer rotationChangeListener) { 139 mContext = context; 140 mSharedPrefs = Utilities.getPrefs(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.allowRotation); 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 222 || (canRecentsActivityRotate() && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) { 223 mOrientationHandler = PagedOrientationHandler.PORTRAIT; 224 } else if (mTouchRotation == ROTATION_90) { 225 mOrientationHandler = PagedOrientationHandler.LANDSCAPE; 226 } else if (mTouchRotation == ROTATION_270) { 227 mOrientationHandler = PagedOrientationHandler.SEASCAPE; 228 } else { 229 mOrientationHandler = PagedOrientationHandler.PORTRAIT; 230 } 231 if (DEBUG) { 232 Log.d(TAG, "current RecentsOrientedState: " + this); 233 } 234 235 int oldStateId = mStateId; 236 // Each SurfaceRotation value takes two bits 237 mStateId = (((((mFlags << 2) 238 | mDisplayRotation) << 2) 239 | mTouchRotation) << 3) 240 | (mRecentsRotation < 0 ? 7 : mRecentsRotation); 241 return mStateId != oldStateId; 242 } 243 244 @SurfaceRotation inferRecentsActivityRotation(@urfaceRotation int displayRotation)245 private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) { 246 if (isRecentsActivityRotationAllowed()) { 247 return mRecentsRotation < 0 ? displayRotation : mRecentsRotation; 248 } else { 249 return ROTATION_0; 250 } 251 } 252 setFlag(int mask, boolean enabled)253 private boolean setFlag(int mask, boolean enabled) { 254 boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation 255 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED 256 && !canRecentsActivityRotate(); 257 if (enabled) { 258 mFlags |= mask; 259 } else { 260 mFlags &= ~mask; 261 } 262 263 boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation 264 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED 265 && !canRecentsActivityRotate(); 266 if (wasRotationEnabled != isRotationEnabled) { 267 UI_HELPER_EXECUTOR.execute(() -> { 268 if (isRotationEnabled) { 269 mOrientationListener.enable(); 270 } else { 271 mOrientationListener.disable(); 272 } 273 }); 274 } 275 return updateHandler(); 276 } 277 278 @Override onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s)279 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { 280 if (ALLOW_ROTATION_PREFERENCE_KEY.equals(s)) { 281 updateHomeRotationSetting(); 282 } 283 } 284 updateAutoRotateSetting()285 private void updateAutoRotateSetting() { 286 setFlag(FLAG_SYSTEM_ROTATION_ALLOWED, 287 mSettingsCache.getValue(ROTATION_SETTING_URI, 1)); 288 } 289 updateHomeRotationSetting()290 private void updateHomeRotationSetting() { 291 boolean homeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false); 292 setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled); 293 SystemUiProxy.INSTANCE.get(mContext).setHomeRotationEnabled(homeRotationEnabled); 294 } 295 initFlags()296 private void initFlags() { 297 setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, mOrientationListener.canDetectOrientation()); 298 299 // initialize external flags 300 updateAutoRotateSetting(); 301 updateHomeRotationSetting(); 302 } 303 initMultipleOrientationListeners()304 private void initMultipleOrientationListeners() { 305 mSharedPrefs.registerOnSharedPreferenceChangeListener(this); 306 mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener); 307 } 308 destroyMultipleOrientationListeners()309 private void destroyMultipleOrientationListeners() { 310 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); 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 return mDisplayRotation; 344 } 345 346 @SurfaceRotation getTouchRotation()347 public int getTouchRotation() { 348 return mTouchRotation; 349 } 350 351 @SurfaceRotation getRecentsActivityRotation()352 public int getRecentsActivityRotation() { 353 return mRecentsActivityRotation; 354 } 355 356 /** 357 * Returns an id that can be used to tracking internal changes 358 */ getStateId()359 public int getStateId() { 360 return mStateId; 361 } 362 isMultipleOrientationSupportedByDevice()363 public boolean isMultipleOrientationSupportedByDevice() { 364 return (mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE) 365 == MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE; 366 } 367 isRecentsActivityRotationAllowed()368 public boolean isRecentsActivityRotationAllowed() { 369 // Activity rotation is allowed if the multi-simulated-rotation is not supported 370 // (fallback recents or tablets) or activity rotation is enabled by various settings. 371 return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE) 372 != MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE) 373 || (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS 374 | FLAG_MULTIWINDOW_ROTATION_ALLOWED 375 | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0; 376 } 377 378 /** 379 * Returns true if the activity can rotate, if allowed by system rotation settings 380 */ canRecentsActivityRotate()381 public boolean canRecentsActivityRotate() { 382 return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isRecentsActivityRotationAllowed(); 383 } 384 385 /** 386 * Enables or disables the rotation watcher for listening to rotation callbacks 387 */ setRotationWatcherEnabled(boolean isEnabled)388 public void setRotationWatcherEnabled(boolean isEnabled) { 389 setFlag(FLAG_ROTATION_WATCHER_ENABLED, isEnabled); 390 } 391 392 /** 393 * Returns the scale and pivot so that the provided taskRect can fit the provided full size 394 */ getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot)395 public float getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot) { 396 Rect insets = dp.getInsets(); 397 float fullWidth = dp.widthPx; 398 float fullHeight = dp.heightPx; 399 if (TaskView.CLIP_STATUS_AND_NAV_BARS) { 400 fullWidth -= insets.left + insets.right; 401 fullHeight -= insets.top + insets.bottom; 402 } 403 404 getTaskDimension(mContext, dp, outPivot); 405 float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height()); 406 // We also scale the preview as part of fullScreenParams, so account for that as well. 407 if (fullWidth > 0) { 408 scale = scale * dp.widthPx / fullWidth; 409 } 410 411 if (scale == 1) { 412 outPivot.set(fullWidth / 2, fullHeight / 2); 413 } else if (dp.isMultiWindowMode) { 414 float denominator = 1 / (scale - 1); 415 // Ensure that the task aligns to right bottom for the root view 416 float y = (scale * taskView.bottom - fullHeight) * denominator; 417 float x = (scale * taskView.right - fullWidth) * denominator; 418 outPivot.set(x, y); 419 } else { 420 float factor = scale / (scale - 1); 421 outPivot.set(taskView.left * factor, taskView.top * factor); 422 } 423 return scale; 424 } 425 getOrientationHandler()426 public PagedOrientationHandler getOrientationHandler() { 427 return mOrientationHandler; 428 } 429 430 /** 431 * For landscape, since the navbar is already in a vertical position, we don't have to do any 432 * rotations as the change in Y coordinate is what is read. We only flip the sign of the 433 * y coordinate to make it match existing behavior of swipe to the top to go previous 434 */ flipVertical(MotionEvent ev)435 public void flipVertical(MotionEvent ev) { 436 mTmpMatrix.setScale(1, -1); 437 ev.transform(mTmpMatrix); 438 } 439 440 /** 441 * Creates a matrix to transform the given motion event specified by degrees. 442 * If inverse is {@code true}, the inverse of that matrix will be applied 443 */ transformEvent(float degrees, MotionEvent ev, boolean inverse)444 public void transformEvent(float degrees, MotionEvent ev, boolean inverse) { 445 mTmpMatrix.setRotate(inverse ? -degrees : degrees); 446 ev.transform(mTmpMatrix); 447 448 // TODO: Add scaling back in based on degrees 449 /* 450 if (getWidth() > 0 && getHeight() > 0) { 451 float scale = ((float) getWidth()) / getHeight(); 452 transform.postScale(scale, 1 / scale); 453 } 454 */ 455 } 456 457 @SurfaceRotation getRotationForUserDegreesRotated(float degrees, int currentRotation)458 public static int getRotationForUserDegreesRotated(float degrees, int currentRotation) { 459 if (degrees == ORIENTATION_UNKNOWN) { 460 return currentRotation; 461 } 462 463 int threshold = 70; 464 switch (currentRotation) { 465 case ROTATION_0: 466 if (degrees > 180 && degrees < (360 - threshold)) { 467 return ROTATION_90; 468 } 469 if (degrees < 180 && degrees > threshold) { 470 return ROTATION_270; 471 } 472 break; 473 case ROTATION_270: 474 if (degrees < (90 - threshold) || 475 (degrees > (270 + threshold) && degrees < 360)) { 476 return ROTATION_0; 477 } 478 if (degrees > (90 + threshold) && degrees < 180) { 479 return ROTATION_180; 480 } 481 // flip from seascape to landscape 482 if (degrees > (180 + threshold) && degrees < 360) { 483 return ROTATION_90; 484 } 485 break; 486 case ROTATION_180: 487 if (degrees < (180 - threshold)) { 488 return ROTATION_270; 489 } 490 if (degrees > (180 + threshold)) { 491 return ROTATION_90; 492 } 493 break; 494 case ROTATION_90: 495 if (degrees < (270 - threshold) && degrees > 90) { 496 return ROTATION_180; 497 } 498 if (degrees > (270 + threshold) && degrees < 360 499 || (degrees >= 0 && degrees < threshold)) { 500 return ROTATION_0; 501 } 502 // flip from landscape to seascape 503 if (degrees > threshold && degrees < 180) { 504 return ROTATION_270; 505 } 506 break; 507 } 508 509 return currentRotation; 510 } 511 isDisplayPhoneNatural()512 public boolean isDisplayPhoneNatural() { 513 return mDisplayRotation == Surface.ROTATION_0 || mDisplayRotation == Surface.ROTATION_180; 514 } 515 516 /** 517 * Posts the transformation on the matrix representing the provided display rotation 518 */ postDisplayRotation(@urfaceRotation int displayRotation, float screenWidth, float screenHeight, Matrix out)519 public static void postDisplayRotation(@SurfaceRotation int displayRotation, 520 float screenWidth, float screenHeight, Matrix out) { 521 switch (displayRotation) { 522 case ROTATION_0: 523 return; 524 case ROTATION_90: 525 out.postRotate(270); 526 out.postTranslate(0, screenWidth); 527 break; 528 case ROTATION_180: 529 out.postRotate(180); 530 out.postTranslate(screenHeight, screenWidth); 531 break; 532 case ROTATION_270: 533 out.postRotate(90); 534 out.postTranslate(screenHeight, 0); 535 break; 536 } 537 } 538 539 /** 540 * Contrary to {@link #postDisplayRotation}. 541 */ preDisplayRotation(@urfaceRotation int displayRotation, float screenWidth, float screenHeight, Matrix out)542 public static void preDisplayRotation(@SurfaceRotation int displayRotation, 543 float screenWidth, float screenHeight, Matrix out) { 544 switch (displayRotation) { 545 case ROTATION_0: 546 return; 547 case ROTATION_90: 548 out.postRotate(90); 549 out.postTranslate(screenWidth, 0); 550 break; 551 case ROTATION_180: 552 out.postRotate(180); 553 out.postTranslate(screenHeight, screenWidth); 554 break; 555 case ROTATION_270: 556 out.postRotate(270); 557 out.postTranslate(0, screenHeight); 558 break; 559 } 560 } 561 562 @NonNull 563 @Override toString()564 public String toString() { 565 boolean systemRotationOn = (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0; 566 return "[" 567 + "this=" + nameAndAddress(this) 568 + " mOrientationHandler=" + nameAndAddress(mOrientationHandler) 569 + " mDisplayRotation=" + mDisplayRotation 570 + " mTouchRotation=" + mTouchRotation 571 + " mRecentsActivityRotation=" + mRecentsActivityRotation 572 + " mRecentsRotation=" + mRecentsRotation 573 + " isRecentsActivityRotationAllowed=" + isRecentsActivityRotationAllowed() 574 + " mSystemRotation=" + systemRotationOn 575 + " mStateId=" + mStateId 576 + " mFlags=" + mFlags 577 + "]"; 578 } 579 580 /** 581 * Returns the device profile based on expected launcher rotation 582 */ getLauncherDeviceProfile()583 public DeviceProfile getLauncherDeviceProfile() { 584 InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext); 585 Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize; 586 587 int width, height; 588 if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) { 589 width = Math.max(currentSize.x, currentSize.y); 590 height = Math.min(currentSize.x, currentSize.y); 591 } else { 592 width = Math.min(currentSize.x, currentSize.y); 593 height = Math.max(currentSize.x, currentSize.y); 594 } 595 596 DeviceProfile bestMatch = idp.supportedProfiles.get(0); 597 float minDiff = Float.MAX_VALUE; 598 for (DeviceProfile profile : idp.supportedProfiles) { 599 float diff = Math.abs(profile.widthPx - width) + Math.abs(profile.heightPx - height); 600 if (diff < minDiff) { 601 minDiff = diff; 602 bestMatch = profile; 603 } 604 } 605 return bestMatch; 606 } 607 nameAndAddress(Object obj)608 private static String nameAndAddress(Object obj) { 609 return obj.getClass().getSimpleName() + "@" + obj.hashCode(); 610 } 611 } 612