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 17package com.android.settingslib.dream; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.pm.PackageManager; 23import android.content.pm.ResolveInfo; 24import android.content.pm.ServiceInfo; 25import android.content.res.Resources; 26import android.content.res.TypedArray; 27import android.content.res.XmlResourceParser; 28import android.graphics.drawable.Drawable; 29import android.os.RemoteException; 30import android.os.ServiceManager; 31import android.provider.Settings; 32import android.service.dreams.DreamService; 33import android.service.dreams.IDreamManager; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.util.Xml; 37 38import org.xmlpull.v1.XmlPullParser; 39import org.xmlpull.v1.XmlPullParserException; 40 41import java.io.IOException; 42import java.util.ArrayList; 43import java.util.Collections; 44import java.util.Comparator; 45import java.util.List; 46 47public 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 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 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 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 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 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 141 public boolean isEnabled() { 142 return getBoolean(Settings.Secure.SCREENSAVER_ENABLED, mDreamsEnabledByDefault); 143 } 144 145 public void setEnabled(boolean value) { 146 logd("setEnabled(%s)", value); 147 setBoolean(Settings.Secure.SCREENSAVER_ENABLED, value); 148 } 149 150 public boolean isActivatedOnDock() { 151 return getBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 152 mDreamsActivatedOnDockByDefault); 153 } 154 155 public void setActivatedOnDock(boolean value) { 156 logd("setActivatedOnDock(%s)", value); 157 setBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, value); 158 } 159 160 public boolean isActivatedOnSleep() { 161 return getBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 162 mDreamsActivatedOnSleepByDefault); 163 } 164 165 public void setActivatedOnSleep(boolean value) { 166 logd("setActivatedOnSleep(%s)", value); 167 setBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, value); 168 } 169 170 private boolean getBoolean(String key, boolean def) { 171 return Settings.Secure.getInt(mContext.getContentResolver(), key, def ? 1 : 0) == 1; 172 } 173 174 private void setBoolean(String key, boolean value) { 175 Settings.Secure.putInt(mContext.getContentResolver(), key, value ? 1 : 0); 176 } 177 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 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 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 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 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 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 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 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 288 public DreamInfoComparator(ComponentName defaultDream) { 289 mDefaultDream = defaultDream; 290 } 291 292 @Override 293 public int compare(DreamInfo lhs, DreamInfo rhs) { 294 return sortKey(lhs).compareTo(sortKey(rhs)); 295 } 296 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