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