1/*
2 * Copyright (C) 2015 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.activities;
18
19import com.android.contacts.ContactSaveService;
20import com.android.contacts.ContactsActivity;
21import com.android.contacts.R;
22import com.android.contacts.common.model.AccountTypeManager;
23import com.android.contacts.common.model.account.AccountType;
24import com.android.contacts.common.model.account.AccountWithDataSet;
25import com.android.contacts.common.util.ImplicitIntentsUtil;
26import com.android.contacts.editor.ContactEditorBaseFragment;
27import com.android.contacts.editor.ContactEditorFragment;
28import com.android.contacts.editor.EditorIntents;
29import com.android.contacts.interactions.ContactDeletionInteraction;
30import com.android.contacts.util.DialogManager;
31
32import android.app.ActionBar;
33import android.app.Dialog;
34import android.content.ContentValues;
35import android.content.Intent;
36import android.net.Uri;
37import android.os.Bundle;
38import android.provider.ContactsContract.Contacts;
39import android.provider.ContactsContract.RawContacts;
40import android.util.Log;
41import android.view.View;
42import android.view.inputmethod.InputMethodManager;
43
44import java.util.ArrayList;
45
46/**
47 * Base Activity for contact editors.
48 */
49abstract public class ContactEditorBaseActivity extends ContactsActivity
50        implements DialogManager.DialogShowingViewActivity {
51    protected static final String TAG = "ContactEditorActivity";
52
53    /**
54     * Intent action to edit a contact with all available field inputs displayed.
55     *
56     * Only used to open the "fully expanded" editor -- {@link ContactEditorActivity}.
57     */
58    public static final String ACTION_EDIT = "com.android.contacts.action.FULL_EDIT";
59
60    /**
61     * Intent action to insert a new contact with all available field inputs displayed.
62     *
63     * Only used to open the "fully expanded" editor -- {@link ContactEditorActivity}.
64     */
65    public static final String ACTION_INSERT = "com.android.contacts.action.FULL_INSERT";
66
67    public static final String ACTION_JOIN_COMPLETED = "joinCompleted";
68    public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
69
70    public static final int RESULT_CODE_SPLIT = 2;
71
72    protected int mActionBarTitleResId;
73
74    /**
75     * Contract for contact editors Fragments that are managed by this Activity.
76     */
77    public interface ContactEditor {
78
79        /**
80         * Modes that specify what the AsyncTask has to perform after saving
81         */
82        public interface SaveMode {
83            /**
84             * Close the editor after saving
85             */
86            public static final int CLOSE = 0;
87
88            /**
89             * Reload the data so that the user can continue editing
90             */
91            public static final int RELOAD = 1;
92
93            /**
94             * Split the contact after saving
95             */
96            public static final int SPLIT = 2;
97
98            /**
99             * Join another contact after saving
100             */
101            public static final int JOIN = 3;
102
103            /**
104             * Navigate to the compact editor view after saving.
105             */
106            public static final int COMPACT = 4;
107        }
108
109        /**
110         * The status of the contact editor.
111         */
112        public interface Status {
113            /**
114             * The loader is fetching data
115             */
116            public static final int LOADING = 0;
117
118            /**
119             * Not currently busy. We are waiting for the user to enter data
120             */
121            public static final int EDITING = 1;
122
123            /**
124             * The data is currently being saved. This is used to prevent more
125             * auto-saves (they shouldn't overlap)
126             */
127            public static final int SAVING = 2;
128
129            /**
130             * Prevents any more saves. This is used if in the following cases:
131             * - After Save/Close
132             * - After Revert
133             * - After the user has accepted an edit suggestion
134             * - After the user chooses to expand the compact editor
135             */
136            public static final int CLOSING = 3;
137
138            /**
139             * Prevents saving while running a child activity.
140             */
141            public static final int SUB_ACTIVITY = 4;
142        }
143
144        /**
145         * Sets the hosting Activity that will receive callbacks from the contact editor.
146         */
147        void setListener(ContactEditorBaseFragment.Listener listener);
148
149        /**
150         * Initialize the contact editor.
151         */
152        void load(String action, Uri lookupUri, Bundle intentExtras);
153
154        /**
155         * Applies extras from the hosting Activity to the first writable raw contact.
156         */
157        void setIntentExtras(Bundle extras);
158
159        /**
160         * Saves or creates the contact based on the mode, and if successful
161         * finishes the activity.
162         */
163        boolean save(int saveMode);
164
165        /**
166         * If there are no unsaved changes, just close the editor, otherwise the user is prompted
167         * before discarding unsaved changes.
168         */
169        boolean revert();
170
171        /**
172         * Invoked after the contact is saved.
173         */
174        void onSaveCompleted(boolean hadChanges, int saveMode, boolean saveSucceeded,
175                Uri contactLookupUri, Long joinContactId);
176
177        /**
178         * Invoked after the contact is joined.
179         */
180        void onJoinCompleted(Uri uri);
181    }
182
183    /**
184     * Boolean intent key that specifies that this activity should finish itself
185     * (instead of launching a new view intent) after the editor changes have been
186     * saved.
187     */
188    public static final String INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED =
189            "finishActivityOnSaveCompleted";
190
191    protected ContactEditor mFragment;
192    private boolean mFinishActivityOnSaveCompleted;
193
194    private DialogManager mDialogManager = new DialogManager(this);
195
196    @Override
197    public void onCreate(Bundle savedState) {
198        super.onCreate(savedState);
199
200        final Intent intent = getIntent();
201        final String action = intent.getAction();
202
203        // Determine whether or not this activity should be finished after the user is done
204        // editing the contact or if this activity should launch another activity to view the
205        // contact's details.
206        mFinishActivityOnSaveCompleted = intent.getBooleanExtra(
207                INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, false);
208
209        // The only situation where action could be ACTION_JOIN_COMPLETED is if the
210        // user joined the contact with another and closed the activity before
211        // the save operation was completed.  The activity should remain closed then.
212        if (ACTION_JOIN_COMPLETED.equals(action)) {
213            finish();
214            return;
215        }
216
217        if (ACTION_SAVE_COMPLETED.equals(action)) {
218            finish();
219            return;
220        }
221
222        ActionBar actionBar = getActionBar();
223        if (actionBar != null) {
224            if (Intent.ACTION_EDIT.equals(action) || ACTION_EDIT.equals(action)) {
225                mActionBarTitleResId = R.string.contact_editor_title_existing_contact;
226            } else {
227                mActionBarTitleResId = R.string.contact_editor_title_new_contact;
228            }
229            actionBar.setTitle(getResources().getString(mActionBarTitleResId));
230            actionBar.setDisplayShowHomeEnabled(true);
231            actionBar.setDisplayHomeAsUpEnabled(true);
232        }
233    }
234
235    @Override
236    protected void onPause() {
237        super.onPause();
238        final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
239        final View currentFocus = getCurrentFocus();
240        if (imm != null && currentFocus != null) {
241            imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
242        }
243    }
244
245    @Override
246    protected void onNewIntent(Intent intent) {
247        super.onNewIntent(intent);
248
249        if (mFragment == null) {
250            return;
251        }
252
253        String action = intent.getAction();
254        if (Intent.ACTION_EDIT.equals(action) || ACTION_EDIT.equals(action)) {
255            mFragment.setIntentExtras(intent.getExtras());
256        } else if (ACTION_SAVE_COMPLETED.equals(action)) {
257            mFragment.onSaveCompleted(true,
258                    intent.getIntExtra(ContactEditorFragment.SAVE_MODE_EXTRA_KEY,
259                            ContactEditor.SaveMode.CLOSE),
260                    intent.getBooleanExtra(ContactSaveService.EXTRA_SAVE_SUCCEEDED, false),
261                    intent.getData(),
262                    intent.getLongExtra(ContactEditorFragment.JOIN_CONTACT_ID_EXTRA_KEY, -1));
263        } else if (ACTION_JOIN_COMPLETED.equals(action)) {
264            mFragment.onJoinCompleted(intent.getData());
265        }
266    }
267
268    @Override
269    protected Dialog onCreateDialog(int id, Bundle args) {
270        if (DialogManager.isManagedId(id)) return mDialogManager.onCreateDialog(id, args);
271
272        // Nobody knows about the Dialog
273        Log.w(TAG, "Unknown dialog requested, id: " + id + ", args: " + args);
274        return null;
275    }
276
277    @Override
278    public void onBackPressed() {
279        if (mFragment != null) {
280            mFragment.revert();
281        }
282    }
283
284    protected final ContactEditorBaseFragment.Listener  mFragmentListener =
285            new ContactEditorBaseFragment.Listener() {
286
287        @Override
288        public void onDeleteRequested(Uri contactUri) {
289            ContactDeletionInteraction.start(ContactEditorBaseActivity.this, contactUri, true);
290        }
291
292        @Override
293        public void onReverted() {
294            finish();
295        }
296
297        @Override
298        public void onSaveFinished(Intent resultIntent) {
299            if (mFinishActivityOnSaveCompleted) {
300                setResult(resultIntent == null ? RESULT_CANCELED : RESULT_OK, resultIntent);
301            } else if (resultIntent != null) {
302                ImplicitIntentsUtil.startActivityInApp(ContactEditorBaseActivity.this,
303                        resultIntent);
304            }
305            finish();
306        }
307
308        @Override
309        public void onContactSplit(Uri newLookupUri) {
310            setResult(RESULT_CODE_SPLIT, /* data */ null);
311            finish();
312        }
313
314        @Override
315        public void onContactNotFound() {
316            finish();
317        }
318
319        @Override
320        public void onEditOtherContactRequested(
321                Uri contactLookupUri, ArrayList<ContentValues> values) {
322            final Intent intent = EditorIntents.createEditOtherContactIntent(
323                    contactLookupUri, values);
324            ImplicitIntentsUtil.startActivityInApp(ContactEditorBaseActivity.this, intent);
325            finish();
326        }
327
328        @Override
329        public void onCustomCreateContactActivityRequested(AccountWithDataSet account,
330                Bundle intentExtras) {
331            final AccountTypeManager accountTypes =
332                    AccountTypeManager.getInstance(ContactEditorBaseActivity.this);
333            final AccountType accountType = accountTypes.getAccountType(
334                    account.type, account.dataSet);
335
336            Intent intent = new Intent();
337            intent.setClassName(accountType.syncAdapterPackageName,
338                    accountType.getCreateContactActivityClassName());
339            intent.setAction(Intent.ACTION_INSERT);
340            intent.setType(Contacts.CONTENT_ITEM_TYPE);
341            if (intentExtras != null) {
342                intent.putExtras(intentExtras);
343            }
344            intent.putExtra(RawContacts.ACCOUNT_NAME, account.name);
345            intent.putExtra(RawContacts.ACCOUNT_TYPE, account.type);
346            intent.putExtra(RawContacts.DATA_SET, account.dataSet);
347            intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
348                    | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
349            startActivity(intent);
350            finish();
351        }
352
353        @Override
354        public void onCustomEditContactActivityRequested(AccountWithDataSet account,
355                Uri rawContactUri, Bundle intentExtras, boolean redirect) {
356            final AccountTypeManager accountTypes =
357                    AccountTypeManager.getInstance(ContactEditorBaseActivity.this);
358            final AccountType accountType = accountTypes.getAccountType(
359                    account.type, account.dataSet);
360
361            Intent intent = new Intent();
362            intent.setClassName(accountType.syncAdapterPackageName,
363                    accountType.getEditContactActivityClassName());
364            intent.setAction(Intent.ACTION_EDIT);
365            intent.setData(rawContactUri);
366            if (intentExtras != null) {
367                intent.putExtras(intentExtras);
368            }
369
370            if (redirect) {
371                intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
372                        | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
373                startActivity(intent);
374                finish();
375            } else {
376                startActivity(intent);
377            }
378        }
379    };
380
381    @Override
382    public DialogManager getDialogManager() {
383        return mDialogManager;
384    }
385}
386