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 
17 package com.android.contacts.activities;
18 
19 import com.android.contacts.ContactSaveService;
20 import com.android.contacts.ContactsActivity;
21 import com.android.contacts.R;
22 import com.android.contacts.common.model.AccountTypeManager;
23 import com.android.contacts.common.model.account.AccountType;
24 import com.android.contacts.common.model.account.AccountWithDataSet;
25 import com.android.contacts.common.util.ImplicitIntentsUtil;
26 import com.android.contacts.editor.ContactEditorBaseFragment;
27 import com.android.contacts.editor.ContactEditorFragment;
28 import com.android.contacts.editor.EditorIntents;
29 import com.android.contacts.interactions.ContactDeletionInteraction;
30 import com.android.contacts.util.DialogManager;
31 
32 import android.app.ActionBar;
33 import android.app.Dialog;
34 import android.content.ContentValues;
35 import android.content.Intent;
36 import android.net.Uri;
37 import android.os.Bundle;
38 import android.provider.ContactsContract.Contacts;
39 import android.provider.ContactsContract.RawContacts;
40 import android.util.Log;
41 import android.view.View;
42 import android.view.inputmethod.InputMethodManager;
43 
44 import java.util.ArrayList;
45 
46 /**
47  * Base Activity for contact editors.
48  */
49 abstract 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          */
setListener(ContactEditorBaseFragment.Listener listener)147         void setListener(ContactEditorBaseFragment.Listener listener);
148 
149         /**
150          * Initialize the contact editor.
151          */
load(String action, Uri lookupUri, Bundle intentExtras)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          */
setIntentExtras(Bundle extras)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          */
save(int saveMode)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          */
revert()169         boolean revert();
170 
171         /**
172          * Invoked after the contact is saved.
173          */
onSaveCompleted(boolean hadChanges, int saveMode, boolean saveSucceeded, Uri contactLookupUri, Long joinContactId)174         void onSaveCompleted(boolean hadChanges, int saveMode, boolean saveSucceeded,
175                 Uri contactLookupUri, Long joinContactId);
176 
177         /**
178          * Invoked after the contact is joined.
179          */
onJoinCompleted(Uri uri)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
onCreate(Bundle savedState)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
onPause()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
onNewIntent(Intent intent)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
onCreateDialog(int id, Bundle args)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
onBackPressed()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
getDialogManager()382     public DialogManager getDialogManager() {
383         return mDialogManager;
384     }
385 }
386