1 /* 2 * Copyright (C) 2021 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 static com.android.launcher3.GridType.GRID_TYPE_ANY; 20 import static com.android.launcher3.GridType.GRID_TYPE_NON_ONE_GRID; 21 import static com.android.launcher3.GridType.GRID_TYPE_ONE_GRID; 22 import static com.android.launcher3.LauncherPrefs.DB_FILE; 23 import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE; 24 import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE; 25 import static com.android.launcher3.LauncherPrefs.GRID_NAME; 26 import static com.android.launcher3.LauncherPrefs.NON_FIXED_LANDSCAPE_GRID_NAME; 27 import static com.android.launcher3.Utilities.dpiFromPx; 28 import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE; 29 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; 30 import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE; 31 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; 32 import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS; 33 import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING; 34 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 35 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.res.Resources; 39 import android.content.res.TypedArray; 40 import android.content.res.XmlResourceParser; 41 import android.graphics.Point; 42 import android.graphics.PointF; 43 import android.graphics.Rect; 44 import android.os.Trace; 45 import android.text.TextUtils; 46 import android.util.AttributeSet; 47 import android.util.DisplayMetrics; 48 import android.util.Log; 49 import android.util.SparseArray; 50 import android.util.Xml; 51 52 import androidx.annotation.DimenRes; 53 import androidx.annotation.IntDef; 54 import androidx.annotation.StyleRes; 55 import androidx.annotation.VisibleForTesting; 56 import androidx.annotation.XmlRes; 57 import androidx.core.content.res.ResourcesCompat; 58 59 import com.android.launcher3.config.FeatureFlags; 60 import com.android.launcher3.dagger.ApplicationContext; 61 import com.android.launcher3.dagger.LauncherAppComponent; 62 import com.android.launcher3.dagger.LauncherAppSingleton; 63 import com.android.launcher3.graphics.ThemeManager; 64 import com.android.launcher3.icons.DotRenderer; 65 import com.android.launcher3.logging.FileLog; 66 import com.android.launcher3.model.DeviceGridState; 67 import com.android.launcher3.provider.RestoreDbTask; 68 import com.android.launcher3.testing.shared.ResourceUtils; 69 import com.android.launcher3.util.DaggerSingletonObject; 70 import com.android.launcher3.util.DaggerSingletonTracker; 71 import com.android.launcher3.util.DisplayController; 72 import com.android.launcher3.util.DisplayController.Info; 73 import com.android.launcher3.util.Partner; 74 import com.android.launcher3.util.ResourceHelper; 75 import com.android.launcher3.util.SimpleBroadcastReceiver; 76 import com.android.launcher3.util.WindowBounds; 77 import com.android.launcher3.util.window.CachedDisplayInfo; 78 import com.android.launcher3.util.window.WindowManagerProxy; 79 80 import org.xmlpull.v1.XmlPullParser; 81 import org.xmlpull.v1.XmlPullParserException; 82 83 import java.io.IOException; 84 import java.lang.annotation.Retention; 85 import java.lang.annotation.RetentionPolicy; 86 import java.util.ArrayList; 87 import java.util.Arrays; 88 import java.util.Collections; 89 import java.util.List; 90 import java.util.Objects; 91 import java.util.concurrent.CopyOnWriteArrayList; 92 import java.util.stream.Collectors; 93 94 import javax.inject.Inject; 95 96 @LauncherAppSingleton 97 public class InvariantDeviceProfile { 98 99 public static final String TAG = "IDP"; 100 // We do not need any synchronization for this variable as its only written on UI thread. 101 public static final DaggerSingletonObject<InvariantDeviceProfile> INSTANCE = 102 new DaggerSingletonObject<>(LauncherAppComponent::getIDP); 103 104 public static final String GRID_NAME_PREFS_KEY = "idp_grid_name"; 105 public static final String NON_FIXED_LANDSCAPE_GRID_NAME_PREFS_KEY = 106 "idp_non_fixed_landscape_grid_name"; 107 108 @Retention(RetentionPolicy.SOURCE) 109 @IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET}) 110 public @interface DeviceType { 111 } 112 113 public static final int TYPE_PHONE = 0; 114 public static final int TYPE_MULTI_DISPLAY = 1; 115 public static final int TYPE_TABLET = 2; 116 117 private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; 118 119 // Constants that affects the interpolation curve between statically defined device profile 120 // buckets. 121 private static final float KNEARESTNEIGHBOR = 3; 122 private static final float WEIGHT_POWER = 5; 123 124 // used to offset float not being able to express extremely small weights in extreme cases. 125 private static final float WEIGHT_EFFICIENT = 100000f; 126 127 // Used for arrays to specify different sizes (e.g. border spaces, width/height) in different 128 // constraints 129 static final int COUNT_SIZES = 4; 130 static final int INDEX_DEFAULT = 0; 131 static final int INDEX_LANDSCAPE = 1; 132 static final int INDEX_TWO_PANEL_PORTRAIT = 2; 133 static final int INDEX_TWO_PANEL_LANDSCAPE = 3; 134 135 /** These resources are used to override the device profile */ 136 private static final String RES_GRID_NUM_ROWS = "grid_num_rows"; 137 private static final String RES_GRID_NUM_COLUMNS = "grid_num_columns"; 138 private static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp"; 139 140 private final DisplayController mDisplayController; 141 private final WindowManagerProxy mWMProxy; 142 private final LauncherPrefs mPrefs; 143 private final ThemeManager mThemeManager; 144 145 /** 146 * Number of icons per row and column in the workspace. 147 */ 148 public int numRows; 149 public int numColumns; 150 public int numSearchContainerColumns; 151 152 /** 153 * Number of icons per row and column in the folder. 154 */ 155 public int[] numFolderRows; 156 public int[] numFolderColumns; 157 public float[] iconSize; 158 public float[] iconTextSize; 159 public int iconBitmapSize; 160 public int fillResIconDpi; 161 public @DeviceType int deviceType; 162 public Info displayInfo; 163 164 public PointF[] minCellSize; 165 166 public PointF[] borderSpaces; 167 public @DimenRes int inlineNavButtonsEndSpacing; 168 169 public @StyleRes int folderStyle; 170 171 public @StyleRes int cellStyle; 172 173 public float[] horizontalMargin; 174 175 public PointF[] allAppsCellSize; 176 public float[] allAppsIconSize; 177 public float[] allAppsIconTextSize; 178 public PointF[] allAppsBorderSpaces; 179 180 public float[] transientTaskbarIconSize; 181 182 public boolean[] startAlignTaskbar; 183 184 /** 185 * Number of icons inside the hotseat area. 186 */ 187 public int numShownHotseatIcons; 188 189 /** 190 * Number of icons inside the hotseat area that is stored in the database. This is greater than 191 * or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat 192 * sizes that share the same DB. 193 */ 194 public int numDatabaseHotseatIcons; 195 196 public float[] hotseatBarBottomSpace; 197 public float[] hotseatQsbSpace; 198 199 /** 200 * Number of columns in the all apps list. 201 */ 202 public int numAllAppsColumns; 203 public int numAllAppsRowsForCellHeightCalculation; 204 public int numDatabaseAllAppsColumns; 205 public @StyleRes int allAppsStyle; 206 207 /** 208 * Do not query directly. see {@link DeviceProfile#isScalableGrid}. 209 */ 210 protected boolean isScalable; 211 @XmlRes 212 public int devicePaddingId = INVALID_RESOURCE_HANDLE; 213 @XmlRes 214 public int workspaceSpecsId = INVALID_RESOURCE_HANDLE; 215 @XmlRes 216 public int gridSizeSpecsId = INVALID_RESOURCE_HANDLE;; 217 @XmlRes 218 public int workspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 219 @XmlRes 220 public int allAppsSpecsId = INVALID_RESOURCE_HANDLE; 221 @XmlRes 222 public int allAppsSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 223 @XmlRes 224 public int folderSpecsId = INVALID_RESOURCE_HANDLE; 225 @XmlRes 226 public int folderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 227 @XmlRes 228 public int hotseatSpecsId = INVALID_RESOURCE_HANDLE; 229 @XmlRes 230 public int hotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 231 @XmlRes 232 public int workspaceCellSpecsId = INVALID_RESOURCE_HANDLE; 233 @XmlRes 234 public int workspaceCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 235 @XmlRes 236 public int allAppsCellSpecsId = INVALID_RESOURCE_HANDLE; 237 @XmlRes 238 public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 239 240 private String mLocale = ""; 241 public boolean enableTwoLinesInAllApps = false; 242 /** 243 * Fixed landscape mode is the landscape on the phones. 244 */ 245 public boolean isFixedLandscape = false; 246 247 @GridType 248 public int gridType; 249 public String dbFile; 250 public int defaultLayoutId; 251 public int demoModeLayoutId; 252 public boolean[] inlineQsb = new boolean[COUNT_SIZES]; 253 254 /** 255 * An immutable list of supported profiles. 256 */ 257 public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST; 258 259 public Point defaultWallpaperSize; 260 261 private final List<OnIDPChangeListener> mChangeListeners = new CopyOnWriteArrayList<>(); 262 263 @Inject InvariantDeviceProfile( @pplicationContext Context context, LauncherPrefs prefs, DisplayController dc, WindowManagerProxy wmProxy, ThemeManager themeManager, DaggerSingletonTracker lifeCycle)264 InvariantDeviceProfile( 265 @ApplicationContext Context context, 266 LauncherPrefs prefs, 267 DisplayController dc, 268 WindowManagerProxy wmProxy, 269 ThemeManager themeManager, 270 DaggerSingletonTracker lifeCycle) { 271 mDisplayController = dc; 272 mWMProxy = wmProxy; 273 mPrefs = prefs; 274 mThemeManager = themeManager; 275 276 String gridName = prefs.get(GRID_NAME); 277 initGrid(context, gridName); 278 279 dc.setPriorityListener( 280 (displayContext, info, flags) -> { 281 if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS 282 | CHANGE_NAVIGATION_MODE | CHANGE_TASKBAR_PINNING 283 | CHANGE_DESKTOP_MODE)) != 0) { 284 onConfigChanged(displayContext); 285 } 286 }); 287 lifeCycle.addCloseable(() -> dc.setPriorityListener(null)); 288 289 LauncherPrefChangeListener prefListener = key -> { 290 if (FIXED_LANDSCAPE_MODE.getSharedPrefKey().equals(key) 291 && isFixedLandscape != prefs.get(FIXED_LANDSCAPE_MODE)) { 292 Trace.beginSection("InvariantDeviceProfile#setFixedLandscape"); 293 if (isFixedLandscape) { 294 setCurrentGrid(context, prefs.get(NON_FIXED_LANDSCAPE_GRID_NAME)); 295 } else { 296 prefs.put(NON_FIXED_LANDSCAPE_GRID_NAME, mPrefs.get(GRID_NAME)); 297 onConfigChanged(context); 298 } 299 Trace.endSection(); 300 } else if (ENABLE_TWOLINE_ALLAPPS_TOGGLE.getSharedPrefKey().equals(key) 301 && enableTwoLinesInAllApps != prefs.get(ENABLE_TWOLINE_ALLAPPS_TOGGLE)) { 302 onConfigChanged(context); 303 } 304 }; 305 prefs.addListener(prefListener, FIXED_LANDSCAPE_MODE, ENABLE_TWOLINE_ALLAPPS_TOGGLE); 306 lifeCycle.addCloseable(() -> prefs.removeListener(prefListener, 307 FIXED_LANDSCAPE_MODE, ENABLE_TWOLINE_ALLAPPS_TOGGLE)); 308 309 SimpleBroadcastReceiver localeReceiver = new SimpleBroadcastReceiver(context, 310 MAIN_EXECUTOR, i -> onConfigChanged(context)); 311 localeReceiver.register(Intent.ACTION_LOCALE_CHANGED); 312 lifeCycle.addCloseable(() -> localeReceiver.unregisterReceiverSafely()); 313 } 314 initGrid(Context context, String gridName)315 private String initGrid(Context context, String gridName) { 316 Info displayInfo = mDisplayController.getInfo(); 317 List<DisplayOption> allOptions = getPredefinedDeviceProfiles( 318 context, 319 gridName, 320 displayInfo, 321 (RestoreDbTask.isPending(mPrefs) && !Flags.oneGridSpecs()), 322 mPrefs.get(FIXED_LANDSCAPE_MODE) 323 ); 324 325 // Filter out options that don't have the same number of columns as the grid 326 DeviceGridState deviceGridState = new DeviceGridState(mPrefs); 327 List<DisplayOption> allOptionsFilteredByColCount = 328 filterByColumnCount(allOptions, deviceGridState.getColumns()); 329 330 DisplayOption displayOption = 331 invDistWeightedInterpolate(displayInfo, allOptionsFilteredByColCount.isEmpty() 332 ? new ArrayList<>(allOptions) 333 : new ArrayList<>(allOptionsFilteredByColCount), 334 displayInfo.getDeviceType()); 335 336 if (!displayOption.grid.name.equals(gridName)) { 337 mPrefs.put(GRID_NAME, displayOption.grid.name); 338 } 339 340 initGrid(context, displayInfo, displayOption); 341 FileLog.d(TAG, "After initGrid:" 342 + "gridName:" + gridName 343 + ", dbFile:" + dbFile 344 + ", LauncherPrefs GRID_NAME:" + mPrefs.get(GRID_NAME) 345 + ", LauncherPrefs DB_FILE:" + mPrefs.get(DB_FILE)); 346 return displayOption.grid.name; 347 } 348 filterByColumnCount( List<DisplayOption> allOptions, int numColumns)349 private List<DisplayOption> filterByColumnCount( 350 List<DisplayOption> allOptions, int numColumns) { 351 return allOptions.stream() 352 .filter(option -> option.grid.numColumns == numColumns) 353 .collect(Collectors.toList()); 354 } 355 356 /** 357 * @deprecated This is a temporary solution because on the backup and restore case we modify the 358 * IDP, this resets it. b/332974074 359 */ 360 @Deprecated reset(Context context)361 public void reset(Context context) { 362 initGrid(context, mPrefs.get(GRID_NAME)); 363 } 364 initGrid(Context context, Info displayInfo, DisplayOption displayOption)365 private void initGrid(Context context, Info displayInfo, DisplayOption displayOption) { 366 enableTwoLinesInAllApps = Flags.enableTwolineToggle() 367 && Utilities.isEnglishLanguage(context) 368 && mPrefs.get(ENABLE_TWOLINE_ALLAPPS_TOGGLE); 369 mLocale = context.getResources().getConfiguration().locale.toString(); 370 371 DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 372 GridOption closestProfile = displayOption.grid; 373 numRows = closestProfile.numRows; 374 numColumns = closestProfile.numColumns; 375 numSearchContainerColumns = closestProfile.numSearchContainerColumns; 376 dbFile = closestProfile.dbFile; 377 gridType = closestProfile.gridType; 378 defaultLayoutId = closestProfile.defaultLayoutId; 379 demoModeLayoutId = closestProfile.demoModeLayoutId; 380 381 numFolderRows = closestProfile.numFolderRows; 382 numFolderColumns = closestProfile.numFolderColumns; 383 folderStyle = closestProfile.folderStyle; 384 385 cellStyle = closestProfile.cellStyle; 386 387 isScalable = closestProfile.isScalable; 388 devicePaddingId = closestProfile.devicePaddingId; 389 workspaceSpecsId = closestProfile.mWorkspaceSpecsId; 390 gridSizeSpecsId = closestProfile.mGridSizeSpecsId; 391 workspaceSpecsTwoPanelId = closestProfile.mWorkspaceSpecsTwoPanelId; 392 allAppsSpecsId = closestProfile.mAllAppsSpecsId; 393 allAppsSpecsTwoPanelId = closestProfile.mAllAppsSpecsTwoPanelId; 394 folderSpecsId = closestProfile.mFolderSpecsId; 395 folderSpecsTwoPanelId = closestProfile.mFolderSpecsTwoPanelId; 396 hotseatSpecsId = closestProfile.mHotseatSpecsId; 397 hotseatSpecsTwoPanelId = closestProfile.mHotseatSpecsTwoPanelId; 398 workspaceCellSpecsId = closestProfile.mWorkspaceCellSpecsId; 399 workspaceCellSpecsTwoPanelId = closestProfile.mWorkspaceCellSpecsTwoPanelId; 400 allAppsCellSpecsId = closestProfile.mAllAppsCellSpecsId; 401 allAppsCellSpecsTwoPanelId = closestProfile.mAllAppsCellSpecsTwoPanelId; 402 numAllAppsRowsForCellHeightCalculation = 403 closestProfile.mNumAllAppsRowsForCellHeightCalculation; 404 this.deviceType = displayInfo.getDeviceType(); 405 this.displayInfo = displayInfo; 406 407 inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing; 408 409 iconSize = displayOption.iconSizes; 410 float maxIconSize = iconSize[0]; 411 for (int i = 1; i < iconSize.length; i++) { 412 maxIconSize = Math.max(maxIconSize, iconSize[i]); 413 } 414 iconBitmapSize = ResourceUtils.pxFromDp(maxIconSize, metrics); 415 fillResIconDpi = getLauncherIconDensity(iconBitmapSize); 416 417 iconTextSize = displayOption.textSizes; 418 419 minCellSize = displayOption.minCellSize; 420 421 borderSpaces = displayOption.borderSpaces; 422 423 horizontalMargin = displayOption.horizontalMargin; 424 425 numShownHotseatIcons = closestProfile.numHotseatIcons; 426 numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY 427 ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons; 428 hotseatBarBottomSpace = displayOption.hotseatBarBottomSpace; 429 hotseatQsbSpace = displayOption.hotseatQsbSpace; 430 431 allAppsStyle = closestProfile.allAppsStyle; 432 433 numAllAppsColumns = closestProfile.numAllAppsColumns; 434 435 numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY 436 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns; 437 438 allAppsCellSize = displayOption.allAppsCellSize; 439 allAppsBorderSpaces = displayOption.allAppsBorderSpaces; 440 allAppsIconSize = displayOption.allAppsIconSizes; 441 allAppsIconTextSize = displayOption.allAppsIconTextSizes; 442 443 inlineQsb = closestProfile.inlineQsb; 444 445 transientTaskbarIconSize = displayOption.transientTaskbarIconSize; 446 447 startAlignTaskbar = displayOption.startAlignTaskbar; 448 449 // Fixed Landscape mode 450 isFixedLandscape = closestProfile.mIsFixedLandscape; 451 452 // If the partner customization apk contains any grid overrides, apply them 453 // Supported overrides: numRows, numColumns, iconSize 454 applyPartnerDeviceProfileOverrides(context, metrics); 455 456 final List<DeviceProfile> localSupportedProfiles = new ArrayList<>(); 457 defaultWallpaperSize = new Point(displayInfo.currentSize); 458 SparseArray<DotRenderer> dotRendererCache = new SparseArray<>(); 459 for (WindowBounds bounds : displayInfo.supportedBounds) { 460 localSupportedProfiles.add(newDPBuilder(context, displayInfo) 461 .setIsMultiDisplay(deviceType == TYPE_MULTI_DISPLAY) 462 .setWindowBounds(bounds) 463 .setDotRendererCache(dotRendererCache) 464 .build()); 465 466 // Wallpaper size should be the maximum of the all possible sizes Launcher expects 467 int displayWidth = bounds.bounds.width(); 468 int displayHeight = bounds.bounds.height(); 469 defaultWallpaperSize.y = Math.max(defaultWallpaperSize.y, displayHeight); 470 471 // We need to ensure that there is enough extra space in the wallpaper 472 // for the intended parallax effects 473 float parallaxFactor = 474 dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.getDensityDpi()) 475 < 720 476 ? 2 477 : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight); 478 defaultWallpaperSize.x = 479 Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth)); 480 } 481 supportedProfiles = Collections.unmodifiableList(localSupportedProfiles); 482 483 int numMinShownHotseatIconsForTablet = supportedProfiles 484 .stream() 485 .filter(deviceProfile -> deviceProfile.isTablet) 486 .mapToInt(deviceProfile -> deviceProfile.numShownHotseatIcons) 487 .min() 488 .orElse(0); 489 490 supportedProfiles 491 .stream() 492 .filter(deviceProfile -> deviceProfile.isTablet) 493 .forEach(deviceProfile -> { 494 deviceProfile.numShownHotseatIcons = numMinShownHotseatIconsForTablet; 495 deviceProfile.recalculateHotseatWidthAndBorderSpace(); 496 }); 497 } 498 newDPBuilder(Context context, Info info)499 DeviceProfile.Builder newDPBuilder(Context context, Info info) { 500 return new DeviceProfile.Builder(context, this, info, mWMProxy, mThemeManager); 501 } 502 addOnChangeListener(OnIDPChangeListener listener)503 public void addOnChangeListener(OnIDPChangeListener listener) { 504 mChangeListeners.add(listener); 505 } 506 removeOnChangeListener(OnIDPChangeListener listener)507 public void removeOnChangeListener(OnIDPChangeListener listener) { 508 mChangeListeners.remove(listener); 509 } 510 511 /** 512 * Updates the current grid, this triggers a new IDP, reloads the database and triggers a grid 513 * migration. 514 */ 515 @VisibleForTesting setCurrentGrid(Context context, String newGridName)516 public void setCurrentGrid(Context context, String newGridName) { 517 mPrefs.put(GRID_NAME, newGridName); 518 MAIN_EXECUTOR.execute(() -> { 519 Trace.beginSection("InvariantDeviceProfile#setCurrentGrid"); 520 onConfigChanged(context.getApplicationContext()); 521 Trace.endSection(); 522 }); 523 } 524 toModelState()525 private Object[] toModelState() { 526 return new Object[]{ 527 numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons, 528 iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile, mLocale}; 529 } 530 531 /** Updates IDP using the provided context. Notifies listeners of change. */ 532 @VisibleForTesting onConfigChanged(Context context)533 public void onConfigChanged(Context context) { 534 Object[] oldState = toModelState(); 535 536 // Re-init grid 537 initGrid(context, mPrefs.get(GRID_NAME)); 538 539 boolean modelPropsChanged = !Arrays.equals(oldState, toModelState()); 540 for (OnIDPChangeListener listener : mChangeListeners) { 541 listener.onIdpChanged(modelPropsChanged); 542 } 543 } 544 firstGridFilter(GridOption gridOption, int deviceType, boolean allowDisabledGrid, boolean isFixedLandscapeMode)545 private static boolean firstGridFilter(GridOption gridOption, int deviceType, 546 boolean allowDisabledGrid, boolean isFixedLandscapeMode) { 547 return (gridOption.isEnabled(deviceType) || allowDisabledGrid) 548 && gridOption.filterByFlag(deviceType, isFixedLandscapeMode); 549 } 550 getPredefinedDeviceProfiles( Context context, String gridName, Info displayInfo, boolean allowDisabledGrid, boolean isFixedLandscapeMode )551 private static List<DisplayOption> getPredefinedDeviceProfiles( 552 Context context, 553 String gridName, 554 Info displayInfo, 555 boolean allowDisabledGrid, 556 boolean isFixedLandscapeMode 557 ) { 558 ArrayList<DisplayOption> profiles = new ArrayList<>(); 559 560 try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { 561 final int depth = parser.getDepth(); 562 int type; 563 while (((type = parser.next()) != XmlPullParser.END_TAG || 564 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 565 if ((type == XmlPullParser.START_TAG) 566 && GridOption.TAG_NAME.equals(parser.getName())) { 567 GridOption gridOption = new GridOption( 568 context, Xml.asAttributeSet(parser), displayInfo); 569 if (firstGridFilter(gridOption, displayInfo.getDeviceType(), allowDisabledGrid, 570 isFixedLandscapeMode)) { 571 final int displayDepth = parser.getDepth(); 572 while (((type = parser.next()) != XmlPullParser.END_TAG 573 || parser.getDepth() > displayDepth) 574 && type != XmlPullParser.END_DOCUMENT) { 575 if ((type == XmlPullParser.START_TAG) && "display-option".equals( 576 parser.getName())) { 577 profiles.add(new DisplayOption(gridOption, context, 578 Xml.asAttributeSet(parser))); 579 } 580 } 581 } 582 } 583 } 584 } catch (IOException | XmlPullParserException e) { 585 throw new RuntimeException(e); 586 } 587 ArrayList<DisplayOption> filteredProfiles = new ArrayList<>(); 588 if (!TextUtils.isEmpty(gridName)) { 589 for (DisplayOption option : profiles) { 590 if (gridName.equals(option.grid.name) && (option.grid.isEnabled( 591 displayInfo.getDeviceType()) || allowDisabledGrid)) { 592 filteredProfiles.add(option); 593 } 594 } 595 } 596 if (filteredProfiles.isEmpty() && TextUtils.isEmpty(gridName)) { 597 // Use the default options since gridName is empty and there's no valid grids. 598 for (DisplayOption option : profiles) { 599 if (option.canBeDefault) { 600 filteredProfiles.add(option); 601 } 602 } 603 } else if (filteredProfiles.isEmpty()) { 604 // In this case we had a grid selected but we couldn't find it. 605 filteredProfiles.addAll(profiles); 606 } 607 if (filteredProfiles.isEmpty()) { 608 throw new RuntimeException("No display option with canBeDefault=true"); 609 } 610 return filteredProfiles; 611 } 612 613 /** 614 * Parses through the xml to find GridSize specs. Then calls findBestGridSize to get the 615 * correct grid size for this GridOption. 616 * 617 * @return the result of {@link #findBestGridSize(List, int, int)}. 618 */ getGridSize(ResourceHelper resourceHelper, Context context, Info displayInfo)619 private static GridSize getGridSize(ResourceHelper resourceHelper, Context context, 620 Info displayInfo) { 621 ArrayList<GridSize> gridSizes = new ArrayList<>(); 622 623 try (XmlResourceParser parser = resourceHelper.getXml()) { 624 final int depth = parser.getDepth(); 625 int type; 626 while (((type = parser.next()) != XmlPullParser.END_TAG 627 || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 628 if ((type == XmlPullParser.START_TAG) 629 && "GridSize".equals(parser.getName())) { 630 gridSizes.add(new GridSize(context, Xml.asAttributeSet(parser))); 631 } 632 } 633 } catch (IOException | XmlPullParserException e) { 634 throw new RuntimeException(e); 635 } 636 637 // Finds the min width and height in dp for all displays. 638 int[] dimens = findMinWidthAndHeightPxForDevice(displayInfo); 639 640 return findBestGridSize(gridSizes, dimens[0], dimens[1]); 641 } 642 643 /** 644 * @return the biggest grid size that fits the display dimensions. 645 * If no best grid size is found, return null. 646 */ findBestGridSize(List<GridSize> list, int minWidthPx, int minHeightPx)647 private static GridSize findBestGridSize(List<GridSize> list, int minWidthPx, 648 int minHeightPx) { 649 GridSize selectedGridSize = null; 650 for (GridSize item: list) { 651 if (minWidthPx >= item.mMinDeviceWidthPx && minHeightPx >= item.mMinDeviceHeightPx) { 652 if (selectedGridSize == null 653 || (selectedGridSize.mNumColumns <= item.mNumColumns 654 && selectedGridSize.mNumRows <= item.mNumRows)) { 655 selectedGridSize = item; 656 } 657 } 658 } 659 return selectedGridSize; 660 } 661 findMinWidthAndHeightPxForDevice(Info displayInfo)662 private static int[] findMinWidthAndHeightPxForDevice(Info displayInfo) { 663 int minDisplayWidthPx = Integer.MAX_VALUE; 664 int minDisplayHeightPx = Integer.MAX_VALUE; 665 for (CachedDisplayInfo display: displayInfo.getAllDisplays()) { 666 minDisplayWidthPx = Math.min(minDisplayWidthPx, display.size.x); 667 minDisplayHeightPx = Math.min(minDisplayHeightPx, display.size.y); 668 } 669 return new int[]{minDisplayWidthPx, minDisplayHeightPx}; 670 } 671 672 /** 673 * Returns the GridOption associated to the given file name or null if the fileName is not 674 * supported. 675 * Ej, launcher.db -> "normal grid", launcher_4_by_4.db -> "practical grid" 676 */ getGridOptionFromFileName(Context context, String fileName)677 public GridOption getGridOptionFromFileName(Context context, String fileName) { 678 return parseAllGridOptions(context).stream() 679 .filter(gridOption -> Objects.equals(gridOption.dbFile, fileName)) 680 .findFirst() 681 .orElse(null); 682 } 683 684 /** 685 * Returns the name of the given size on the current device or empty string if the size is not 686 * supported. Ej. 4x4 -> normal, 5x4 -> practical, etc. 687 * (Note: the name of the grid can be different for the same grid size depending of 688 * the values of the InvariantDeviceProfile) 689 */ getGridNameFromSize(Context context, Point size)690 public String getGridNameFromSize(Context context, Point size) { 691 return parseAllGridOptions(context).stream() 692 .filter(gridOption -> gridOption.numColumns == size.x 693 && gridOption.numRows == size.y) 694 .map(gridOption -> gridOption.name) 695 .findFirst() 696 .orElse(""); 697 } 698 699 /** 700 * Returns the grid option for the given gridName on the current device (Note: the gridOption 701 * be different for the same gridName depending on the values of the InvariantDeviceProfile). 702 */ getGridOptionFromName(Context context, String gridName)703 public GridOption getGridOptionFromName(Context context, String gridName) { 704 return parseAllGridOptions(context).stream() 705 .filter(gridOption -> Objects.equals(gridOption.name, gridName)) 706 .findFirst() 707 .orElse(null); 708 } 709 710 /** 711 * @return all the grid options that can be shown on the device 712 */ parseAllGridOptions(Context context)713 public List<GridOption> parseAllGridOptions(Context context) { 714 return parseAllDefinedGridOptions(context, displayInfo) 715 .stream() 716 .filter(go -> go.isEnabled(deviceType)) 717 .filter(go -> go.filterByFlag(deviceType, isFixedLandscape)) 718 .collect(Collectors.toList()); 719 } 720 721 /** 722 * @return all the grid options that can be shown on the device 723 */ parseAllDefinedGridOptions(Context context, Info displayInfo)724 public static List<GridOption> parseAllDefinedGridOptions(Context context, Info displayInfo) { 725 List<GridOption> result = new ArrayList<>(); 726 try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { 727 final int depth = parser.getDepth(); 728 int type; 729 while (((type = parser.next()) != XmlPullParser.END_TAG 730 || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 731 if ((type == XmlPullParser.START_TAG) 732 && GridOption.TAG_NAME.equals(parser.getName())) { 733 result.add(new GridOption(context, Xml.asAttributeSet(parser), displayInfo)); 734 } 735 } 736 } catch (IOException | XmlPullParserException e) { 737 Log.e(TAG, "Error parsing device profile", e); 738 return Collections.emptyList(); 739 } 740 return result; 741 } 742 getLauncherIconDensity(int requiredSize)743 private int getLauncherIconDensity(int requiredSize) { 744 // Densities typically defined by an app. 745 int[] densityBuckets = new int[]{ 746 DisplayMetrics.DENSITY_LOW, 747 DisplayMetrics.DENSITY_MEDIUM, 748 DisplayMetrics.DENSITY_TV, 749 DisplayMetrics.DENSITY_HIGH, 750 DisplayMetrics.DENSITY_XHIGH, 751 DisplayMetrics.DENSITY_XXHIGH, 752 DisplayMetrics.DENSITY_XXXHIGH 753 }; 754 755 int density = DisplayMetrics.DENSITY_XXXHIGH; 756 for (int i = densityBuckets.length - 1; i >= 0; i--) { 757 float expectedSize = ICON_SIZE_DEFINED_IN_APP_DP * densityBuckets[i] 758 / DisplayMetrics.DENSITY_DEFAULT; 759 if (expectedSize >= requiredSize) { 760 density = densityBuckets[i]; 761 } 762 } 763 764 return density; 765 } 766 767 /** 768 * Apply any Partner customization grid overrides. 769 * 770 * Currently we support: all apps row / column count. 771 */ applyPartnerDeviceProfileOverrides(Context context, DisplayMetrics dm)772 private void applyPartnerDeviceProfileOverrides(Context context, DisplayMetrics dm) { 773 Partner p = Partner.get(context.getPackageManager()); 774 if (p == null) { 775 return; 776 } 777 try { 778 int numRows = p.getIntValue(RES_GRID_NUM_ROWS, -1); 779 int numColumns = p.getIntValue(RES_GRID_NUM_COLUMNS, -1); 780 float iconSizePx = p.getDimenValue(RES_GRID_ICON_SIZE_DP, -1); 781 782 if (numRows > 0 && numColumns > 0) { 783 this.numRows = numRows; 784 this.numColumns = numColumns; 785 } 786 if (iconSizePx > 0) { 787 this.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] = 788 Utilities.dpiFromPx(iconSizePx, dm.densityDpi); 789 } 790 } catch (Resources.NotFoundException ex) { 791 Log.e(TAG, "Invalid Partner grid resource!", ex); 792 } 793 } 794 dist(float x0, float y0, float x1, float y1)795 private static float dist(float x0, float y0, float x1, float y1) { 796 return (float) Math.hypot(x1 - x0, y1 - y0); 797 } 798 invDistWeightedInterpolate( Info displayInfo, List<DisplayOption> points, @DeviceType int deviceType)799 private static DisplayOption invDistWeightedInterpolate( 800 Info displayInfo, List<DisplayOption> points, @DeviceType int deviceType) { 801 int minWidthPx = Integer.MAX_VALUE; 802 int minHeightPx = Integer.MAX_VALUE; 803 for (WindowBounds bounds : displayInfo.supportedBounds) { 804 boolean isTablet = displayInfo.isTablet(bounds); 805 if (isTablet && deviceType == TYPE_MULTI_DISPLAY) { 806 // For split displays, take half width per page 807 minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2); 808 minHeightPx = Math.min(minHeightPx, bounds.availableSize.y); 809 810 } else if (!isTablet && bounds.isLandscape()) { 811 // We will use transposed layout in this case 812 minWidthPx = Math.min(minWidthPx, bounds.availableSize.y); 813 minHeightPx = Math.min(minHeightPx, bounds.availableSize.x); 814 } else { 815 minWidthPx = Math.min(minWidthPx, bounds.availableSize.x); 816 minHeightPx = Math.min(minHeightPx, bounds.availableSize.y); 817 } 818 } 819 820 float width = dpiFromPx(minWidthPx, displayInfo.getDensityDpi()); 821 float height = dpiFromPx(minHeightPx, displayInfo.getDensityDpi()); 822 823 // Sort the profiles based on the closeness to the device size 824 points.sort((a, b) -> 825 Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps), 826 dist(width, height, b.minWidthDps, b.minHeightDps))); 827 828 DisplayOption closestPoint = points.get(0); 829 GridOption closestOption = closestPoint.grid; 830 float weights = 0; 831 832 if (dist(width, height, closestPoint.minWidthDps, closestPoint.minHeightDps) == 0) { 833 return closestPoint; 834 } 835 836 DisplayOption out = new DisplayOption(closestOption); 837 for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) { 838 DisplayOption p = points.get(i); 839 float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER); 840 weights += w; 841 out.add(new DisplayOption().add(p).multiply(w)); 842 } 843 out.multiply(1.0f / weights); 844 845 // Since the bitmaps are persisted, ensure that all bitmap sizes are not larger than 846 // predefined size to avoid cache invalidation 847 for (int i = INDEX_DEFAULT; i < COUNT_SIZES; i++) { 848 out.iconSizes[i] = Math.min(out.iconSizes[i], closestPoint.iconSizes[i]); 849 } 850 851 return out; 852 } 853 createDeviceProfileForSecondaryDisplay(Context displayContext)854 public DeviceProfile createDeviceProfileForSecondaryDisplay(Context displayContext) { 855 // Disable transpose layout and use multi-window mode so that the icons are scaled properly 856 return newDPBuilder(displayContext, new Info(displayContext)) 857 .setIsMultiDisplay(false) 858 .setMultiWindowMode(true) 859 .setWindowBounds(mWMProxy.getRealBounds( 860 displayContext, mWMProxy.getDisplayInfo(displayContext))) 861 .setTransposeLayoutWithOrientation(false) 862 .build(); 863 } 864 getDeviceProfile(Context context)865 public DeviceProfile getDeviceProfile(Context context) { 866 Rect bounds = mWMProxy.getCurrentBounds(context); 867 int rotation = mWMProxy.getRotation(context); 868 return getBestMatch(bounds.width(), bounds.height(), rotation); 869 } 870 871 /** 872 * Returns the device profile matching the provided screen configuration 873 */ getBestMatch(float screenWidth, float screenHeight, int rotation)874 public DeviceProfile getBestMatch(float screenWidth, float screenHeight, int rotation) { 875 DeviceProfile bestMatch = supportedProfiles.get(0); 876 float minDiff = Float.MAX_VALUE; 877 878 for (DeviceProfile profile : supportedProfiles) { 879 float diff = Math.abs(profile.widthPx - screenWidth) 880 + Math.abs(profile.heightPx - screenHeight); 881 if (diff < minDiff) { 882 minDiff = diff; 883 bestMatch = profile; 884 } else if (diff == minDiff && profile.rotationHint == rotation) { 885 bestMatch = profile; 886 } 887 } 888 return bestMatch; 889 } 890 weight(float x0, float y0, float x1, float y1, float pow)891 private static float weight(float x0, float y0, float x1, float y1, float pow) { 892 float d = dist(x0, y0, x1, y1); 893 if (Float.compare(d, 0f) == 0) { 894 return Float.POSITIVE_INFINITY; 895 } 896 return (float) (WEIGHT_EFFICIENT / Math.pow(d, pow)); 897 } 898 899 /** 900 * As a ratio of screen height, the total distance we want the parallax effect to span 901 * horizontally 902 */ wallpaperTravelToScreenWidthRatio(int width, int height)903 private static float wallpaperTravelToScreenWidthRatio(int width, int height) { 904 float aspectRatio = width / (float) height; 905 906 // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width 907 // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width 908 // We will use these two data points to extrapolate how much the wallpaper parallax effect 909 // to span (ie travel) at any aspect ratio: 910 911 final float ASPECT_RATIO_LANDSCAPE = 16 / 10f; 912 final float ASPECT_RATIO_PORTRAIT = 10 / 16f; 913 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; 914 final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; 915 916 // To find out the desired width at different aspect ratios, we use the following two 917 // formulas, where the coefficient on x is the aspect ratio (width/height): 918 // (16/10)x + y = 1.5 919 // (10/16)x + y = 1.2 920 // We solve for x and y and end up with a final formula: 921 final float x = 922 (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE 923 - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / 924 (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); 925 final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; 926 return x * aspectRatio + y; 927 } 928 929 public interface OnIDPChangeListener { 930 931 /** 932 * Called when the device provide changes 933 */ 934 void onIdpChanged(boolean modelPropertiesChanged); 935 } 936 937 938 public static final class GridOption { 939 940 public static final String TAG_NAME = "grid-option"; 941 942 private static final int DEVICE_CATEGORY_PHONE = 1 << 0; 943 private static final int DEVICE_CATEGORY_TABLET = 1 << 1; 944 private static final int DEVICE_CATEGORY_MULTI_DISPLAY = 1 << 2; 945 private static final int DEVICE_CATEGORY_ANY = 946 DEVICE_CATEGORY_PHONE | DEVICE_CATEGORY_TABLET | DEVICE_CATEGORY_MULTI_DISPLAY; 947 948 private static final int INLINE_QSB_FOR_PORTRAIT = 1 << 0; 949 private static final int INLINE_QSB_FOR_LANDSCAPE = 1 << 1; 950 private static final int INLINE_QSB_FOR_TWO_PANEL_PORTRAIT = 1 << 2; 951 private static final int INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE = 1 << 3; 952 private static final int DONT_INLINE_QSB = 0; 953 954 public final String name; 955 public final String gridTitle; 956 public final int gridIconId; 957 public final int numRows; 958 public final int numColumns; 959 public final int numSearchContainerColumns; 960 public final int deviceCategory; 961 @GridType 962 public final int gridType; 963 964 private final int[] numFolderRows = new int[COUNT_SIZES]; 965 private final int[] numFolderColumns = new int[COUNT_SIZES]; 966 private final @StyleRes int folderStyle; 967 private final @StyleRes int cellStyle; 968 969 private final @StyleRes int allAppsStyle; 970 private final int numAllAppsColumns; 971 private final int mNumAllAppsRowsForCellHeightCalculation; 972 private final int numDatabaseAllAppsColumns; 973 private final int numHotseatIcons; 974 private final int numDatabaseHotseatIcons; 975 976 private final boolean[] inlineQsb = new boolean[COUNT_SIZES]; 977 978 private @DimenRes int inlineNavButtonsEndSpacing; 979 private final String dbFile; 980 981 private final int defaultLayoutId; 982 private final int demoModeLayoutId; 983 984 private final boolean isScalable; 985 private final boolean mIsDualGrid; 986 private final int devicePaddingId; 987 private final int mWorkspaceSpecsId; 988 private final int mWorkspaceSpecsTwoPanelId; 989 private final int mAllAppsSpecsId; 990 private final int mAllAppsSpecsTwoPanelId; 991 private final int mFolderSpecsId; 992 private final int mFolderSpecsTwoPanelId; 993 private final int mHotseatSpecsId; 994 private final int mHotseatSpecsTwoPanelId; 995 private final int mWorkspaceCellSpecsId; 996 private final int mWorkspaceCellSpecsTwoPanelId; 997 private final int mAllAppsCellSpecsId; 998 private final int mAllAppsCellSpecsTwoPanelId; 999 private final int mGridSizeSpecsId; 1000 private final boolean mIsFixedLandscape; 1001 GridOption(Context context, AttributeSet attrs, Info displayInfo)1002 public GridOption(Context context, AttributeSet attrs, Info displayInfo) { 1003 TypedArray a = context.obtainStyledAttributes( 1004 attrs, R.styleable.GridDisplayOption); 1005 name = a.getString(R.styleable.GridDisplayOption_name); 1006 gridTitle = a.getString(R.styleable.GridDisplayOption_gridTitle); 1007 gridIconId = a.getResourceId( 1008 R.styleable.GridDisplayOption_gridIconId, INVALID_RESOURCE_HANDLE); 1009 deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory, 1010 DEVICE_CATEGORY_ANY); 1011 mGridSizeSpecsId = a.getResourceId( 1012 R.styleable.GridDisplayOption_gridSizeSpecsId, INVALID_RESOURCE_HANDLE); 1013 mIsDualGrid = a.getBoolean(R.styleable.GridDisplayOption_isDualGrid, false); 1014 if (mGridSizeSpecsId != INVALID_RESOURCE_HANDLE) { 1015 ResourceHelper resourceHelper = new ResourceHelper(context, mGridSizeSpecsId); 1016 GridSize gridSize = getGridSize(resourceHelper, context, displayInfo); 1017 numColumns = gridSize.mNumColumns; 1018 numRows = gridSize.mNumRows; 1019 dbFile = gridSize.mDbFile; 1020 defaultLayoutId = gridSize.mDefaultLayoutId; 1021 demoModeLayoutId = gridSize.mDemoModeLayoutId; 1022 } else { 1023 numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0); 1024 numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0); 1025 dbFile = a.getString(R.styleable.GridDisplayOption_dbFile); 1026 defaultLayoutId = a.getResourceId( 1027 R.styleable.GridDisplayOption_defaultLayoutId, 0); 1028 demoModeLayoutId = a.getResourceId( 1029 R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId); 1030 } 1031 1032 numSearchContainerColumns = a.getInt( 1033 R.styleable.GridDisplayOption_numSearchContainerColumns, numColumns); 1034 1035 allAppsStyle = a.getResourceId(R.styleable.GridDisplayOption_allAppsStyle, 1036 R.style.AllAppsStyleDefault); 1037 numAllAppsColumns = a.getInt( 1038 R.styleable.GridDisplayOption_numAllAppsColumns, numColumns); 1039 numDatabaseAllAppsColumns = a.getInt( 1040 R.styleable.GridDisplayOption_numExtendedAllAppsColumns, 2 * numAllAppsColumns); 1041 1042 numHotseatIcons = a.getInt( 1043 R.styleable.GridDisplayOption_numHotseatIcons, numColumns); 1044 numDatabaseHotseatIcons = a.getInt( 1045 R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons); 1046 1047 inlineNavButtonsEndSpacing = 1048 a.getResourceId(R.styleable.GridDisplayOption_inlineNavButtonsEndSpacing, 1049 R.dimen.taskbar_button_margin_default); 1050 1051 numFolderRows[INDEX_DEFAULT] = a.getInt( 1052 R.styleable.GridDisplayOption_numFolderRows, numRows); 1053 numFolderColumns[INDEX_DEFAULT] = a.getInt( 1054 R.styleable.GridDisplayOption_numFolderColumns, numColumns); 1055 1056 if (FeatureFlags.enableResponsiveWorkspace()) { 1057 numFolderRows[INDEX_LANDSCAPE] = a.getInt( 1058 R.styleable.GridDisplayOption_numFolderRowsLandscape, 1059 numFolderRows[INDEX_DEFAULT]); 1060 numFolderColumns[INDEX_LANDSCAPE] = a.getInt( 1061 R.styleable.GridDisplayOption_numFolderColumnsLandscape, 1062 numFolderColumns[INDEX_DEFAULT]); 1063 numFolderRows[INDEX_TWO_PANEL_PORTRAIT] = a.getInt( 1064 R.styleable.GridDisplayOption_numFolderRowsTwoPanelPortrait, 1065 numFolderRows[INDEX_DEFAULT]); 1066 numFolderColumns[INDEX_TWO_PANEL_PORTRAIT] = a.getInt( 1067 R.styleable.GridDisplayOption_numFolderColumnsTwoPanelPortrait, 1068 numFolderColumns[INDEX_DEFAULT]); 1069 numFolderRows[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt( 1070 R.styleable.GridDisplayOption_numFolderRowsTwoPanelLandscape, 1071 numFolderRows[INDEX_DEFAULT]); 1072 numFolderColumns[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt( 1073 R.styleable.GridDisplayOption_numFolderColumnsTwoPanelLandscape, 1074 numFolderColumns[INDEX_DEFAULT]); 1075 } else { 1076 numFolderRows[INDEX_LANDSCAPE] = numFolderRows[INDEX_DEFAULT]; 1077 numFolderColumns[INDEX_LANDSCAPE] = numFolderColumns[INDEX_DEFAULT]; 1078 numFolderRows[INDEX_TWO_PANEL_PORTRAIT] = numFolderRows[INDEX_DEFAULT]; 1079 numFolderColumns[INDEX_TWO_PANEL_PORTRAIT] = numFolderColumns[INDEX_DEFAULT]; 1080 numFolderRows[INDEX_TWO_PANEL_LANDSCAPE] = numFolderRows[INDEX_DEFAULT]; 1081 numFolderColumns[INDEX_TWO_PANEL_LANDSCAPE] = numFolderColumns[INDEX_DEFAULT]; 1082 } 1083 1084 folderStyle = a.getResourceId(R.styleable.GridDisplayOption_folderStyle, 1085 INVALID_RESOURCE_HANDLE); 1086 1087 cellStyle = a.getResourceId(R.styleable.GridDisplayOption_cellStyle, 1088 R.style.CellStyleDefault); 1089 1090 isScalable = a.getBoolean( 1091 R.styleable.GridDisplayOption_isScalable, false); 1092 devicePaddingId = a.getResourceId( 1093 R.styleable.GridDisplayOption_devicePaddingId, INVALID_RESOURCE_HANDLE); 1094 1095 if (FeatureFlags.enableResponsiveWorkspace()) { 1096 mWorkspaceSpecsId = a.getResourceId( 1097 R.styleable.GridDisplayOption_workspaceSpecsId, INVALID_RESOURCE_HANDLE); 1098 mWorkspaceSpecsTwoPanelId = a.getResourceId( 1099 R.styleable.GridDisplayOption_workspaceSpecsTwoPanelId, 1100 mWorkspaceSpecsId); 1101 mAllAppsSpecsId = a.getResourceId( 1102 R.styleable.GridDisplayOption_allAppsSpecsId, INVALID_RESOURCE_HANDLE); 1103 mAllAppsSpecsTwoPanelId = a.getResourceId( 1104 R.styleable.GridDisplayOption_allAppsSpecsTwoPanelId, 1105 mAllAppsSpecsId); 1106 mFolderSpecsId = a.getResourceId( 1107 R.styleable.GridDisplayOption_folderSpecsId, INVALID_RESOURCE_HANDLE); 1108 mFolderSpecsTwoPanelId = a.getResourceId( 1109 R.styleable.GridDisplayOption_folderSpecsTwoPanelId, 1110 mFolderSpecsId); 1111 mHotseatSpecsId = a.getResourceId( 1112 R.styleable.GridDisplayOption_hotseatSpecsId, INVALID_RESOURCE_HANDLE); 1113 mHotseatSpecsTwoPanelId = a.getResourceId( 1114 R.styleable.GridDisplayOption_hotseatSpecsTwoPanelId, 1115 mHotseatSpecsId); 1116 mWorkspaceCellSpecsId = a.getResourceId( 1117 R.styleable.GridDisplayOption_workspaceCellSpecsId, 1118 INVALID_RESOURCE_HANDLE); 1119 mWorkspaceCellSpecsTwoPanelId = a.getResourceId( 1120 R.styleable.GridDisplayOption_workspaceCellSpecsTwoPanelId, 1121 mWorkspaceCellSpecsId); 1122 mAllAppsCellSpecsId = a.getResourceId( 1123 R.styleable.GridDisplayOption_allAppsCellSpecsId, 1124 INVALID_RESOURCE_HANDLE); 1125 mAllAppsCellSpecsTwoPanelId = a.getResourceId( 1126 R.styleable.GridDisplayOption_allAppsCellSpecsTwoPanelId, 1127 mAllAppsCellSpecsId); 1128 mNumAllAppsRowsForCellHeightCalculation = a.getInt( 1129 R.styleable.GridDisplayOption_numAllAppsRowsForCellHeightCalculation, 1130 numRows); 1131 } else { 1132 mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE; 1133 mWorkspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 1134 mAllAppsSpecsId = INVALID_RESOURCE_HANDLE; 1135 mAllAppsSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 1136 mFolderSpecsId = INVALID_RESOURCE_HANDLE; 1137 mFolderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 1138 mHotseatSpecsId = INVALID_RESOURCE_HANDLE; 1139 mHotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 1140 mWorkspaceCellSpecsId = INVALID_RESOURCE_HANDLE; 1141 mWorkspaceCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 1142 mAllAppsCellSpecsId = INVALID_RESOURCE_HANDLE; 1143 mAllAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE; 1144 mNumAllAppsRowsForCellHeightCalculation = numRows; 1145 } 1146 1147 mIsFixedLandscape = a.getBoolean(R.styleable.GridDisplayOption_isFixedLandscape, false); 1148 gridType = a.getInt(R.styleable.GridDisplayOption_gridType, GRID_TYPE_ANY); 1149 1150 int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb, 1151 DONT_INLINE_QSB); 1152 inlineQsb[INDEX_DEFAULT] = 1153 (inlineForRotation & INLINE_QSB_FOR_PORTRAIT) == INLINE_QSB_FOR_PORTRAIT; 1154 inlineQsb[INDEX_LANDSCAPE] = 1155 (inlineForRotation & INLINE_QSB_FOR_LANDSCAPE) == INLINE_QSB_FOR_LANDSCAPE; 1156 inlineQsb[INDEX_TWO_PANEL_PORTRAIT] = 1157 (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_PORTRAIT) 1158 == INLINE_QSB_FOR_TWO_PANEL_PORTRAIT; 1159 inlineQsb[INDEX_TWO_PANEL_LANDSCAPE] = 1160 (inlineForRotation & INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE) 1161 == INLINE_QSB_FOR_TWO_PANEL_LANDSCAPE; 1162 1163 a.recycle(); 1164 } 1165 isEnabled(@eviceType int deviceType)1166 public boolean isEnabled(@DeviceType int deviceType) { 1167 switch (deviceType) { 1168 case TYPE_PHONE: 1169 return (deviceCategory & DEVICE_CATEGORY_PHONE) == DEVICE_CATEGORY_PHONE; 1170 case TYPE_TABLET: 1171 return (deviceCategory & DEVICE_CATEGORY_TABLET) == DEVICE_CATEGORY_TABLET; 1172 case TYPE_MULTI_DISPLAY: 1173 return (deviceCategory & DEVICE_CATEGORY_MULTI_DISPLAY) 1174 == DEVICE_CATEGORY_MULTI_DISPLAY; 1175 default: 1176 return false; 1177 } 1178 } 1179 1180 /** 1181 * Returns true if the grid option should be used given the flags that are toggled on/off. 1182 */ filterByFlag(int deviceType, boolean isFixedLandscape)1183 public boolean filterByFlag(int deviceType, boolean isFixedLandscape) { 1184 if (deviceType == TYPE_TABLET) { 1185 return Flags.oneGridRotationHandling() == mIsDualGrid; 1186 } 1187 1188 // Here we return true if fixed landscape mode should be on. 1189 if (mIsFixedLandscape || isFixedLandscape) { 1190 return mIsFixedLandscape && isFixedLandscape && Flags.oneGridSpecs(); 1191 } 1192 1193 // If the grid type is one grid we return true when the flag is on, if the grid type 1194 // is non-one grid we return true when the flag is off. Otherwise, we return true. 1195 if (gridType == GRID_TYPE_ONE_GRID) { 1196 return Flags.oneGridSpecs(); 1197 } else if (gridType == GRID_TYPE_NON_ONE_GRID) { 1198 return !Flags.oneGridSpecs(); 1199 } 1200 1201 return true; 1202 } 1203 } 1204 1205 public static final class GridSize { 1206 final int mNumRows; 1207 final int mNumColumns; 1208 final float mMinDeviceWidthPx; 1209 final float mMinDeviceHeightPx; 1210 final String mDbFile; 1211 final int mDefaultLayoutId; 1212 final int mDemoModeLayoutId; 1213 1214 GridSize(Context context, AttributeSet attrs)1215 GridSize(Context context, AttributeSet attrs) { 1216 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GridSize); 1217 1218 mNumRows = (int) a.getFloat(R.styleable.GridSize_numGridRows, 0); 1219 mNumColumns = (int) a.getFloat(R.styleable.GridSize_numGridColumns, 0); 1220 mMinDeviceWidthPx = a.getFloat(R.styleable.GridSize_minDeviceWidthPx, 0); 1221 mMinDeviceHeightPx = a.getFloat(R.styleable.GridSize_minDeviceHeightPx, 0); 1222 mDbFile = a.getString(R.styleable.GridSize_dbFile); 1223 mDefaultLayoutId = a.getResourceId( 1224 R.styleable.GridSize_defaultLayoutId, 0); 1225 mDemoModeLayoutId = a.getResourceId( 1226 R.styleable.GridSize_demoModeLayoutId, mDefaultLayoutId); 1227 1228 a.recycle(); 1229 } 1230 } 1231 1232 @VisibleForTesting 1233 static final class DisplayOption { 1234 public final GridOption grid; 1235 1236 private final float minWidthDps; 1237 private final float minHeightDps; 1238 private final boolean canBeDefault; 1239 1240 private final PointF[] minCellSize = new PointF[COUNT_SIZES]; 1241 1242 private final PointF[] borderSpaces = new PointF[COUNT_SIZES]; 1243 private final float[] horizontalMargin = new float[COUNT_SIZES]; 1244 private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES]; 1245 private final float[] hotseatQsbSpace = new float[COUNT_SIZES]; 1246 1247 private final float[] iconSizes = new float[COUNT_SIZES]; 1248 private final float[] textSizes = new float[COUNT_SIZES]; 1249 1250 private final PointF[] allAppsCellSize = new PointF[COUNT_SIZES]; 1251 private final float[] allAppsIconSizes = new float[COUNT_SIZES]; 1252 private final float[] allAppsIconTextSizes = new float[COUNT_SIZES]; 1253 private final PointF[] allAppsBorderSpaces = new PointF[COUNT_SIZES]; 1254 1255 private final float[] transientTaskbarIconSize = new float[COUNT_SIZES]; 1256 1257 private final boolean[] startAlignTaskbar = new boolean[COUNT_SIZES]; 1258 DisplayOption(GridOption grid, Context context, AttributeSet attrs)1259 DisplayOption(GridOption grid, Context context, AttributeSet attrs) { 1260 this.grid = grid; 1261 1262 Resources res = context.getResources(); 1263 1264 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProfileDisplayOption); 1265 1266 minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0); 1267 minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0); 1268 1269 canBeDefault = a.getBoolean(R.styleable.ProfileDisplayOption_canBeDefault, false); 1270 1271 float x; 1272 float y; 1273 1274 x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidth, 0); 1275 y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeight, 0); 1276 minCellSize[INDEX_DEFAULT] = new PointF(x, y); 1277 1278 x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthLandscape, 1279 minCellSize[INDEX_DEFAULT].x); 1280 y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightLandscape, 1281 minCellSize[INDEX_DEFAULT].y); 1282 minCellSize[INDEX_LANDSCAPE] = new PointF(x, y); 1283 1284 x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelPortrait, 1285 minCellSize[INDEX_DEFAULT].x); 1286 y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightTwoPanelPortrait, 1287 minCellSize[INDEX_DEFAULT].y); 1288 minCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y); 1289 1290 x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelLandscape, 1291 minCellSize[INDEX_DEFAULT].x); 1292 y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightTwoPanelLandscape, 1293 minCellSize[INDEX_DEFAULT].y); 1294 minCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y); 1295 1296 float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpace, 0); 1297 float borderSpaceLandscape = a.getFloat( 1298 R.styleable.ProfileDisplayOption_borderSpaceLandscape, borderSpace); 1299 float borderSpaceTwoPanelPortrait = a.getFloat( 1300 R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortrait, borderSpace); 1301 float borderSpaceTwoPanelLandscape = a.getFloat( 1302 R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscape, borderSpace); 1303 1304 x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontal, borderSpace); 1305 y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVertical, borderSpace); 1306 borderSpaces[INDEX_DEFAULT] = new PointF(x, y); 1307 1308 x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceLandscapeHorizontal, 1309 borderSpaceLandscape); 1310 y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceLandscapeVertical, 1311 borderSpaceLandscape); 1312 borderSpaces[INDEX_LANDSCAPE] = new PointF(x, y); 1313 1314 x = a.getFloat( 1315 R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortraitHorizontal, 1316 borderSpaceTwoPanelPortrait); 1317 y = a.getFloat( 1318 R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortraitVertical, 1319 borderSpaceTwoPanelPortrait); 1320 borderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y); 1321 1322 x = a.getFloat( 1323 R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscapeHorizontal, 1324 borderSpaceTwoPanelLandscape); 1325 y = a.getFloat( 1326 R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscapeVertical, 1327 borderSpaceTwoPanelLandscape); 1328 borderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y); 1329 1330 x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidth, 1331 minCellSize[INDEX_DEFAULT].x); 1332 y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeight, 1333 minCellSize[INDEX_DEFAULT].y); 1334 allAppsCellSize[INDEX_DEFAULT] = new PointF(x, y); 1335 1336 x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthLandscape, 1337 allAppsCellSize[INDEX_DEFAULT].x); 1338 y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightLandscape, 1339 allAppsCellSize[INDEX_DEFAULT].y); 1340 allAppsCellSize[INDEX_LANDSCAPE] = new PointF(x, y); 1341 1342 x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthTwoPanelPortrait, 1343 allAppsCellSize[INDEX_DEFAULT].x); 1344 y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightTwoPanelPortrait, 1345 allAppsCellSize[INDEX_DEFAULT].y); 1346 allAppsCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y); 1347 1348 x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellWidthTwoPanelLandscape, 1349 allAppsCellSize[INDEX_DEFAULT].x); 1350 y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellHeightTwoPanelLandscape, 1351 allAppsCellSize[INDEX_DEFAULT].y); 1352 allAppsCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y); 1353 1354 float allAppsBorderSpace = a.getFloat( 1355 R.styleable.ProfileDisplayOption_allAppsBorderSpace, borderSpace); 1356 float allAppsBorderSpaceLandscape = a.getFloat( 1357 R.styleable.ProfileDisplayOption_allAppsBorderSpaceLandscape, 1358 allAppsBorderSpace); 1359 float allAppsBorderSpaceTwoPanelPortrait = a.getFloat( 1360 R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortrait, 1361 allAppsBorderSpace); 1362 float allAppsBorderSpaceTwoPanelLandscape = a.getFloat( 1363 R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelLandscape, 1364 allAppsBorderSpace); 1365 1366 x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpaceHorizontal, 1367 allAppsBorderSpace); 1368 y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpaceVertical, 1369 allAppsBorderSpace); 1370 allAppsBorderSpaces[INDEX_DEFAULT] = new PointF(x, y); 1371 1372 x = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpaceLandscapeHorizontal, 1373 allAppsBorderSpaceLandscape); 1374 y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpaceLandscapeVertical, 1375 allAppsBorderSpaceLandscape); 1376 allAppsBorderSpaces[INDEX_LANDSCAPE] = new PointF(x, y); 1377 1378 x = a.getFloat( 1379 R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortraitHorizontal, 1380 allAppsBorderSpaceTwoPanelPortrait); 1381 y = a.getFloat( 1382 R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortraitVertical, 1383 allAppsBorderSpaceTwoPanelPortrait); 1384 allAppsBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y); 1385 1386 x = a.getFloat( 1387 R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelLandscapeHorizontal, 1388 allAppsBorderSpaceTwoPanelLandscape); 1389 y = a.getFloat( 1390 R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelLandscapeVertical, 1391 allAppsBorderSpaceTwoPanelLandscape); 1392 allAppsBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y); 1393 1394 iconSizes[INDEX_DEFAULT] = 1395 a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0); 1396 iconSizes[INDEX_LANDSCAPE] = 1397 a.getFloat(R.styleable.ProfileDisplayOption_iconSizeLandscape, 1398 iconSizes[INDEX_DEFAULT]); 1399 iconSizes[INDEX_TWO_PANEL_PORTRAIT] = 1400 a.getFloat(R.styleable.ProfileDisplayOption_iconSizeTwoPanelPortrait, 1401 iconSizes[INDEX_DEFAULT]); 1402 iconSizes[INDEX_TWO_PANEL_LANDSCAPE] = 1403 a.getFloat(R.styleable.ProfileDisplayOption_iconSizeTwoPanelLandscape, 1404 iconSizes[INDEX_DEFAULT]); 1405 1406 allAppsIconSizes[INDEX_DEFAULT] = a.getFloat( 1407 R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]); 1408 allAppsIconSizes[INDEX_LANDSCAPE] = a.getFloat( 1409 R.styleable.ProfileDisplayOption_allAppsIconSizeLandscape, 1410 allAppsIconSizes[INDEX_DEFAULT]); 1411 allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( 1412 R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait, 1413 allAppsIconSizes[INDEX_DEFAULT]); 1414 allAppsIconSizes[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( 1415 R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelLandscape, 1416 allAppsIconSizes[INDEX_DEFAULT]); 1417 1418 textSizes[INDEX_DEFAULT] = 1419 a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0); 1420 textSizes[INDEX_LANDSCAPE] = 1421 a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeLandscape, 1422 textSizes[INDEX_DEFAULT]); 1423 textSizes[INDEX_TWO_PANEL_PORTRAIT] = 1424 a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeTwoPanelPortrait, 1425 textSizes[INDEX_DEFAULT]); 1426 textSizes[INDEX_TWO_PANEL_LANDSCAPE] = 1427 a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeTwoPanelLandscape, 1428 textSizes[INDEX_DEFAULT]); 1429 1430 allAppsIconTextSizes[INDEX_DEFAULT] = a.getFloat( 1431 R.styleable.ProfileDisplayOption_allAppsIconTextSize, textSizes[INDEX_DEFAULT]); 1432 allAppsIconTextSizes[INDEX_LANDSCAPE] = allAppsIconTextSizes[INDEX_DEFAULT]; 1433 allAppsIconTextSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( 1434 R.styleable.ProfileDisplayOption_allAppsIconTextSizeTwoPanelPortrait, 1435 allAppsIconTextSizes[INDEX_DEFAULT]); 1436 allAppsIconTextSizes[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( 1437 R.styleable.ProfileDisplayOption_allAppsIconTextSizeTwoPanelLandscape, 1438 allAppsIconTextSizes[INDEX_DEFAULT]); 1439 1440 horizontalMargin[INDEX_DEFAULT] = a.getFloat( 1441 R.styleable.ProfileDisplayOption_horizontalMargin, 0); 1442 horizontalMargin[INDEX_LANDSCAPE] = a.getFloat( 1443 R.styleable.ProfileDisplayOption_horizontalMarginLandscape, 1444 horizontalMargin[INDEX_DEFAULT]); 1445 horizontalMargin[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( 1446 R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelLandscape, 1447 horizontalMargin[INDEX_DEFAULT]); 1448 horizontalMargin[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( 1449 R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelPortrait, 1450 horizontalMargin[INDEX_DEFAULT]); 1451 1452 hotseatBarBottomSpace[INDEX_DEFAULT] = a.getFloat( 1453 R.styleable.ProfileDisplayOption_hotseatBarBottomSpace, 1454 ResourcesCompat.getFloat(res, R.dimen.hotseat_bar_bottom_space_default)); 1455 hotseatBarBottomSpace[INDEX_LANDSCAPE] = a.getFloat( 1456 R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceLandscape, 1457 hotseatBarBottomSpace[INDEX_DEFAULT]); 1458 hotseatBarBottomSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( 1459 R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelLandscape, 1460 hotseatBarBottomSpace[INDEX_DEFAULT]); 1461 hotseatBarBottomSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( 1462 R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelPortrait, 1463 hotseatBarBottomSpace[INDEX_DEFAULT]); 1464 1465 hotseatQsbSpace[INDEX_DEFAULT] = a.getFloat( 1466 R.styleable.ProfileDisplayOption_hotseatQsbSpace, 1467 ResourcesCompat.getFloat(res, R.dimen.hotseat_qsb_space_default)); 1468 hotseatQsbSpace[INDEX_LANDSCAPE] = a.getFloat( 1469 R.styleable.ProfileDisplayOption_hotseatQsbSpaceLandscape, 1470 hotseatQsbSpace[INDEX_DEFAULT]); 1471 hotseatQsbSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( 1472 R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelLandscape, 1473 hotseatQsbSpace[INDEX_DEFAULT]); 1474 hotseatQsbSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( 1475 R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelPortrait, 1476 hotseatQsbSpace[INDEX_DEFAULT]); 1477 1478 transientTaskbarIconSize[INDEX_DEFAULT] = a.getFloat( 1479 R.styleable.ProfileDisplayOption_transientTaskbarIconSize, 1480 ResourcesCompat.getFloat(res, R.dimen.taskbar_icon_size)); 1481 transientTaskbarIconSize[INDEX_LANDSCAPE] = a.getFloat( 1482 R.styleable.ProfileDisplayOption_transientTaskbarIconSizeLandscape, 1483 transientTaskbarIconSize[INDEX_DEFAULT]); 1484 transientTaskbarIconSize[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat( 1485 R.styleable.ProfileDisplayOption_transientTaskbarIconSizeTwoPanelLandscape, 1486 transientTaskbarIconSize[INDEX_DEFAULT]); 1487 transientTaskbarIconSize[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat( 1488 R.styleable.ProfileDisplayOption_transientTaskbarIconSizeTwoPanelPortrait, 1489 transientTaskbarIconSize[INDEX_DEFAULT]); 1490 1491 startAlignTaskbar[INDEX_DEFAULT] = a.getBoolean( 1492 R.styleable.ProfileDisplayOption_startAlignTaskbar, false); 1493 startAlignTaskbar[INDEX_LANDSCAPE] = a.getBoolean( 1494 R.styleable.ProfileDisplayOption_startAlignTaskbarLandscape, 1495 startAlignTaskbar[INDEX_DEFAULT]); 1496 startAlignTaskbar[INDEX_TWO_PANEL_LANDSCAPE] = a.getBoolean( 1497 R.styleable.ProfileDisplayOption_startAlignTaskbarTwoPanelLandscape, 1498 startAlignTaskbar[INDEX_LANDSCAPE]); 1499 startAlignTaskbar[INDEX_TWO_PANEL_PORTRAIT] = a.getBoolean( 1500 R.styleable.ProfileDisplayOption_startAlignTaskbarTwoPanelPortrait, 1501 startAlignTaskbar[INDEX_DEFAULT]); 1502 1503 a.recycle(); 1504 } 1505 DisplayOption()1506 DisplayOption() { 1507 this(null); 1508 } 1509 DisplayOption(GridOption grid)1510 DisplayOption(GridOption grid) { 1511 this.grid = grid; 1512 minWidthDps = 0; 1513 minHeightDps = 0; 1514 canBeDefault = false; 1515 for (int i = 0; i < COUNT_SIZES; i++) { 1516 iconSizes[i] = 0; 1517 textSizes[i] = 0; 1518 borderSpaces[i] = new PointF(); 1519 minCellSize[i] = new PointF(); 1520 allAppsCellSize[i] = new PointF(); 1521 allAppsIconSizes[i] = 0; 1522 allAppsIconTextSizes[i] = 0; 1523 allAppsBorderSpaces[i] = new PointF(); 1524 transientTaskbarIconSize[i] = 0; 1525 startAlignTaskbar[i] = false; 1526 } 1527 } 1528 multiply(float w)1529 private DisplayOption multiply(float w) { 1530 for (int i = 0; i < COUNT_SIZES; i++) { 1531 iconSizes[i] *= w; 1532 textSizes[i] *= w; 1533 borderSpaces[i].x *= w; 1534 borderSpaces[i].y *= w; 1535 minCellSize[i].x *= w; 1536 minCellSize[i].y *= w; 1537 horizontalMargin[i] *= w; 1538 hotseatBarBottomSpace[i] *= w; 1539 hotseatQsbSpace[i] *= w; 1540 allAppsCellSize[i].x *= w; 1541 allAppsCellSize[i].y *= w; 1542 allAppsIconSizes[i] *= w; 1543 allAppsIconTextSizes[i] *= w; 1544 allAppsBorderSpaces[i].x *= w; 1545 allAppsBorderSpaces[i].y *= w; 1546 transientTaskbarIconSize[i] *= w; 1547 } 1548 1549 return this; 1550 } 1551 add(DisplayOption p)1552 private DisplayOption add(DisplayOption p) { 1553 for (int i = 0; i < COUNT_SIZES; i++) { 1554 iconSizes[i] += p.iconSizes[i]; 1555 textSizes[i] += p.textSizes[i]; 1556 borderSpaces[i].x += p.borderSpaces[i].x; 1557 borderSpaces[i].y += p.borderSpaces[i].y; 1558 minCellSize[i].x += p.minCellSize[i].x; 1559 minCellSize[i].y += p.minCellSize[i].y; 1560 horizontalMargin[i] += p.horizontalMargin[i]; 1561 hotseatBarBottomSpace[i] += p.hotseatBarBottomSpace[i]; 1562 hotseatQsbSpace[i] += p.hotseatQsbSpace[i]; 1563 allAppsCellSize[i].x += p.allAppsCellSize[i].x; 1564 allAppsCellSize[i].y += p.allAppsCellSize[i].y; 1565 allAppsIconSizes[i] += p.allAppsIconSizes[i]; 1566 allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i]; 1567 allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x; 1568 allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y; 1569 transientTaskbarIconSize[i] += p.transientTaskbarIconSize[i]; 1570 startAlignTaskbar[i] |= p.startAlignTaskbar[i]; 1571 } 1572 1573 return this; 1574 } 1575 } 1576 } 1577