Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 1 | package com.android.contacts.activities; |
| 2 | |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 3 | import android.app.Activity; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 4 | import android.app.FragmentManager; |
| 5 | import android.app.FragmentTransaction; |
| 6 | import android.app.LoaderManager; |
| 7 | import android.content.ContentUris; |
| 8 | import android.content.Intent; |
| 9 | import android.content.Loader; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 10 | import android.net.Uri; |
| 11 | import android.os.Bundle; |
| 12 | import android.provider.ContactsContract; |
| 13 | import android.provider.ContactsContract.RawContacts; |
| 14 | import android.widget.Toast; |
| 15 | |
| 16 | import com.android.contacts.AppCompatContactsActivity; |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 17 | import com.android.contacts.ContactSaveService; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 18 | import com.android.contacts.R; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 19 | import com.android.contacts.editor.ContactEditorFragment; |
| 20 | import com.android.contacts.editor.EditorIntents; |
| 21 | import com.android.contacts.editor.PickRawContactDialogFragment; |
| 22 | import com.android.contacts.editor.PickRawContactLoader; |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 23 | import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata; |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 24 | import com.android.contacts.editor.SplitContactConfirmationDialogFragment; |
Gary Mai | 0a49afa | 2016-12-05 15:53:58 -0800 | [diff] [blame^] | 25 | import com.android.contacts.logging.EditorEvent; |
| 26 | import com.android.contacts.logging.Logger; |
| 27 | import com.android.contacts.model.AccountTypeManager; |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 28 | import com.android.contacts.quickcontact.QuickContactActivity; |
Gary Mai | 0a49afa | 2016-12-05 15:53:58 -0800 | [diff] [blame^] | 29 | import com.android.contacts.util.ImplicitIntentsUtil; |
| 30 | import com.android.contacts.util.MaterialColorMapUtils.MaterialPalette; |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 31 | import com.android.contactsbind.FeedbackHelper; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 32 | |
| 33 | /** |
| 34 | * Transparent springboard activity that hosts a dialog to select a raw contact to edit. |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 35 | * All intents coming out from this activity have {@code FLAG_ACTIVITY_FORWARD_RESULT} set. |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 36 | */ |
Gary Mai | 5336e6e | 2016-10-23 14:17:03 -0700 | [diff] [blame] | 37 | public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity implements |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 38 | PickRawContactDialogFragment.PickRawContactListener, |
| 39 | SplitContactConfirmationDialogFragment.Listener { |
| 40 | |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 41 | private static final String TAG = "EditorSpringBoard"; |
| 42 | private static final String TAG_RAW_CONTACTS_DIALOG = "rawContactsDialog"; |
Gary Mai | a84f1d4 | 2016-11-29 11:16:27 -0800 | [diff] [blame] | 43 | private static final String KEY_RAW_CONTACTS_METADATA = "rawContactsMetadata"; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 44 | private static final int LOADER_RAW_CONTACTS = 1; |
| 45 | |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 46 | public static final String EXTRA_SHOW_READ_ONLY = "showReadOnly"; |
| 47 | |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 48 | private Uri mUri; |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 49 | private RawContactsMetadata mResult; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 50 | private MaterialPalette mMaterialPalette; |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 51 | private boolean mHasWritableAccount; |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 52 | private boolean mShowReadOnly; |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 53 | private int mWritableAccountPosition; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 54 | |
| 55 | /** |
| 56 | * The contact data loader listener. |
| 57 | */ |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 58 | protected final LoaderManager.LoaderCallbacks<RawContactsMetadata> mRawContactLoaderListener = |
| 59 | new LoaderManager.LoaderCallbacks<RawContactsMetadata>() { |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 60 | |
| 61 | @Override |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 62 | public Loader<RawContactsMetadata> onCreateLoader(int id, Bundle args) { |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 63 | return new PickRawContactLoader(ContactEditorSpringBoardActivity.this, mUri); |
| 64 | } |
| 65 | |
| 66 | @Override |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 67 | public void onLoadFinished(Loader<RawContactsMetadata> loader, |
| 68 | RawContactsMetadata result) { |
| 69 | if (result == null) { |
| 70 | toastErrorAndFinish(); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 71 | return; |
| 72 | } |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 73 | mResult = result; |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 74 | onLoad(); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 75 | } |
| 76 | |
| 77 | @Override |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 78 | public void onLoaderReset(Loader<RawContactsMetadata> loader) { |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 79 | } |
| 80 | }; |
| 81 | |
| 82 | |
| 83 | @Override |
| 84 | protected void onCreate(Bundle savedInstanceState) { |
| 85 | super.onCreate(savedInstanceState); |
| 86 | |
| 87 | if (RequestPermissionsActivity.startPermissionActivity(this)) { |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | final Intent intent = getIntent(); |
| 92 | final String action = intent.getAction(); |
| 93 | |
| 94 | if (!Intent.ACTION_EDIT.equals(action)) { |
| 95 | finish(); |
| 96 | return; |
| 97 | } |
| 98 | // Just for shorter variable names. |
| 99 | final String primary = ContactEditorFragment.INTENT_EXTRA_MATERIAL_PALETTE_PRIMARY_COLOR; |
| 100 | final String secondary = |
| 101 | ContactEditorFragment.INTENT_EXTRA_MATERIAL_PALETTE_SECONDARY_COLOR; |
| 102 | if (intent.hasExtra(primary) && intent.hasExtra(secondary)) { |
| 103 | mMaterialPalette = new MaterialPalette(intent.getIntExtra(primary, -1), |
| 104 | intent.getIntExtra(secondary, -1)); |
| 105 | } |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 106 | mShowReadOnly = intent.getBooleanExtra(EXTRA_SHOW_READ_ONLY, false); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 107 | |
| 108 | mUri = intent.getData(); |
| 109 | final String authority = mUri.getAuthority(); |
| 110 | final String type = getContentResolver().getType(mUri); |
| 111 | // Go straight to editor if we're passed a raw contact Uri. |
| 112 | if (ContactsContract.AUTHORITY.equals(authority) && |
| 113 | RawContacts.CONTENT_ITEM_TYPE.equals(type)) { |
yaolu | 341d104 | 2016-11-07 15:46:46 -0800 | [diff] [blame] | 114 | Logger.logEditorEvent( |
| 115 | EditorEvent.EventType.SHOW_RAW_CONTACT_PICKER, /* numberRawContacts */ 0); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 116 | final long rawContactId = ContentUris.parseId(mUri); |
Gary Mai | 5336e6e | 2016-10-23 14:17:03 -0700 | [diff] [blame] | 117 | startEditorAndForwardExtras(getIntentForRawContact(rawContactId)); |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 118 | } else if (android.provider.Contacts.AUTHORITY.equals(authority)) { |
| 119 | // Fail if given a legacy URI. |
| 120 | FeedbackHelper.sendFeedback(this, TAG, |
| 121 | "Legacy Uri was passed to editor.", new IllegalArgumentException()); |
| 122 | toastErrorAndFinish(); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 123 | } else { |
| 124 | getLoaderManager().initLoader(LOADER_RAW_CONTACTS, null, mRawContactLoaderListener); |
| 125 | } |
| 126 | } |
| 127 | |
Gary Mai | 5336e6e | 2016-10-23 14:17:03 -0700 | [diff] [blame] | 128 | @Override |
| 129 | public void onPickRawContact(long rawContactId) { |
| 130 | startEditorAndForwardExtras(getIntentForRawContact(rawContactId)); |
| 131 | } |
| 132 | |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 133 | /** |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 134 | * Once the load is finished, decide whether to show the dialog or load the editor directly. |
| 135 | */ |
| 136 | private void onLoad() { |
| 137 | maybeTrimReadOnly(); |
| 138 | setHasWritableAccount(); |
| 139 | if (mShowReadOnly || (mResult.rawContacts.size() > 1 && mHasWritableAccount)) { |
| 140 | showDialog(); |
| 141 | } else { |
| 142 | loadEditor(); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | /** |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 147 | * If not configured to show read only raw contact, trim them from the result. |
| 148 | */ |
| 149 | private void maybeTrimReadOnly() { |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 150 | mResult.showReadOnly = mShowReadOnly; |
| 151 | if (mShowReadOnly) { |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 152 | return; |
| 153 | } |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 154 | |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 155 | mResult.trimReadOnly(AccountTypeManager.getInstance(this)); |
| 156 | } |
| 157 | |
| 158 | /** |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 159 | * Start the dialog to pick the raw contact to edit. |
| 160 | */ |
| 161 | private void showDialog() { |
| 162 | final FragmentManager fm = getFragmentManager(); |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 163 | final SplitContactConfirmationDialogFragment split = |
| 164 | (SplitContactConfirmationDialogFragment) fm |
| 165 | .findFragmentByTag(SplitContactConfirmationDialogFragment.TAG); |
| 166 | // If we were showing the split confirmation before show it again. |
| 167 | if (split != null && split.isAdded()) { |
| 168 | fm.beginTransaction().show(split).commitAllowingStateLoss(); |
Gary Mai | ea2f357 | 2016-10-11 17:30:28 -0700 | [diff] [blame] | 169 | return; |
| 170 | } |
Gary Mai | 91e8d84 | 2016-11-17 18:06:02 -0800 | [diff] [blame] | 171 | PickRawContactDialogFragment pick = (PickRawContactDialogFragment) fm |
| 172 | .findFragmentByTag(TAG_RAW_CONTACTS_DIALOG); |
| 173 | if (pick == null) { |
| 174 | pick = PickRawContactDialogFragment.getInstance(mResult); |
| 175 | final FragmentTransaction ft = fm.beginTransaction(); |
| 176 | ft.add(pick, TAG_RAW_CONTACTS_DIALOG); |
| 177 | // commitAllowingStateLoss is safe in this activity because the fragment entirely |
| 178 | // depends on the result of the loader. Even if we lose the fragment because the |
| 179 | // activity was in the background, when it comes back onLoadFinished will be called |
| 180 | // again which will have all the state the picker needs. This situation should be |
| 181 | // very rare, since the load should be quick. |
| 182 | ft.commitAllowingStateLoss(); |
| 183 | } |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | /** |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 187 | * Starts the editor for the only writable raw contact in the cursor if one exists. Otherwise, |
| 188 | * the editor is started normally and handles creation of a new writable raw contact. |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 189 | */ |
| 190 | private void loadEditor() { |
yaolu | 341d104 | 2016-11-07 15:46:46 -0800 | [diff] [blame] | 191 | Logger.logEditorEvent( |
| 192 | EditorEvent.EventType.SHOW_RAW_CONTACT_PICKER, /* numberRawContacts */ 0); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 193 | final Intent intent; |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 194 | if (mHasWritableAccount) { |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 195 | intent = getIntentForRawContact(mResult.rawContacts.get(mWritableAccountPosition).id); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 196 | } else { |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 197 | // If the contact has only read-only raw contacts, we'll want to let the editor create |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 198 | // the writable raw contact for it. |
| 199 | intent = EditorIntents.createEditContactIntent(this, mUri, mMaterialPalette, -1); |
| 200 | intent.setClass(this, ContactEditorActivity.class); |
| 201 | } |
Gary Mai | 5336e6e | 2016-10-23 14:17:03 -0700 | [diff] [blame] | 202 | startEditorAndForwardExtras(intent); |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | /** |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 206 | * Determines if this contact has a writable account. |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 207 | */ |
Gary Mai | 5a00de3 | 2016-10-19 18:20:41 -0700 | [diff] [blame] | 208 | private void setHasWritableAccount() { |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 209 | mWritableAccountPosition = mResult.getIndexOfFirstWritableAccount( |
| 210 | AccountTypeManager.getInstance(this)); |
| 211 | mHasWritableAccount = mWritableAccountPosition != -1; |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | /** |
| 215 | * Returns an intent to load the editor for the given raw contact. Sets |
| 216 | * {@code FLAG_ACTIVITY_FORWARD_RESULT} in case the activity that started us expects a result. |
| 217 | * @param rawContactId Raw contact to edit |
| 218 | */ |
| 219 | private Intent getIntentForRawContact(long rawContactId) { |
| 220 | final Intent intent = EditorIntents.createEditContactIntentForRawContact( |
| 221 | this, mUri, rawContactId, mMaterialPalette); |
| 222 | intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); |
| 223 | return intent; |
| 224 | } |
Gary Mai | 5336e6e | 2016-10-23 14:17:03 -0700 | [diff] [blame] | 225 | |
| 226 | /** |
| 227 | * Starts the given intent within the app, attaching any extras to it that were passed to us. |
| 228 | */ |
| 229 | private void startEditorAndForwardExtras(Intent intent) { |
| 230 | final Bundle extras = getIntent().getExtras(); |
| 231 | if (extras != null) { |
| 232 | intent.putExtras(extras); |
| 233 | } |
| 234 | ImplicitIntentsUtil.startActivityInApp(this, intent); |
Gary Mai | b2bec43 | 2016-11-21 11:05:30 -0800 | [diff] [blame] | 235 | finish(); |
Gary Mai | 5336e6e | 2016-10-23 14:17:03 -0700 | [diff] [blame] | 236 | } |
Gary Mai | a172180 | 2016-11-01 17:55:31 -0700 | [diff] [blame] | 237 | |
| 238 | private void toastErrorAndFinish() { |
| 239 | Toast.makeText(ContactEditorSpringBoardActivity.this, |
| 240 | R.string.editor_failed_to_load, Toast.LENGTH_SHORT).show(); |
| 241 | setResult(RESULT_CANCELED, null); |
| 242 | finish(); |
| 243 | } |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 244 | |
| 245 | @Override |
| 246 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| 247 | // Ignore failed requests |
| 248 | if (resultCode != Activity.RESULT_OK) { |
| 249 | finish(); |
| 250 | } |
| 251 | if (data != null) { |
| 252 | final Intent intent = ContactSaveService.createJoinContactsIntent( |
| 253 | this, mResult.contactId, ContentUris.parseId(data.getData()), |
| 254 | QuickContactActivity.class, Intent.ACTION_VIEW); |
| 255 | startService(intent); |
| 256 | finish(); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | @Override |
| 261 | public void onSplitContactConfirmed(boolean hasPendingChanges) { |
| 262 | final long[][] rawContactIds = getRawContactIds(); |
| 263 | final Intent intent = ContactSaveService.createHardSplitContactIntent(this, rawContactIds); |
| 264 | startService(intent); |
| 265 | finish(); |
| 266 | } |
| 267 | |
| 268 | @Override |
| 269 | public void onSplitContactCanceled() { |
| 270 | finish(); |
| 271 | } |
| 272 | |
Gary Mai | a84f1d4 | 2016-11-29 11:16:27 -0800 | [diff] [blame] | 273 | @Override |
| 274 | protected void onSaveInstanceState(Bundle outState) { |
| 275 | super.onSaveInstanceState(outState); |
| 276 | outState.putParcelable(KEY_RAW_CONTACTS_METADATA, mResult); |
| 277 | } |
| 278 | |
| 279 | @Override |
| 280 | protected void onRestoreInstanceState(Bundle savedInstanceState) { |
| 281 | super.onRestoreInstanceState(savedInstanceState); |
| 282 | mResult = savedInstanceState.getParcelable(KEY_RAW_CONTACTS_METADATA); |
| 283 | } |
| 284 | |
Gary Mai | b9065dd | 2016-11-08 10:49:00 -0800 | [diff] [blame] | 285 | private long[][] getRawContactIds() { |
| 286 | final long[][] result = new long[mResult.rawContacts.size()][1]; |
| 287 | for (int i = 0; i < mResult.rawContacts.size(); i++) { |
| 288 | result[i][0] = mResult.rawContacts.get(i).id; |
| 289 | } |
| 290 | return result; |
| 291 | } |
Gary Mai | a6c80b3 | 2016-09-30 16:34:55 -0700 | [diff] [blame] | 292 | } |