ImportExportDialogFragment.java revision 941090ea5f2cd169038bd9f6c2811d6bc12db6a5
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
17package com.android.contacts.common.interactions;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.app.DialogFragment;
23import android.app.FragmentManager;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.res.Resources;
28import android.database.Cursor;
29import android.net.Uri;
30import android.os.Bundle;
31import android.provider.ContactsContract.Contacts;
32import android.telephony.PhoneNumberUtils;
33import android.telephony.SubscriptionInfo;
34import android.telephony.SubscriptionManager;
35import android.telephony.TelephonyManager;
36import android.text.TextUtils;
37import android.util.Log;
38import android.view.LayoutInflater;
39import android.view.View;
40import android.view.ViewGroup;
41import android.widget.ArrayAdapter;
42import android.widget.TextView;
43import android.widget.Toast;
44
45import com.android.contacts.common.R;
46import com.android.contacts.common.editor.SelectAccountDialogFragment;
47import com.android.contacts.common.model.AccountTypeManager;
48import com.android.contacts.common.model.account.AccountWithDataSet;
49import com.android.contacts.common.util.AccountSelectionUtil;
50import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
51import com.android.contacts.common.vcard.ExportVCardActivity;
52import com.android.contacts.common.vcard.VCardCommonArguments;
53import com.android.contacts.commonbind.analytics.AnalyticsUtil;
54
55import java.util.Collections;
56import java.util.List;
57
58/**
59 * An dialog invoked to import/export contacts.
60 */
61public 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.getPhoneTtsSpannable(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            // Store a nonsense value for mSubscriptionId. If this constructor is used,
308            // the mSubscriptionId value should not be read later.
309            this(label, resId, /* subId = */ -1);
310        }
311    }
312}
313