Android 系统设置中显示设置之休眠和屏保设置篇
在上一篇中我们学习了Android系统设置中字体大小和屏幕旋转设置基本内容,在这一篇中我们继续学习显示设置中的休眠和屏保设置。
1、休眠设置
首先我们来看一下休眠设置在界面中的定义:
1 <ListPreference 2 android:key="screen_timeout" 3 android:title="@string/screen_timeout" 4 android:summary="@string/screen_timeout_summary" 5 android:persistent="false" 6 android:entries="@array/screen_timeout_entries" //当点击的时候弹出的listView显示的内容 7 android:entryValues="@array/screen_timeout_values" //listView显示的内容对应的时间 8 />
1 <string-array name="screen_timeout_entries"> 2 <item msgid="3342301044271143016">"15 秒"</item> 3 <item msgid="8881760709354815449">"30 秒"</item> 4 <item msgid="7589406073232279088">"1 分钟"</item> 5 <item msgid="7001195990902244174">"2 分钟"</item> 6 <item msgid="7489864775127957179">"5 分钟"</item> 7 <item msgid="2314124409517439288">"10 分钟"</item> 8 <item msgid="6864027152847611413">"30 分钟"</item> 9 </string-array>
1 <!-- Do not translate. --> 2 <string-array name="screen_timeout_values" translatable="false"> 3 <!-- Do not translate. --> 4 <item>15000</item> 5 <!-- Do not translate. --> 6 <item>30000</item> 7 <!-- Do not translate. --> 8 <item>60000</item> 9 <!-- Do not translate. --> 10 <item>120000</item> 11 <!-- Do not translate. --> 12 <item>300000</item> 13 <!-- Do not translate. --> 14 <item>600000</item> 15 <!-- Do not translate. --> 16 <item>1800000</item> 17 </string-array>
接下来我们看看DisplaySettings.java中休眠的相关代码:
1 mScreenTimeoutPreference = (ListPreference) findPreference(KEY_SCREEN_TIMEOUT); 2 //说明我们的休眠时间是保存在Settings.System中,如果获取不到,则默认值为FALLBACK_SCREEN_TIMEOUT_VALUE = 30000,也就是30秒 3 final long currentTimeout = Settings.System.getLong(resolver, SCREEN_OFF_TIMEOUT, 4 FALLBACK_SCREEN_TIMEOUT_VALUE); 5 mScreenTimeoutPreference.setValue(String.valueOf(currentTimeout)); 6 mScreenTimeoutPreference.setOnPreferenceChangeListener(this); 7 //更新休眠时间列表 8 disableUnusableTimeouts(mScreenTimeoutPreference); 9 //修改值之后,更新summary的信息 10 updateTimeoutPreferenceDescription(currentTimeout);
至于disableUnusableTimeouts和updateTimeoutPreferenceDescription函数,是比较容易理解的,我这里就只是贴出代码。
1 private void updateTimeoutPreferenceDescription(long currentTimeout) { 2 ListPreference preference = mScreenTimeoutPreference; 3 String summary; 4 if (currentTimeout < 0) { 5 // Unsupported value 6 summary = ""; 7 } else { 8 final CharSequence[] entries = preference.getEntries(); 9 final CharSequence[] values = preference.getEntryValues(); 10 if (entries == null || entries.length == 0) { 11 summary = ""; 12 } else { 13 int best = 0; 14 for (int i = 0; i < values.length; i++) { 15 long timeout = Long.parseLong(values[i].toString()); 16 if (currentTimeout >= timeout) { 17 best = i; 18 } 19 } 20 summary = preference.getContext().getString(R.string.screen_timeout_summary, 21 entries[best]); 22 } 23 } 24 preference.setSummary(summary); 25 } 26 27 private void disableUnusableTimeouts(ListPreference screenTimeoutPreference) { 28 final DevicePolicyManager dpm = 29 (DevicePolicyManager) getActivity().getSystemService( 30 Context.DEVICE_POLICY_SERVICE); 31 final long maxTimeout = dpm != null ? dpm.getMaximumTimeToLock(null) : 0; 32 if (maxTimeout == 0) { 33 return; // policy not enforced 34 } 35 final CharSequence[] entries = screenTimeoutPreference.getEntries(); 36 final CharSequence[] values = screenTimeoutPreference.getEntryValues(); 37 ArrayList<CharSequence> revisedEntries = new ArrayList<CharSequence>(); 38 ArrayList<CharSequence> revisedValues = new ArrayList<CharSequence>(); 39 for (int i = 0; i < values.length; i++) { 40 long timeout = Long.parseLong(values[i].toString()); 41 if (timeout <= maxTimeout) { 42 revisedEntries.add(entries[i]); 43 revisedValues.add(values[i]); 44 } 45 } 46 if (revisedEntries.size() != entries.length || revisedValues.size() != values.length) { 47 final int userPreference = Integer.parseInt(screenTimeoutPreference.getValue()); 48 screenTimeoutPreference.setEntries( 49 revisedEntries.toArray(new CharSequence[revisedEntries.size()])); 50 screenTimeoutPreference.setEntryValues( 51 revisedValues.toArray(new CharSequence[revisedValues.size()])); 52 if (userPreference <= maxTimeout) { 53 screenTimeoutPreference.setValue(String.valueOf(userPreference)); 54 } else if (revisedValues.size() > 0 55 && Long.parseLong(revisedValues.get(revisedValues.size() - 1).toString()) 56 == maxTimeout) { 57 // If the last one happens to be the same as the max timeout, select that 58 screenTimeoutPreference.setValue(String.valueOf(maxTimeout)); 59 } else { 60 // There will be no highlighted selection since nothing in the list matches 61 // maxTimeout. The user can still select anything less than maxTimeout. 62 // TODO: maybe append maxTimeout to the list and mark selected. 63 } 64 } 65 screenTimeoutPreference.setEnabled(revisedEntries.size() > 0); 66 }
既然我们的休眠时间是从Settings.System中获取的,那我们又是怎么存的呢?
1 public boolean onPreferenceChange(Preference preference, Object objValue) { 2 final String key = preference.getKey(); 3 if (KEY_SCREEN_TIMEOUT.equals(key)) { 4 int value = Integer.parseInt((String) objValue); 5 try { 6 Settings.System.putInt(getContentResolver(), SCREEN_OFF_TIMEOUT, value); 7 updateTimeoutPreferenceDescription(value); 8 } catch (NumberFormatException e) { 9 Log.e(TAG, "could not persist screen timeout setting", e); 10 } 11 } 12 if (KEY_FONT_SIZE.equals(key)) { 13 //该函数对字体进行设置 14 writeFontSizePreference(objValue); 15 } 16 17 return true; 18 }
有时候需要设备不进入休眠,我们只需要将SCREEN_OFF_TIMEOUT属性设置为-1即可。
Settings.System.putInt(getContentResolver(),android.provider.Settings.System.SCREEN_OFF_TIMEOUT, -1);
权限:<uses-permission android:name="android.permission.WRITE_SETTINGS" />
以上便是系统设置中休眠的相关内容。
2、屏幕保护
说完了休眠设置,我们接下来了解一下屏幕保护吧!首先来看看对应的布局:
1 <PreferenceScreen 2 android:key="screensaver" 3 android:title="@string/screensaver_settings_title" android:fragment="com.android.settings.DreamSettings" />
我们可以看到其中定义了android:fragment,也就是说当点击该item的时候会跳转到DreamSettings这个fragment。我们还是先看看DisplaySettings中有关屏保的代码吧!
1 mScreenSaverPreference = findPreference(KEY_SCREEN_SAVER); 2 //此处读取com.android.internal.R.bool.config_dreamsSupported的值,true表示支持屏保,false表示不支持 3 if (mScreenSaverPreference != null 4 && getResources().getBoolean( 5 com.android.internal.R.bool.config_dreamsSupported) == false) { 6 getPreferenceScreen().removePreference(mScreenSaverPreference); 7 }
那初始化的代码在哪里呢?
private void updateScreenSaverSummary() { if (mScreenSaverPreference != null) { mScreenSaverPreference.setSummary( DreamSettings.getSummaryTextWithDreamName(getActivity()));//获取当前的屏保的名称 } }
我们知道在定义屏保的时候指定了android:fragment属性,该属性的意思是当点击该preference时跳转到DreamSettings的fragment中,这里想问的是为什么这里指定了android:fragment就能跳转到对应的fragment呢? 原来在preference.java的构造函数中,保存了fragment这个成员变量,在PreferenceFragment中定了onPreferenceTreeClick函数,当preference点击的时候就会调用该函数,由于在preference中保存了fragment,所以会跳转到该fragment中。
1 /** 2 * {@inheritDoc} 3 */ 4 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, 5 Preference preference) { 6 if (preference.getFragment() != null && 7 getActivity() instanceof OnPreferenceStartFragmentCallback) { 8 return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment( 9 this, preference); 10 } 11 return false; 12 }
好的,刚刚解释了点击该preference为何能跳转到指定的fragment的原理。现在就让我们进入DreamSettings中一探究竟,Let‘s go!
首先找到onCreate函数,里面定义了一个控制打开关闭屏保的开关switch:
1 @Override 2 public void onCreate(Bundle icicle) { 3 logd("onCreate(%s)", icicle); 4 super.onCreate(icicle); 5 Activity activity = getActivity(); 6 7 mBackend = new DreamBackend(activity);//对屏保的一个管理的类 8 mSwitch = new Switch(activity); //控制屏保是否打开的开关 9 mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { 10 @Override 11 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 12 if (!mRefreshing) { //默认或者屏保刷新完毕即可进行下来的修改,否则跳过 13 mBackend.setEnabled(isChecked);//设置开关的状态 14 refreshFromBackend(); //刷新屏保信息 15 } 16 } 17 });
当屏保打开的时候,我们的界面是用listView进行显示的,listView的初始化是放在onActivityCreate中的,如下:
1 @Override 2 public void onActivityCreated(Bundle savedInstanceState) { 3 logd("onActivityCreated(%s)", savedInstanceState); 4 super.onActivityCreated(savedInstanceState); 5 6 ListView listView = getListView(); 7 8 listView.setItemsCanFocus(true); 9 10 TextView emptyView = (TextView) getView().findViewById(android.R.id.empty); 11 emptyView.setText(R.string.screensaver_settings_disabled_prompt); 12 listView.setEmptyView(emptyView); 13 14 mAdapter = new DreamInfoAdapter(mContext); 15 listView.setAdapter(mAdapter); 16 }
屏保中还有两个菜单供用户进行选择,他们分别是开始播放和设置时间进行播放,这部分的代码:
1 @Override 2 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 3 logd("onCreateOptionsMenu()"); 4 5 boolean isEnabled = mBackend.isEnabled(); 6 7 // create "start" action 8 MenuItem start = createMenuItem(menu, R.string.screensaver_settings_dream_start, 9 MenuItem.SHOW_AS_ACTION_ALWAYS, 10 isEnabled, new Runnable(){ 11 @Override 12 public void run() { 13 mBackend.startDreaming();//调用錽tartDreaming进行屏保的播放 14 }}); 15 16 // create "when to dream" overflow menu item 17 MenuItem whenToDream = createMenuItem(menu, 18 R.string.screensaver_settings_when_to_dream, 19 MenuItem.SHOW_AS_ACTION_IF_ROOM, 20 isEnabled, 21 new Runnable() { 22 @Override 23 public void run() { 24 showDialog(DIALOG_WHEN_TO_DREAM);//弹出对话框让用户设置时间进行播放 25 }}); 26 27 // create "help" overflow menu item (make sure it appears last) 28 super.onCreateOptionsMenu(menu, inflater); 29 30 mMenuItemsWhenEnabled = new MenuItem[] { start, whenToDream }; 31 }
接下来我们需要学习一下刷新的功能,refreshFromBackend函数:
1 private void refreshFromBackend() { 2 logd("refreshFromBackend()"); 3 mRefreshing = true; 4 boolean dreamsEnabled = mBackend.isEnabled(); 5 if (mSwitch.isChecked() != dreamsEnabled) 6 mSwitch.setChecked(dreamsEnabled); 7 8 mAdapter.clear();//清空列表 9 if (dreamsEnabled) { 10 List<DreamInfo> dreamInfos = mBackend.getDreamInfos();//通过getDreamInfos获取所有屏保信息 11 mAdapter.addAll(dreamInfos);//重新加载数据并调用notifyDataSetChanged进行刷新 12 } 13 if (mMenuItemsWhenEnabled != null) 14 for (MenuItem menuItem : mMenuItemsWhenEnabled) 15 menuItem.setEnabled(dreamsEnabled); 16 mRefreshing = false; 17 }
当然我们这里需要注意的是有时候我们的屏保是个apk,所以我们需要对apk卸载和安装进行监听,这样能够保证屏保的及时更新。这个操作的代码为:
1 @Override 2 public void onResume() { 3 logd("onResume()"); 4 super.onResume(); 5 refreshFromBackend(); 6 7 // listen for package changes 8 IntentFilter filter = new IntentFilter(); 9 filter.addAction(Intent.ACTION_PACKAGE_ADDED);//安装 10 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);//改变 11 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);//卸载 12 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);//替换 13 filter.addDataScheme(PACKAGE_SCHEME); 14 mContext.registerReceiver(mPackageReceiver , filter); 15 } 16 private class PackageReceiver extends BroadcastReceiver { 17 @Override 18 public void onReceive(Context context, Intent intent) { 19 logd("PackageReceiver.onReceive"); 20 refreshFromBackend(); 21 } 22 }
以上就是屏保的设置内容。您的支持是我不懈的动力,当然不喜勿喷,希望与之交流!有错误或者错字的地方请指正,谢谢!
补充:这里有个重点没有介绍,那就是DreamBackend这个对屏保管理的类没有进行介绍,我这里只是将代码提供出来,感兴趣的朋友可以一起看看。
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 }