Add display preference for scaling / UI Zoom feature

Bug: 22450672
Change-Id: If665576869743fd7d5fc23103260eecaa366e7aa
This commit is contained in:
Alan Viverette
2015-11-03 13:18:44 -05:00
parent 514b81d233
commit 74dbcb091b
4 changed files with 276 additions and 0 deletions

View File

@@ -6978,4 +6978,22 @@
<!-- Description of feature to change color setting for the display [CHAR LIMIT=NONE] --> <!-- Description of feature to change color setting for the display [CHAR LIMIT=NONE] -->
<string name="picture_color_mode_desc">Use sRGB</string> <string name="picture_color_mode_desc">Use sRGB</string>
<!-- Title of setting that controls display scale (e.g. density). [CHAR LIMIT=40] -->
<string name="force_density_preference_title">Display scale</string>
<!-- Keywords for setting that controls display scale (e.g. density). [CHAR LIMIT=NONE] -->
<string name="force_density_keywords">display density zoom scale scaling</string>
<!-- Description for the display scale that makes interface elements small. [CHAR LIMIT=24] -->
<string name="force_density_summary_small">Small</string>
<!-- Description for the device's default display scale. [CHAR LIMIT=24] -->
<string name="force_density_summary_normal">Normal</string>
<!-- Description for the display scale that makes interface elements large. [CHAR LIMIT=24] -->
<string name="force_density_summary_large">Large</string>
<!-- Description for the display scale that makes interface elements larger. [CHAR LIMIT=24] -->
<string name="force_density_summary_very_large">Larger</string>
<!-- Description for the display scale that makes interface elements largest. [CHAR LIMIT=24] -->
<string name="force_density_summary_extremely_large">Largest</string>
<!-- Description for a custom display scale. This shows the requested display
density in raw pixels per inch rather than computing a scale amount. [CHAR LIMIT=24] -->
<string name="force_density_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
</resources> </resources>

View File

@@ -99,6 +99,13 @@
android:entries="@array/entries_font_size" android:entries="@array/entries_font_size"
android:entryValues="@array/entryvalues_font_size" /> android:entryValues="@array/entryvalues_font_size" />
<com.android.settings.DisplayDensityPreference
android:key="display_density"
android:title="@string/force_density_preference_title"
android:summary="%s"
settings:keywords="@string/force_density_keywords"
android:persistent="false" />
<com.android.settings.DropDownPreference <com.android.settings.DropDownPreference
android:key="auto_rotate" android:key="auto_rotate"
android:title="@string/display_auto_rotate_title" /> android:title="@string/display_auto_rotate_title" />

View File

@@ -0,0 +1,247 @@
/*
* 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;
import android.content.Context;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.support.annotation.ArrayRes;
import android.support.v7.preference.ListPreference;
import android.util.AttributeSet;
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 com.android.settings.R;
import java.util.Arrays;
/**
* Preference for changing the density of the display on which the preference
* is visible.
*/
public class DisplayDensityPreference extends ListPreference {
private static final String LOG_TAG = "DisplayDensityPreference";
/** 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;
/** Sentinel value for "normal" scaling (effectively disabled). */
private static final int DENSITY_VALUE_NORMAL = -1;
/** Summary used for "normal" scale. */
private static final int DENSITY_SUMMARY_NORMAL = R.string.force_density_summary_normal;
/**
* Summaries for scales smaller than "normal" in order of smallest to
* largest.
*/
private static final int[] SMALLER_SUMMARIES = new int[] {
R.string.force_density_summary_small
};
/**
* Summaries for scales larger than "normal" in order of smallest to
* largest.
*/
private static final int[] LARGER_SUMMARIES = new int[] {
R.string.force_density_summary_large,
R.string.force_density_summary_very_large,
R.string.force_density_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;
/** The ID of the display affected by this preference. */
private int mDisplayId = Display.DEFAULT_DISPLAY;
public DisplayDensityPreference(Context context, AttributeSet attrs) {
super(context, attrs);
if (!prepareList()) {
setEnabled(false);
}
}
private boolean prepareList() {
final int initialDensity = getInitialDisplayDensity(mDisplayId);
if (initialDensity <= 0) {
return false;
}
final Resources res = getContext().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) initialDensity);
final float minScale = MIN_SCALE;
final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
0, LARGER_SUMMARIES.length);
final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
0, SMALLER_SUMMARIES.length);
CharSequence[] values = new CharSequence[1 + numSmaller + numLarger];
CharSequence[] entries = new CharSequence[values.length];
int curIndex = 0;
if (numSmaller > 0) {
final float interval = (1 - minScale) / numSmaller;
for (int i = numSmaller - 1; i >= 0; i--) {
final int density = (int) (initialDensity * (1 - (i + 1) * interval));
if (currentDensity == density) {
currentDensityIndex = curIndex;
}
values[curIndex] = Integer.toString(density);
entries[curIndex] = res.getText(SMALLER_SUMMARIES[i]);
curIndex++;
}
}
if (currentDensity == initialDensity) {
currentDensityIndex = curIndex;
}
values[curIndex] = Integer.toString(DENSITY_VALUE_NORMAL);
entries[curIndex] = res.getText(DENSITY_SUMMARY_NORMAL);
curIndex++;
if (numLarger > 0) {
final float interval = (maxScale - 1) / numLarger;
for (int i = 0; i < numLarger; i++) {
final int density = (int) (initialDensity * (1 + (i + 1) * interval));
if (currentDensity == density) {
currentDensityIndex = curIndex;
}
values[curIndex] = Integer.toString(density);
entries[curIndex] = res.getText(LARGER_SUMMARIES[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] = res.getString(R.string.force_density_summary_custom, currentDensity);
entries = Arrays.copyOf(entries, values.length + 1);
entries[curIndex] = Integer.toString(currentDensity);
displayIndex = curIndex;
}
super.setEntryValues(values);
super.setEntries(entries);
setValueIndex(displayIndex);
return true;
}
@Override
public boolean callChangeListener(Object newValue) {
final boolean allowed = super.callChangeListener(newValue);
if (allowed) {
final int density = Integer.parseInt((String) newValue);
setForcedDisplayDensity(mDisplayId, density);
}
return allowed;
}
@Override
public void setEntries(CharSequence[] entries) {
throw new UnsupportedOperationException();
}
@Override
public void setEntries(@ArrayRes int entriesResId) {
throw new UnsupportedOperationException();
}
@Override
public void setEntryValues(CharSequence[] entryValues) {
throw new UnsupportedOperationException();
}
@Override
public void setEntryValues(@ArrayRes int entryValuesResId) {
throw new UnsupportedOperationException();
}
/**
* Returns the initial density for the specified display.
*
* @param displayId the identifier of the display
* @return the initial density of the specified display, or {@code -1} if
* the display does not exist or the density could not be obtained
*/
private static int getInitialDisplayDensity(int displayId) {
try {
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
* @param density the density to force for the specified display, or <= 0
* to clear any previously forced density
*/
private static void setForcedDisplayDensity(final int displayId, final int density) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (density <= 0) {
wm.clearForcedDisplayDensity(displayId);
} else {
wm.setForcedDisplayDensity(displayId, density);
}
} catch (RemoteException exc) {
Log.w(LOG_TAG, "Unable to save forced display density setting");
}
}
});
}
}

View File

@@ -76,6 +76,7 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
private static final String KEY_CAMERA_GESTURE = "camera_gesture"; private static final String KEY_CAMERA_GESTURE = "camera_gesture";
private static final String KEY_CAMERA_DOUBLE_TAP_POWER_GESTURE private static final String KEY_CAMERA_DOUBLE_TAP_POWER_GESTURE
= "camera_double_tap_power_gesture"; = "camera_double_tap_power_gesture";
private static final String KEY_DISPLAY_DENSITY = "display_density";
private DropDownPreference mFontSizePref; private DropDownPreference mFontSizePref;
@@ -213,6 +214,9 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
mNightModePreference.setValue(String.valueOf(currentNightMode)); mNightModePreference.setValue(String.valueOf(currentNightMode));
mNightModePreference.setOnPreferenceChangeListener(this); mNightModePreference.setOnPreferenceChangeListener(this);
} }
final Preference displayDensity = findPreference(KEY_DISPLAY_DENSITY);
displayDensity.setOnPreferenceChangeListener(this);
} }
private static boolean allowAllRotations(Context context) { private static boolean allowAllRotations(Context context) {