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