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