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