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