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