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 getNumFeaturedCategoryColumns(Activity activity, int windowWidthPx)127 private static int getNumFeaturedCategoryColumns(Activity activity, int windowWidthPx) { 128 return getNumColumns(activity, windowWidthPx, FEATURED_CATEGORY_FEWER_COLUMNS, 129 FEATURED_CATEGORY_MORE_COLUMNS); 130 } 131 getNumIndividualColumns(Activity activity, int windowWidthPx)132 private static int getNumIndividualColumns(Activity activity, int windowWidthPx) { 133 return getNumColumns( 134 activity, windowWidthPx, INDIVIDUAL_FEWER_COLUMNS, INDIVIDUAL_MORE_COLUMNS); 135 } 136 getNumFeaturedIndividualColumns(Activity activity, int windowWidthPx)137 private static int getNumFeaturedIndividualColumns(Activity activity, int windowWidthPx) { 138 return getNumColumns(activity, windowWidthPx, FEATURED_INDIVIDUAL_FEWER_COLUMNS, 139 FEATURED_INDIVIDUAL_MORE_COLUMNS); 140 } 141 getNumColumns( Context context, int windowWidthPx, int fewerCount, int moreCount)142 private static int getNumColumns( 143 Context context, int windowWidthPx, int fewerCount, int moreCount) { 144 WindowManager windowManager = (WindowManager) 145 context.getSystemService(Context.WINDOW_SERVICE); 146 Display display = windowManager.getDefaultDisplay(); 147 148 DisplayMetrics metrics = DisplayMetricsRetriever.getInstance() 149 .getDisplayMetrics(context.getResources(), display); 150 151 // Columns should be based on the size of the window, not the size of the display. 152 int windowWidthDp = (int) (windowWidthPx / metrics.density); 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 Resources res = context.getResources(); 251 int gridPaddingPx = res.getDimensionPixelSize(R.dimen.grid_padding); 252 253 // Note: don't need to multiply by density because getting the dimension from resources 254 // already does that. 255 int guttersWidthPx = (columnCount + 1) * gridPaddingPx; 256 int availableWidthPx = windowWidthPx - guttersWidthPx; 257 258 int widthPx = Math.round((float) availableWidthPx / columnCount); 259 int heightPx = Math.round(((float) availableWidthPx / columnCount) 260 //* screenSizePx.y / screenSizePx.x); 261 * res.getDimensionPixelSize(R.dimen.grid_tile_aspect_height) 262 / res.getDimensionPixelSize(R.dimen.grid_tile_aspect_width)); 263 return new Point(widthPx, heightPx); 264 } 265 266 /** 267 * Returns the size of a grid tile with the given "fewer" count and "more" count, on the given 268 * display. The size is determined by these counts with the aspect ratio of 1:1 and is in units 269 * of px. 270 */ getSquareTileSize(int columnCount, int windowWidthPx, int gridPaddingPx, int gridEdgeSpacePx)271 private static Point getSquareTileSize(int columnCount, int windowWidthPx, int gridPaddingPx, 272 int gridEdgeSpacePx) { 273 int availableWidthPx = windowWidthPx 274 - gridPaddingPx * 2 * columnCount // Item's left and right padding * column count 275 - gridEdgeSpacePx * 2; // Grid view's left and right edge's space 276 int widthPx = Math.round((float) availableWidthPx / columnCount); 277 278 return new Point(widthPx, widthPx); 279 } 280 281 /** 282 * Returns the available width of the activity window in pixels. 283 */ getActivityWindowWidthPx(Activity activity)284 private static int getActivityWindowWidthPx(Activity activity) { 285 Display display = activity.getWindowManager().getDefaultDisplay(); 286 287 Point outPoint = new Point(); 288 display.getSize(outPoint); 289 290 return outPoint.x; 291 } 292 293 /** 294 * Returns the available width of the device's default display in pixels. 295 */ getDeviceDisplayWidthPx(Context appContext)296 private static int getDeviceDisplayWidthPx(Context appContext) { 297 WindowManager windowManager = 298 (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE); 299 Display defaultDisplay = windowManager.getDefaultDisplay(); 300 301 Point outPoint = new Point(); 302 defaultDisplay.getSize(outPoint); 303 304 return outPoint.x; 305 } 306 307 /** 308 * Adjusts the corner radius of the given view by doubling their current values 309 * 310 * @param view whose background is set to a GradientDrawable 311 */ adjustBackgroundCornerRadius(View view)312 public static void adjustBackgroundCornerRadius(View view) { 313 GradientDrawable background = (GradientDrawable) view.getBackground(); 314 // Using try/catch because currently GradientDrawable has a bug where when the radii array 315 // is null, instead of getCornerRadii returning null, it throws NPE. 316 try { 317 float[] radii = background.getCornerRadii(); 318 if (radii == null) { 319 return; 320 } 321 for (int i = 0; i < radii.length; i++) { 322 radii[i] *= 2f; 323 } 324 background = ((GradientDrawable) background.mutate()); 325 background.setCornerRadii(radii); 326 view.setBackground(background); 327 } catch (NullPointerException e) { 328 //Ignore in this case, since it means the radius was 0. 329 } 330 } 331 } 332