1 /*
2  * Copyright (C) 2012 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.settings;
18 
19 import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
20 import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP;
21 import static android.provider.Settings.Secure.SCREENSAVER_ENABLED;
22 
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.pm.ServiceInfo;
30 import android.content.res.Resources;
31 import android.content.res.TypedArray;
32 import android.content.res.XmlResourceParser;
33 import android.graphics.drawable.Drawable;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import android.provider.Settings;
37 import android.service.dreams.DreamService;
38 import android.service.dreams.IDreamManager;
39 import android.util.AttributeSet;
40 import android.util.Log;
41 import android.util.Xml;
42 
43 import org.xmlpull.v1.XmlPullParser;
44 import org.xmlpull.v1.XmlPullParserException;
45 
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.Comparator;
50 import java.util.List;
51 
52 public class DreamBackend {
53     private static final String TAG = DreamSettings.class.getSimpleName() + ".Backend";
54 
55     public static class DreamInfo {
56         CharSequence caption;
57         Drawable icon;
58         boolean isActive;
59         public ComponentName componentName;
60         public ComponentName settingsComponentName;
61 
62         @Override
toString()63         public String toString() {
64             StringBuilder sb = new StringBuilder(DreamInfo.class.getSimpleName());
65             sb.append('[').append(caption);
66             if (isActive)
67                 sb.append(",active");
68             sb.append(',').append(componentName);
69             if (settingsComponentName != null)
70                 sb.append("settings=").append(settingsComponentName);
71             return sb.append(']').toString();
72         }
73     }
74 
75     private final Context mContext;
76     private final IDreamManager mDreamManager;
77     private final DreamInfoComparator mComparator;
78     private final boolean mDreamsEnabledByDefault;
79     private final boolean mDreamsActivatedOnSleepByDefault;
80     private final boolean mDreamsActivatedOnDockByDefault;
81 
DreamBackend(Context context)82     public DreamBackend(Context context) {
83         mContext = context;
84         mDreamManager = IDreamManager.Stub.asInterface(
85                 ServiceManager.getService(DreamService.DREAM_SERVICE));
86         mComparator = new DreamInfoComparator(getDefaultDream());
87         mDreamsEnabledByDefault = context.getResources()
88                 .getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault);
89         mDreamsActivatedOnSleepByDefault = context.getResources()
90                 .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
91         mDreamsActivatedOnDockByDefault = context.getResources()
92                 .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
93     }
94 
getDreamInfos()95     public List<DreamInfo> getDreamInfos() {
96         logd("getDreamInfos()");
97         ComponentName activeDream = getActiveDream();
98         PackageManager pm = mContext.getPackageManager();
99         Intent dreamIntent = new Intent(DreamService.SERVICE_INTERFACE);
100         List<ResolveInfo> resolveInfos = pm.queryIntentServices(dreamIntent,
101                 PackageManager.GET_META_DATA);
102         List<DreamInfo> dreamInfos = new ArrayList<DreamInfo>(resolveInfos.size());
103         for (ResolveInfo resolveInfo : resolveInfos) {
104             if (resolveInfo.serviceInfo == null)
105                 continue;
106             DreamInfo dreamInfo = new DreamInfo();
107             dreamInfo.caption = resolveInfo.loadLabel(pm);
108             dreamInfo.icon = resolveInfo.loadIcon(pm);
109             dreamInfo.componentName = getDreamComponentName(resolveInfo);
110             dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
111             dreamInfo.settingsComponentName = getSettingsComponentName(pm, resolveInfo);
112             dreamInfos.add(dreamInfo);
113         }
114         Collections.sort(dreamInfos, mComparator);
115         return dreamInfos;
116     }
117 
getDefaultDream()118     public ComponentName getDefaultDream() {
119         if (mDreamManager == null)
120             return null;
121         try {
122             return mDreamManager.getDefaultDreamComponent();
123         } catch (RemoteException e) {
124             Log.w(TAG, "Failed to get default dream", e);
125             return null;
126         }
127     }
128 
getActiveDreamName()129     public CharSequence getActiveDreamName() {
130         ComponentName cn = getActiveDream();
131         if (cn != null) {
132             PackageManager pm = mContext.getPackageManager();
133             try {
134                 ServiceInfo ri = pm.getServiceInfo(cn, 0);
135                 if (ri != null) {
136                     return ri.loadLabel(pm);
137                 }
138             } catch (PackageManager.NameNotFoundException exc) {
139                 return null; // uninstalled?
140             }
141         }
142         return null;
143     }
144 
isEnabled()145     public boolean isEnabled() {
146         return getBoolean(SCREENSAVER_ENABLED, mDreamsEnabledByDefault);
147     }
148 
setEnabled(boolean value)149     public void setEnabled(boolean value) {
150         logd("setEnabled(%s)", value);
151         setBoolean(SCREENSAVER_ENABLED, value);
152     }
153 
isActivatedOnDock()154     public boolean isActivatedOnDock() {
155         return getBoolean(SCREENSAVER_ACTIVATE_ON_DOCK, mDreamsActivatedOnDockByDefault);
156     }
157 
setActivatedOnDock(boolean value)158     public void setActivatedOnDock(boolean value) {
159         logd("setActivatedOnDock(%s)", value);
160         setBoolean(SCREENSAVER_ACTIVATE_ON_DOCK, value);
161     }
162 
isActivatedOnSleep()163     public boolean isActivatedOnSleep() {
164         return getBoolean(SCREENSAVER_ACTIVATE_ON_SLEEP, mDreamsActivatedOnSleepByDefault);
165     }
166 
setActivatedOnSleep(boolean value)167     public void setActivatedOnSleep(boolean value) {
168         logd("setActivatedOnSleep(%s)", value);
169         setBoolean(SCREENSAVER_ACTIVATE_ON_SLEEP, value);
170     }
171 
getBoolean(String key, boolean def)172     private boolean getBoolean(String key, boolean def) {
173         return Settings.Secure.getInt(mContext.getContentResolver(), key, def ? 1 : 0) == 1;
174     }
175 
setBoolean(String key, boolean value)176     private void setBoolean(String key, boolean value) {
177         Settings.Secure.putInt(mContext.getContentResolver(), key, value ? 1 : 0);
178     }
179 
setActiveDream(ComponentName dream)180     public void setActiveDream(ComponentName dream) {
181         logd("setActiveDream(%s)", dream);
182         if (mDreamManager == null)
183             return;
184         try {
185             ComponentName[] dreams = { dream };
186             mDreamManager.setDreamComponents(dream == null ? null : dreams);
187         } catch (RemoteException e) {
188             Log.w(TAG, "Failed to set active dream to " + dream, e);
189         }
190     }
191 
getActiveDream()192     public ComponentName getActiveDream() {
193         if (mDreamManager == null)
194             return null;
195         try {
196             ComponentName[] dreams = mDreamManager.getDreamComponents();
197             return dreams != null && dreams.length > 0 ? dreams[0] : null;
198         } catch (RemoteException e) {
199             Log.w(TAG, "Failed to get active dream", e);
200             return null;
201         }
202     }
203 
launchSettings(DreamInfo dreamInfo)204     public void launchSettings(DreamInfo dreamInfo) {
205         logd("launchSettings(%s)", dreamInfo);
206         if (dreamInfo == null || dreamInfo.settingsComponentName == null)
207             return;
208         mContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
209     }
210 
preview(DreamInfo dreamInfo)211     public void preview(DreamInfo dreamInfo) {
212         logd("preview(%s)", dreamInfo);
213         if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null)
214             return;
215         try {
216             mDreamManager.testDream(dreamInfo.componentName);
217         } catch (RemoteException e) {
218             Log.w(TAG, "Failed to preview " + dreamInfo, e);
219         }
220     }
221 
startDreaming()222     public void startDreaming() {
223         logd("startDreaming()");
224         if (mDreamManager == null)
225             return;
226         try {
227             mDreamManager.dream();
228         } catch (RemoteException e) {
229             Log.w(TAG, "Failed to dream", e);
230         }
231     }
232 
getDreamComponentName(ResolveInfo resolveInfo)233     private static ComponentName getDreamComponentName(ResolveInfo resolveInfo) {
234         if (resolveInfo == null || resolveInfo.serviceInfo == null)
235             return null;
236         return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
237     }
238 
getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo)239     private static ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
240         if (resolveInfo == null
241                 || resolveInfo.serviceInfo == null
242                 || resolveInfo.serviceInfo.metaData == null)
243             return null;
244         String cn = null;
245         XmlResourceParser parser = null;
246         Exception caughtException = null;
247         try {
248             parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA);
249             if (parser == null) {
250                 Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " meta-data");
251                 return null;
252             }
253             Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
254             AttributeSet attrs = Xml.asAttributeSet(parser);
255             int type;
256             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
257                     && type != XmlPullParser.START_TAG) {
258             }
259             String nodeName = parser.getName();
260             if (!"dream".equals(nodeName)) {
261                 Log.w(TAG, "Meta-data does not start with dream tag");
262                 return null;
263             }
264             TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
265             cn = sa.getString(com.android.internal.R.styleable.Dream_settingsActivity);
266             sa.recycle();
267         } catch (NameNotFoundException e) {
268             caughtException = e;
269         } catch (IOException e) {
270             caughtException = e;
271         } catch (XmlPullParserException e) {
272             caughtException = e;
273         } finally {
274             if (parser != null) parser.close();
275         }
276         if (caughtException != null) {
277             Log.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
278             return null;
279         }
280         if (cn != null && cn.indexOf('/') < 0) {
281             cn = resolveInfo.serviceInfo.packageName + "/" + cn;
282         }
283         return cn == null ? null : ComponentName.unflattenFromString(cn);
284     }
285 
logd(String msg, Object... args)286     private static void logd(String msg, Object... args) {
287         if (DreamSettings.DEBUG)
288             Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
289     }
290 
291     private static class DreamInfoComparator implements Comparator<DreamInfo> {
292         private final ComponentName mDefaultDream;
293 
DreamInfoComparator(ComponentName defaultDream)294         public DreamInfoComparator(ComponentName defaultDream) {
295             mDefaultDream = defaultDream;
296         }
297 
298         @Override
compare(DreamInfo lhs, DreamInfo rhs)299         public int compare(DreamInfo lhs, DreamInfo rhs) {
300             return sortKey(lhs).compareTo(sortKey(rhs));
301         }
302 
sortKey(DreamInfo di)303         private String sortKey(DreamInfo di) {
304             StringBuilder sb = new StringBuilder();
305             sb.append(di.componentName.equals(mDefaultDream) ? '0' : '1');
306             sb.append(di.caption);
307             return sb.toString();
308         }
309     }
310 }
311