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