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 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 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 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 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 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 145 public boolean isEnabled() { 146 return getBoolean(SCREENSAVER_ENABLED, mDreamsEnabledByDefault); 147 } 148 149 public void setEnabled(boolean value) { 150 logd("setEnabled(%s)", value); 151 setBoolean(SCREENSAVER_ENABLED, value); 152 } 153 154 public boolean isActivatedOnDock() { 155 return getBoolean(SCREENSAVER_ACTIVATE_ON_DOCK, mDreamsActivatedOnDockByDefault); 156 } 157 158 public void setActivatedOnDock(boolean value) { 159 logd("setActivatedOnDock(%s)", value); 160 setBoolean(SCREENSAVER_ACTIVATE_ON_DOCK, value); 161 } 162 163 public boolean isActivatedOnSleep() { 164 return getBoolean(SCREENSAVER_ACTIVATE_ON_SLEEP, mDreamsActivatedOnSleepByDefault); 165 } 166 167 public void setActivatedOnSleep(boolean value) { 168 logd("setActivatedOnSleep(%s)", value); 169 setBoolean(SCREENSAVER_ACTIVATE_ON_SLEEP, value); 170 } 171 172 private boolean getBoolean(String key, boolean def) { 173 return Settings.Secure.getInt(mContext.getContentResolver(), key, def ? 1 : 0) == 1; 174 } 175 176 private void setBoolean(String key, boolean value) { 177 Settings.Secure.putInt(mContext.getContentResolver(), key, value ? 1 : 0); 178 } 179 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 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 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 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 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 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 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 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 294 public DreamInfoComparator(ComponentName defaultDream) { 295 mDefaultDream = defaultDream; 296 } 297 298 @Override 299 public int compare(DreamInfo lhs, DreamInfo rhs) { 300 return sortKey(lhs).compareTo(sortKey(rhs)); 301 } 302 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