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