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