1 /* 2 * Copyright (C) 2007 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.phone; 18 19 import android.accounts.Account; 20 import android.app.ActionBar; 21 import android.app.ProgressDialog; 22 import android.content.ContentProviderOperation; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.content.DialogInterface; 26 import android.content.DialogInterface.OnCancelListener; 27 import android.content.DialogInterface.OnClickListener; 28 import android.content.Intent; 29 import android.content.OperationApplicationException; 30 import android.database.Cursor; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.os.RemoteException; 34 import android.provider.ContactsContract; 35 import android.provider.ContactsContract.CommonDataKinds.Email; 36 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 37 import android.provider.ContactsContract.CommonDataKinds.Phone; 38 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 39 import android.provider.ContactsContract.Data; 40 import android.provider.ContactsContract.RawContacts; 41 import android.text.TextUtils; 42 import android.util.Log; 43 import android.view.ContextMenu; 44 import android.view.KeyEvent; 45 import android.view.Menu; 46 import android.view.MenuItem; 47 import android.view.View; 48 import android.widget.AdapterView; 49 import android.widget.CursorAdapter; 50 import android.widget.ListView; 51 import android.widget.SimpleCursorAdapter; 52 import android.widget.TextView; 53 54 import java.util.ArrayList; 55 56 /** 57 * SIM Address Book UI for the Phone app. 58 */ 59 public class SimContacts extends ADNList { 60 private static final String LOG_TAG = "SimContacts"; 61 62 private static final String UP_ACTIVITY_PACKAGE = "com.android.contacts"; 63 private static final String UP_ACTIVITY_CLASS = 64 "com.android.contacts.activities.PeopleActivity"; 65 66 static final ContentValues sEmptyContentValues = new ContentValues(); 67 68 private static final int MENU_IMPORT_ONE = 1; 69 private static final int MENU_IMPORT_ALL = 2; 70 private ProgressDialog mProgressDialog; 71 72 private Account mAccount; 73 74 private static class NamePhoneTypePair { 75 final String name; 76 final int phoneType; NamePhoneTypePair(String nameWithPhoneType)77 public NamePhoneTypePair(String nameWithPhoneType) { 78 // Look for /W /H /M or /O at the end of the name signifying the type 79 int nameLen = nameWithPhoneType.length(); 80 if (nameLen - 2 >= 0 && nameWithPhoneType.charAt(nameLen - 2) == '/') { 81 char c = Character.toUpperCase(nameWithPhoneType.charAt(nameLen - 1)); 82 if (c == 'W') { 83 phoneType = Phone.TYPE_WORK; 84 } else if (c == 'M' || c == 'O') { 85 phoneType = Phone.TYPE_MOBILE; 86 } else if (c == 'H') { 87 phoneType = Phone.TYPE_HOME; 88 } else { 89 phoneType = Phone.TYPE_OTHER; 90 } 91 name = nameWithPhoneType.substring(0, nameLen - 2); 92 } else { 93 phoneType = Phone.TYPE_OTHER; 94 name = nameWithPhoneType; 95 } 96 } 97 } 98 99 private class ImportAllSimContactsThread extends Thread 100 implements OnCancelListener, OnClickListener { 101 102 boolean mCanceled = false; 103 ImportAllSimContactsThread()104 public ImportAllSimContactsThread() { 105 super("ImportAllSimContactsThread"); 106 } 107 108 @Override run()109 public void run() { 110 final ContentValues emptyContentValues = new ContentValues(); 111 final ContentResolver resolver = getContentResolver(); 112 113 mCursor.moveToPosition(-1); 114 while (!mCanceled && mCursor.moveToNext()) { 115 actuallyImportOneSimContact(mCursor, resolver, mAccount); 116 mProgressDialog.incrementProgressBy(1); 117 } 118 119 mProgressDialog.dismiss(); 120 finish(); 121 } 122 onCancel(DialogInterface dialog)123 public void onCancel(DialogInterface dialog) { 124 mCanceled = true; 125 } 126 onClick(DialogInterface dialog, int which)127 public void onClick(DialogInterface dialog, int which) { 128 if (which == DialogInterface.BUTTON_NEGATIVE) { 129 mCanceled = true; 130 mProgressDialog.dismiss(); 131 } else { 132 Log.e(LOG_TAG, "Unknown button event has come: " + dialog.toString()); 133 } 134 } 135 } 136 actuallyImportOneSimContact( final Cursor cursor, final ContentResolver resolver, Account account)137 private static void actuallyImportOneSimContact( 138 final Cursor cursor, final ContentResolver resolver, Account account) { 139 final NamePhoneTypePair namePhoneTypePair = 140 new NamePhoneTypePair(cursor.getString(NAME_COLUMN)); 141 final String name = namePhoneTypePair.name; 142 final int phoneType = namePhoneTypePair.phoneType; 143 final String phoneNumber = cursor.getString(NUMBER_COLUMN); 144 final String emailAddresses = cursor.getString(EMAILS_COLUMN); 145 final String[] emailAddressArray; 146 if (!TextUtils.isEmpty(emailAddresses)) { 147 emailAddressArray = emailAddresses.split(","); 148 } else { 149 emailAddressArray = null; 150 } 151 152 final ArrayList<ContentProviderOperation> operationList = 153 new ArrayList<ContentProviderOperation>(); 154 ContentProviderOperation.Builder builder = 155 ContentProviderOperation.newInsert(RawContacts.CONTENT_URI); 156 String myGroupsId = null; 157 if (account != null) { 158 builder.withValue(RawContacts.ACCOUNT_NAME, account.name); 159 builder.withValue(RawContacts.ACCOUNT_TYPE, account.type); 160 } else { 161 builder.withValues(sEmptyContentValues); 162 } 163 operationList.add(builder.build()); 164 165 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 166 builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0); 167 builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 168 builder.withValue(StructuredName.DISPLAY_NAME, name); 169 operationList.add(builder.build()); 170 171 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 172 builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0); 173 builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 174 builder.withValue(Phone.TYPE, phoneType); 175 builder.withValue(Phone.NUMBER, phoneNumber); 176 builder.withValue(Data.IS_PRIMARY, 1); 177 operationList.add(builder.build()); 178 179 if (emailAddresses != null) { 180 for (String emailAddress : emailAddressArray) { 181 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 182 builder.withValueBackReference(Email.RAW_CONTACT_ID, 0); 183 builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 184 builder.withValue(Email.TYPE, Email.TYPE_MOBILE); 185 builder.withValue(Email.DATA, emailAddress); 186 operationList.add(builder.build()); 187 } 188 } 189 190 if (myGroupsId != null) { 191 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 192 builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0); 193 builder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 194 builder.withValue(GroupMembership.GROUP_SOURCE_ID, myGroupsId); 195 operationList.add(builder.build()); 196 } 197 198 try { 199 resolver.applyBatch(ContactsContract.AUTHORITY, operationList); 200 } catch (RemoteException e) { 201 Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage())); 202 } catch (OperationApplicationException e) { 203 Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage())); 204 } 205 } 206 importOneSimContact(int position)207 private void importOneSimContact(int position) { 208 final ContentResolver resolver = getContentResolver(); 209 if (mCursor.moveToPosition(position)) { 210 actuallyImportOneSimContact(mCursor, resolver, mAccount); 211 } else { 212 Log.e(LOG_TAG, "Failed to move the cursor to the position \"" + position + "\""); 213 } 214 } 215 216 /* Followings are overridden methods */ 217 218 @Override onCreate(Bundle icicle)219 protected void onCreate(Bundle icicle) { 220 super.onCreate(icicle); 221 222 Intent intent = getIntent(); 223 if (intent != null) { 224 final String accountName = intent.getStringExtra("account_name"); 225 final String accountType = intent.getStringExtra("account_type"); 226 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) { 227 mAccount = new Account(accountName, accountType); 228 } 229 } 230 231 registerForContextMenu(getListView()); 232 233 ActionBar actionBar = getActionBar(); 234 if (actionBar != null) { 235 // android.R.id.home will be triggered in onOptionsItemSelected() 236 actionBar.setDisplayHomeAsUpEnabled(true); 237 } 238 } 239 240 @Override newAdapter()241 protected CursorAdapter newAdapter() { 242 return new SimpleCursorAdapter(this, R.layout.sim_import_list_entry, mCursor, 243 new String[] { "name" }, new int[] { android.R.id.text1 }); 244 } 245 246 @Override resolveIntent()247 protected Uri resolveIntent() { 248 Intent intent = getIntent(); 249 intent.setData(Uri.parse("content://icc/adn")); 250 if (Intent.ACTION_PICK.equals(intent.getAction())) { 251 // "index" is 1-based 252 mInitialSelection = intent.getIntExtra("index", 0) - 1; 253 } 254 return intent.getData(); 255 } 256 257 @Override onCreateOptionsMenu(Menu menu)258 public boolean onCreateOptionsMenu(Menu menu) { 259 super.onCreateOptionsMenu(menu); 260 menu.add(0, MENU_IMPORT_ALL, 0, R.string.importAllSimEntries); 261 return true; 262 } 263 264 @Override onPrepareOptionsMenu(Menu menu)265 public boolean onPrepareOptionsMenu(Menu menu) { 266 MenuItem item = menu.findItem(MENU_IMPORT_ALL); 267 if (item != null) { 268 item.setVisible(mCursor != null && mCursor.getCount() > 0); 269 } 270 return super.onPrepareOptionsMenu(menu); 271 } 272 273 @Override onOptionsItemSelected(MenuItem item)274 public boolean onOptionsItemSelected(MenuItem item) { 275 switch (item.getItemId()) { 276 case android.R.id.home: 277 Intent intent = new Intent(); 278 intent.setClassName(UP_ACTIVITY_PACKAGE, UP_ACTIVITY_CLASS); 279 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 280 startActivity(intent); 281 finish(); 282 return true; 283 case MENU_IMPORT_ALL: 284 CharSequence title = getString(R.string.importAllSimEntries); 285 CharSequence message = getString(R.string.importingSimContacts); 286 287 ImportAllSimContactsThread thread = new ImportAllSimContactsThread(); 288 289 // TODO: need to show some error dialog. 290 if (mCursor == null) { 291 Log.e(LOG_TAG, "cursor is null. Ignore silently."); 292 break; 293 } 294 mProgressDialog = new ProgressDialog(this); 295 mProgressDialog.setTitle(title); 296 mProgressDialog.setMessage(message); 297 mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 298 mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, 299 getString(R.string.cancel), thread); 300 mProgressDialog.setProgress(0); 301 mProgressDialog.setMax(mCursor.getCount()); 302 mProgressDialog.show(); 303 304 thread.start(); 305 306 return true; 307 } 308 return super.onOptionsItemSelected(item); 309 } 310 311 @Override onContextItemSelected(MenuItem item)312 public boolean onContextItemSelected(MenuItem item) { 313 switch (item.getItemId()) { 314 case MENU_IMPORT_ONE: 315 ContextMenu.ContextMenuInfo menuInfo = item.getMenuInfo(); 316 if (menuInfo instanceof AdapterView.AdapterContextMenuInfo) { 317 int position = ((AdapterView.AdapterContextMenuInfo)menuInfo).position; 318 importOneSimContact(position); 319 return true; 320 } 321 } 322 return super.onContextItemSelected(item); 323 } 324 325 @Override onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)326 public void onCreateContextMenu(ContextMenu menu, View v, 327 ContextMenu.ContextMenuInfo menuInfo) { 328 if (menuInfo instanceof AdapterView.AdapterContextMenuInfo) { 329 AdapterView.AdapterContextMenuInfo itemInfo = 330 (AdapterView.AdapterContextMenuInfo) menuInfo; 331 TextView textView = (TextView) itemInfo.targetView.findViewById(android.R.id.text1); 332 if (textView != null) { 333 menu.setHeaderTitle(textView.getText()); 334 } 335 menu.add(0, MENU_IMPORT_ONE, 0, R.string.importSimEntry); 336 } 337 } 338 339 @Override onListItemClick(ListView l, View v, int position, long id)340 public void onListItemClick(ListView l, View v, int position, long id) { 341 importOneSimContact(position); 342 } 343 344 @Override onKeyDown(int keyCode, KeyEvent event)345 public boolean onKeyDown(int keyCode, KeyEvent event) { 346 switch (keyCode) { 347 case KeyEvent.KEYCODE_CALL: { 348 if (mCursor != null && mCursor.moveToPosition(getSelectedItemPosition())) { 349 String phoneNumber = mCursor.getString(NUMBER_COLUMN); 350 if (phoneNumber == null || !TextUtils.isGraphic(phoneNumber)) { 351 // There is no number entered. 352 //TODO play error sound or something... 353 return true; 354 } 355 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 356 Uri.fromParts(Constants.SCHEME_TEL, phoneNumber, null)); 357 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 358 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 359 startActivity(intent); 360 finish(); 361 return true; 362 } 363 } 364 } 365 return super.onKeyDown(keyCode, event); 366 } 367 } 368