Merge "Implement Settings UI for Screen zoom preference"

This commit is contained in:
Alan Viverette
2015-12-10 19:20:59 +00:00
committed by Android (Google) Code Review
19 changed files with 812 additions and 137 deletions

View File

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

View File

@@ -14,11 +14,11 @@
limitations under the License. limitations under the License.
--> -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp" android:width="24dp"
android:height="24.0dp" android:height="24dp"
android:viewportWidth="24.0" android:viewportWidth="24"
android:viewportHeight="24.0"> android:viewportHeight="24">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="@android:color/white"
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: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> </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 --> <!-- Fingerprint -->
<item name="fingerprint_illustration_aspect_ratio" format="float" type="dimen">0.0</item> <item name="fingerprint_illustration_aspect_ratio" format="float" type="dimen">0.0</item>
<dimen name="fingerprint_decor_padding_top">24dp</dimen> <dimen name="fingerprint_decor_padding_top">24dp</dimen>
<!-- Display, Screen zoom -->
<dimen name="screen_zoom_preview_height">160dp</dimen>
</resources> </resources>

View File

@@ -252,4 +252,7 @@
<!-- Button bar padding for unmount button. --> <!-- Button bar padding for unmount button. -->
<dimen name="unmount_button_padding">8dp</dimen> <dimen name="unmount_button_padding">8dp</dimen>
<!-- Display, Screen zoom -->
<dimen name="screen_zoom_preview_height">240dp</dimen>
</resources> </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] --> <!-- 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> <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] --> <!-- Title of setting that controls screen zoom (e.g. how large interface elements appear). [CHAR LIMIT=40] -->
<string name="force_density_preference_title">Display scale</string> <string name="screen_zoom_title">Screen zoom</string>
<!-- Keywords for setting that controls display scale (e.g. density). [CHAR LIMIT=NONE] --> <!-- Keywords for setting that controls screen zoom (e.g. how large interface elements appear). [CHAR LIMIT=NONE] -->
<string name="force_density_keywords">display density zoom scale scaling</string> <string name="screen_zoom_keywords">display density screen 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> <string name="screen_zoom_summary">Choose how zoomed you want the screen using the slider below the preview image.</string>
<!-- Description for the device's default display scale. [CHAR LIMIT=24] -->
<string name="force_density_summary_normal">Normal</string> <!-- Title of the screen zoom preview activity. -->
<!-- Description for the display scale that makes interface elements large. [CHAR LIMIT=24] --> <string name="screen_zoom_preview_title">Preview</string>
<string name="force_density_summary_large">Large</string> <!-- Description for the button that makes interface elements smaller. [CHAR_LIMIT=NONE] -->
<!-- Description for the display scale that makes interface elements larger. [CHAR LIMIT=24] --> <string name="screen_zoom_make_smaller_desc">Make smaller</string>
<string name="force_density_summary_very_large">Larger</string> <!-- Description for the button that makes interface elements larger. [CHAR_LIMIT=NONE] -->
<!-- Description for the display scale that makes interface elements largest. [CHAR LIMIT=24] --> <string name="screen_zoom_make_larger_desc">Make larger</string>
<string name="force_density_summary_extremely_large">Largest</string>
<!-- Description for a custom display scale. This shows the requested display <!-- Description for the screen zoom level that makes interface elements small. [CHAR LIMIT=24] -->
density in raw pixels per inch rather than computing a scale amount. [CHAR LIMIT=24] --> <string name="screen_zoom_summary_small">Small</string>
<string name="force_density_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</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] --> <!-- Button to show all top-level settings items [CHAR LIMIT=20] -->
<string name="see_all">See all</string> <string name="see_all">See all</string>

View File

@@ -99,12 +99,10 @@
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 <com.android.settings.display.ScreenZoomPreference
android:key="display_density" android:key="screen_zoom"
android:title="@string/force_density_preference_title" android:title="@string/screen_zoom_title"
android:summary="%s" settings:keywords="@string/screen_zoom_keywords" />
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"

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_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;
@@ -214,9 +213,6 @@ 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) {

View File

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

View File

@@ -11,18 +11,17 @@
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * 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.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.RemoteException; 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.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.MathUtils; import android.util.MathUtils;
@@ -30,16 +29,13 @@ import android.view.Display;
import android.view.IWindowManager; import android.view.IWindowManager;
import android.view.WindowManagerGlobal; import android.view.WindowManagerGlobal;
import com.android.settings.R;
import java.util.Arrays; import java.util.Arrays;
/** /**
* Preference for changing the density of the display on which the preference * Utility methods for working with display density.
* is visible.
*/ */
public class DisplayDensityPreference extends ListPreference { class DisplayDensityUtils {
private static final String LOG_TAG = "DisplayDensityPreference"; private static final String LOG_TAG = "DisplayDensityUtils";
/** Minimum increment between density scales. */ /** Minimum increment between density scales. */
private static final float MIN_SCALE_INTERVAL = 0.09f; 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. */ /** Maximum density scale. The actual scale used depends on the device. */
private static final float MAX_SCALE = 1.50f; 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. */ /** 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 * Summaries for scales smaller than "normal" in order of smallest to
* largest. * largest.
*/ */
private static final int[] SMALLER_SUMMARIES = new int[] { private static final int[] SUMMARIES_SMALLER = new int[] {
R.string.force_density_summary_small R.string.screen_zoom_summary_small
}; };
/** /**
* Summaries for scales larger than "normal" in order of smallest to * Summaries for scales larger than "normal" in order of smallest to
* largest. * largest.
*/ */
private static final int[] LARGER_SUMMARIES = new int[] { private static final int[] SUMMARIES_LARGER = new int[] {
R.string.force_density_summary_large, R.string.screen_zoom_summary_large,
R.string.force_density_summary_very_large, R.string.screen_zoom_summary_very_large,
R.string.force_density_summary_extremely_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; private static final int MIN_DIMENSION_DP = 320;
/** The ID of the display affected by this preference. */ private final String[] mEntries;
private int mDisplayId = Display.DEFAULT_DISPLAY; private final int[] mValues;
public DisplayDensityPreference(Context context, AttributeSet attrs) { private final int mNormalDensity;
super(context, attrs); private final int mCurrentIndex;
if (!prepareList()) { public DisplayDensityUtils(Context context) {
setEnabled(false); final int normalDensity = DisplayDensityUtils.getNormalDisplayDensity(
} Display.DEFAULT_DISPLAY);
} if (normalDensity <= 0) {
mEntries = null;
private boolean prepareList() { mValues = null;
final int initialDensity = getInitialDisplayDensity(mDisplayId); mNormalDensity = 0;
if (initialDensity <= 0) { mCurrentIndex = -1;
return false; return;
} }
final Resources res = getContext().getResources(); final Resources res = context.getResources();
final DisplayMetrics metrics = res.getDisplayMetrics(); final DisplayMetrics metrics = res.getDisplayMetrics();
final int currentDensity = metrics.densityDpi; final int currentDensity = metrics.densityDpi;
int currentDensityIndex = -1; int currentDensityIndex = -1;
@@ -106,46 +102,46 @@ public class DisplayDensityPreference extends ListPreference {
// Compute number of "larger" and "smaller" scales for this display. // Compute number of "larger" and "smaller" scales for this display.
final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels); final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP; 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 float minScale = MIN_SCALE;
final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL, 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, 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]; String[] entries = new String[1 + numSmaller + numLarger];
CharSequence[] entries = new CharSequence[values.length]; int[] values = new int[entries.length];
int curIndex = 0; int curIndex = 0;
if (numSmaller > 0) { if (numSmaller > 0) {
final float interval = (1 - minScale) / numSmaller; final float interval = (1 - minScale) / numSmaller;
for (int i = numSmaller - 1; i >= 0; i--) { 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) { if (currentDensity == density) {
currentDensityIndex = curIndex; currentDensityIndex = curIndex;
} }
values[curIndex] = Integer.toString(density); entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
entries[curIndex] = res.getText(SMALLER_SUMMARIES[i]); values[curIndex] = density;
curIndex++; curIndex++;
} }
} }
if (currentDensity == initialDensity) { if (currentDensity == normalDensity) {
currentDensityIndex = curIndex; currentDensityIndex = curIndex;
} }
values[curIndex] = Integer.toString(DENSITY_VALUE_NORMAL); values[curIndex] = normalDensity;
entries[curIndex] = res.getText(DENSITY_SUMMARY_NORMAL); entries[curIndex] = res.getString(SUMMARY_NORMAL);
curIndex++; curIndex++;
if (numLarger > 0) { if (numLarger > 0) {
final float interval = (maxScale - 1) / numLarger; final float interval = (maxScale - 1) / numLarger;
for (int i = 0; i < numLarger; i++) { 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) { if (currentDensity == density) {
currentDensityIndex = curIndex; currentDensityIndex = curIndex;
} }
values[curIndex] = Integer.toString(density); values[curIndex] = density;
entries[curIndex] = res.getText(LARGER_SUMMARIES[i]); entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
curIndex++; curIndex++;
} }
} }
@@ -157,87 +153,84 @@ public class DisplayDensityPreference extends ListPreference {
// We don't understand the current density. Must have been set by // We don't understand the current density. Must have been set by
// someone else. Make room for another entry... // someone else. Make room for another entry...
values = Arrays.copyOf(values, values.length + 1); 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 = Arrays.copyOf(entries, values.length + 1);
entries[curIndex] = Integer.toString(currentDensity); entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
displayIndex = curIndex; displayIndex = curIndex;
} }
super.setEntryValues(values); mNormalDensity = normalDensity;
super.setEntries(entries); mCurrentIndex = displayIndex;
mEntries = entries;
setValueIndex(displayIndex); mValues = values;
return true;
} }
@Override public String[] getEntries() {
public boolean callChangeListener(Object newValue) { return mEntries;
final boolean allowed = super.callChangeListener(newValue);
if (allowed) {
final int density = Integer.parseInt((String) newValue);
setForcedDisplayDensity(mDisplayId, density);
}
return allowed;
} }
@Override public int[] getValues() {
public void setEntries(CharSequence[] entries) { return mValues;
throw new UnsupportedOperationException();
} }
@Override public int getCurrentIndex() {
public void setEntries(@ArrayRes int entriesResId) { return mCurrentIndex;
throw new UnsupportedOperationException();
} }
@Override public int getNormalDensity() {
public void setEntryValues(CharSequence[] entryValues) { return mNormalDensity;
throw new UnsupportedOperationException();
}
@Override
public void setEntryValues(@ArrayRes int entryValuesResId) {
throw new UnsupportedOperationException();
} }
/** /**
* Returns the initial density for the specified display. * Returns the normal (default) density for the specified display.
* *
* @param displayId the identifier of the 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 * 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 { try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
return wm.getInitialDisplayDensity(displayId); return wm.getInitialDisplayDensity(displayId);
} catch (RemoteException exc) { } catch (RemoteException exc) {
return -1; return -1;
} }
} }
/** /**
* Asynchronously applies display density changes to the specified display. * Asynchronously applies display density changes to the specified display.
* *
* @param displayId the identifier of the display to modify * @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() { AsyncTask.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (density <= 0) { wm.clearForcedDisplayDensity(displayId);
wm.clearForcedDisplayDensity(displayId); } catch (RemoteException exc) {
} else { Log.w(LOG_TAG, "Unable to clear forced display density setting");
wm.setForcedDisplayDensity(displayId, density); }
} }
});
}
/**
* 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) { } catch (RemoteException exc) {
Log.w(LOG_TAG, "Unable to save forced display density setting"); 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.applications.ManageDefaultApps;
import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.StorageSettings; import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.fuelgauge.BatterySaverSettings; import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary; import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings; import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
@@ -122,6 +123,7 @@ public final class Ranking {
// Display // Display
sRankMap.put(DisplaySettings.class.getName(), RANK_DISPLAY); sRankMap.put(DisplaySettings.class.getName(), RANK_DISPLAY);
sRankMap.put(ScreenZoomSettings.class.getName(), RANK_WIFI);
// Wallpapers // Wallpapers
sRankMap.put(WallpaperTypeSettings.class.getName(), RANK_WALLPAPER); 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.applications.ManageDefaultApps;
import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.StorageSettings; import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.fuelgauge.BatterySaverSettings; import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary; import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings; import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
@@ -131,6 +132,13 @@ public final class SearchIndexableResources {
HomeSettings.class.getName(), HomeSettings.class.getName(),
R.drawable.ic_settings_home)); 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(), sResMap.put(DisplaySettings.class.getName(),
new SearchIndexableResource( new SearchIndexableResource(
Ranking.getRankForClassName(DisplaySettings.class.getName()), Ranking.getRankForClassName(DisplaySettings.class.getName()),