Home | History | Annotate | Download | only in activities
      1 package com.android.contacts.activities;
      2 
      3 import android.app.Activity;
      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;
     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;
     17 import com.android.contacts.ContactSaveService;
     18 import com.android.contacts.R;
     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;
     23 import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata;
     24 import com.android.contacts.editor.SplitContactConfirmationDialogFragment;
     25 import com.android.contacts.logging.EditorEvent;
     26 import com.android.contacts.logging.Logger;
     27 import com.android.contacts.model.AccountTypeManager;
     28 import com.android.contacts.quickcontact.QuickContactActivity;
     29 import com.android.contacts.util.ImplicitIntentsUtil;
     30 import com.android.contacts.util.MaterialColorMapUtils.MaterialPalette;
     31 import com.android.contactsbind.FeedbackHelper;
     32 
     33 /**
     34  * Transparent springboard activity that hosts a dialog to select a raw contact to edit.
     35  * All intents coming out from this activity have {@code FLAG_ACTIVITY_FORWARD_RESULT} set.
     36  */
     37 public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity implements
     38         PickRawContactDialogFragment.PickRawContactListener,
     39         SplitContactConfirmationDialogFragment.Listener {
     40 
     41     private static final String TAG = "EditorSpringBoard";
     42     private static final String TAG_RAW_CONTACTS_DIALOG = "rawContactsDialog";
     43     private static final String KEY_RAW_CONTACTS_METADATA = "rawContactsMetadata";
     44     private static final int LOADER_RAW_CONTACTS = 1;
     45 
     46     public static final String EXTRA_SHOW_READ_ONLY = "showReadOnly";
     47 
     48     private Uri mUri;
     49     private RawContactsMetadata mResult;
     50     private MaterialPalette mMaterialPalette;
     51     private boolean mHasWritableAccount;
     52     private boolean mShowReadOnly;
     53     private int mWritableAccountPosition;
     54 
     55     /**
     56      * The contact data loader listener.
     57      */
     58     protected final LoaderManager.LoaderCallbacks<RawContactsMetadata> mRawContactLoaderListener =
     59             new LoaderManager.LoaderCallbacks<RawContactsMetadata>() {
     60 
     61                 @Override
     62                 public Loader<RawContactsMetadata> onCreateLoader(int id, Bundle args) {
     63                     return new PickRawContactLoader(ContactEditorSpringBoardActivity.this, mUri);
     64                 }
     65 
     66                 @Override
     67                 public void onLoadFinished(Loader<RawContactsMetadata> loader,
     68                         RawContactsMetadata result) {
     69                     if (result == null) {
     70                         toastErrorAndFinish();
     71                         return;
     72                     }
     73                     mResult = result;
     74                     onLoad();
     75                 }
     76 
     77                 @Override
     78                 public void onLoaderReset(Loader<RawContactsMetadata> loader) {
     79                 }
     80             };
     81 
     82 
     83     @Override
     84     protected void onCreate(Bundle savedInstanceState) {
     85         super.onCreate(savedInstanceState);
     86 
     87         if (RequestPermissionsActivity.startPermissionActivityIfNeeded(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         }
    106         mShowReadOnly = intent.getBooleanExtra(EXTRA_SHOW_READ_ONLY, false);
    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)) {
    114             Logger.logEditorEvent(
    115                     EditorEvent.EventType.SHOW_RAW_CONTACT_PICKER, /* numberRawContacts */ 0);
    116             final long rawContactId = ContentUris.parseId(mUri);
    117             startEditorAndForwardExtras(getIntentForRawContact(rawContactId));
    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();
    123         } else {
    124             getLoaderManager().initLoader(LOADER_RAW_CONTACTS, null, mRawContactLoaderListener);
    125         }
    126     }
    127 
    128     @Override
    129     public void onPickRawContact(long rawContactId) {
    130         startEditorAndForwardExtras(getIntentForRawContact(rawContactId));
    131     }
    132 
    133     /**
    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     /**
    147      * If not configured to show read only raw contact, trim them from the result.
    148      */
    149     private void maybeTrimReadOnly() {
    150         mResult.showReadOnly = mShowReadOnly;
    151         if (mShowReadOnly) {
    152             return;
    153         }
    154 
    155         mResult.trimReadOnly(AccountTypeManager.getInstance(this));
    156     }
    157 
    158     /**
    159      * Start the dialog to pick the raw contact to edit.
    160      */
    161     private void showDialog() {
    162         final FragmentManager fm = getFragmentManager();
    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();
    169             return;
    170         }
    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         }
    184     }
    185 
    186     /**
    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.
    189      */
    190     private void loadEditor() {
    191         Logger.logEditorEvent(
    192                 EditorEvent.EventType.SHOW_RAW_CONTACT_PICKER, /* numberRawContacts */ 0);
    193         final Intent intent;
    194         if (mHasWritableAccount) {
    195             intent = getIntentForRawContact(mResult.rawContacts.get(mWritableAccountPosition).id);
    196         } else {
    197             // If the contact has only read-only raw contacts, we'll want to let the editor create
    198             // the writable raw contact for it.
    199             intent = EditorIntents.createEditContactIntent(this, mUri, mMaterialPalette, -1);
    200             intent.setClass(this, ContactEditorActivity.class);
    201         }
    202         startEditorAndForwardExtras(intent);
    203     }
    204 
    205     /**
    206      * Determines if this contact has a writable account.
    207      */
    208     private void setHasWritableAccount() {
    209         mWritableAccountPosition = mResult.getIndexOfFirstWritableAccount(
    210                 AccountTypeManager.getInstance(this));
    211         mHasWritableAccount = mWritableAccountPosition != -1;
    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     }
    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);
    235         finish();
    236     }
    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     }
    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 
    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 
    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     }
    292 }
    293