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