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.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.os.Bundle;
     29 import android.support.v4.text.BidiFormatter;
     30 import android.support.v4.text.TextDirectionHeuristicsCompat;
     31 import android.text.TextUtils;
     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 
     39 import com.android.contacts.R;
     40 import com.android.contacts.activities.SimImportActivity;
     41 import com.android.contacts.compat.CompatUtils;
     42 import com.android.contacts.compat.PhoneNumberUtilsCompat;
     43 import com.android.contacts.database.SimContactDao;
     44 import com.android.contacts.editor.SelectAccountDialogFragment;
     45 import com.android.contacts.model.AccountTypeManager;
     46 import com.android.contacts.model.SimCard;
     47 import com.android.contacts.model.SimContact;
     48 import com.android.contacts.model.account.AccountInfo;
     49 import com.android.contacts.model.account.AccountWithDataSet;
     50 import com.android.contacts.util.AccountSelectionUtil;
     51 import com.google.common.util.concurrent.Futures;
     52 
     53 import java.util.List;
     54 import java.util.concurrent.Future;
     55 
     56 /**
     57  * An dialog invoked to import/export contacts.
     58  */
     59 public class ImportDialogFragment extends DialogFragment {
     60     public static final String TAG = "ImportDialogFragment";
     61 
     62     public static final String KEY_RES_ID = "resourceId";
     63     public static final String KEY_SUBSCRIPTION_ID = "subscriptionId";
     64 
     65     public static final String EXTRA_SIM_ONLY = "extraSimOnly";
     66 
     67     public static final String EXTRA_SIM_CONTACT_COUNT_PREFIX = "simContactCount_";
     68 
     69     private boolean mSimOnly = false;
     70     private SimContactDao mSimDao;
     71 
     72     private Future<List<AccountInfo>> mAccountsFuture;
     73 
     74     private static BidiFormatter sBidiFormatter = BidiFormatter.getInstance();
     75 
     76     /** Preferred way to show this dialog */
     77     public static void show(FragmentManager fragmentManager) {
     78         final ImportDialogFragment fragment = new ImportDialogFragment();
     79         fragment.show(fragmentManager, TAG);
     80     }
     81 
     82     public static void show(FragmentManager fragmentManager, List<SimCard> sims,
     83             boolean includeVcf) {
     84         final ImportDialogFragment fragment = new ImportDialogFragment();
     85         final Bundle args = new Bundle();
     86         args.putBoolean(EXTRA_SIM_ONLY, !includeVcf);
     87         for (SimCard sim : sims) {
     88             final List<SimContact> contacts = sim.getContacts();
     89             if (contacts == null) {
     90                 continue;
     91             }
     92             args.putInt(EXTRA_SIM_CONTACT_COUNT_PREFIX + sim.getSimId(), contacts.size());
     93         }
     94 
     95         fragment.setArguments(args);
     96         fragment.show(fragmentManager, TAG);
     97     }
     98 
     99     @Override
    100     public void onCreate(Bundle savedInstanceState) {
    101         super.onCreate(savedInstanceState);
    102 
    103         setStyle(STYLE_NORMAL, R.style.ContactsAlertDialogTheme);
    104 
    105         final Bundle args = getArguments();
    106         mSimOnly = args != null && args.getBoolean(EXTRA_SIM_ONLY, false);
    107         mSimDao = SimContactDao.create(getContext());
    108     }
    109 
    110     @Override
    111     public void onResume() {
    112         super.onResume();
    113 
    114         // Start loading the accounts. This is done in onResume in case they were refreshed.
    115         mAccountsFuture = AccountTypeManager.getInstance(getActivity()).filterAccountsAsync(
    116                 AccountTypeManager.writableFilter());
    117     }
    118 
    119     @Override
    120     public Context getContext() {
    121         return getActivity();
    122     }
    123 
    124     @Override
    125     public void onAttach(Activity activity) {
    126         super.onAttach(activity);
    127     }
    128 
    129     @Override
    130     public Dialog onCreateDialog(Bundle savedInstanceState) {
    131         final LayoutInflater dialogInflater = (LayoutInflater)
    132                 getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    133 
    134         // Adapter that shows a list of string resources
    135         final ArrayAdapter<AdapterEntry> adapter = new ArrayAdapter<AdapterEntry>(getActivity(),
    136                 R.layout.select_dialog_item) {
    137 
    138             @Override
    139             public View getView(int position, View convertView, ViewGroup parent) {
    140                 final View result = convertView != null ? convertView :
    141                         dialogInflater.inflate(R.layout.select_dialog_item, parent, false);
    142                 final TextView primaryText = (TextView) result.findViewById(R.id.primary_text);
    143                 final TextView secondaryText = (TextView) result.findViewById(R.id.secondary_text);
    144                 final AdapterEntry entry = getItem(position);
    145                 secondaryText.setVisibility(View.GONE);
    146                 if (entry.mChoiceResourceId == R.string.import_from_sim) {
    147                     final CharSequence secondary = getSimSecondaryText(entry.mSim);
    148                     if (TextUtils.isEmpty(secondary)) {
    149                         secondaryText.setVisibility(View.GONE);
    150                     } else {
    151                         secondaryText.setText(secondary);
    152                         secondaryText.setVisibility(View.VISIBLE);
    153                     }
    154                 }
    155                 primaryText.setText(entry.mLabel);
    156                 return result;
    157             }
    158 
    159             CharSequence getSimSecondaryText(SimCard sim) {
    160                 int count = getSimContactCount(sim);
    161 
    162                 CharSequence phone = sim.getFormattedPhone();
    163                 if (phone == null) {
    164                     phone = sim.getPhone();
    165                 }
    166                 if (phone != null) {
    167                     phone = sBidiFormatter.unicodeWrap(
    168                             PhoneNumberUtilsCompat.createTtsSpannable(phone),
    169                             TextDirectionHeuristicsCompat.LTR);
    170                 }
    171 
    172                 if (count != -1 && phone != null) {
    173                     // We use a template instead of format string so that the TTS span is preserved
    174                     final CharSequence template = getResources()
    175                             .getQuantityString(R.plurals.import_from_sim_secondary_template, count);
    176                     return TextUtils.expandTemplate(template, String.valueOf(count), phone);
    177                 } else if (phone != null) {
    178                     return phone;
    179                 } else if (count != -1) {
    180                     // count != -1
    181                     return getResources()
    182                             .getQuantityString(
    183                                     R.plurals.import_from_sim_secondary_contact_count_fmt, count,
    184                                     count);
    185                 } else {
    186                     return null;
    187                 }
    188             }
    189         };
    190 
    191         addItems(adapter);
    192 
    193         final DialogInterface.OnClickListener clickListener =
    194                 new DialogInterface.OnClickListener() {
    195             @Override
    196             public void onClick(DialogInterface dialog, int which) {
    197                 final int resId = adapter.getItem(which).mChoiceResourceId;
    198                 if (resId == R.string.import_from_sim) {
    199                     handleSimImportRequest(adapter.getItem(which).mSim);
    200                 } else if (resId == R.string.import_from_vcf_file) {
    201                     handleImportRequest(resId, SimCard.NO_SUBSCRIPTION_ID);
    202                 } else {
    203                     Log.e(TAG, "Unexpected resource: "
    204                             + getActivity().getResources().getResourceEntryName(resId));
    205                 }
    206                 dialog.dismiss();
    207             }
    208         };
    209 
    210         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), getTheme())
    211                 .setTitle(R.string.dialog_import)
    212                 .setNegativeButton(android.R.string.cancel, null);
    213         if (adapter.isEmpty()) {
    214             // Handle edge case; e.g. SIM card was removed.
    215             builder.setMessage(R.string.nothing_to_import_message);
    216         } else {
    217             builder.setSingleChoiceItems(adapter, -1, clickListener);
    218         }
    219 
    220         return builder.create();
    221     }
    222 
    223     private int getSimContactCount(SimCard sim) {
    224         if (sim.getContacts() != null) {
    225             return sim.getContacts().size();
    226         }
    227         final Bundle args = getArguments();
    228         if (args == null) {
    229             return -1;
    230         }
    231         return args.getInt(EXTRA_SIM_CONTACT_COUNT_PREFIX + sim.getSimId(), -1);
    232     }
    233 
    234     private void addItems(ArrayAdapter<AdapterEntry> adapter) {
    235         final Resources res = getActivity().getResources();
    236         if (res.getBoolean(R.bool.config_allow_import_from_vcf_file) && !mSimOnly) {
    237             adapter.add(new AdapterEntry(getString(R.string.import_from_vcf_file),
    238                     R.string.import_from_vcf_file));
    239         }
    240         final List<SimCard> sims = mSimDao.getSimCards();
    241 
    242         if (sims.size() == 1) {
    243             adapter.add(new AdapterEntry(getString(R.string.import_from_sim),
    244                     R.string.import_from_sim, sims.get(0)));
    245             return;
    246         }
    247         for (int i = 0; i < sims.size(); i++) {
    248             final SimCard sim = sims.get(i);
    249             adapter.add(new AdapterEntry(getSimDescription(sim, i), R.string.import_from_sim, sim));
    250         }
    251     }
    252 
    253     private void handleSimImportRequest(SimCard sim) {
    254         startActivity(new Intent(getActivity(), SimImportActivity.class)
    255                 .putExtra(SimImportActivity.EXTRA_SUBSCRIPTION_ID, sim.getSubscriptionId()));
    256     }
    257 
    258     /**
    259      * Handle "import from SD".
    260      */
    261     private void handleImportRequest(int resId, int subscriptionId) {
    262         // Get the accounts. Because this only happens after a user action this should pretty
    263         // much never block since it will usually be at least several seconds before the user
    264         // interacts with the view
    265         final List<AccountWithDataSet> accountList = AccountInfo.extractAccounts(
    266                 Futures.getUnchecked(mAccountsFuture));
    267 
    268         // There are three possibilities:
    269         // - more than one accounts -> ask the user
    270         // - just one account -> use the account without asking the user
    271         // - no account -> use phone-local storage without asking the user
    272         final int size = accountList.size();
    273         if (size > 1) {
    274             // Send over to the account selector
    275             final Bundle args = new Bundle();
    276             args.putInt(KEY_RES_ID, resId);
    277             args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
    278             SelectAccountDialogFragment.show(
    279                     getFragmentManager(), R.string.dialog_new_contact_account,
    280                     AccountTypeManager.AccountFilter.CONTACTS_WRITABLE, args);
    281         } else {
    282             AccountSelectionUtil.doImport(getActivity(), resId,
    283                     (size == 1 ? accountList.get(0) : null),
    284                     (CompatUtils.isMSIMCompatible() ? subscriptionId : -1));
    285         }
    286     }
    287 
    288     private CharSequence getSimDescription(SimCard sim, int index) {
    289         final CharSequence name = sim.getDisplayName();
    290         if (name != null) {
    291             return getString(R.string.import_from_sim_summary_fmt, name);
    292         } else {
    293             return getString(R.string.import_from_sim_summary_fmt, String.valueOf(index));
    294         }
    295     }
    296 
    297     private static class AdapterEntry {
    298         public final CharSequence mLabel;
    299         public final int mChoiceResourceId;
    300         public final SimCard mSim;
    301 
    302         public AdapterEntry(CharSequence label, int resId, SimCard sim) {
    303             mLabel = label;
    304             mChoiceResourceId = resId;
    305             mSim = sim;
    306         }
    307 
    308         public AdapterEntry(String label, int resId) {
    309             // Store a nonsense value for mSubscriptionId. If this constructor is used,
    310             // the mSubscriptionId value should not be read later.
    311             this(label, resId, /* sim= */ null);
    312         }
    313     }
    314 }
    315