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 */ 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