1 /* 2 * Copyright (C) 2015 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.settingslib.display; 18 19 import com.android.settingslib.R; 20 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.hardware.display.DisplayManager; 24 import android.os.AsyncTask; 25 import android.os.RemoteException; 26 import android.os.UserHandle; 27 import android.util.DisplayMetrics; 28 import android.util.Log; 29 import android.util.MathUtils; 30 import android.view.Display; 31 import android.view.IWindowManager; 32 import android.view.WindowManagerGlobal; 33 34 import java.util.Arrays; 35 36 /** 37 * Utility methods for working with display density. 38 */ 39 public class DisplayDensityUtils { 40 private static final String LOG_TAG = "DisplayDensityUtils"; 41 42 /** Minimum increment between density scales. */ 43 private static final float MIN_SCALE_INTERVAL = 0.09f; 44 45 /** Minimum density scale. This is available on all devices. */ 46 private static final float MIN_SCALE = 0.85f; 47 48 /** Maximum density scale. The actual scale used depends on the device. */ 49 private static final float MAX_SCALE = 1.50f; 50 51 /** Summary used for "default" scale. */ 52 public static final int SUMMARY_DEFAULT = R.string.screen_zoom_summary_default; 53 54 /** Summary used for "custom" scale. */ 55 private static final int SUMMARY_CUSTOM = R.string.screen_zoom_summary_custom; 56 57 /** 58 * Summaries for scales smaller than "default" in order of smallest to 59 * largest. 60 */ 61 private static final int[] SUMMARIES_SMALLER = new int[] { 62 R.string.screen_zoom_summary_small 63 }; 64 65 /** 66 * Summaries for scales larger than "default" in order of smallest to 67 * largest. 68 */ 69 private static final int[] SUMMARIES_LARGER = new int[] { 70 R.string.screen_zoom_summary_large, 71 R.string.screen_zoom_summary_very_large, 72 R.string.screen_zoom_summary_extremely_large, 73 }; 74 75 /** 76 * Minimum allowed screen dimension, corresponds to resource qualifiers 77 * "small" or "sw320dp". This value must be at least the minimum screen 78 * size required by the CDD so that we meet developer expectations. 79 */ 80 private static final int MIN_DIMENSION_DP = 320; 81 82 private final String[] mEntries; 83 private final int[] mValues; 84 85 private final int mDefaultDensity; 86 private final int mCurrentIndex; 87 88 public DisplayDensityUtils(Context context) { 89 final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity( 90 Display.DEFAULT_DISPLAY); 91 if (defaultDensity <= 0) { 92 mEntries = null; 93 mValues = null; 94 mDefaultDensity = 0; 95 mCurrentIndex = -1; 96 return; 97 } 98 99 final Resources res = context.getResources(); 100 final DisplayMetrics metrics = new DisplayMetrics(); 101 context.getDisplay().getRealMetrics(metrics); 102 103 final int currentDensity = metrics.densityDpi; 104 int currentDensityIndex = -1; 105 106 // Compute number of "larger" and "smaller" scales for this display. 107 final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels); 108 final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP; 109 final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) defaultDensity); 110 final float minScale = MIN_SCALE; 111 final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL, 112 0, SUMMARIES_LARGER.length); 113 final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL, 114 0, SUMMARIES_SMALLER.length); 115 116 String[] entries = new String[1 + numSmaller + numLarger]; 117 int[] values = new int[entries.length]; 118 int curIndex = 0; 119 120 if (numSmaller > 0) { 121 final float interval = (1 - minScale) / numSmaller; 122 for (int i = numSmaller - 1; i >= 0; i--) { 123 // Round down to a multiple of 2 by truncating the low bit. 124 final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1; 125 if (currentDensity == density) { 126 currentDensityIndex = curIndex; 127 } 128 entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]); 129 values[curIndex] = density; 130 curIndex++; 131 } 132 } 133 134 if (currentDensity == defaultDensity) { 135 currentDensityIndex = curIndex; 136 } 137 values[curIndex] = defaultDensity; 138 entries[curIndex] = res.getString(SUMMARY_DEFAULT); 139 curIndex++; 140 141 if (numLarger > 0) { 142 final float interval = (maxScale - 1) / numLarger; 143 for (int i = 0; i < numLarger; i++) { 144 // Round down to a multiple of 2 by truncating the low bit. 145 final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1; 146 if (currentDensity == density) { 147 currentDensityIndex = curIndex; 148 } 149 values[curIndex] = density; 150 entries[curIndex] = res.getString(SUMMARIES_LARGER[i]); 151 curIndex++; 152 } 153 } 154 155 final int displayIndex; 156 if (currentDensityIndex >= 0) { 157 displayIndex = currentDensityIndex; 158 } else { 159 // We don't understand the current density. Must have been set by 160 // someone else. Make room for another entry... 161 int newLength = values.length + 1; 162 values = Arrays.copyOf(values, newLength); 163 values[curIndex] = currentDensity; 164 165 entries = Arrays.copyOf(entries, newLength); 166 entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity); 167 168 displayIndex = curIndex; 169 } 170 171 mDefaultDensity = defaultDensity; 172 mCurrentIndex = displayIndex; 173 mEntries = entries; 174 mValues = values; 175 } 176 177 public String[] getEntries() { 178 return mEntries; 179 } 180 181 public int[] getValues() { 182 return mValues; 183 } 184 185 public int getCurrentIndex() { 186 return mCurrentIndex; 187 } 188 189 public int getDefaultDensity() { 190 return mDefaultDensity; 191 } 192 193 /** 194 * Returns the default density for the specified display. 195 * 196 * @param displayId the identifier of the display 197 * @return the default density of the specified display, or {@code -1} if 198 * the display does not exist or the density could not be obtained 199 */ 200 private static int getDefaultDisplayDensity(int displayId) { 201 try { 202 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 203 return wm.getInitialDisplayDensity(displayId); 204 } catch (RemoteException exc) { 205 return -1; 206 } 207 } 208 209 /** 210 * Asynchronously applies display density changes to the specified display. 211 * <p> 212 * The change will be applied to the user specified by the value of 213 * {@link UserHandle#myUserId()} at the time the method is called. 214 * 215 * @param displayId the identifier of the display to modify 216 */ 217 public static void clearForcedDisplayDensity(final int displayId) { 218 final int userId = UserHandle.myUserId(); 219 AsyncTask.execute(() -> { 220 try { 221 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 222 wm.clearForcedDisplayDensityForUser(displayId, userId); 223 } catch (RemoteException exc) { 224 Log.w(LOG_TAG, "Unable to clear forced display density setting"); 225 } 226 }); 227 } 228 229 /** 230 * Asynchronously applies display density changes to the specified display. 231 * <p> 232 * The change will be applied to the user specified by the value of 233 * {@link UserHandle#myUserId()} at the time the method is called. 234 * 235 * @param displayId the identifier of the display to modify 236 * @param density the density to force for the specified display 237 */ 238 public static void setForcedDisplayDensity(final int displayId, final int density) { 239 final int userId = UserHandle.myUserId(); 240 AsyncTask.execute(() -> { 241 try { 242 final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 243 wm.setForcedDisplayDensityForUser(displayId, density, userId); 244 } catch (RemoteException exc) { 245 Log.w(LOG_TAG, "Unable to save forced display density setting"); 246 } 247 }); 248 } 249 } 250