1 /* 2 * Copyright (C) 2014 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.tv.settings; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.accounts.AuthenticatorDescription; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.ComponentName; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.Intent.ShortcutIconResource; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageManager; 31 import android.content.pm.PackageManager.NameNotFoundException; 32 import android.content.pm.ResolveInfo; 33 import android.content.res.Resources; 34 import android.content.res.Resources.NotFoundException; 35 import android.content.res.TypedArray; 36 import android.content.res.XmlResourceParser; 37 import android.media.tv.TvInputInfo; 38 import android.media.tv.TvInputManager; 39 import android.net.Uri; 40 import android.os.Bundle; 41 import android.os.Handler; 42 import android.preference.PreferenceActivity; 43 import android.support.v17.leanback.widget.ArrayObjectAdapter; 44 import android.support.v17.leanback.widget.HeaderItem; 45 import android.support.v17.leanback.widget.ObjectAdapter; 46 import android.support.v17.leanback.widget.ListRow; 47 import android.text.TextUtils; 48 import android.util.AttributeSet; 49 import android.util.Log; 50 import android.util.TypedValue; 51 import android.util.Xml; 52 53 import com.android.internal.util.XmlUtils; 54 import com.android.tv.settings.accessories.AccessoryUtils; 55 import com.android.tv.settings.accessories.BluetoothAccessoryActivity; 56 import com.android.tv.settings.accessories.BluetoothConnectionsManager; 57 import com.android.tv.settings.accounts.AccountImageUriGetter; 58 import com.android.tv.settings.accounts.AccountSettingsActivity; 59 import com.android.tv.settings.accounts.AddAccountWithTypeActivity; 60 import com.android.tv.settings.accounts.AuthenticatorHelper; 61 import com.android.tv.settings.connectivity.ConnectivityStatusIconUriGetter; 62 import com.android.tv.settings.connectivity.ConnectivityStatusTextGetter; 63 import com.android.tv.settings.connectivity.WifiNetworksActivity; 64 import com.android.tv.settings.device.sound.SoundActivity; 65 import com.android.tv.settings.users.RestrictedProfileActivity; 66 import com.android.tv.settings.util.UriUtils; 67 import com.android.tv.settings.util.AccountImageHelper; 68 69 import org.xmlpull.v1.XmlPullParser; 70 import org.xmlpull.v1.XmlPullParserException; 71 72 import java.io.IOException; 73 import java.util.ArrayList; 74 import java.util.Set; 75 76 /** 77 * Gets the list of browse headers and browse items. 78 */ 79 public class BrowseInfo extends BrowseInfoBase { 80 81 private static final String TAG = "CanvasSettings.BrowseInfo"; 82 private static final boolean DEBUG = false; 83 84 public static final String EXTRA_ACCESSORY_ADDRESS = "accessory_address"; 85 public static final String EXTRA_ACCESSORY_NAME = "accessory_name"; 86 public static final String EXTRA_ACCESSORY_ICON_ID = "accessory_icon_res"; 87 88 private static final String ACCOUNT_TYPE_GOOGLE = "com.google"; 89 90 private static final String ETHERNET_PREFERENCE_KEY = "ethernet"; 91 92 interface XmlReaderListener { handleRequestedNode(Context context, XmlResourceParser parser, AttributeSet attrs)93 void handleRequestedNode(Context context, XmlResourceParser parser, AttributeSet attrs) 94 throws org.xmlpull.v1.XmlPullParserException, IOException; 95 } 96 97 static class SoundActivityImageUriGetter implements MenuItem.UriGetter { 98 99 private final Context mContext; 100 SoundActivityImageUriGetter(Context context)101 SoundActivityImageUriGetter(Context context) { 102 mContext = context; 103 } 104 105 @Override getUri()106 public String getUri() { 107 return UriUtils.getAndroidResourceUri(mContext.getResources(), 108 SoundActivity.getIconResource(mContext.getContentResolver())); 109 } 110 } 111 112 static class XmlReader { 113 114 private final Context mContext; 115 private final int mXmlResource; 116 private final String mRootNodeName; 117 private final String mNodeNameRequested; 118 private final XmlReaderListener mListener; 119 XmlReader(Context context, int xmlResource, String rootNodeName, String nodeNameRequested, XmlReaderListener listener)120 XmlReader(Context context, int xmlResource, String rootNodeName, String nodeNameRequested, 121 XmlReaderListener listener) { 122 mContext = context; 123 mXmlResource = xmlResource; 124 mRootNodeName = rootNodeName; 125 mNodeNameRequested = nodeNameRequested; 126 mListener = listener; 127 } 128 read()129 void read() { 130 XmlResourceParser parser = null; 131 try { 132 parser = mContext.getResources().getXml(mXmlResource); 133 AttributeSet attrs = Xml.asAttributeSet(parser); 134 135 int type; 136 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 137 && type != XmlPullParser.START_TAG) { 138 // Parse next until start tag is found 139 } 140 141 String nodeName = parser.getName(); 142 if (!mRootNodeName.equals(nodeName)) { 143 throw new RuntimeException("XML document must start with <" + mRootNodeName 144 + "> tag; found" + nodeName + " at " + parser.getPositionDescription()); 145 } 146 147 Bundle curBundle = null; 148 149 final int outerDepth = parser.getDepth(); 150 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 151 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 152 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 153 continue; 154 } 155 156 nodeName = parser.getName(); 157 if (mNodeNameRequested.equals(nodeName)) { 158 mListener.handleRequestedNode(mContext, parser, attrs); 159 } else { 160 XmlUtils.skipCurrentTag(parser); 161 } 162 } 163 164 } catch (XmlPullParserException e) { 165 throw new RuntimeException("Error parsing headers", e); 166 } catch (IOException e) { 167 throw new RuntimeException("Error parsing headers", e); 168 } finally { 169 if (parser != null) 170 parser.close(); 171 } 172 } 173 } 174 175 private static final String PREF_KEY_ADD_ACCOUNT = "add_account"; 176 private static final String PREF_KEY_ADD_ACCESSORY = "add_accessory"; 177 private static final String PREF_KEY_WIFI = "network"; 178 private static final String PREF_KEY_DEVELOPER = "developer"; 179 private static final String PREF_KEY_INPUTS = "inputs"; 180 private static final String PREF_KEY_HOME = "home"; 181 182 private final Context mContext; 183 private final AuthenticatorHelper mAuthenticatorHelper; 184 private int mNextItemId; 185 private int mAccountHeaderId; 186 private final BluetoothAdapter mBtAdapter; 187 private final Object mGuard = new Object(); 188 private MenuItem mWifiItem = null; 189 private ArrayObjectAdapter mWifiRow = null; 190 private final Handler mHandler = new Handler(); 191 192 private PreferenceUtils mPreferenceUtils; 193 private boolean mDeveloperEnabled; 194 private boolean mInputSettingNeeded; 195 BrowseInfo(Context context)196 BrowseInfo(Context context) { 197 mContext = context; 198 mAuthenticatorHelper = new AuthenticatorHelper(); 199 mAuthenticatorHelper.updateAuthDescriptions(context); 200 mAuthenticatorHelper.onAccountsUpdated(context, null); 201 mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 202 mNextItemId = 0; 203 mPreferenceUtils = new PreferenceUtils(context); 204 mDeveloperEnabled = mPreferenceUtils.isDeveloperEnabled(); 205 mInputSettingNeeded = isInputSettingNeeded(); 206 } 207 init()208 void init() { 209 synchronized (mGuard) { 210 mHeaderItems.clear(); 211 mRows.clear(); 212 int settingsXml = isRestricted() ? R.xml.restricted_main : R.xml.main; 213 new XmlReader(mContext, settingsXml, "preference-headers", "header", 214 new HeaderXmlReaderListener()).read(); 215 updateAccessories(R.id.accessories); 216 } 217 } 218 checkForDeveloperOptionUpdate()219 void checkForDeveloperOptionUpdate() { 220 final boolean developerEnabled = mPreferenceUtils.isDeveloperEnabled(); 221 if (developerEnabled != mDeveloperEnabled) { 222 mDeveloperEnabled = developerEnabled; 223 init(); 224 } 225 } 226 227 private class HeaderXmlReaderListener implements XmlReaderListener { 228 @Override handleRequestedNode(Context context, XmlResourceParser parser, AttributeSet attrs)229 public void handleRequestedNode(Context context, XmlResourceParser parser, 230 AttributeSet attrs) 231 throws XmlPullParserException, IOException { 232 TypedArray sa = mContext.getResources().obtainAttributes(attrs, 233 com.android.internal.R.styleable.PreferenceHeader); 234 final int headerId = sa.getResourceId( 235 com.android.internal.R.styleable.PreferenceHeader_id, 236 (int) PreferenceActivity.HEADER_ID_UNDEFINED); 237 String title = getStringFromTypedArray(sa, 238 com.android.internal.R.styleable.PreferenceHeader_title); 239 sa.recycle(); 240 sa = context.getResources().obtainAttributes(attrs, R.styleable.CanvasSettings); 241 int preferenceRes = sa.getResourceId(R.styleable.CanvasSettings_preference, 0); 242 sa.recycle(); 243 mHeaderItems.add(new HeaderItem(headerId, title)); 244 final ArrayObjectAdapter currentRow = new ArrayObjectAdapter(); 245 mRows.put(headerId, currentRow); 246 if (headerId != R.id.accessories) { 247 new XmlReader(context, preferenceRes, "PreferenceScreen", "Preference", 248 new PreferenceXmlReaderListener(headerId, currentRow)).read(); 249 } 250 } 251 } 252 isRestricted()253 private boolean isRestricted() { 254 return RestrictedProfileActivity.isRestrictedProfileInEffect(mContext); 255 } 256 257 private class PreferenceXmlReaderListener implements XmlReaderListener { 258 259 private final int mHeaderId; 260 private final ArrayObjectAdapter mRow; 261 PreferenceXmlReaderListener(int headerId, ArrayObjectAdapter row)262 PreferenceXmlReaderListener(int headerId, ArrayObjectAdapter row) { 263 mHeaderId = headerId; 264 mRow = row; 265 } 266 267 @Override handleRequestedNode(Context context, XmlResourceParser parser, AttributeSet attrs)268 public void handleRequestedNode(Context context, XmlResourceParser parser, 269 AttributeSet attrs) throws XmlPullParserException, IOException { 270 TypedArray sa = context.getResources().obtainAttributes(attrs, 271 com.android.internal.R.styleable.Preference); 272 273 String key = getStringFromTypedArray(sa, 274 com.android.internal.R.styleable.Preference_key); 275 String title = getStringFromTypedArray(sa, 276 com.android.internal.R.styleable.Preference_title); 277 int iconRes = sa.getResourceId(com.android.internal.R.styleable.Preference_icon, 278 R.drawable.settings_default_icon); 279 sa.recycle(); 280 281 if (PREF_KEY_ADD_ACCOUNT.equals(key)) { 282 mAccountHeaderId = mHeaderId; 283 addAccounts(mRow); 284 } else if (PREF_KEY_HOME.equals(key)) { 285 // Only show home screen setting if there's a system app to handle the intent. 286 Intent recIntent = getIntent(parser, attrs, mHeaderId); 287 if (systemIntentIsHandled(recIntent)) { 288 mRow.add(new MenuItem.Builder() 289 .id(mNextItemId++) 290 .title(title) 291 .imageResourceId(mContext, iconRes) 292 .intent(recIntent) 293 .build()); 294 } 295 } else if ((!key.equals(PREF_KEY_DEVELOPER) || mDeveloperEnabled) 296 && (!key.equals(PREF_KEY_INPUTS) || mInputSettingNeeded)) { 297 MenuItem.TextGetter descriptionGetter = getDescriptionTextGetterFromKey(key); 298 MenuItem.UriGetter uriGetter = getIconUriGetterFromKey(key); 299 MenuItem.Builder builder = new MenuItem.Builder().id(mNextItemId++).title(title) 300 .descriptionGetter(descriptionGetter) 301 .intent(getIntent(parser, attrs, mHeaderId)); 302 if(uriGetter == null) { 303 builder.imageResourceId(mContext, iconRes); 304 } else { 305 builder.imageUriGetter(uriGetter); 306 } 307 if (key.equals(PREF_KEY_WIFI)) { 308 mWifiItem = builder.build(); 309 mRow.add(mWifiItem); 310 mWifiRow = mRow; 311 } else { 312 mRow.add(builder.build()); 313 } 314 } 315 } 316 } 317 rebuildInfo()318 void rebuildInfo() { 319 init(); 320 } 321 updateAccounts()322 void updateAccounts() { 323 synchronized (mGuard) { 324 if (isRestricted()) { 325 // We don't display the accounts in restricted mode 326 return; 327 } 328 ArrayObjectAdapter row = mRows.get(mAccountHeaderId); 329 // Clear any account row cards that are not "Location" or "Security". 330 String dontDelete[] = new String[2]; 331 dontDelete[0] = mContext.getString(R.string.system_location); 332 dontDelete[1] = mContext.getString(R.string.system_security); 333 int i = 0; 334 while (i < row.size ()) { 335 MenuItem menuItem = (MenuItem) row.get(i); 336 String title = menuItem.getTitle (); 337 boolean deleteItem = true; 338 for (int j = 0; j < dontDelete.length; ++j) { 339 if (title.equals(dontDelete[j])) { 340 deleteItem = false; 341 break; 342 } 343 } 344 if (deleteItem) { 345 row.removeItems(i, 1); 346 } else { 347 ++i; 348 } 349 } 350 // Add accounts to end of row. 351 addAccounts(row); 352 } 353 } 354 updateAccessories()355 void updateAccessories() { 356 synchronized (mGuard) { 357 updateAccessories(R.id.accessories); 358 } 359 } 360 updateWifi(final boolean isEthernetAvailable)361 public void updateWifi(final boolean isEthernetAvailable) { 362 if (mWifiItem != null) { 363 int index = mWifiRow.indexOf(mWifiItem); 364 if (index >= 0) { 365 mWifiItem = new MenuItem.Builder().from(mWifiItem) 366 .title(mContext.getString(isEthernetAvailable 367 ? R.string.connectivity_network : R.string.connectivity_wifi)) 368 .build(); 369 mWifiRow.replace(index, mWifiItem); 370 } 371 } 372 } 373 isInputSettingNeeded()374 private boolean isInputSettingNeeded() { 375 TvInputManager manager = (TvInputManager) mContext.getSystemService( 376 Context.TV_INPUT_SERVICE); 377 if (manager != null) { 378 for (TvInputInfo input : manager.getTvInputList()) { 379 if (input.isPassthroughInput()) { 380 return true; 381 } 382 } 383 } 384 return false; 385 } 386 updateAccessories(int headerId)387 private void updateAccessories(int headerId) { 388 ArrayObjectAdapter row = mRows.get(headerId); 389 row.clear(); 390 391 addAccessories(row); 392 393 // Add new accessory activity icon 394 ComponentName componentName = new ComponentName("com.android.tv.settings", 395 "com.android.tv.settings.accessories.AddAccessoryActivity"); 396 Intent i = new Intent().setComponent(componentName); 397 i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 398 row.add(new MenuItem.Builder().id(mNextItemId++) 399 .title(mContext.getString(R.string.accessories_add)) 400 .imageResourceId(mContext, R.drawable.ic_settings_bluetooth) 401 .intent(i).build()); 402 } 403 getIntent(XmlResourceParser parser, AttributeSet attrs, int headerId)404 private Intent getIntent(XmlResourceParser parser, AttributeSet attrs, int headerId) 405 throws org.xmlpull.v1.XmlPullParserException, IOException { 406 Intent intent = null; 407 if (parser.next() == XmlPullParser.START_TAG && "intent".equals(parser.getName())) { 408 TypedArray sa = mContext.getResources() 409 .obtainAttributes(attrs, com.android.internal.R.styleable.Intent); 410 String targetClass = getStringFromTypedArray( 411 sa, com.android.internal.R.styleable.Intent_targetClass); 412 String targetPackage = getStringFromTypedArray( 413 sa, com.android.internal.R.styleable.Intent_targetPackage); 414 String action = getStringFromTypedArray( 415 sa, com.android.internal.R.styleable.Intent_action); 416 if (targetClass != null && targetPackage != null) { 417 ComponentName componentName = new ComponentName(targetPackage, targetClass); 418 intent = new Intent(); 419 intent.setComponent(componentName); 420 } else if (action != null) { 421 intent = new Intent(action); 422 } 423 424 XmlUtils.skipCurrentTag(parser); 425 } 426 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 427 return intent; 428 } 429 getStringFromTypedArray(TypedArray sa, int resourceId)430 private String getStringFromTypedArray(TypedArray sa, int resourceId) { 431 String value = null; 432 TypedValue tv = sa.peekValue(resourceId); 433 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 434 if (tv.resourceId != 0) { 435 value = mContext.getString(tv.resourceId); 436 } else { 437 value = tv.string.toString(); 438 } 439 } 440 return value; 441 } 442 getDescriptionTextGetterFromKey(String key)443 private MenuItem.TextGetter getDescriptionTextGetterFromKey(String key) { 444 if (WifiNetworksActivity.PREFERENCE_KEY.equals(key)) { 445 return ConnectivityStatusTextGetter.createWifiStatusTextGetter(mContext); 446 } 447 448 if (ETHERNET_PREFERENCE_KEY.equals(key)) { 449 return ConnectivityStatusTextGetter.createEthernetStatusTextGetter(mContext); 450 } 451 452 return null; 453 } 454 getIconUriGetterFromKey(String key)455 private MenuItem.UriGetter getIconUriGetterFromKey(String key) { 456 if (SoundActivity.getPreferenceKey().equals(key)) { 457 return new SoundActivityImageUriGetter(mContext); 458 } 459 460 if (WifiNetworksActivity.PREFERENCE_KEY.equals(key)) { 461 return ConnectivityStatusIconUriGetter.createWifiStatusIconUriGetter(mContext); 462 } 463 464 return null; 465 } 466 addAccounts(ArrayObjectAdapter row)467 private void addAccounts(ArrayObjectAdapter row) { 468 AccountManager am = AccountManager.get(mContext); 469 AuthenticatorDescription[] authTypes = am.getAuthenticatorTypes(); 470 ArrayList<String> allowableAccountTypes = new ArrayList<>(authTypes.length); 471 PackageManager pm = mContext.getPackageManager(); 472 473 int googleAccountCount = 0; 474 475 for (AuthenticatorDescription authDesc : authTypes) { 476 Resources resources = null; 477 try { 478 resources = pm.getResourcesForApplication(authDesc.packageName); 479 } catch (NameNotFoundException e) { 480 Log.e(TAG, "Authenticator description with bad package name", e); 481 continue; 482 } 483 484 allowableAccountTypes.add(authDesc.type); 485 486 // Main title text comes from the authenticator description (e.g. "Google"). 487 String authTitle = null; 488 try { 489 authTitle = resources.getString(authDesc.labelId); 490 if (TextUtils.isEmpty(authTitle)) { 491 authTitle = null; // Handled later when we add the row. 492 } 493 } catch (NotFoundException e) { 494 Log.e(TAG, "Authenticator description with bad label id", e); 495 } 496 497 Account[] accounts = am.getAccountsByType(authDesc.type); 498 499 // Icon URI to be displayed for each account is based on the type of authenticator. 500 String imageUri = null; 501 if (ACCOUNT_TYPE_GOOGLE.equals(authDesc.type)) { 502 googleAccountCount = accounts.length; 503 imageUri = googleAccountIconUri(mContext); 504 } else { 505 imageUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + 506 authDesc.packageName + '/' + 507 resources.getResourceTypeName(authDesc.iconId) + '/' + 508 resources.getResourceEntryName(authDesc.iconId)) 509 .toString(); 510 } 511 512 // Display an entry for each installed account we have. 513 for (final Account account : accounts) { 514 Intent i = new Intent(mContext, AccountSettingsActivity.class) 515 .putExtra(AccountSettingsActivity.EXTRA_ACCOUNT, account.name); 516 i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 517 row.add(new MenuItem.Builder().id(mNextItemId++) 518 .title(authTitle != null ? authTitle : account.name) 519 .imageUri(imageUri) 520 .description(authTitle != null ? account.name : null) 521 .intent(i) 522 .build()); 523 } 524 } 525 526 // Never allow restricted profile to add accounts. 527 if (!isRestricted()) { 528 529 // If there's already a Google account installed, disallow installing a second one. 530 if (googleAccountCount > 0) { 531 allowableAccountTypes.remove(ACCOUNT_TYPE_GOOGLE); 532 } 533 534 // If there are available account types, add the "add account" button. 535 if (!allowableAccountTypes.isEmpty()) { 536 Intent i = new Intent().setComponent(new ComponentName("com.android.tv.settings", 537 "com.android.tv.settings.accounts.AddAccountWithTypeActivity")); 538 i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 539 i.putExtra(AddAccountWithTypeActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY, 540 allowableAccountTypes.toArray(new String[allowableAccountTypes.size()])); 541 542 row.add(new MenuItem.Builder().id(mNextItemId++) 543 .title(mContext.getString(R.string.add_account)) 544 .imageResourceId(mContext, R.drawable.ic_settings_add) 545 .intent(i).build()); 546 } 547 } 548 } 549 addAccessories(ArrayObjectAdapter row)550 private void addAccessories(ArrayObjectAdapter row) { 551 if (mBtAdapter != null) { 552 Set<BluetoothDevice> bondedDevices = mBtAdapter.getBondedDevices(); 553 if (DEBUG) { 554 Log.d(TAG, "List of Bonded BT Devices:"); 555 } 556 557 Set<String> connectedBluetoothAddresses = 558 BluetoothConnectionsManager.getConnectedSet(mContext); 559 560 for (BluetoothDevice device : bondedDevices) { 561 if (DEBUG) { 562 Log.d(TAG, " Device name: " + device.getName() + " , Class: " + 563 device.getBluetoothClass().getDeviceClass()); 564 } 565 566 int resourceId = AccessoryUtils.getImageIdForDevice(device); 567 Intent i = BluetoothAccessoryActivity.getIntent(mContext, device.getAddress(), 568 device.getName(), resourceId); 569 i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 570 571 String desc = connectedBluetoothAddresses.contains(device.getAddress()) 572 ? mContext.getString(R.string.accessory_connected) 573 : null; 574 575 row.add(new MenuItem.Builder().id(mNextItemId++).title(device.getName()) 576 .description(desc).imageResourceId(mContext, resourceId) 577 .intent(i).build()); 578 } 579 } 580 } 581 googleAccountIconUri(Context context)582 private static String googleAccountIconUri(Context context) { 583 ShortcutIconResource iconResource = new ShortcutIconResource(); 584 iconResource.packageName = context.getPackageName(); 585 iconResource.resourceName = context.getResources().getResourceName( 586 R.drawable.ic_settings_google_account); 587 return UriUtils.getShortcutIconResourceUri(iconResource).toString(); 588 } 589 systemIntentIsHandled(Intent intent)590 private boolean systemIntentIsHandled(Intent intent) { 591 if (mContext == null || intent == null) { 592 return false; 593 } 594 595 PackageManager pm = mContext.getPackageManager(); 596 if (pm == null) { 597 return false; 598 } 599 600 for (ResolveInfo info : pm.queryIntentActivities(intent, 0)) { 601 if (info.activityInfo != null && info.activityInfo.enabled && 602 (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 603 ApplicationInfo.FLAG_SYSTEM) { 604 return true; 605 } 606 } 607 return false; 608 } 609 } 610