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