blob: 1a5c82c82fef3b70263ba7a7803d9ed941d4a94e [file] [log] [blame]
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001/**
2 * Copyright (C) 2007 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16
17package com.android.settings;
18
Amith Yamasaniae697552011-09-27 11:33:17 -070019import android.app.ActivityManager;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080020import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.PackageManager;
Amith Yamasani02cf71a2010-09-21 15:48:52 -070024import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070025import android.content.pm.ResolveInfo;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070026import android.content.pm.UserInfo;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020027import android.content.res.Resources;
28import android.content.res.Resources.NotFoundException;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070029import android.database.Cursor;
Amith Yamasanif34a85d2012-09-17 18:31:45 -070030import android.graphics.Bitmap;
31import android.graphics.BitmapFactory;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020032import android.graphics.drawable.Drawable;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080033import android.net.ConnectivityManager;
34import android.net.LinkProperties;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070035import android.net.Uri;
Amith Yamasania4379d62011-07-22 10:34:58 -070036import android.os.BatteryManager;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020037import android.os.Bundle;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070038import android.os.ParcelFileDescriptor;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070039import android.os.UserHandle;
40import android.os.UserManager;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080041import android.preference.Preference;
Amith Yamasani02cf71a2010-09-21 15:48:52 -070042import android.preference.PreferenceActivity.Header;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070043import android.preference.PreferenceFrameLayout;
44import android.preference.PreferenceGroup;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070045import android.provider.ContactsContract.CommonDataKinds.Phone;
46import android.provider.ContactsContract.Contacts;
47import android.provider.ContactsContract.Profile;
Amith Yamasani60133dd2010-09-11 14:17:31 -070048import android.telephony.TelephonyManager;
Anders Hammar1b2dd9032010-04-08 10:03:50 +020049import android.text.TextUtils;
Amith Yamasaniae47ef42012-09-16 17:53:35 -070050import android.util.Log;
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -070051import android.view.View;
52import android.view.ViewGroup;
53import android.widget.ListView;
54import android.widget.TabWidget;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -070055
Amith Yamasaniae47ef42012-09-16 17:53:35 -070056import com.android.settings.users.ProfileUpdateReceiver;
57
58import java.io.FileOutputStream;
59import java.io.IOException;
60import java.io.InputStream;
Amith Yamasanic06d4c42011-02-25 14:35:20 -080061import java.net.InetAddress;
62import java.util.Iterator;
Daisuke Miyakawaa2633d02010-09-15 20:09:12 -070063import java.util.List;
Jean Chalard71ad1f42011-05-12 15:06:16 +090064import java.util.Locale;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080065
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080066public class Utils {
67
68 /**
69 * Set the preference's title to the matching activity's label.
70 */
71 public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
72
73 /**
Anders Hammar1b2dd9032010-04-08 10:03:50 +020074 * Name of the meta-data item that should be set in the AndroidManifest.xml
75 * to specify the icon that should be displayed for the preference.
76 */
77 private static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
78
79 /**
80 * Name of the meta-data item that should be set in the AndroidManifest.xml
81 * to specify the title that should be displayed for the preference.
82 */
83 private static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
84
85 /**
86 * Name of the meta-data item that should be set in the AndroidManifest.xml
87 * to specify the summary text that should be displayed for the preference.
88 */
89 private static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
90
91 /**
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080092 * Finds a matching activity for a preference's intent. If a matching
93 * activity is not found, it will remove the preference.
Ying Wanga7188322010-01-04 18:45:10 -080094 *
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080095 * @param context The context.
96 * @param parentPreferenceGroup The preference group that contains the
97 * preference whose intent is being resolved.
98 * @param preferenceKey The key of the preference whose intent is being
99 * resolved.
100 * @param flags 0 or one or more of
101 * {@link #UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY}
102 * .
103 * @return Whether an activity was found. If false, the preference was
104 * removed.
105 */
106 public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
107 PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
Ying Wanga7188322010-01-04 18:45:10 -0800108
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800109 Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
110 if (preference == null) {
111 return false;
112 }
Ying Wanga7188322010-01-04 18:45:10 -0800113
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800114 Intent intent = preference.getIntent();
115 if (intent != null) {
116 // Find the activity that is in the system image
117 PackageManager pm = context.getPackageManager();
118 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
119 int listSize = list.size();
120 for (int i = 0; i < listSize; i++) {
121 ResolveInfo resolveInfo = list.get(i);
122 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
123 != 0) {
Ying Wanga7188322010-01-04 18:45:10 -0800124
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800125 // Replace the intent with this specific activity
126 preference.setIntent(new Intent().setClassName(
127 resolveInfo.activityInfo.packageName,
128 resolveInfo.activityInfo.name));
129
130 if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
131 // Set the preference title to the activity's label
132 preference.setTitle(resolveInfo.loadLabel(pm));
133 }
Ying Wanga7188322010-01-04 18:45:10 -0800134
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800135 return true;
136 }
137 }
138 }
139
140 // Did not find a matching activity, so remove the preference
141 parentPreferenceGroup.removePreference(preference);
Ying Wanga7188322010-01-04 18:45:10 -0800142
Shuhrat Dehkanov7dc567a2012-04-23 01:59:56 +0900143 return false;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800144 }
Ying Wanga7188322010-01-04 18:45:10 -0800145
146 /**
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200147 * Finds a matching activity for a preference's intent. If a matching
148 * activity is not found, it will remove the preference. The icon, title and
149 * summary of the preference will also be updated with the values retrieved
150 * from the activity's meta-data elements. If no meta-data elements are
151 * specified then the preference title will be set to match the label of the
152 * activity, an icon and summary text will not be displayed.
153 *
154 * @param context The context.
155 * @param parentPreferenceGroup The preference group that contains the
156 * preference whose intent is being resolved.
157 * @param preferenceKey The key of the preference whose intent is being
158 * resolved.
159 *
160 * @return Whether an activity was found. If false, the preference was
161 * removed.
162 *
163 * @see {@link #META_DATA_PREFERENCE_ICON}
164 * {@link #META_DATA_PREFERENCE_TITLE}
165 * {@link #META_DATA_PREFERENCE_SUMMARY}
166 */
167 public static boolean updatePreferenceToSpecificActivityFromMetaDataOrRemove(Context context,
168 PreferenceGroup parentPreferenceGroup, String preferenceKey) {
169
170 IconPreferenceScreen preference = (IconPreferenceScreen)parentPreferenceGroup
171 .findPreference(preferenceKey);
172 if (preference == null) {
173 return false;
174 }
175
176 Intent intent = preference.getIntent();
177 if (intent != null) {
178 // Find the activity that is in the system image
179 PackageManager pm = context.getPackageManager();
180 List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
181 int listSize = list.size();
182 for (int i = 0; i < listSize; i++) {
183 ResolveInfo resolveInfo = list.get(i);
184 if ((resolveInfo.activityInfo.applicationInfo.flags
185 & ApplicationInfo.FLAG_SYSTEM) != 0) {
186 Drawable icon = null;
187 String title = null;
188 String summary = null;
189
190 // Get the activity's meta-data
191 try {
192 Resources res = pm
193 .getResourcesForApplication(resolveInfo.activityInfo.packageName);
194 Bundle metaData = resolveInfo.activityInfo.metaData;
195
196 if (res != null && metaData != null) {
197 icon = res.getDrawable(metaData.getInt(META_DATA_PREFERENCE_ICON));
198 title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
199 summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
200 }
201 } catch (NameNotFoundException e) {
202 // Ignore
203 } catch (NotFoundException e) {
204 // Ignore
205 }
206
207 // Set the preference title to the activity's label if no
208 // meta-data is found
209 if (TextUtils.isEmpty(title)) {
210 title = resolveInfo.loadLabel(pm).toString();
211 }
212
213 // Set icon, title and summary for the preference
214 preference.setIcon(icon);
215 preference.setTitle(title);
216 preference.setSummary(summary);
217
218 // Replace the intent with this specific activity
219 preference.setIntent(new Intent().setClassName(
220 resolveInfo.activityInfo.packageName,
221 resolveInfo.activityInfo.name));
222
223 return true;
224 }
225 }
226 }
227
228 // Did not find a matching activity, so remove the preference
229 parentPreferenceGroup.removePreference(preference);
230
231 return false;
232 }
233
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700234 public static boolean updateHeaderToSpecificActivityFromMetaDataOrRemove(Context context,
235 List<Header> target, Header header) {
236
237 Intent intent = header.intent;
238 if (intent != null) {
239 // Find the activity that is in the system image
240 PackageManager pm = context.getPackageManager();
241 List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
242 int listSize = list.size();
243 for (int i = 0; i < listSize; i++) {
244 ResolveInfo resolveInfo = list.get(i);
245 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
246 != 0) {
247 Drawable icon = null;
248 String title = null;
249 String summary = null;
250
251 // Get the activity's meta-data
252 try {
253 Resources res = pm.getResourcesForApplication(
254 resolveInfo.activityInfo.packageName);
255 Bundle metaData = resolveInfo.activityInfo.metaData;
256
257 if (res != null && metaData != null) {
258 icon = res.getDrawable(metaData.getInt(META_DATA_PREFERENCE_ICON));
259 title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
260 summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
261 }
262 } catch (NameNotFoundException e) {
263 // Ignore
264 } catch (NotFoundException e) {
265 // Ignore
266 }
267
268 // Set the preference title to the activity's label if no
269 // meta-data is found
270 if (TextUtils.isEmpty(title)) {
271 title = resolveInfo.loadLabel(pm).toString();
272 }
273
274 // Set icon, title and summary for the preference
275 // TODO:
276 //header.icon = icon;
277 header.title = title;
278 header.summary = summary;
279 // Replace the intent with this specific activity
280 header.intent = new Intent().setClassName(resolveInfo.activityInfo.packageName,
281 resolveInfo.activityInfo.name);
282
283 return true;
284 }
285 }
286 }
287
288 // Did not find a matching activity, so remove the preference
289 if (target.remove(header)) System.err.println("Removed " + header.id);
290
291 return false;
292 }
293
Anders Hammar1b2dd9032010-04-08 10:03:50 +0200294 /**
Ying Wanga7188322010-01-04 18:45:10 -0800295 * Returns true if Monkey is running.
296 */
297 public static boolean isMonkeyRunning() {
Amith Yamasaniae697552011-09-27 11:33:17 -0700298 return ActivityManager.isUserAMonkey();
Ying Wanga7188322010-01-04 18:45:10 -0800299 }
Amith Yamasani60133dd2010-09-11 14:17:31 -0700300
301 /**
302 * Returns whether the device is voice-capable (meaning, it is also a phone).
303 */
304 public static boolean isVoiceCapable(Context context) {
305 TelephonyManager telephony =
306 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
307 return telephony != null && telephony.isVoiceCapable();
308 }
Amith Yamasani0f85c482011-02-23 17:19:11 -0800309
Robert Greenwalt8af88fb2011-08-31 11:17:47 -0700310 public static boolean isWifiOnly(Context context) {
311 ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
312 Context.CONNECTIVITY_SERVICE);
313 return (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
Amith Yamasani0f85c482011-02-23 17:19:11 -0800314 }
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800315
316 /**
317 * Returns the WIFI IP Addresses, if any, taking into account IPv4 and IPv6 style addresses.
318 * @param context the application context
319 * @return the formatted and comma-separated IP addresses, or null if none.
320 */
321 public static String getWifiIpAddresses(Context context) {
322 ConnectivityManager cm = (ConnectivityManager)
323 context.getSystemService(Context.CONNECTIVITY_SERVICE);
324 LinkProperties prop = cm.getLinkProperties(ConnectivityManager.TYPE_WIFI);
Amith Yamasani6822b742011-10-17 16:41:00 -0700325 return formatIpAddresses(prop);
326 }
327
328 /**
329 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
330 * addresses.
331 * @param context the application context
332 * @return the formatted and comma-separated IP addresses, or null if none.
333 */
334 public static String getDefaultIpAddresses(Context context) {
335 ConnectivityManager cm = (ConnectivityManager)
336 context.getSystemService(Context.CONNECTIVITY_SERVICE);
337 LinkProperties prop = cm.getActiveLinkProperties();
338 return formatIpAddresses(prop);
339 }
340
341 private static String formatIpAddresses(LinkProperties prop) {
Amith Yamasanic06d4c42011-02-25 14:35:20 -0800342 if (prop == null) return null;
343 Iterator<InetAddress> iter = prop.getAddresses().iterator();
344 // If there are no entries, return null
345 if (!iter.hasNext()) return null;
346 // Concatenate all available addresses, comma separated
347 String addresses = "";
348 while (iter.hasNext()) {
349 addresses += iter.next().getHostAddress();
350 if (iter.hasNext()) addresses += ", ";
351 }
352 return addresses;
353 }
Jean Chalard71ad1f42011-05-12 15:06:16 +0900354
355 public static Locale createLocaleFromString(String localeStr) {
356 // TODO: is there a better way to actually construct a locale that will match?
357 // The main problem is, on top of Java specs, locale.toString() and
358 // new Locale(locale.toString()).toString() do not return equal() strings in
359 // many cases, because the constructor takes the only string as the language
360 // code. So : new Locale("en", "US").toString() => "en_US"
361 // And : new Locale("en_US").toString() => "en_us"
362 if (null == localeStr)
363 return Locale.getDefault();
364 String[] brokenDownLocale = localeStr.split("_", 3);
365 // split may not return a 0-length array.
366 if (1 == brokenDownLocale.length) {
367 return new Locale(brokenDownLocale[0]);
368 } else if (2 == brokenDownLocale.length) {
369 return new Locale(brokenDownLocale[0], brokenDownLocale[1]);
370 } else {
371 return new Locale(brokenDownLocale[0], brokenDownLocale[1], brokenDownLocale[2]);
372 }
373 }
Amith Yamasania4379d62011-07-22 10:34:58 -0700374
375 public static String getBatteryPercentage(Intent batteryChangedIntent) {
376 int level = batteryChangedIntent.getIntExtra("level", 0);
377 int scale = batteryChangedIntent.getIntExtra("scale", 100);
378 return String.valueOf(level * 100 / scale) + "%";
379 }
380
381 public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
382 final Intent intent = batteryChangedIntent;
383
384 int plugType = intent.getIntExtra("plugged", 0);
385 int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
386 String statusString;
387 if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
388 statusString = res.getString(R.string.battery_info_status_charging);
389 if (plugType > 0) {
Brian Muramatsu0bd54a62012-08-14 15:13:16 -0700390 int resId;
391 if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
392 resId = R.string.battery_info_status_charging_ac;
393 } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
394 resId = R.string.battery_info_status_charging_usb;
395 } else {
396 resId = R.string.battery_info_status_charging_wireless;
397 }
398 statusString = statusString + " " + res.getString(resId);
Amith Yamasania4379d62011-07-22 10:34:58 -0700399 }
400 } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
401 statusString = res.getString(R.string.battery_info_status_discharging);
402 } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
403 statusString = res.getString(R.string.battery_info_status_not_charging);
404 } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
405 statusString = res.getString(R.string.battery_info_status_full);
406 } else {
407 statusString = res.getString(R.string.battery_info_status_unknown);
408 }
409
410 return statusString;
411 }
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700412
413 /**
414 * Prepare a custom preferences layout, moving padding to {@link ListView}
415 * when outside scrollbars are requested. Usually used to display
416 * {@link ListView} and {@link TabWidget} with correct padding.
417 */
Jeff Sharkey5d706792011-09-08 18:57:17 -0700418 public static void prepareCustomPreferencesList(
419 ViewGroup parent, View child, ListView list, boolean ignoreSidePadding) {
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700420 final boolean movePadding = list.getScrollBarStyle() == View.SCROLLBARS_OUTSIDE_OVERLAY;
421 if (movePadding && parent instanceof PreferenceFrameLayout) {
422 ((PreferenceFrameLayout.LayoutParams) child.getLayoutParams()).removeBorders = true;
423
424 final Resources res = list.getResources();
425 final int paddingSide = res.getDimensionPixelSize(
426 com.android.internal.R.dimen.preference_fragment_padding_side);
427 final int paddingBottom = res.getDimensionPixelSize(
428 com.android.internal.R.dimen.preference_fragment_padding_bottom);
Jeff Sharkey5d706792011-09-08 18:57:17 -0700429
Amith Yamasanif0b05de2012-05-09 09:43:33 -0700430 final int effectivePaddingSide = ignoreSidePadding ? 0 : paddingSide;
Jeff Sharkey5d706792011-09-08 18:57:17 -0700431 list.setPadding(effectivePaddingSide, 0, effectivePaddingSide, paddingBottom);
Jeff Sharkeyb654cbb2011-08-18 11:59:19 -0700432 }
433 }
Jeff Sharkeya83a24f2011-09-16 01:52:39 -0700434
435 /**
436 * Return string resource that best describes combination of tethering
437 * options available on this device.
438 */
439 public static int getTetheringLabel(ConnectivityManager cm) {
440 String[] usbRegexs = cm.getTetherableUsbRegexs();
441 String[] wifiRegexs = cm.getTetherableWifiRegexs();
442 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
443
444 boolean usbAvailable = usbRegexs.length != 0;
445 boolean wifiAvailable = wifiRegexs.length != 0;
446 boolean bluetoothAvailable = bluetoothRegexs.length != 0;
447
448 if (wifiAvailable && usbAvailable && bluetoothAvailable) {
449 return R.string.tether_settings_title_all;
450 } else if (wifiAvailable && usbAvailable) {
451 return R.string.tether_settings_title_all;
452 } else if (wifiAvailable && bluetoothAvailable) {
453 return R.string.tether_settings_title_all;
454 } else if (wifiAvailable) {
455 return R.string.tether_settings_title_wifi;
456 } else if (usbAvailable && bluetoothAvailable) {
457 return R.string.tether_settings_title_usb_bluetooth;
458 } else if (usbAvailable) {
459 return R.string.tether_settings_title_usb;
460 } else {
461 return R.string.tether_settings_title_bluetooth;
462 }
463 }
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700464
465 /* Used by UserSettings as well. Call this on a non-ui thread. */
466 public static boolean copyMeProfilePhoto(Context context, UserInfo user) {
467 Uri contactUri = Profile.CONTENT_URI;
468
469 InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
470 context.getContentResolver(),
471 contactUri, true);
472 // If there's no profile photo, assign a default avatar
473 if (avatarDataStream == null) {
474 return false;
475 }
476 int userId = user != null ? user.id : UserHandle.myUserId();
477 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700478 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
479 um.setUserIcon(userId, icon);
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700480 try {
Amith Yamasanif34a85d2012-09-17 18:31:45 -0700481 avatarDataStream.close();
482 } catch (IOException ioe) { }
483 return true;
Amith Yamasaniae47ef42012-09-16 17:53:35 -0700484 }
485
486 public static String getMeProfileName(Context context) {
487 Cursor cursor = context.getContentResolver().query(
488 Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME},
489 null, null, null);
490 if (cursor == null) {
491 return null;
492 }
493
494 try {
495 if (cursor.moveToFirst()) {
496 return cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
497 }
498 } finally {
499 cursor.close();
500 }
501 return null;
502 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800503}