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 */ getInstance(Context context)60 static public Settings getInstance(Context context) { 61 if (sInstance == null) { 62 sInstance = new Settings(context); 63 } 64 return sInstance; 65 } 66 Settings(Context context)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) preferStaticShadows()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) isOutlineClippingDisabled()87 public boolean isOutlineClippingDisabled() { 88 return mOutlineClippingDisabled; 89 } 90 91 /** 92 * Returns the boolean preference for the given key. 93 */ getBoolean(String key)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 */ setBoolean(String key, boolean value)102 public void setBoolean(String key, boolean value) { 103 getOrSetBoolean(key, true, value); 104 } 105 getOrSetBoolean(String key, boolean set, boolean value)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 generateSetting(Customizations customizations)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 Customizations(Resources resources, String packageName)144 public Customizations(Resources resources, String packageName) { 145 mResources = resources; 146 mPackageName = packageName; 147 } 148 getBoolean(String resourceName, boolean defaultValue)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 getCustomizations(Context context)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 isSystemApp(ResolveInfo info)180 private static boolean isSystemApp(ResolveInfo info) { 181 return (info.activityInfo != null 182 && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); 183 } 184 } 185