Home | History | Annotate | Download | only in interactions
      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.common.interactions;
     18 
     19 import android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.DialogFragment;
     23 import android.app.FragmentManager;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.res.Resources;
     28 import android.database.Cursor;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.provider.ContactsContract.Contacts;
     32 import android.telephony.PhoneNumberUtils;
     33 import android.telephony.SubscriptionInfo;
     34 import android.telephony.SubscriptionManager;
     35 import android.telephony.TelephonyManager;
     36 import android.text.TextUtils;
     37 import android.util.Log;
     38 import android.view.LayoutInflater;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 import android.widget.ArrayAdapter;
     42 import android.widget.TextView;
     43 import android.widget.Toast;
     44 
     45 import com.android.contacts.common.R;
     46 import com.android.contacts.common.editor.SelectAccountDialogFragment;
     47 import com.android.contacts.common.model.AccountTypeManager;
     48 import com.android.contacts.common.model.account.AccountWithDataSet;
     49 import com.android.contacts.common.util.AccountSelectionUtil;
     50 import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
     51 import com.android.contacts.common.vcard.ExportVCardActivity;
     52 import com.android.contacts.common.vcard.VCardCommonArguments;
     53 import com.android.contacts.commonbind.analytics.AnalyticsUtil;
     54 
     55 import java.util.Collections;
     56 import java.util.List;
     57 
     58 /**
     59  * An dialog invoked to import/export contacts.
     60  */
     61 public class ImportExportDialogFragment extends DialogFragment
     62         implements SelectAccountDialogFragment.Listener {
     63     public static final String TAG = "ImportExportDialogFragment";
     64 
     65     private static final String KEY_RES_ID = "resourceId";
     66     private static final String KEY_SUBSCRIPTION_ID = "subscriptionId";
     67     private static final String ARG_CONTACTS_ARE_AVAILABLE = "CONTACTS_ARE_AVAILABLE";
     68 
     69     private final String[] LOOKUP_PROJECTION = new String[] {
     70             Contacts.LOOKUP_KEY
     71     };
     72 
     73     private SubscriptionManager mSubscriptionManager;
     74 
     75     /** Preferred way to show this dialog */
     76     public static void show(FragmentManager fragmentManager, boolean contactsAreAvailable,
     77             Class callingActivity) {
     78         final ImportExportDialogFragment fragment = new ImportExportDialogFragment();
     79         Bundle args = new Bundle();
     80         args.putBoolean(ARG_CONTACTS_ARE_AVAILABLE, contactsAreAvailable);
     81         args.putString(VCardCommonArguments.ARG_CALLING_ACTIVITY, callingActivity.getName());
     82         fragment.setArguments(args);
     83         fragment.show(fragmentManager, ImportExportDialogFragment.TAG);
     84     }
     85 
     86     @Override
     87     public void onAttach(Activity activity) {
     88         super.onAttach(activity);
     89         AnalyticsUtil.sendScreenView(this);
     90     }
     91 
     92     @Override
     93     public Dialog onCreateDialog(Bundle savedInstanceState) {
     94         // Wrap our context to inflate list items using the correct theme
     95         final Resources res = getActivity().getResources();
     96         final LayoutInflater dialogInflater = (LayoutInflater)getActivity()
     97                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     98         final boolean contactsAreAvailable = getArguments().getBoolean(ARG_CONTACTS_ARE_AVAILABLE);
     99         final String callingActivity = getArguments().getString(
    100                 VCardCommonArguments.ARG_CALLING_ACTIVITY);
    101 
    102         // Adapter that shows a list of string resources
    103         final ArrayAdapter<AdapterEntry> adapter = new ArrayAdapter<AdapterEntry>(getActivity(),
    104                 R.layout.select_dialog_item) {
    105             @Override
    106             public View getView(int position, View convertView, ViewGroup parent) {
    107                 final TextView result = (TextView)(convertView != null ? convertView :
    108                         dialogInflater.inflate(R.layout.select_dialog_item, parent, false));
    109 
    110                 result.setText(getItem(position).mLabel);
    111                 return result;
    112             }
    113         };
    114 
    115         final TelephonyManager manager =
    116                 (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
    117 
    118         mSubscriptionManager = SubscriptionManager.from(getActivity());
    119 
    120         if (res.getBoolean(R.bool.config_allow_import_from_sdcard)) {
    121             adapter.add(new AdapterEntry(getString(R.string.import_from_sdcard),
    122                     R.string.import_from_sdcard));
    123         }
    124         if (manager != null && res.getBoolean(R.bool.config_allow_sim_import)) {
    125             final List<SubscriptionInfo> subInfoRecords =
    126                     mSubscriptionManager.getActiveSubscriptionInfoList();
    127             if (subInfoRecords != null) {
    128                 if (subInfoRecords.size() == 1) {
    129                     adapter.add(new AdapterEntry(getString(R.string.import_from_sim),
    130                             R.string.import_from_sim, subInfoRecords.get(0).getSubscriptionId()));
    131                 } else {
    132                     for (SubscriptionInfo record : subInfoRecords) {
    133                         adapter.add(new AdapterEntry(getSubDescription(record),
    134                                 R.string.import_from_sim, record.getSubscriptionId()));
    135                     }
    136                 }
    137             }
    138         }
    139         if (res.getBoolean(R.bool.config_allow_export_to_sdcard)) {
    140             if (contactsAreAvailable) {
    141                 adapter.add(new AdapterEntry(getString(R.string.export_to_sdcard),
    142                         R.string.export_to_sdcard));
    143             }
    144         }
    145         if (res.getBoolean(R.bool.config_allow_share_visible_contacts)) {
    146             if (contactsAreAvailable) {
    147                 adapter.add(new AdapterEntry(getString(R.string.share_visible_contacts),
    148                         R.string.share_visible_contacts));
    149             }
    150         }
    151 
    152         final DialogInterface.OnClickListener clickListener =
    153                 new DialogInterface.OnClickListener() {
    154             @Override
    155             public void onClick(DialogInterface dialog, int which) {
    156                 boolean dismissDialog;
    157                 final int resId = adapter.getItem(which).mChoiceResourceId;
    158                 switch (resId) {
    159                     case R.string.import_from_sim:
    160                     case R.string.import_from_sdcard: {
    161                         dismissDialog = handleImportRequest(resId,
    162                                 adapter.getItem(which).mSubscriptionId);
    163                         break;
    164                     }
    165                     case R.string.export_to_sdcard: {
    166                         dismissDialog = true;
    167                         Intent exportIntent = new Intent(getActivity(), ExportVCardActivity.class);
    168                         exportIntent.putExtra(VCardCommonArguments.ARG_CALLING_ACTIVITY,
    169                                 callingActivity);
    170                         getActivity().startActivity(exportIntent);
    171                         break;
    172                     }
    173                     case R.string.share_visible_contacts: {
    174                         dismissDialog = true;
    175                         doShareVisibleContacts();
    176                         break;
    177                     }
    178                     default: {
    179                         dismissDialog = true;
    180                         Log.e(TAG, "Unexpected resource: "
    181                                 + getActivity().getResources().getResourceEntryName(resId));
    182                     }
    183                 }
    184                 if (dismissDialog) {
    185                     dialog.dismiss();
    186                 }
    187             }
    188         };
    189         return new AlertDialog.Builder(getActivity())
    190                 .setTitle(contactsAreAvailable
    191                         ? R.string.dialog_import_export
    192                         : R.string.dialog_import)
    193                 .setSingleChoiceItems(adapter, -1, clickListener)
    194                 .create();
    195     }
    196 
    197     private void doShareVisibleContacts() {
    198         // TODO move the query into a loader and do this in a background thread
    199         final Cursor cursor = getActivity().getContentResolver().query(Contacts.CONTENT_URI,
    200                 LOOKUP_PROJECTION, Contacts.IN_VISIBLE_GROUP + "!=0", null, null);
    201         if (cursor != null) {
    202             try {
    203                 if (!cursor.moveToFirst()) {
    204                     Toast.makeText(getActivity(), R.string.share_error, Toast.LENGTH_SHORT).show();
    205                     return;
    206                 }
    207 
    208                 StringBuilder uriListBuilder = new StringBuilder();
    209                 int index = 0;
    210                 do {
    211                     if (index != 0)
    212                         uriListBuilder.append(':');
    213                     uriListBuilder.append(cursor.getString(0));
    214                     index++;
    215                 } while (cursor.moveToNext());
    216                 Uri uri = Uri.withAppendedPath(
    217                         Contacts.CONTENT_MULTI_VCARD_URI,
    218                         Uri.encode(uriListBuilder.toString()));
    219 
    220                 final Intent intent = new Intent(Intent.ACTION_SEND);
    221                 intent.setType(Contacts.CONTENT_VCARD_TYPE);
    222                 intent.putExtra(Intent.EXTRA_STREAM, uri);
    223                 getActivity().startActivity(intent);
    224             } finally {
    225                 cursor.close();
    226             }
    227         }
    228     }
    229 
    230     /**
    231      * Handle "import from SIM" and "import from SD".
    232      *
    233      * @return {@code true} if the dialog show be closed.  {@code false} otherwise.
    234      */
    235     private boolean handleImportRequest(int resId, int subscriptionId) {
    236         // There are three possibilities:
    237         // - more than one accounts -> ask the user
    238         // - just one account -> use the account without asking the user
    239         // - no account -> use phone-local storage without asking the user
    240         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
    241         final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
    242         final int size = accountList.size();
    243         if (size > 1) {
    244             // Send over to the account selector
    245             final Bundle args = new Bundle();
    246             args.putInt(KEY_RES_ID, resId);
    247             args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
    248             SelectAccountDialogFragment.show(
    249                     getFragmentManager(), this,
    250                     R.string.dialog_new_contact_account,
    251                     AccountListFilter.ACCOUNTS_CONTACT_WRITABLE, args);
    252 
    253             // In this case, because this DialogFragment is used as a target fragment to
    254             // SelectAccountDialogFragment, we can't close it yet.  We close the dialog when
    255             // we get a callback from it.
    256             return false;
    257         }
    258 
    259         AccountSelectionUtil.doImport(getActivity(), resId,
    260                 (size == 1 ? accountList.get(0) : null), subscriptionId);
    261         return true; // Close the dialog.
    262     }
    263 
    264     /**
    265      * Called when an account is selected on {@link SelectAccountDialogFragment}.
    266      */
    267     @Override
    268     public void onAccountChosen(AccountWithDataSet account, Bundle extraArgs) {
    269         AccountSelectionUtil.doImport(getActivity(), extraArgs.getInt(KEY_RES_ID),
    270                 account, extraArgs.getInt(KEY_SUBSCRIPTION_ID));
    271 
    272         // At this point the dialog is still showing (which is why we can use getActivity() above)
    273         // So close it.
    274         dismiss();
    275     }
    276 
    277     @Override
    278     public void onAccountSelectorCancelled() {
    279         // See onAccountChosen() -- at this point the dialog is still showing.  Close it.
    280         dismiss();
    281     }
    282 
    283     private CharSequence getSubDescription(SubscriptionInfo record) {
    284         CharSequence name = record.getDisplayName();
    285         if (TextUtils.isEmpty(record.getNumber())) {
    286             // Don't include the phone number in the description, since we don't know the number.
    287             return getString(R.string.import_from_sim_summary_no_number, name);
    288         }
    289         return TextUtils.expandTemplate(
    290                 getString(R.string.import_from_sim_summary),
    291                 name,
    292                 PhoneNumberUtils.ttsSpanAsPhoneNumber(record.getNumber()));
    293     }
    294 
    295     private static class AdapterEntry {
    296         public final CharSequence mLabel;
    297         public final int mChoiceResourceId;
    298         public final int mSubscriptionId;
    299 
    300         public AdapterEntry(CharSequence label, int resId, int subId) {
    301             mLabel = label;
    302             mChoiceResourceId = resId;
    303             mSubscriptionId = subId;
    304         }
    305 
    306         public AdapterEntry(String label, int resId) {
    307             this(label, resId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
    308         }
    309     }
    310 }
    311