1 /* 2 * Copyright (C) 2008 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.launcher3; 18 19 import android.appwidget.AppWidgetHostView; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.res.Configuration; 23 import android.content.res.Resources; 24 import android.graphics.Point; 25 import android.graphics.PointF; 26 import android.graphics.Rect; 27 import android.util.DisplayMetrics; 28 import android.view.Surface; 29 import android.view.WindowManager; 30 31 import com.android.launcher3.CellLayout.ContainerType; 32 import com.android.launcher3.badge.BadgeRenderer; 33 import com.android.launcher3.graphics.IconNormalizer; 34 35 public class DeviceProfile { 36 37 public final InvariantDeviceProfile inv; 38 39 // Device properties 40 public final boolean isTablet; 41 public final boolean isLargeTablet; 42 public final boolean isPhone; 43 public final boolean transposeLayoutWithOrientation; 44 45 // Device properties in current orientation 46 public final boolean isLandscape; 47 public final boolean isMultiWindowMode; 48 49 public final int widthPx; 50 public final int heightPx; 51 public final int availableWidthPx; 52 public final int availableHeightPx; 53 /** 54 * The maximum amount of left/right workspace padding as a percentage of the screen width. 55 * To be clear, this means that up to 7% of the screen width can be used as left padding, and 56 * 7% of the screen width can be used as right padding. 57 */ 58 private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f; 59 60 private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f; 61 62 // Workspace 63 public final int desiredWorkspaceLeftRightMarginPx; 64 public final int cellLayoutPaddingLeftRightPx; 65 public final int cellLayoutBottomPaddingPx; 66 public final int edgeMarginPx; 67 public final Rect defaultWidgetPadding; 68 public final int defaultPageSpacingPx; 69 private final int topWorkspacePadding; 70 public float workspaceSpringLoadShrinkFactor; 71 public final int workspaceSpringLoadedBottomSpace; 72 73 // Drag handle 74 public final int verticalDragHandleSizePx; 75 76 // Workspace icons 77 public int iconSizePx; 78 public int iconTextSizePx; 79 public int iconDrawablePaddingPx; 80 public int iconDrawablePaddingOriginalPx; 81 82 public int cellWidthPx; 83 public int cellHeightPx; 84 public int workspaceCellPaddingXPx; 85 86 // Folder 87 public int folderIconSizePx; 88 public int folderIconOffsetYPx; 89 90 // Folder cell 91 public int folderCellWidthPx; 92 public int folderCellHeightPx; 93 94 // Folder child 95 public int folderChildIconSizePx; 96 public int folderChildTextSizePx; 97 public int folderChildDrawablePaddingPx; 98 99 // Hotseat 100 public int hotseatCellHeightPx; 101 // In portrait: size = height, in landscape: size = width 102 public int hotseatBarSizePx; 103 public final int hotseatBarTopPaddingPx; 104 public final int hotseatBarBottomPaddingPx; 105 public final int hotseatBarSidePaddingPx; 106 107 // All apps 108 public int allAppsCellHeightPx; 109 public int allAppsIconSizePx; 110 public int allAppsIconDrawablePaddingPx; 111 public float allAppsIconTextSizePx; 112 113 // Widgets 114 public final PointF appWidgetScale = new PointF(1.0f, 1.0f); 115 116 // Drop Target 117 public int dropTargetBarSizePx; 118 119 // Insets 120 private final Rect mInsets = new Rect(); 121 public final Rect workspacePadding = new Rect(); 122 private final Rect mHotseatPadding = new Rect(); 123 private boolean mIsSeascape; 124 125 // Icon badges 126 public BadgeRenderer mBadgeRenderer; 127 128 public DeviceProfile(Context context, InvariantDeviceProfile inv, 129 Point minSize, Point maxSize, 130 int width, int height, boolean isLandscape, boolean isMultiWindowMode) { 131 132 this.inv = inv; 133 this.isLandscape = isLandscape; 134 this.isMultiWindowMode = isMultiWindowMode; 135 136 Resources res = context.getResources(); 137 DisplayMetrics dm = res.getDisplayMetrics(); 138 139 // Constants from resources 140 isTablet = res.getBoolean(R.bool.is_tablet); 141 isLargeTablet = res.getBoolean(R.bool.is_large_tablet); 142 isPhone = !isTablet && !isLargeTablet; 143 144 // Some more constants 145 transposeLayoutWithOrientation = 146 res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation); 147 148 context = getContext(context, isVerticalBarLayout() 149 ? Configuration.ORIENTATION_LANDSCAPE 150 : Configuration.ORIENTATION_PORTRAIT); 151 res = context.getResources(); 152 153 154 ComponentName cn = new ComponentName(context.getPackageName(), 155 this.getClass().getName()); 156 defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null); 157 edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); 158 desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx; 159 cellLayoutPaddingLeftRightPx = 160 res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding); 161 cellLayoutBottomPaddingPx = 162 res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding); 163 verticalDragHandleSizePx = res.getDimensionPixelSize( 164 R.dimen.vertical_drag_handle_size); 165 defaultPageSpacingPx = 166 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing); 167 topWorkspacePadding = 168 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding); 169 iconDrawablePaddingOriginalPx = 170 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); 171 dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size); 172 workspaceSpringLoadedBottomSpace = 173 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space); 174 175 workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x); 176 177 hotseatBarTopPaddingPx = 178 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); 179 hotseatBarBottomPaddingPx = 180 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); 181 hotseatBarSidePaddingPx = 182 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding); 183 hotseatBarSizePx = isVerticalBarLayout() 184 ? Utilities.pxFromDp(inv.iconSize, dm) 185 : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size) 186 + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx; 187 188 // Determine sizes. 189 widthPx = width; 190 heightPx = height; 191 if (isLandscape) { 192 availableWidthPx = maxSize.x; 193 availableHeightPx = minSize.y; 194 } else { 195 availableWidthPx = minSize.x; 196 availableHeightPx = maxSize.y; 197 } 198 199 // Calculate all of the remaining variables. 200 updateAvailableDimensions(dm, res); 201 202 // Now that we have all of the variables calculated, we can tune certain sizes. 203 float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); 204 boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; 205 if (!isVerticalBarLayout() && isPhone && isTallDevice) { 206 // We increase the hotseat size when there is extra space. 207 // ie. For a display with a large aspect ratio, we can keep the icons on the workspace 208 // in portrait mode closer together by adding more height to the hotseat. 209 // Note: This calculation was created after noticing a pattern in the design spec. 210 int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx; 211 hotseatBarSizePx += extraSpace - verticalDragHandleSizePx; 212 213 // Recalculate the available dimensions using the new hotseat size. 214 updateAvailableDimensions(dm, res); 215 } 216 updateWorkspacePadding(); 217 218 // This is done last, after iconSizePx is calculated above. 219 mBadgeRenderer = new BadgeRenderer(iconSizePx); 220 } 221 222 public DeviceProfile copy(Context context) { 223 Point size = new Point(availableWidthPx, availableHeightPx); 224 return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape, 225 isMultiWindowMode); 226 } 227 228 public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) { 229 // We take the minimum sizes of this profile and it's multi-window variant to ensure that 230 // the system decor is always excluded. 231 mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y)); 232 233 // In multi-window mode, we can have widthPx = availableWidthPx 234 // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles' 235 // widthPx and heightPx values where it's needed. 236 DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y, 237 isLandscape, true); 238 239 // If there isn't enough vertical cell padding with the labels displayed, hide the labels. 240 float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx 241 - iconDrawablePaddingPx - profile.iconTextSizePx; 242 if (workspaceCellPaddingY < profile.iconDrawablePaddingPx * 2) { 243 profile.adjustToHideWorkspaceLabels(); 244 } 245 246 // We use these scales to measure and layout the widgets using their full invariant profile 247 // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans. 248 float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x; 249 float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y; 250 profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY); 251 profile.updateWorkspacePadding(); 252 253 return profile; 254 } 255 256 /** 257 * Inverse of {@link #getMultiWindowProfile(Context, Point)} 258 * @return device profile corresponding to the current orientation in non multi-window mode. 259 */ 260 public DeviceProfile getFullScreenProfile() { 261 return isLandscape ? inv.landscapeProfile : inv.portraitProfile; 262 } 263 264 /** 265 * Adjusts the profile so that the labels on the Workspace are hidden. 266 * It is important to call this method after the All Apps variables have been set. 267 */ 268 private void adjustToHideWorkspaceLabels() { 269 iconTextSizePx = 0; 270 iconDrawablePaddingPx = 0; 271 cellHeightPx = iconSizePx; 272 273 // In normal cases, All Apps cell height should equal the Workspace cell height. 274 // Since we are removing labels from the Workspace, we need to manually compute the 275 // All Apps cell height. 276 int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1); 277 allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx 278 + Utilities.calculateTextHeight(allAppsIconTextSizePx) 279 + topBottomPadding * 2; 280 } 281 282 private void updateAvailableDimensions(DisplayMetrics dm, Resources res) { 283 updateIconSize(1f, res, dm); 284 285 // Check to see if the icons fit within the available height. If not, then scale down. 286 float usedHeight = (cellHeightPx * inv.numRows); 287 int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y); 288 if (usedHeight > maxHeight) { 289 float scale = maxHeight / usedHeight; 290 updateIconSize(scale, res, dm); 291 } 292 updateAvailableFolderCellDimensions(dm, res); 293 } 294 295 private void updateIconSize(float scale, Resources res, DisplayMetrics dm) { 296 // Workspace 297 final boolean isVerticalLayout = isVerticalBarLayout(); 298 float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize; 299 iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale); 300 iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); 301 iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); 302 303 cellHeightPx = iconSizePx + iconDrawablePaddingPx 304 + Utilities.calculateTextHeight(iconTextSizePx); 305 int cellYPadding = (getCellSize().y - cellHeightPx) / 2; 306 if (iconDrawablePaddingPx > cellYPadding && !isVerticalLayout 307 && !isMultiWindowMode) { 308 // Ensures that the label is closer to its corresponding icon. This is not an issue 309 // with vertical bar layout or multi-window mode since the issue is handled separately 310 // with their calls to {@link #adjustToHideWorkspaceLabels}. 311 cellHeightPx -= (iconDrawablePaddingPx - cellYPadding); 312 iconDrawablePaddingPx = cellYPadding; 313 } 314 cellWidthPx = iconSizePx + iconDrawablePaddingPx; 315 316 // All apps 317 allAppsIconTextSizePx = iconTextSizePx; 318 allAppsIconSizePx = iconSizePx; 319 allAppsIconDrawablePaddingPx = iconDrawablePaddingPx; 320 allAppsCellHeightPx = getCellSize().y; 321 322 if (isVerticalLayout) { 323 // Always hide the Workspace text with vertical bar layout. 324 adjustToHideWorkspaceLabels(); 325 } 326 327 // Hotseat 328 if (isVerticalLayout) { 329 hotseatBarSizePx = iconSizePx; 330 } 331 hotseatCellHeightPx = iconSizePx; 332 333 if (!isVerticalLayout) { 334 int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx 335 - verticalDragHandleSizePx - topWorkspacePadding; 336 float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace; 337 workspaceSpringLoadShrinkFactor = Math.min( 338 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f, 339 1 - (minRequiredHeight / expectedWorkspaceHeight)); 340 } else { 341 workspaceSpringLoadShrinkFactor = 342 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; 343 } 344 345 // Folder icon 346 folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx); 347 folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2; 348 } 349 350 private void updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res) { 351 int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_padding_top) 352 + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom) 353 + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size)); 354 355 updateFolderCellSize(1f, dm, res); 356 357 // Don't let the folder get too close to the edges of the screen. 358 int folderMargin = edgeMarginPx; 359 Point totalWorkspacePadding = getTotalWorkspacePadding(); 360 361 // Check if the icons fit within the available height. 362 float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize; 363 int maxHeight = availableHeightPx - totalWorkspacePadding.y - folderMargin; 364 float scaleY = maxHeight / usedHeight; 365 366 // Check if the icons fit within the available width. 367 float usedWidth = folderCellWidthPx * inv.numFolderColumns; 368 int maxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin; 369 float scaleX = maxWidth / usedWidth; 370 371 float scale = Math.min(scaleX, scaleY); 372 if (scale < 1f) { 373 updateFolderCellSize(scale, dm, res); 374 } 375 } 376 377 private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) { 378 folderChildIconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale); 379 folderChildTextSizePx = 380 (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale); 381 382 int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx); 383 int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale); 384 int cellPaddingY = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_y_padding) * scale); 385 386 folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX; 387 folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight; 388 folderChildDrawablePaddingPx = Math.max(0, 389 (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3); 390 } 391 392 public void updateInsets(Rect insets) { 393 mInsets.set(insets); 394 updateWorkspacePadding(); 395 } 396 397 public Rect getInsets() { 398 return mInsets; 399 } 400 401 public Point getCellSize() { 402 Point result = new Point(); 403 // Since we are only concerned with the overall padding, layout direction does 404 // not matter. 405 Point padding = getTotalWorkspacePadding(); 406 result.x = calculateCellWidth(availableWidthPx - padding.x 407 - cellLayoutPaddingLeftRightPx * 2, inv.numColumns); 408 result.y = calculateCellHeight(availableHeightPx - padding.y 409 - cellLayoutBottomPaddingPx, inv.numRows); 410 return result; 411 } 412 413 public Point getTotalWorkspacePadding() { 414 updateWorkspacePadding(); 415 return new Point(workspacePadding.left + workspacePadding.right, 416 workspacePadding.top + workspacePadding.bottom); 417 } 418 419 /** 420 * Updates {@link #workspacePadding} as a result of any internal value change to reflect the 421 * new workspace padding 422 */ 423 private void updateWorkspacePadding() { 424 Rect padding = workspacePadding; 425 if (isVerticalBarLayout()) { 426 padding.top = 0; 427 padding.bottom = edgeMarginPx; 428 padding.left = hotseatBarSidePaddingPx; 429 padding.right = hotseatBarSidePaddingPx; 430 if (isSeascape()) { 431 padding.left += hotseatBarSizePx; 432 padding.right += verticalDragHandleSizePx; 433 } else { 434 padding.left += verticalDragHandleSizePx; 435 padding.right += hotseatBarSizePx; 436 } 437 } else { 438 int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx; 439 if (isTablet) { 440 // Pad the left and right of the workspace to ensure consistent spacing 441 // between all icons 442 // The amount of screen space available for left/right padding. 443 int availablePaddingX = Math.max(0, widthPx - ((inv.numColumns * cellWidthPx) + 444 ((inv.numColumns - 1) * cellWidthPx))); 445 availablePaddingX = (int) Math.min(availablePaddingX, 446 widthPx * MAX_HORIZONTAL_PADDING_PERCENT); 447 int availablePaddingY = Math.max(0, heightPx - topWorkspacePadding - paddingBottom 448 - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx 449 - hotseatBarBottomPaddingPx); 450 padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2, 451 availablePaddingX / 2, paddingBottom + availablePaddingY / 2); 452 } else { 453 // Pad the top and bottom of the workspace with search/hotseat bar sizes 454 padding.set(desiredWorkspaceLeftRightMarginPx, 455 topWorkspacePadding, 456 desiredWorkspaceLeftRightMarginPx, 457 paddingBottom); 458 } 459 } 460 } 461 462 public Rect getHotseatLayoutPadding() { 463 if (isVerticalBarLayout()) { 464 if (isSeascape()) { 465 mHotseatPadding.set( 466 mInsets.left, mInsets.top, hotseatBarSidePaddingPx, mInsets.bottom); 467 } else { 468 mHotseatPadding.set( 469 hotseatBarSidePaddingPx, mInsets.top, mInsets.right, mInsets.bottom); 470 } 471 } else { 472 473 // We want the edges of the hotseat to line up with the edges of the workspace, but the 474 // icons in the hotseat are a different size, and so don't line up perfectly. To account 475 // for this, we pad the left and right of the hotseat with half of the difference of a 476 // workspace cell vs a hotseat cell. 477 float workspaceCellWidth = (float) widthPx / inv.numColumns; 478 float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons; 479 int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); 480 mHotseatPadding.set( 481 hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx, 482 hotseatBarTopPaddingPx, 483 hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx, 484 hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx); 485 } 486 return mHotseatPadding; 487 } 488 489 /** 490 * @return the bounds for which the open folders should be contained within 491 */ 492 public Rect getAbsoluteOpenFolderBounds() { 493 if (isVerticalBarLayout()) { 494 // Folders should only appear right of the drop target bar and left of the hotseat 495 return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx, 496 mInsets.top, 497 mInsets.left + availableWidthPx - hotseatBarSizePx - edgeMarginPx, 498 mInsets.top + availableHeightPx); 499 } else { 500 // Folders should only appear below the drop target bar and above the hotseat 501 return new Rect(mInsets.left + edgeMarginPx, 502 mInsets.top + dropTargetBarSizePx + edgeMarginPx, 503 mInsets.left + availableWidthPx - edgeMarginPx, 504 mInsets.top + availableHeightPx - hotseatBarSizePx 505 - verticalDragHandleSizePx - edgeMarginPx); 506 } 507 } 508 509 public static int calculateCellWidth(int width, int countX) { 510 return width / countX; 511 } 512 public static int calculateCellHeight(int height, int countY) { 513 return height / countY; 514 } 515 516 /** 517 * When {@code true}, the device is in landscape mode and the hotseat is on the right column. 518 * When {@code false}, either device is in portrait mode or the device is in landscape mode and 519 * the hotseat is on the bottom row. 520 */ 521 public boolean isVerticalBarLayout() { 522 return isLandscape && transposeLayoutWithOrientation; 523 } 524 525 /** 526 * Updates orientation information and returns true if it has changed from the previous value. 527 */ 528 public boolean updateIsSeascape(WindowManager wm) { 529 if (isVerticalBarLayout()) { 530 boolean isSeascape = wm.getDefaultDisplay().getRotation() == Surface.ROTATION_270; 531 if (mIsSeascape != isSeascape) { 532 mIsSeascape = isSeascape; 533 return true; 534 } 535 } 536 return false; 537 } 538 539 public boolean isSeascape() { 540 return isVerticalBarLayout() && mIsSeascape; 541 } 542 543 public boolean shouldFadeAdjacentWorkspaceScreens() { 544 return isVerticalBarLayout() || isLargeTablet; 545 } 546 547 public int getCellHeight(@ContainerType int containerType) { 548 switch (containerType) { 549 case CellLayout.WORKSPACE: 550 return cellHeightPx; 551 case CellLayout.FOLDER: 552 return folderCellHeightPx; 553 case CellLayout.HOTSEAT: 554 return hotseatCellHeightPx; 555 default: 556 // ?? 557 return 0; 558 } 559 } 560 561 private static Context getContext(Context c, int orientation) { 562 Configuration context = new Configuration(c.getResources().getConfiguration()); 563 context.orientation = orientation; 564 return c.createConfigurationContext(context); 565 } 566 567 /** 568 * Callback when a component changes the DeviceProfile associated with it, as a result of 569 * configuration change 570 */ 571 public interface OnDeviceProfileChangeListener { 572 573 /** 574 * Called when the device profile is reassigned. Note that for layout and measurements, it 575 * is sufficient to listen for inset changes. Use this callback when you need to perform 576 * a one time operation. 577 */ 578 void onDeviceProfileChanged(DeviceProfile dp); 579 } 580 } 581