1 /* 2 * Copyright (C) 2017 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 package com.android.wallpaper.util; 17 18 import android.app.Activity; 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.graphics.Point; 22 import android.graphics.drawable.GradientDrawable; 23 import android.util.DisplayMetrics; 24 import android.view.Display; 25 import android.view.View; 26 import android.view.WindowManager; 27 28 import androidx.annotation.NonNull; 29 30 import com.android.systemui.shared.system.QuickStepContract; 31 import com.android.wallpaper.R; 32 33 /** 34 * Simple utility class that calculates various sizes relative to the display or current 35 * configuration. 36 */ 37 public class SizeCalculator { 38 39 /** 40 * This parameter is used for comparing the threshold DP of the screen on whether we want a 41 * "fewer columns" configuration or a "more columns" configuration. 42 */ 43 private static final int COLUMN_COUNT_THRESHOLD_DP = 820; 44 45 /** 46 * The number of columns for a "fewer columns" configuration of the category tiles grid. 47 */ 48 private static final int CATEGORY_FEWER_COLUMNS = 3; 49 50 /** 51 * The number of columns for a "more columns" configuration of the category tiles grid. 52 */ 53 private static final int CATEGORY_MORE_COLUMNS = 3; 54 55 /** 56 * The number of columns for a "fewer columns" configuration of the featured category tiles 57 * grid. 58 */ 59 private static final int FEATURED_CATEGORY_FEWER_COLUMNS = 2; 60 61 /** 62 * The number of columns for a "more columns" configuration of the featured category tiles grid. 63 */ 64 private static final int FEATURED_CATEGORY_MORE_COLUMNS = 2; 65 66 /** 67 * The number of columns for a "fewer columns" configuration of the individual wallpaper tiles 68 * grid. 69 */ 70 private static final int INDIVIDUAL_FEWER_COLUMNS = 3; 71 72 /** 73 * The number of columns for a "more columns" configuration of the individual wallpaper tiles 74 * grid. 75 */ 76 private static final int INDIVIDUAL_MORE_COLUMNS = 4; 77 78 /** 79 * The number of columns for a "fewer columns" configuration of the featured individual 80 * wallpaper tiles grid. 81 */ 82 private static final int FEATURED_INDIVIDUAL_FEWER_COLUMNS = 2; 83 84 /** 85 * The number of columns for a "more columns" configuration of the featured individual wallpaper 86 * tiles grid. 87 */ 88 private static final int FEATURED_INDIVIDUAL_MORE_COLUMNS = 2; 89 90 // Suppress default constructor for noninstantiability. SizeCalculator()91 private SizeCalculator() { 92 throw new AssertionError("Can't initialize a SizeCalculator."); 93 } 94 95 /** 96 * Returns the number of columns for a grid of category tiles. Selects from fewer and more 97 * columns based on the width of the activity. 98 */ getNumCategoryColumns(@onNull Activity activity)99 public static int getNumCategoryColumns(@NonNull Activity activity) { 100 int windowWidthPx = getActivityWindowWidthPx(activity); 101 return getNumCategoryColumns(activity, windowWidthPx); 102 } 103 104 /** 105 * Returns the number of columns for a grid of individual tiles. Selects from fewer and more 106 * columns based on the width of the activity. 107 */ getNumIndividualColumns(@onNull Activity activity)108 public static int getNumIndividualColumns(@NonNull Activity activity) { 109 int windowWidthPx = getActivityWindowWidthPx(activity); 110 return getNumIndividualColumns(activity, windowWidthPx); 111 } 112 113 /** 114 * Returns the number of columns for a grid of featured individual tiles. Selects from fewer and 115 * more columns based on the width of the activity. 116 */ getNumFeaturedIndividualColumns(@onNull Activity activity)117 public static int getNumFeaturedIndividualColumns(@NonNull Activity activity) { 118 int windowWidthPx = getActivityWindowWidthPx(activity); 119 return getNumFeaturedIndividualColumns(activity, windowWidthPx); 120 } 121 getNumCategoryColumns(Activity activity, int windowWidthPx)122 private static int getNumCategoryColumns(Activity activity, int windowWidthPx) { 123 return getNumColumns(activity, windowWidthPx, CATEGORY_FEWER_COLUMNS, 124 CATEGORY_MORE_COLUMNS); 125 } 126 getNumCategoryColumns(Context context, int windowWidthPx)127 private static int getNumCategoryColumns(Context context, int windowWidthPx) { 128 return getNumColumns(context, windowWidthPx, CATEGORY_FEWER_COLUMNS, 129 CATEGORY_MORE_COLUMNS); 130 } 131 getNumFeaturedCategoryColumns(Activity activity, int windowWidthPx)132 private static int getNumFeaturedCategoryColumns(Activity activity, int windowWidthPx) { 133 return getNumColumns(activity, windowWidthPx, FEATURED_CATEGORY_FEWER_COLUMNS, 134 FEATURED_CATEGORY_MORE_COLUMNS); 135 } 136 getNumFeaturedCategoryColumns(Context context, int windowWidthPx)137 private static int getNumFeaturedCategoryColumns(Context context, int windowWidthPx) { 138 return getNumColumns(context, windowWidthPx, FEATURED_CATEGORY_FEWER_COLUMNS, 139 FEATURED_CATEGORY_MORE_COLUMNS); 140 } 141 getNumIndividualColumns(Activity activity, int windowWidthPx)142 private static int getNumIndividualColumns(Activity activity, int windowWidthPx) { 143 return getNumColumns( 144 activity, windowWidthPx, INDIVIDUAL_FEWER_COLUMNS, INDIVIDUAL_MORE_COLUMNS); 145 } 146 getNumFeaturedIndividualColumns(Activity activity, int windowWidthPx)147 private static int getNumFeaturedIndividualColumns(Activity activity, int windowWidthPx) { 148 return getNumColumns(activity, windowWidthPx, FEATURED_INDIVIDUAL_FEWER_COLUMNS, 149 FEATURED_INDIVIDUAL_MORE_COLUMNS); 150 } 151 getNumColumns( Context context, int windowWidthPx, int fewerCount, int moreCount)152 private static int getNumColumns( 153 Context context, int windowWidthPx, int fewerCount, int moreCount) { 154 WindowManager windowManager = (WindowManager) 155 context.getSystemService(Context.WINDOW_SERVICE); 156 Display display = windowManager.getDefaultDisplay(); 157 158 DisplayMetrics metrics = DisplayMetricsRetriever.getInstance() 159 .getDisplayMetrics(context.getResources(), display); 160 161 // Columns should be based on the size of the window, not the size of the display. 162 int windowWidthDp = (int) (windowWidthPx / metrics.density); 163 if (windowWidthDp < COLUMN_COUNT_THRESHOLD_DP) { 164 return fewerCount; 165 } else { 166 return moreCount; 167 } 168 } 169 170 /** 171 * Returns the size of a category grid tile in px. 172 */ getCategoryTileSize(@onNull Activity activity)173 public static Point getCategoryTileSize(@NonNull Activity activity) { 174 Resources res = activity.getResources(); 175 int windowWidthPx = getActivityWindowWidthPx(activity); 176 177 int columnCount = getNumCategoryColumns(activity, windowWidthPx); 178 return getSquareTileSize(columnCount, windowWidthPx, 179 res.getDimensionPixelSize(R.dimen.grid_item_category_padding_horizontal), 180 res.getDimensionPixelSize(R.dimen.category_grid_edge_space)); 181 } 182 183 /** 184 * Returns the size of a category grid tile in px. 185 */ getCategoryTileSize(Context context, int windowWidthPx)186 public static Point getCategoryTileSize(Context context, int windowWidthPx) { 187 Resources res = context.getResources(); 188 189 int columnCount = getNumCategoryColumns(context, windowWidthPx); 190 return getSquareTileSize(columnCount, windowWidthPx, 191 res.getDimensionPixelSize(R.dimen.grid_item_category_padding_horizontal), 192 res.getDimensionPixelSize(R.dimen.category_grid_edge_space)); 193 } 194 195 /** 196 * Returns the size of a featured category grid tile in px. 197 */ getFeaturedCategoryTileSize(@onNull Activity activity)198 public static Point getFeaturedCategoryTileSize(@NonNull Activity activity) { 199 Resources res = activity.getResources(); 200 int windowWidthPx = getActivityWindowWidthPx(activity); 201 202 int columnCount = getNumFeaturedCategoryColumns(activity, windowWidthPx); 203 return getSquareTileSize(columnCount, windowWidthPx, 204 res.getDimensionPixelSize(R.dimen.grid_item_category_padding_horizontal), 205 res.getDimensionPixelSize(R.dimen.category_grid_edge_space)); 206 } 207 208 /** 209 * Returns the size of a featured category grid tile in px. 210 */ getFeaturedCategoryTileSize(Context context, int windowWidthPx)211 public static Point getFeaturedCategoryTileSize(Context context, int windowWidthPx) { 212 Resources res = context.getResources(); 213 214 int columnCount = getNumFeaturedCategoryColumns(context, windowWidthPx); 215 return getSquareTileSize(columnCount, windowWidthPx, 216 res.getDimensionPixelSize(R.dimen.grid_item_category_padding_horizontal), 217 res.getDimensionPixelSize(R.dimen.category_grid_edge_space)); 218 } 219 220 /** 221 * Returns the size of an individual grid tile for the given activity in px. 222 */ getIndividualTileSize(@onNull Activity activity)223 public static Point getIndividualTileSize(@NonNull Activity activity) { 224 Resources res = activity.getResources(); 225 int windowWidthPx = getActivityWindowWidthPx(activity); 226 227 int columnCount = getNumIndividualColumns(activity, windowWidthPx); 228 return getSquareTileSize(columnCount, windowWidthPx, 229 res.getDimensionPixelSize(R.dimen.grid_item_individual_padding_horizontal), 230 res.getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space)); 231 } 232 233 /** 234 * Returns the size of a featured individual grid tile for the given activity in px. 235 */ getFeaturedIndividualTileSize(@onNull Activity activity)236 public static Point getFeaturedIndividualTileSize(@NonNull Activity activity) { 237 Resources res = activity.getResources(); 238 int windowWidthPx = getActivityWindowWidthPx(activity); 239 240 int columnCount = getNumFeaturedIndividualColumns(activity, windowWidthPx); 241 return getSquareTileSize(columnCount, windowWidthPx, 242 res.getDimensionPixelSize(R.dimen.grid_item_featured_individual_padding_horizontal), 243 res.getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)); 244 } 245 246 /** 247 * Returns a suggested thumbnail tile size for images that may be presented either as a 248 * category or individual tile on any-sized activity on the device. This size matches the 249 * individual tile size when an activity takes up the entire screen's width. 250 */ getSuggestedThumbnailSize(@onNull Context appContext)251 public static Point getSuggestedThumbnailSize(@NonNull Context appContext) { 252 // Category tiles are larger than individual tiles, so get the number of columns for 253 // categories and then calculate a tile size for when the app window takes up the entire 254 // display. 255 int windowWidthPx = getDeviceDisplayWidthPx(appContext); 256 int columnCount = getNumColumns( 257 appContext, windowWidthPx, INDIVIDUAL_FEWER_COLUMNS, INDIVIDUAL_MORE_COLUMNS); 258 return getTileSize(appContext, columnCount, windowWidthPx); 259 } 260 261 /** 262 * Returns the corner radius to use in a wallpaper preview view so that it's proportional 263 * to the screen's corner radius 264 */ getPreviewCornerRadius(@onNull Activity activity, int previewWidth)265 public static float getPreviewCornerRadius(@NonNull Activity activity, int previewWidth) { 266 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize( 267 activity.getWindowManager().getDefaultDisplay()); 268 269 return QuickStepContract.getWindowCornerRadius(activity) 270 / ((float) screenSize.x / previewWidth); 271 } 272 273 /** 274 * Returns the size of a grid tile with the given "fewer" count and "more" count, on the given 275 * display. The size is determined by these counts and by the aspect ratio of the display and is 276 * in units of px. 277 */ getTileSize(Context context, int columnCount, int windowWidthPx)278 private static Point getTileSize(Context context, int columnCount, int windowWidthPx) { 279 WindowManager windowManager = (WindowManager) 280 context.getSystemService(Context.WINDOW_SERVICE); 281 Display display = windowManager.getDefaultDisplay(); 282 Point screenSizePx = ScreenSizeCalculator.getInstance().getScreenSize(display); 283 284 Resources res = context.getResources(); 285 int gridPaddingPx = res.getDimensionPixelSize(R.dimen.grid_padding); 286 287 // Note: don't need to multiply by density because getting the dimension from resources 288 // already does that. 289 int guttersWidthPx = (columnCount + 1) * gridPaddingPx; 290 int availableWidthPx = windowWidthPx - guttersWidthPx; 291 292 int widthPx = Math.round((float) availableWidthPx / columnCount); 293 int heightPx = Math.round(((float) availableWidthPx / columnCount) 294 //* screenSizePx.y / screenSizePx.x); 295 * res.getDimensionPixelSize(R.dimen.grid_tile_aspect_height) 296 / res.getDimensionPixelSize(R.dimen.grid_tile_aspect_width)); 297 return new Point(widthPx, heightPx); 298 } 299 300 /** 301 * Returns the size of a grid tile with the given "fewer" count and "more" count, on the given 302 * display. The size is determined by these counts with the aspect ratio of 1:1 and is in units 303 * of px. 304 */ getSquareTileSize(int columnCount, int windowWidthPx, int gridPaddingPx, int gridEdgeSpacePx)305 private static Point getSquareTileSize(int columnCount, int windowWidthPx, int gridPaddingPx, 306 int gridEdgeSpacePx) { 307 int availableWidthPx = windowWidthPx 308 - gridPaddingPx * 2 * columnCount // Item's left and right padding * column count 309 - gridEdgeSpacePx * 2; // Grid view's left and right edge's space 310 int widthPx = Math.round((float) availableWidthPx / columnCount); 311 312 return new Point(widthPx, widthPx); 313 } 314 315 /** 316 * Returns the available width of the activity window in pixels. 317 */ getActivityWindowWidthPx(Activity activity)318 public static int getActivityWindowWidthPx(Activity activity) { 319 Display display = activity.getWindowManager().getDefaultDisplay(); 320 Point outPoint = new Point(); 321 display.getSize(outPoint); 322 323 return outPoint.x; 324 } 325 326 /** 327 * Returns the available width of the device's default display in pixels. 328 */ getDeviceDisplayWidthPx(Context appContext)329 private static int getDeviceDisplayWidthPx(Context appContext) { 330 WindowManager windowManager = 331 (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE); 332 Display defaultDisplay = windowManager.getDefaultDisplay(); 333 334 Point outPoint = new Point(); 335 defaultDisplay.getSize(outPoint); 336 337 return outPoint.x; 338 } 339 340 /** 341 * Adjusts the corner radius of the given view by doubling their current values 342 * 343 * @param view whose background is set to a GradientDrawable 344 */ adjustBackgroundCornerRadius(View view)345 public static void adjustBackgroundCornerRadius(View view) { 346 GradientDrawable background = (GradientDrawable) view.getBackground(); 347 // Using try/catch because currently GradientDrawable has a bug where when the radii array 348 // is null, instead of getCornerRadii returning null, it throws NPE. 349 try { 350 float[] radii = background.getCornerRadii(); 351 if (radii == null) { 352 return; 353 } 354 for (int i = 0; i < radii.length; i++) { 355 radii[i] *= 2f; 356 } 357 background = ((GradientDrawable) background.mutate()); 358 background.setCornerRadii(radii); 359 view.setBackground(background); 360 } catch (NullPointerException e) { 361 //Ignore in this case, since it means the radius was 0. 362 } 363 } 364 } 365