1 /* 2 * Copyright (C) 2010 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.contacts.interactions; 18 19 import android.app.AlertDialog; 20 import android.app.Dialog; 21 import android.app.DialogFragment; 22 import android.app.FragmentManager; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.res.Resources; 27 import android.database.Cursor; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.provider.ContactsContract.Contacts; 31 import android.telephony.TelephonyManager; 32 import android.util.Log; 33 import android.view.LayoutInflater; 34 import android.view.View; 35 import android.view.ViewGroup; 36 import android.widget.ArrayAdapter; 37 import android.widget.TextView; 38 import android.widget.Toast; 39 40 import com.android.contacts.R; 41 import com.android.contacts.editor.SelectAccountDialogFragment; 42 import com.android.contacts.model.AccountTypeManager; 43 import com.android.contacts.model.account.AccountWithDataSet; 44 import com.android.contacts.util.AccountSelectionUtil; 45 import com.android.contacts.util.AccountsListAdapter.AccountListFilter; 46 import com.android.contacts.vcard.ExportVCardActivity; 47 48 import java.util.List; 49 50 /** 51 * An dialog invoked to import/export contacts. 52 */ 53 public class ImportExportDialogFragment extends DialogFragment 54 implements SelectAccountDialogFragment.Listener { 55 public static final String TAG = "ImportExportDialogFragment"; 56 57 private static final String KEY_RES_ID = "resourceId"; 58 private static final String ARG_CONTACTS_ARE_AVAILABLE = "CONTACTS_ARE_AVAILABLE"; 59 60 private final String[] LOOKUP_PROJECTION = new String[] { 61 Contacts.LOOKUP_KEY 62 }; 63 64 /** Preferred way to show this dialog */ 65 public static void show(FragmentManager fragmentManager, boolean contactsAreAvailable) { 66 final ImportExportDialogFragment fragment = new ImportExportDialogFragment(); 67 Bundle args = new Bundle(); 68 args.putBoolean(ARG_CONTACTS_ARE_AVAILABLE, contactsAreAvailable); 69 fragment.setArguments(args); 70 fragment.show(fragmentManager, ImportExportDialogFragment.TAG); 71 } 72 73 @Override 74 public Dialog onCreateDialog(Bundle savedInstanceState) { 75 // Wrap our context to inflate list items using the correct theme 76 final Resources res = getActivity().getResources(); 77 final LayoutInflater dialogInflater = (LayoutInflater)getActivity() 78 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 79 final boolean contactsAreAvailable = getArguments().getBoolean(ARG_CONTACTS_ARE_AVAILABLE); 80 81 // Adapter that shows a list of string resources 82 final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(getActivity(), 83 R.layout.select_dialog_item) { 84 @Override 85 public View getView(int position, View convertView, ViewGroup parent) { 86 final TextView result = (TextView)(convertView != null ? convertView : 87 dialogInflater.inflate(R.layout.select_dialog_item, parent, false)); 88 89 final int resId = getItem(position); 90 result.setText(resId); 91 return result; 92 } 93 }; 94 95 if (TelephonyManager.getDefault().hasIccCard() 96 && res.getBoolean(R.bool.config_allow_sim_import)) { 97 adapter.add(R.string.import_from_sim); 98 } 99 if (res.getBoolean(R.bool.config_allow_import_from_sdcard)) { 100 adapter.add(R.string.import_from_sdcard); 101 } 102 if (res.getBoolean(R.bool.config_allow_export_to_sdcard)) { 103 if (contactsAreAvailable) { 104 adapter.add(R.string.export_to_sdcard); 105 } 106 } 107 if (res.getBoolean(R.bool.config_allow_share_visible_contacts)) { 108 if (contactsAreAvailable) { 109 adapter.add(R.string.share_visible_contacts); 110 } 111 } 112 113 final DialogInterface.OnClickListener clickListener = 114 new DialogInterface.OnClickListener() { 115 @Override 116 public void onClick(DialogInterface dialog, int which) { 117 boolean dismissDialog; 118 final int resId = adapter.getItem(which); 119 switch (resId) { 120 case R.string.import_from_sim: 121 case R.string.import_from_sdcard: { 122 dismissDialog = handleImportRequest(resId); 123 break; 124 } 125 case R.string.export_to_sdcard: { 126 dismissDialog = true; 127 Intent exportIntent = new Intent(getActivity(), ExportVCardActivity.class); 128 getActivity().startActivity(exportIntent); 129 break; 130 } 131 case R.string.share_visible_contacts: { 132 dismissDialog = true; 133 doShareVisibleContacts(); 134 break; 135 } 136 default: { 137 dismissDialog = true; 138 Log.e(TAG, "Unexpected resource: " 139 + getActivity().getResources().getResourceEntryName(resId)); 140 } 141 } 142 if (dismissDialog) { 143 dialog.dismiss(); 144 } 145 } 146 }; 147 return new AlertDialog.Builder(getActivity()) 148 .setTitle(contactsAreAvailable 149 ? R.string.dialog_import_export 150 : R.string.dialog_import) 151 .setSingleChoiceItems(adapter, -1, clickListener) 152 .create(); 153 } 154 155 private void doShareVisibleContacts() { 156 // TODO move the query into a loader and do this in a background thread 157 final Cursor cursor = getActivity().getContentResolver().query(Contacts.CONTENT_URI, 158 LOOKUP_PROJECTION, Contacts.IN_VISIBLE_GROUP + "!=0", null, null); 159 if (cursor != null) { 160 try { 161 if (!cursor.moveToFirst()) { 162 Toast.makeText(getActivity(), R.string.share_error, Toast.LENGTH_SHORT).show(); 163 return; 164 } 165 166 StringBuilder uriListBuilder = new StringBuilder(); 167 int index = 0; 168 do { 169 if (index != 0) 170 uriListBuilder.append(':'); 171 uriListBuilder.append(cursor.getString(0)); 172 index++; 173 } while (cursor.moveToNext()); 174 Uri uri = Uri.withAppendedPath( 175 Contacts.CONTENT_MULTI_VCARD_URI, 176 Uri.encode(uriListBuilder.toString())); 177 178 final Intent intent = new Intent(Intent.ACTION_SEND); 179 intent.setType(Contacts.CONTENT_VCARD_TYPE); 180 intent.putExtra(Intent.EXTRA_STREAM, uri); 181 getActivity().startActivity(intent); 182 } finally { 183 cursor.close(); 184 } 185 } 186 } 187 188 /** 189 * Handle "import from SIM" and "import from SD". 190 * 191 * @return {@code true} if the dialog show be closed. {@code false} otherwise. 192 */ 193 private boolean handleImportRequest(int resId) { 194 // There are three possibilities: 195 // - more than one accounts -> ask the user 196 // - just one account -> use the account without asking the user 197 // - no account -> use phone-local storage without asking the user 198 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity()); 199 final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true); 200 final int size = accountList.size(); 201 if (size > 1) { 202 // Send over to the account selector 203 final Bundle args = new Bundle(); 204 args.putInt(KEY_RES_ID, resId); 205 SelectAccountDialogFragment.show( 206 getFragmentManager(), this, 207 R.string.dialog_new_contact_account, 208 AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, args); 209 210 // In this case, because this DialogFragment is used as a target fragment to 211 // SelectAccountDialogFragment, we can't close it yet. We close the dialog when 212 // we get a callback from it. 213 return false; 214 } 215 216 AccountSelectionUtil.doImport(getActivity(), resId, 217 (size == 1 ? accountList.get(0) : null)); 218 return true; // Close the dialog. 219 } 220 221 /** 222 * Called when an account is selected on {@link SelectAccountDialogFragment}. 223 */ 224 @Override 225 public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) { 226 AccountSelectionUtil.doImport(getActivity(), extraArgs.getInt(KEY_RES_ID), account); 227 228 // At this point the dialog is still showing (which is why we can use getActivity() above) 229 // So close it. 230 dismiss(); 231 } 232 233 @Override 234 public void onAccountSelectorCancelled() { 235 // See onAccountChosen() -- at this point the dialog is still showing. Close it. 236 dismiss(); 237 } 238 } 239