Files
packages_apps_Settings/src/com/android/settings/display/DisplayDensityUtils.java
Alan Viverette 656143240c Round display scaling densities down to the nearest 2 DPI
This will prevent scaling artifacts due to odd densities. We will not
make any adjustments to the "normal" density, which will preserve
tvdpi or whatever the device manufacturer needed to use.

Rounding the density down is always safe, since it will not push us
below our 320dp lower bound on minimum effective screen dimension.

Bug: 27225670
Change-Id: Ib084bb3d4fc70a59106ac74df5394c21e9f8c7bd
2016-02-18 15:56:25 -05:00

243 lines
8.5 KiB
Java

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.display;
import com.android.settings.R;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import java.util.Arrays;
/**
* Utility methods for working with display density.
*/
class DisplayDensityUtils {
private static final String LOG_TAG = "DisplayDensityUtils";
/** Minimum increment between density scales. */
private static final float MIN_SCALE_INTERVAL = 0.09f;
/** Minimum density scale. This is available on all devices. */
private static final float MIN_SCALE = 0.85f;
/** Maximum density scale. The actual scale used depends on the device. */
private static final float MAX_SCALE = 1.50f;
/** Summary used for "normal" scale. */
private static final int SUMMARY_NORMAL = R.string.screen_zoom_summary_normal;
/** Summary used for "custom" scale. */
private static final int SUMMARY_CUSTOM = R.string.screen_zoom_summary_custom;
/**
* Summaries for scales smaller than "normal" in order of smallest to
* largest.
*/
private static final int[] SUMMARIES_SMALLER = new int[] {
R.string.screen_zoom_summary_small
};
/**
* Summaries for scales larger than "normal" in order of smallest to
* largest.
*/
private static final int[] SUMMARIES_LARGER = new int[] {
R.string.screen_zoom_summary_large,
R.string.screen_zoom_summary_very_large,
R.string.screen_zoom_summary_extremely_large,
};
/**
* Minimum allowed screen dimension, corresponds to resource qualifiers
* "small" or "sw320dp". This value must be at least the minimum screen
* size required by the CDD so that we meet developer expectations.
*/
private static final int MIN_DIMENSION_DP = 320;
private final String[] mEntries;
private final int[] mValues;
private final int mNormalDensity;
private final int mCurrentIndex;
public DisplayDensityUtils(Context context) {
final int normalDensity = DisplayDensityUtils.getNormalDisplayDensity(
Display.DEFAULT_DISPLAY);
if (normalDensity <= 0) {
mEntries = null;
mValues = null;
mNormalDensity = 0;
mCurrentIndex = -1;
return;
}
final Resources res = context.getResources();
final DisplayMetrics metrics = res.getDisplayMetrics();
final int currentDensity = metrics.densityDpi;
int currentDensityIndex = -1;
// Compute number of "larger" and "smaller" scales for this display.
final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) normalDensity);
final float minScale = MIN_SCALE;
final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
0, SUMMARIES_LARGER.length);
final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
0, SUMMARIES_SMALLER.length);
String[] entries = new String[1 + numSmaller + numLarger];
int[] values = new int[entries.length];
int curIndex = 0;
if (numSmaller > 0) {
final float interval = (1 - minScale) / numSmaller;
for (int i = numSmaller - 1; i >= 0; i--) {
// Round down to a multiple of 2 by truncating the low bit.
final int density = ((int) (normalDensity * (1 - (i + 1) * interval))) & ~1;
if (currentDensity == density) {
currentDensityIndex = curIndex;
}
entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
values[curIndex] = density;
curIndex++;
}
}
if (currentDensity == normalDensity) {
currentDensityIndex = curIndex;
}
values[curIndex] = normalDensity;
entries[curIndex] = res.getString(SUMMARY_NORMAL);
curIndex++;
if (numLarger > 0) {
final float interval = (maxScale - 1) / numLarger;
for (int i = 0; i < numLarger; i++) {
// Round down to a multiple of 2 by truncating the low bit.
final int density = ((int) (normalDensity * (1 + (i + 1) * interval))) & ~1;
if (currentDensity == density) {
currentDensityIndex = curIndex;
}
values[curIndex] = density;
entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
curIndex++;
}
}
final int displayIndex;
if (currentDensityIndex >= 0) {
displayIndex = currentDensityIndex;
} else {
// We don't understand the current density. Must have been set by
// someone else. Make room for another entry...
values = Arrays.copyOf(values, values.length + 1);
values[curIndex] = currentDensity;
entries = Arrays.copyOf(entries, values.length + 1);
entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
displayIndex = curIndex;
}
mNormalDensity = normalDensity;
mCurrentIndex = displayIndex;
mEntries = entries;
mValues = values;
}
public String[] getEntries() {
return mEntries;
}
public int[] getValues() {
return mValues;
}
public int getCurrentIndex() {
return mCurrentIndex;
}
public int getNormalDensity() {
return mNormalDensity;
}
/**
* Returns the normal (default) density for the specified display.
*
* @param displayId the identifier of the display
* @return the normal density of the specified display, or {@code -1} if
* the display does not exist or the density could not be obtained
*/
private static int getNormalDisplayDensity(int displayId) {
try {
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
return wm.getInitialDisplayDensity(displayId);
} catch (RemoteException exc) {
return -1;
}
}
/**
* Asynchronously applies display density changes to the specified display.
*
* @param displayId the identifier of the display to modify
*/
public static void clearForcedDisplayDensity(final int displayId) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
wm.clearForcedDisplayDensity(displayId);
} catch (RemoteException exc) {
Log.w(LOG_TAG, "Unable to clear forced display density setting");
}
}
});
}
/**
* Asynchronously applies display density changes to the specified display.
*
* @param displayId the identifier of the display to modify
* @param density the density to force for the specified display
*/
public static void setForcedDisplayDensity(final int displayId, final int density) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
wm.setForcedDisplayDensity(displayId, density);
} catch (RemoteException exc) {
Log.w(LOG_TAG, "Unable to save forced display density setting");
}
}
});
}
}