blob: 9f7f868d561a021f2e9a9f2e48c48d6826239448 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
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;
Tyler Gunn4d45d1c2014-09-12 22:17:53 -070041import android.telecom.PhoneAccount;
Brian Attwellc35836d2014-10-06 16:09:31 -070042import android.telephony.SubscriptionManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043import android.text.TextUtils;
44import android.util.Log;
45import android.view.ContextMenu;
46import android.view.KeyEvent;
47import android.view.Menu;
48import android.view.MenuItem;
49import android.view.View;
50import android.widget.AdapterView;
51import android.widget.CursorAdapter;
52import android.widget.ListView;
53import android.widget.SimpleCursorAdapter;
54import android.widget.TextView;
55
56import java.util.ArrayList;
57
58/**
59 * SIM Address Book UI for the Phone app.
60 */
61public class SimContacts extends ADNList {
62 private static final String LOG_TAG = "SimContacts";
63
Santos Cordon7d4ddf62013-07-10 11:58:08 -070064 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;
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
102 public ImportAllSimContactsThread() {
103 super("ImportAllSimContactsThread");
104 }
105
106 @Override
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
121 public void onCancel(DialogInterface dialog) {
122 mCanceled = true;
123 }
124
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
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
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
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
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
245 protected Uri resolveIntent() {
Brian Attwellc35836d2014-10-06 16:09:31 -0700246 final Intent intent = getIntent();
Jay Shraunerc46700b2014-11-25 14:03:44 -0800247 int subId = -1;
Brian Attwellc35836d2014-10-06 16:09:31 -0700248 if (intent.hasExtra("subscription_id")) {
Jay Shraunerc46700b2014-11-25 14:03:44 -0800249 subId = intent.getIntExtra("subscription_id", -1);
250 }
251 if (subId != -1) {
Brian Attwellc35836d2014-10-06 16:09:31 -0700252 intent.setData(Uri.parse("content://icc/adn/subId/" + subId));
253 } else {
254 intent.setData(Uri.parse("content://icc/adn"));
255 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700256 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
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
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
280 public boolean onOptionsItemSelected(MenuItem item) {
281 switch (item.getItemId()) {
282 case android.R.id.home:
Jay Shrauner3a124e62014-07-17 09:58:34 -0700283 onBackPressed();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700284 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
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
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
342 public void onListItemClick(ListView l, View v, int position, long id) {
343 importOneSimContact(position);
344 }
345
346 @Override
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,
Jay Shrauner137458b2014-09-05 14:27:25 -0700358 Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700359 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}