Implement Settings UI for Screen zoom preference

Also cleans up unnecessary 0s and moves tint to the proper place in
relevant vector drawable XML files.

Preview layout is still WIP pending UX input, but I wanted to get this
CL in place so that we can test/demo the feature ASAP.

Bug: 22450672
Change-Id: Ic3f1500006c763df99fba2d1d16782b89d6fcae1
This commit is contained in:
Alan Viverette
2015-12-10 14:07:51 -05:00
parent 1fa5385166
commit 01a04f132b
19 changed files with 812 additions and 137 deletions

View File

@@ -14,11 +14,12 @@
~ limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?android:attr/colorAccent">
<path
android:fillColor="?android:attr/colorAccent"
android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
android:fillColor="@android:color/white"
android:pathData="M38,26L26,26l0,12l-4,0L22,26L10,26l0,-4l12,0L22,10l4,0l0,12l12,0l0,4.0z"/>
</vector>

View File

@@ -14,11 +14,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M11.0,17.0l2.0,0.0l0.0,-6.0l-2.0,0.0l0.0,6.0zm1.0,-15.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zm0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0zM11.0,9.0l2.0,0.0L13.0,7.0l-2.0,0.0l0.0,2.0z"/>
android:fillColor="@android:color/white"
android:pathData="M11,17l2,0l0,-6l-2,0l0,6.0zm1,-15.0C6.48,2 2,6.48 2,12.0s4.48,10 10,10 10,-4.48 10,-10.0S17.52,2 12,2.0zm0,18.0c-4.41,0 -8,-3.59 -8,-8.0s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8.0zM11,9l2,0L13,7l-2,0l0,2.0z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
~ 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?android:attr/colorAccent">
<path
android:fillColor="@android:color/white"
android:pathData="M38 26H10v-4h28v4z"/>
</vector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size android:width="24dp"
android:height="24dp" />
<solid android:color="@android:color/white" />
</shape>

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingLeft="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screen_zoom_summary"
android:layout_marginBottom="16dp"
android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/screen_zoom_preview_height"
android:background="?android:attr/colorBackgroundFloating"
android:elevation="2dp">
<com.android.settings.display.TouchBlockingFrameLayout
android:id="@+id/preview_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/current_density"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:padding="8dp"
android:theme="@android:style/Theme.Material"
android:background="?android:attr/colorBackgroundFloating"
android:textAppearance="@android:style/TextAppearance.Material.Caption"
android:textAllCaps="true"
android:elevation="2dp" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp">
<ImageView
android:id="@+id/smaller"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_remove_24dp"
android:tint="?android:attr/colorControlNormal"
android:tintMode="src_in"
android:scaleType="center"
android:focusable="true"
android:contentDescription="@string/screen_zoom_make_smaller_desc" />
<SeekBar
android:id="@+id/seek_bar"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1" />
<ImageView
android:id="@+id/larger"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_add_24dp"
android:tint="?android:attr/colorControlNormal"
android:tintMode="src_in"
android:scaleType="center"
android:focusable="true"
android:contentDescription="@string/screen_zoom_make_larger_desc" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:orientation="horizontal"
android:theme="?android:attr/actionBarTheme"
style="?android:attr/actionBarStyle">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="16dp"
android:text="@string/screen_zoom_preview_title"
android:textAppearance="@android:style/TextAppearance.Material.Widget.ActionBar.Title" />
<ImageView
android:layout_width="36dp"
android:layout_height="48dp"
style="?android:attr/actionOverflowButtonStyle" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:background="?android:attr/colorBackgroundFloating">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="@android:style/TextAppearance.Material.Subhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:text="@string/permissions_label" />
<include layout="@layout/screen_zoom_preview_item" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="62dp"
android:layout_marginRight="8dp"
android:background="#36000000" />
<include layout="@layout/screen_zoom_preview_item" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="62dp"
android:layout_marginRight="8dp"
android:background="#36000000" />
<include layout="@layout/screen_zoom_preview_item" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="16dp"
tools:showIn="@layout/screen_zoom_preview">
<ImageView
android:id="@+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/screen_zoom_preview_action_background"
android:backgroundTint="?android:attr/colorAccent"
android:src="@drawable/ic_settings_32dp"
android:scaleType="center" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="16dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/usage_access"
style="@android:style/TextAppearance.Material.Body1" />
<TextView
android:id="@+id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/usage_access_description"
style="@android:style/TextAppearance.Material.Caption" />
</LinearLayout>
<ImageView
android:id="@+id/action"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginLeft="24dp"
android:src="@drawable/ic_info"
android:tint="?android:attr/colorControlNormal"
android:tintMode="src_in"
android:scaleType="center" />
</LinearLayout>

View File

@@ -48,4 +48,7 @@
<!-- Fingerprint -->
<item name="fingerprint_illustration_aspect_ratio" format="float" type="dimen">0.0</item>
<dimen name="fingerprint_decor_padding_top">24dp</dimen>
<!-- Display, Screen zoom -->
<dimen name="screen_zoom_preview_height">160dp</dimen>
</resources>

View File

@@ -252,4 +252,7 @@
<!-- Button bar padding for unmount button. -->
<dimen name="unmount_button_padding">8dp</dimen>
<!-- Display, Screen zoom -->
<dimen name="screen_zoom_preview_height">240dp</dimen>
</resources>

View File

@@ -6613,23 +6613,33 @@
<!-- Description of setting that controls gesture to open camera by double tapping the power button [CHAR LIMIT=NONE] -->
<string name="camera_double_tap_power_gesture_desc">Quickly open camera without unlocking your screen</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>
<!-- Title of setting that controls screen zoom (e.g. how large interface elements appear). [CHAR LIMIT=40] -->
<string name="screen_zoom_title">Screen zoom</string>
<!-- Keywords for setting that controls screen zoom (e.g. how large interface elements appear). [CHAR LIMIT=NONE] -->
<string name="screen_zoom_keywords">display density screen zoom scale scaling</string>
<string name="screen_zoom_summary">Choose how zoomed you want the screen using the slider below the preview image.</string>
<!-- Title of the screen zoom preview activity. -->
<string name="screen_zoom_preview_title">Preview</string>
<!-- Description for the button that makes interface elements smaller. [CHAR_LIMIT=NONE] -->
<string name="screen_zoom_make_smaller_desc">Make smaller</string>
<!-- Description for the button that makes interface elements larger. [CHAR_LIMIT=NONE] -->
<string name="screen_zoom_make_larger_desc">Make larger</string>
<!-- Description for the screen zoom level that makes interface elements small. [CHAR LIMIT=24] -->
<string name="screen_zoom_summary_small">Small</string>
<!-- Description for the device's default screen zoom level. [CHAR LIMIT=24] -->
<string name="screen_zoom_summary_normal">Normal</string>
<!-- Description for the screen zoom level that makes interface elements large. [CHAR LIMIT=24] -->
<string name="screen_zoom_summary_large">Large</string>
<!-- Description for the screen zoom level that makes interface elements larger. [CHAR LIMIT=24] -->
<string name="screen_zoom_summary_very_large">Larger</string>
<!-- Description for the screen zoom level that makes interface elements largest. [CHAR LIMIT=24] -->
<string name="screen_zoom_summary_extremely_large">Largest</string>
<!-- Description for a custom screen zoom level. This shows the requested display
density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
<string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
<!-- Button to show all top-level settings items [CHAR LIMIT=20] -->
<string name="see_all">See all</string>

View File

@@ -99,12 +99,10 @@
android:entries="@array/entries_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.display.ScreenZoomPreference
android:key="screen_zoom"
android:title="@string/screen_zoom_title"
settings:keywords="@string/screen_zoom_keywords" />
<com.android.settings.DropDownPreference
android:key="auto_rotate"

View File

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

View File

@@ -32,6 +32,7 @@ public abstract class InstrumentedFragment extends PreferenceFragment {
public static final int SOUND = UNDECLARED + 2;
public static final int CONFIGURE_NOTIFICATION = UNDECLARED + 3;
public static final int CONFIGURE_WIFI = UNDECLARED + 4;
public static final int DISPLAY_SCREEN_ZOOM = UNDECLARED + 5;
/**
* Declare the view of this category.

View File

@@ -11,18 +11,17 @@
* 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
* limitations under the License.
*/
package com.android.settings;
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.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;
@@ -30,16 +29,13 @@ 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.
* Utility methods for working with display density.
*/
public class DisplayDensityPreference extends ListPreference {
private static final String LOG_TAG = "DisplayDensityPreference";
class DisplayDensityUtils {
private static final String LOG_TAG = "DisplayDensityUtils";
/** Minimum increment between density scales. */
private static final float MIN_SCALE_INTERVAL = 0.09f;
@@ -50,28 +46,28 @@ public class DisplayDensityPreference extends ListPreference {
/** 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;
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[] SMALLER_SUMMARIES = new int[] {
R.string.force_density_summary_small
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[] LARGER_SUMMARIES = new int[] {
R.string.force_density_summary_large,
R.string.force_density_summary_very_large,
R.string.force_density_summary_extremely_large,
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,
};
/**
@@ -81,24 +77,24 @@ public class DisplayDensityPreference extends ListPreference {
*/
private static final int MIN_DIMENSION_DP = 320;
/** The ID of the display affected by this preference. */
private int mDisplayId = Display.DEFAULT_DISPLAY;
private final String[] mEntries;
private final int[] mValues;
public DisplayDensityPreference(Context context, AttributeSet attrs) {
super(context, attrs);
private final int mNormalDensity;
private final int mCurrentIndex;
if (!prepareList()) {
setEnabled(false);
}
public DisplayDensityUtils(Context context) {
final int normalDensity = DisplayDensityUtils.getNormalDisplayDensity(
Display.DEFAULT_DISPLAY);
if (normalDensity <= 0) {
mEntries = null;
mValues = null;
mNormalDensity = 0;
mCurrentIndex = -1;
return;
}
private boolean prepareList() {
final int initialDensity = getInitialDisplayDensity(mDisplayId);
if (initialDensity <= 0) {
return false;
}
final Resources res = getContext().getResources();
final Resources res = context.getResources();
final DisplayMetrics metrics = res.getDisplayMetrics();
final int currentDensity = metrics.densityDpi;
int currentDensityIndex = -1;
@@ -106,46 +102,46 @@ public class DisplayDensityPreference extends ListPreference {
// 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 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, LARGER_SUMMARIES.length);
0, SUMMARIES_LARGER.length);
final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
0, SMALLER_SUMMARIES.length);
0, SUMMARIES_SMALLER.length);
CharSequence[] values = new CharSequence[1 + numSmaller + numLarger];
CharSequence[] entries = new CharSequence[values.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--) {
final int density = (int) (initialDensity * (1 - (i + 1) * interval));
final int density = (int) (normalDensity * (1 - (i + 1) * interval));
if (currentDensity == density) {
currentDensityIndex = curIndex;
}
values[curIndex] = Integer.toString(density);
entries[curIndex] = res.getText(SMALLER_SUMMARIES[i]);
entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
values[curIndex] = density;
curIndex++;
}
}
if (currentDensity == initialDensity) {
if (currentDensity == normalDensity) {
currentDensityIndex = curIndex;
}
values[curIndex] = Integer.toString(DENSITY_VALUE_NORMAL);
entries[curIndex] = res.getText(DENSITY_SUMMARY_NORMAL);
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++) {
final int density = (int) (initialDensity * (1 + (i + 1) * interval));
final int density = (int) (normalDensity * (1 + (i + 1) * interval));
if (currentDensity == density) {
currentDensityIndex = curIndex;
}
values[curIndex] = Integer.toString(density);
entries[curIndex] = res.getText(LARGER_SUMMARIES[i]);
values[curIndex] = density;
entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
curIndex++;
}
}
@@ -157,63 +153,46 @@ public class DisplayDensityPreference extends ListPreference {
// 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);
values[curIndex] = currentDensity;
entries = Arrays.copyOf(entries, values.length + 1);
entries[curIndex] = Integer.toString(currentDensity);
entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
displayIndex = curIndex;
}
super.setEntryValues(values);
super.setEntries(entries);
setValueIndex(displayIndex);
return true;
mNormalDensity = normalDensity;
mCurrentIndex = displayIndex;
mEntries = entries;
mValues = values;
}
@Override
public boolean callChangeListener(Object newValue) {
final boolean allowed = super.callChangeListener(newValue);
if (allowed) {
final int density = Integer.parseInt((String) newValue);
setForcedDisplayDensity(mDisplayId, density);
public String[] getEntries() {
return mEntries;
}
return allowed;
public int[] getValues() {
return mValues;
}
@Override
public void setEntries(CharSequence[] entries) {
throw new UnsupportedOperationException();
public int getCurrentIndex() {
return mCurrentIndex;
}
@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();
public int getNormalDensity() {
return mNormalDensity;
}
/**
* Returns the initial density for the specified display.
* Returns the normal (default) density for the specified display.
*
* @param displayId the identifier of the display
* @return the initial density of the specified display, or {@code -1} if
* @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 getInitialDisplayDensity(int displayId) {
private static int getNormalDisplayDensity(int displayId) {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
return wm.getInitialDisplayDensity(displayId);
} catch (RemoteException exc) {
return -1;
@@ -224,20 +203,34 @@ public class DisplayDensityPreference extends ListPreference {
* 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) {
public static void clearForcedDisplayDensity(final int displayId) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (density <= 0) {
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
wm.clearForcedDisplayDensity(displayId);
} else {
wm.setForcedDisplayDensity(displayId, density);
} 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");
}

View File

@@ -0,0 +1,53 @@
/*
* 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 android.content.Context;
import android.content.Intent;
import android.support.v4.content.res.TypedArrayUtils;
import android.support.v7.preference.PreferenceGroup;
import android.util.AttributeSet;
/**
* Preference for changing the density of the display on which the preference
* is visible.
*/
public class ScreenZoomPreference extends PreferenceGroup {
public ScreenZoomPreference(Context context, AttributeSet attrs) {
super(context, attrs, TypedArrayUtils.getAttr(context,
android.support.v7.preference.R.attr.preferenceScreenStyle,
android.R.attr.preferenceScreenStyle));
setFragment("com.android.settings.display.ScreenZoomSettings");
final DisplayDensityUtils density = new DisplayDensityUtils(context);
final int defaultIndex = density.getCurrentIndex();
if (defaultIndex < 0) {
setVisible(false);
setEnabled(false);
} else {
final String[] entries = density.getEntries();
final int currentIndex = density.getCurrentIndex();
setSummary(entries[currentIndex]);
}
}
@Override
protected boolean isOnSameScreenAsChildren() {
return false;
}
}

View File

@@ -0,0 +1,272 @@
/*
* 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.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* Preference fragment used to control screen zoom.
*/
public class ScreenZoomSettings extends SettingsPreferenceFragment implements Indexable {
/** Duration to use when cross-fading between previews. */
private static final long CROSS_FADE_DURATION_MS = 400;
/** Interpolator to use when cross-fading between previews. */
private static final Interpolator FADE_IN_INTERPOLATOR = new DecelerateInterpolator();
/** Interpolator to use when cross-fading between previews. */
private static final Interpolator FADE_OUT_INTERPOLATOR = new AccelerateInterpolator();
private ViewGroup mPreviewFrame;
private TextView mLabel;
private View mLarger;
private View mSmaller;
private String[] mEntries;
private int[] mValues;
private int mNormalDensity;
private int mInitialIndex;
private int mCurrentIndex;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final DisplayDensityUtils density = new DisplayDensityUtils(getContext());
final int initialIndex = density.getCurrentIndex();
if (initialIndex < 0) {
// Failed to obtain normal density, which means we failed to
// connect to the window manager service. Just use the current
// density and don't let the user change anything.
final int densityDpi = getResources().getDisplayMetrics().densityDpi;
mValues = new int[] { densityDpi };
mEntries = new String[] { getString(R.string.screen_zoom_summary_normal) };
mInitialIndex = 0;
mNormalDensity = densityDpi;
} else {
mValues = density.getValues();
mEntries = density.getEntries();
mInitialIndex = initialIndex;
mNormalDensity = density.getNormalDensity();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View root = super.onCreateView(inflater, container, savedInstanceState);
final ViewGroup list_container = (ViewGroup) root.findViewById(android.R.id.list_container);
list_container.removeAllViews();
final View content = inflater.inflate(R.layout.screen_zoom_activity, list_container, false);
list_container.addView(content);
mLabel = (TextView) content.findViewById(R.id.current_density);
// The maximum SeekBar value always needs to be non-zero. If there's
// only one available zoom level, we'll handle this by disabling the
// seek bar.
final int max = Math.max(1, mValues.length - 1);
final SeekBar seekBar = (SeekBar) content.findViewById(R.id.seek_bar);
seekBar.setMax(max);
seekBar.setProgress(mInitialIndex);
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
setPreviewLayer(progress, true);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
mSmaller = content.findViewById(R.id.smaller);
mSmaller.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final int progress = seekBar.getProgress();
if (progress > 0) {
seekBar.setProgress(progress - 1, true);
}
}
});
mLarger = content.findViewById(R.id.larger);
mLarger.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final int progress = seekBar.getProgress();
if (progress < seekBar.getMax()) {
seekBar.setProgress(progress + 1, true);
}
}
});
if (mValues.length == 1) {
// The larger and smaller buttons will be disabled when we call
// setPreviewLayer() later in this method.
seekBar.setEnabled(false);
}
mPreviewFrame = (FrameLayout) content.findViewById(R.id.preview_frame);
// Populate the sample layouts.
final Context context = getContext();
final Configuration origConfig = context.getResources().getConfiguration();
for (int mValue : mValues) {
final Configuration config = new Configuration(origConfig);
config.densityDpi = mValue;
// Create a new configuration for the specified density. It won't
// have any theme set, so manually apply the current theme.
final Context configContext = context.createConfigurationContext(config);
configContext.setTheme(context.getThemeResId());
final LayoutInflater configInflater = LayoutInflater.from(configContext);
final View sampleView = configInflater.inflate(
R.layout.screen_zoom_preview, mPreviewFrame, false);
sampleView.setAlpha(0);
sampleView.setVisibility(View.INVISIBLE);
mPreviewFrame.addView(sampleView);
}
setPreviewLayer(mInitialIndex, false);
return root;
}
@Override
public void onDetach() {
super.onDetach();
// This will adjust the density SLIGHTLY after the activity has
// finished, which could be considered a feature or a bug...
commit();
}
private void setPreviewLayer(int index, boolean animate) {
mLabel.setText(mEntries[index]);
if (mCurrentIndex >= 0) {
final View lastLayer = mPreviewFrame.getChildAt(mCurrentIndex);
if (animate) {
lastLayer.animate()
.alpha(0)
.setInterpolator(FADE_OUT_INTERPOLATOR)
.setDuration(CROSS_FADE_DURATION_MS)
.withEndAction(new Runnable() {
@Override
public void run() {
lastLayer.setVisibility(View.INVISIBLE);
}
});
} else {
lastLayer.setAlpha(0);
lastLayer.setVisibility(View.INVISIBLE);
}
}
final View nextLayer = mPreviewFrame.getChildAt(index);
if (animate) {
nextLayer.animate()
.alpha(1)
.setInterpolator(FADE_IN_INTERPOLATOR)
.setDuration(CROSS_FADE_DURATION_MS)
.withStartAction(new Runnable() {
@Override
public void run() {
nextLayer.setVisibility(View.VISIBLE);
}
});
} else {
nextLayer.setVisibility(View.VISIBLE);
nextLayer.setAlpha(1);
}
mSmaller.setEnabled(index > 0);
mLarger.setEnabled(index < mEntries.length - 1);
mCurrentIndex = index;
}
/**
* Persists the selected density and sends a configuration change.
*/
private void commit() {
final int densityDpi = mValues[mCurrentIndex];
if (densityDpi == mNormalDensity) {
DisplayDensityUtils.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
} else {
DisplayDensityUtils.setForcedDisplayDensity(Display.DEFAULT_DISPLAY, densityDpi);
}
}
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.DISPLAY_SCREEN_ZOOM;
}
/** Index provider used to expose this fragment in search. */
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
final Resources res = context.getResources();
final SearchIndexableRaw data = new SearchIndexableRaw(context);
data.title = res.getString(R.string.screen_zoom_title);
data.screenTitle = res.getString(R.string.screen_zoom_title);
data.keywords = res.getString(R.string.screen_zoom_keywords);
final List<SearchIndexableRaw> result = new ArrayList<>(1);
result.add(data);
return result;
}
};
}

View File

@@ -0,0 +1,42 @@
/*
* 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 android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* Extension of FrameLayout that consumes all touch events.
*/
public class TouchBlockingFrameLayout extends FrameLayout {
public TouchBlockingFrameLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
}

View File

@@ -36,6 +36,7 @@ import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.ManageDefaultApps;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
@@ -122,6 +123,7 @@ public final class Ranking {
// Display
sRankMap.put(DisplaySettings.class.getName(), RANK_DISPLAY);
sRankMap.put(ScreenZoomSettings.class.getName(), RANK_WIFI);
// Wallpapers
sRankMap.put(WallpaperTypeSettings.class.getName(), RANK_WALLPAPER);

View File

@@ -38,6 +38,7 @@ import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.ManageDefaultApps;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
@@ -131,6 +132,13 @@ public final class SearchIndexableResources {
HomeSettings.class.getName(),
R.drawable.ic_settings_home));
sResMap.put(ScreenZoomSettings.class.getName(),
new SearchIndexableResource(
Ranking.getRankForClassName(ScreenZoomSettings.class.getName()),
NO_DATA_RES_ID,
ScreenZoomSettings.class.getName(),
R.drawable.ic_settings_display));
sResMap.put(DisplaySettings.class.getName(),
new SearchIndexableResource(
Ranking.getRankForClassName(DisplaySettings.class.getName()),