Home | History | Annotate | Download | only in activities
      1 /*
      2  * Copyright (C) 2010 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 android.app.ActionBar;
     20 import android.app.Fragment;
     21 import android.content.ActivityNotFoundException;
     22 import android.content.ContentValues;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.text.TextUtils;
     29 import android.util.Log;
     30 import android.view.KeyEvent;
     31 import android.view.Menu;
     32 import android.view.MenuInflater;
     33 import android.view.MenuItem;
     34 import android.view.MenuItem.OnMenuItemClickListener;
     35 import android.view.View;
     36 import android.view.accessibility.AccessibilityEvent;
     37 import android.view.accessibility.AccessibilityManager;
     38 import android.widget.Toast;
     39 
     40 import com.android.contacts.ContactSaveService;
     41 import com.android.contacts.ContactsActivity;
     42 import com.android.contacts.R;
     43 import com.android.contacts.detail.ContactDetailDisplayUtils;
     44 import com.android.contacts.detail.ContactDetailFragment;
     45 import com.android.contacts.detail.ContactDetailLayoutController;
     46 import com.android.contacts.detail.ContactLoaderFragment;
     47 import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
     48 import com.android.contacts.interactions.ContactDeletionInteraction;
     49 import com.android.contacts.common.model.Contact;
     50 import com.android.contacts.common.model.account.AccountWithDataSet;
     51 import com.android.contacts.util.PhoneCapabilityTester;
     52 
     53 import java.util.ArrayList;
     54 
     55 public class ContactDetailActivity extends ContactsActivity {
     56     private static final String TAG = "ContactDetailActivity";
     57 
     58     private Contact mContactData;
     59     private Uri mLookupUri;
     60 
     61     private ContactDetailLayoutController mContactDetailLayoutController;
     62     private ContactLoaderFragment mLoaderFragment;
     63 
     64     private Handler mHandler = new Handler();
     65 
     66     @Override
     67     protected void onCreate(Bundle savedState) {
     68         super.onCreate(savedState);
     69         if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
     70             // This activity must not be shown. We have to select the contact in the
     71             // PeopleActivity instead ==> Create a forward intent and finish
     72             final Intent originalIntent = getIntent();
     73             Intent intent = new Intent();
     74             intent.setAction(originalIntent.getAction());
     75             intent.setDataAndType(originalIntent.getData(), originalIntent.getType());
     76 
     77             // If we are launched from the outside, we should create a new task, because the user
     78             // can freely navigate the app (this is different from phones, where only the UP button
     79             // kicks the user into the full app)
     80             if (shouldUpRecreateTask(intent)) {
     81                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
     82             } else {
     83                 intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
     84                         Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_SINGLE_TOP |
     85                         Intent.FLAG_ACTIVITY_CLEAR_TOP);
     86             }
     87             intent.setClass(this, PeopleActivity.class);
     88             startActivity(intent);
     89             finish();
     90             return;
     91         }
     92 
     93         setContentView(R.layout.contact_detail_activity);
     94 
     95         mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState,
     96                 getFragmentManager(), null, findViewById(R.id.contact_detail_container),
     97                 mContactDetailFragmentListener);
     98 
     99         // We want the UP affordance but no app icon.
    100         // Setting HOME_AS_UP, SHOW_TITLE and clearing SHOW_HOME does the trick.
    101         ActionBar actionBar = getActionBar();
    102         if (actionBar != null) {
    103             actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE,
    104                     ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE
    105                     | ActionBar.DISPLAY_SHOW_HOME);
    106             actionBar.setTitle("");
    107         }
    108 
    109         Log.i(TAG, getIntent().getData().toString());
    110     }
    111 
    112     @Override
    113     public void onAttachFragment(Fragment fragment) {
    114          if (fragment instanceof ContactLoaderFragment) {
    115             mLoaderFragment = (ContactLoaderFragment) fragment;
    116             mLoaderFragment.setListener(mLoaderFragmentListener);
    117             mLoaderFragment.loadUri(getIntent().getData());
    118         }
    119     }
    120 
    121     @Override
    122     public boolean onCreateOptionsMenu(Menu menu) {
    123         super.onCreateOptionsMenu(menu);
    124         MenuInflater inflater = getMenuInflater();
    125         inflater.inflate(R.menu.star, menu);
    126         return true;
    127     }
    128 
    129     @Override
    130     public boolean onPrepareOptionsMenu(Menu menu) {
    131         final MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
    132         starredMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
    133             @Override
    134             public boolean onMenuItemClick(MenuItem item) {
    135                 // Toggle "starred" state
    136                 // Make sure there is a contact
    137                 if (mLookupUri != null) {
    138                     // Read the current starred value from the UI instead of using the last
    139                     // loaded state. This allows rapid tapping without writing the same
    140                     // value several times
    141                     final boolean isStarred = starredMenuItem.isChecked();
    142 
    143                     // To improve responsiveness, swap out the picture (and tag) in the UI already
    144                     ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
    145                             mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
    146                             !isStarred);
    147 
    148                     // Now perform the real save
    149                     Intent intent = ContactSaveService.createSetStarredIntent(
    150                             ContactDetailActivity.this, mLookupUri, !isStarred);
    151                     ContactDetailActivity.this.startService(intent);
    152                 }
    153                 return true;
    154             }
    155         });
    156         // If there is contact data, update the starred state
    157         if (mContactData != null) {
    158             ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
    159                     mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
    160                     mContactData.getStarred());
    161         }
    162         return true;
    163     }
    164 
    165     @Override
    166     public boolean onKeyDown(int keyCode, KeyEvent event) {
    167         // First check if the {@link ContactLoaderFragment} can handle the key
    168         if (mLoaderFragment != null && mLoaderFragment.handleKeyDown(keyCode)) return true;
    169 
    170         // Otherwise find the correct fragment to handle the event
    171         FragmentKeyListener mCurrentFragment = mContactDetailLayoutController.getCurrentPage();
    172         if (mCurrentFragment != null && mCurrentFragment.handleKeyDown(keyCode)) return true;
    173 
    174         // In the last case, give the key event to the superclass.
    175         return super.onKeyDown(keyCode, event);
    176     }
    177 
    178     @Override
    179     protected void onSaveInstanceState(Bundle outState) {
    180         super.onSaveInstanceState(outState);
    181         if (mContactDetailLayoutController != null) {
    182             mContactDetailLayoutController.onSaveInstanceState(outState);
    183         }
    184     }
    185 
    186     private final ContactLoaderFragmentListener mLoaderFragmentListener =
    187             new ContactLoaderFragmentListener() {
    188         @Override
    189         public void onContactNotFound() {
    190             finish();
    191         }
    192 
    193         @Override
    194         public void onDetailsLoaded(final Contact result) {
    195             if (result == null) {
    196                 return;
    197             }
    198             // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
    199             // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
    200             // on the main thread to execute later.
    201             mHandler.post(new Runnable() {
    202                 @Override
    203                 public void run() {
    204                     // If the activity is destroyed (or will be destroyed soon), don't update the UI
    205                     if (isFinishing()) {
    206                         return;
    207                     }
    208                     mContactData = result;
    209                     mLookupUri = result.getLookupUri();
    210                     invalidateOptionsMenu();
    211                     setupTitle();
    212                     mContactDetailLayoutController.setContactData(mContactData);
    213                 }
    214             });
    215         }
    216 
    217         @Override
    218         public void onEditRequested(Uri contactLookupUri) {
    219             Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
    220             intent.putExtra(
    221                     ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
    222             // Don't finish the detail activity after launching the editor because when the
    223             // editor is done, we will still want to show the updated contact details using
    224             // this activity.
    225             startActivity(intent);
    226         }
    227 
    228         @Override
    229         public void onDeleteRequested(Uri contactUri) {
    230             ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
    231         }
    232     };
    233 
    234     /**
    235      * Setup the activity title and subtitle with contact name and company.
    236      */
    237     private void setupTitle() {
    238         CharSequence displayName = ContactDetailDisplayUtils.getDisplayName(this, mContactData);
    239         String company =  ContactDetailDisplayUtils.getCompany(this, mContactData);
    240 
    241         ActionBar actionBar = getActionBar();
    242         actionBar.setTitle(displayName);
    243         actionBar.setSubtitle(company);
    244 
    245         final StringBuilder talkback = new StringBuilder();
    246         if (!TextUtils.isEmpty(displayName)) {
    247             talkback.append(displayName);
    248         }
    249         if (!TextUtils.isEmpty(company)) {
    250             if (talkback.length() != 0) {
    251                 talkback.append(", ");
    252             }
    253             talkback.append(company);
    254         }
    255 
    256         if (talkback.length() != 0) {
    257             AccessibilityManager accessibilityManager =
    258                     (AccessibilityManager) this.getSystemService(Context.ACCESSIBILITY_SERVICE);
    259             if (accessibilityManager.isEnabled()) {
    260                 View decorView = getWindow().getDecorView();
    261                 decorView.setContentDescription(talkback);
    262                 decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    263             }
    264         }
    265     }
    266 
    267     private final ContactDetailFragment.Listener mContactDetailFragmentListener =
    268             new ContactDetailFragment.Listener() {
    269         @Override
    270         public void onItemClicked(Intent intent) {
    271             if (intent == null) {
    272                 return;
    273             }
    274             try {
    275                 startActivity(intent);
    276             } catch (ActivityNotFoundException e) {
    277                 Log.e(TAG, "No activity found for intent: " + intent);
    278             }
    279         }
    280 
    281         @Override
    282         public void onCreateRawContactRequested(
    283                 ArrayList<ContentValues> values, AccountWithDataSet account) {
    284             Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
    285                     Toast.LENGTH_LONG).show();
    286             Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
    287                     ContactDetailActivity.this, values, account,
    288                     ContactDetailActivity.class, Intent.ACTION_VIEW);
    289             startService(serviceIntent);
    290 
    291         }
    292     };
    293 
    294     /**
    295      * This interface should be implemented by {@link Fragment}s within this
    296      * activity so that the activity can determine whether the currently
    297      * displayed view is handling the key event or not.
    298      */
    299     public interface FragmentKeyListener {
    300         /**
    301          * Returns true if the key down event will be handled by the implementing class, or false
    302          * otherwise.
    303          */
    304         public boolean handleKeyDown(int keyCode);
    305     }
    306 }
    307