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