Home | History | Annotate | Download | only in system
      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 androidx.leanback.system;
     18 
     19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     20 
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.pm.ApplicationInfo;
     24 import android.content.pm.PackageManager;
     25 import android.content.pm.ResolveInfo;
     26 import android.content.res.Resources;
     27 import android.os.Build;
     28 import android.util.Log;
     29 
     30 import androidx.annotation.RestrictTo;
     31 import androidx.leanback.widget.ShadowOverlayContainer;
     32 
     33 /**
     34  * Provides various preferences affecting Leanback runtime behavior.
     35  * <p>Note this class is not thread safe and its methods should only
     36  * be invoked from the UI thread
     37  * </p>
     38  */
     39 public class Settings {
     40     static private final String TAG = "Settings";
     41     static private final boolean DEBUG = false;
     42 
     43     // The intent action that must be provided by a broadcast receiver
     44     // in a customization package.
     45     private static final String ACTION_PARTNER_CUSTOMIZATION =
     46             "android.support.v17.leanback.action.PARTNER_CUSTOMIZATION";
     47 
     48     public static final String PREFER_STATIC_SHADOWS = "PREFER_STATIC_SHADOWS";
     49 
     50     public static final String OUTLINE_CLIPPING_DISABLED = "OUTLINE_CLIPPING_DISABLED";
     51 
     52     static private Settings sInstance;
     53 
     54     private boolean mPreferStaticShadows;
     55     private boolean mOutlineClippingDisabled;
     56 
     57     /**
     58      * Returns the singleton Settings instance.
     59      */
     60     static public Settings getInstance(Context context) {
     61         if (sInstance == null) {
     62             sInstance = new Settings(context);
     63         }
     64         return sInstance;
     65     }
     66 
     67     private Settings(Context context) {
     68         if (DEBUG) Log.v(TAG, "generating preferences");
     69         Customizations customizations = getCustomizations(context);
     70         generateSetting(customizations);
     71     }
     72 
     73     /**
     74      * Returns true if static shadows are recommended.
     75      * @hide
     76      */
     77     @RestrictTo(LIBRARY_GROUP)
     78     public boolean preferStaticShadows() {
     79         return mPreferStaticShadows;
     80     }
     81 
     82     /**
     83      * Returns true if view outline is disabled on low power chipset.
     84      * @hide
     85      */
     86     @RestrictTo(LIBRARY_GROUP)
     87     public boolean isOutlineClippingDisabled() {
     88         return mOutlineClippingDisabled;
     89     }
     90 
     91     /**
     92      * Returns the boolean preference for the given key.
     93      */
     94     public boolean getBoolean(String key) {
     95         return getOrSetBoolean(key, false, false);
     96     }
     97 
     98     /**
     99      * Sets the boolean preference for the given key.  If an app uses this api to override
    100      * a default preference, it must do so on every activity create.
    101      */
    102     public void setBoolean(String key, boolean value) {
    103         getOrSetBoolean(key, true, value);
    104     }
    105 
    106     boolean getOrSetBoolean(String key, boolean set, boolean value) {
    107         if (key.compareTo(PREFER_STATIC_SHADOWS) == 0) {
    108             return set ? (mPreferStaticShadows = value) : mPreferStaticShadows;
    109         } else if (key.compareTo(OUTLINE_CLIPPING_DISABLED) == 0) {
    110             return set ? (mOutlineClippingDisabled = value) : mOutlineClippingDisabled;
    111         }
    112         throw new IllegalArgumentException("Invalid key");
    113     }
    114 
    115     private void generateSetting(Customizations customizations) {
    116         if (ShadowOverlayContainer.supportsDynamicShadow()) {
    117             mPreferStaticShadows = false;
    118             if (customizations != null) {
    119                 mPreferStaticShadows = customizations.getBoolean(
    120                         "leanback_prefer_static_shadows", mPreferStaticShadows);
    121             }
    122         } else {
    123             mPreferStaticShadows = true;
    124         }
    125 
    126         if (Build.VERSION.SDK_INT >= 21) {
    127             mOutlineClippingDisabled = false;
    128             if (customizations != null) {
    129                 mOutlineClippingDisabled = customizations.getBoolean(
    130                         "leanback_outline_clipping_disabled", mOutlineClippingDisabled);
    131             }
    132         } else {
    133             mOutlineClippingDisabled = true;
    134         }
    135         if (DEBUG) Log.v(TAG, "generated preference " + PREFER_STATIC_SHADOWS + ": "
    136                 + mPreferStaticShadows + " "
    137                 + OUTLINE_CLIPPING_DISABLED + " : " + mOutlineClippingDisabled);
    138     }
    139 
    140     static class Customizations {
    141         Resources mResources;
    142         String mPackageName;
    143 
    144         public Customizations(Resources resources, String packageName) {
    145             mResources = resources;
    146             mPackageName = packageName;
    147         }
    148 
    149         public boolean getBoolean(String resourceName, boolean defaultValue) {
    150             int resId = mResources.getIdentifier(resourceName, "bool", mPackageName);
    151             return resId > 0 ? mResources.getBoolean(resId) : defaultValue;
    152         }
    153     };
    154 
    155     private Customizations getCustomizations(Context context) {
    156         final PackageManager pm = context.getPackageManager();
    157         final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
    158         if (DEBUG) {
    159             Log.v(TAG, "getting oem customizations by intent: " + ACTION_PARTNER_CUSTOMIZATION);
    160         }
    161 
    162         Resources resources = null;
    163         String packageName = null;
    164         for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
    165             packageName = info.activityInfo.packageName;
    166             if (DEBUG) Log.v(TAG, "got package " + packageName);
    167             if (packageName != null && isSystemApp(info)) try {
    168                 resources = pm.getResourcesForApplication(packageName);
    169             } catch (PackageManager.NameNotFoundException ex) {
    170                 // Do nothing
    171             }
    172             if (resources != null) {
    173                 if (DEBUG) Log.v(TAG, "found customization package: " + packageName);
    174                 break;
    175             }
    176         }
    177         return resources == null ? null : new Customizations(resources, packageName);
    178     }
    179 
    180     private static boolean isSystemApp(ResolveInfo info) {
    181         return (info.activityInfo != null
    182                 && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
    183     }
    184 }
    185