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 
    110     @Override
    111     public void onAttachFragment(Fragment fragment) {
    112          if (fragment instanceof ContactLoaderFragment) {
    113             mLoaderFragment = (ContactLoaderFragment) fragment;
    114             mLoaderFragment.setListener(mLoaderFragmentListener);
    115             mLoaderFragment.loadUri(getIntent().getData());
    116         }
    117     }
    118 
    119     @Override
    120     public boolean onCreateOptionsMenu(Menu menu) {
    121         super.onCreateOptionsMenu(menu);
    122         MenuInflater inflater = getMenuInflater();
    123         inflater.inflate(R.menu.star, menu);
    124         return true;
    125     }
    126 
    127     @Override
    128     public boolean onPrepareOptionsMenu(Menu menu) {
    129         final MenuItem starredMenuItem = menu.findItem(R.id.menu_star);
    130         starredMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
    131             @Override
    132             public boolean onMenuItemClick(MenuItem item) {
    133                 // Toggle "starred" state
    134                 // Make sure there is a contact
    135                 if (mLookupUri != null) {
    136                     // Read the current starred value from the UI instead of using the last
    137                     // loaded state. This allows rapid tapping without writing the same
    138                     // value several times
    139                     final boolean isStarred = starredMenuItem.isChecked();
    140 
    141                     // To improve responsiveness, swap out the picture (and tag) in the UI already
    142                     ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
    143                             mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
    144                             !isStarred);
    145 
    146                     // Now perform the real save
    147                     Intent intent = ContactSaveService.createSetStarredIntent(
    148                             ContactDetailActivity.this, mLookupUri, !isStarred);
    149                     ContactDetailActivity.this.startService(intent);
    150                 }
    151                 return true;
    152             }
    153         });
    154         // If there is contact data, update the starred state
    155         if (mContactData != null) {
    156             ContactDetailDisplayUtils.configureStarredMenuItem(starredMenuItem,
    157                     mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
    158                     mContactData.getStarred());
    159         }
    160         return true;
    161     }
    162 
    163     @Override
    164     public boolean onKeyDown(int keyCode, KeyEvent event) {
    165         // First check if the {@link ContactLoaderFragment} can handle the key
    166         if (mLoaderFragment != null && mLoaderFragment.handleKeyDown(keyCode)) return true;
    167 
    168         // Otherwise find the correct fragment to handle the event
    169         FragmentKeyListener mCurrentFragment = mContactDetailLayoutController.getCurrentPage();
    170         if (mCurrentFragment != null && mCurrentFragment.handleKeyDown(keyCode)) return true;
    171 
    172         // In the last case, give the key event to the superclass.
    173         return super.onKeyDown(keyCode, event);
    174     }
    175 
    176     @Override
    177     protected void onSaveInstanceState(Bundle outState) {
    178         super.onSaveInstanceState(outState);
    179         if (mContactDetailLayoutController != null) {
    180             mContactDetailLayoutController.onSaveInstanceState(outState);
    181         }
    182     }
    183 
    184     private final ContactLoaderFragmentListener mLoaderFragmentListener =
    185             new ContactLoaderFragmentListener() {
    186         @Override
    187         public void onContactNotFound() {
    188             finish();
    189         }
    190 
    191         @Override
    192         public void onDetailsLoaded(final Contact result) {
    193             if (result == null) {
    194                 return;
    195             }
    196             // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
    197             // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
    198             // on the main thread to execute later.
    199             mHandler.post(new Runnable() {
    200                 @Override
    201                 public void run() {
    202                     // If the activity is destroyed (or will be destroyed soon), don't update the UI
    203                     if (isFinishing()) {
    204                         return;
    205                     }
    206                     mContactData = result;
    207                     mLookupUri = result.getLookupUri();
    208                     invalidateOptionsMenu();
    209                     setupTitle();
    210                     mContactDetailLayoutController.setContactData(mContactData);
    211                 }
    212             });
    213         }
    214 
    215         @Override
    216         public void onEditRequested(Uri contactLookupUri) {
    217             Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
    218             intent.putExtra(
    219                     ContactEditorActivity.INTENT_KEY_FINISH_ACTIVITY_ON_SAVE_COMPLETED, true);
    220             // Don't finish the detail activity after launching the editor because when the
    221             // editor is done, we will still want to show the updated contact details using
    222             // this activity.
    223             startActivity(intent);
    224         }
    225 
    226         @Override
    227         public void onDeleteRequested(Uri contactUri) {
    228             ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
    229         }
    230     };
    231 
    232     /**
    233      * Setup the activity title and subtitle with contact name and company.
    234      */
    235     private void setupTitle() {
    236         CharSequence displayName = ContactDetailDisplayUtils.getDisplayName(this, mContactData);
    237         String company =  ContactDetailDisplayUtils.getCompany(this, mContactData);
    238 
    239         ActionBar actionBar = getActionBar();
    240         actionBar.setTitle(displayName);
    241         actionBar.setSubtitle(company);
    242 
    243         final StringBuilder talkback = new StringBuilder();
    244         if (!TextUtils.isEmpty(displayName)) {
    245             talkback.append(displayName);
    246         }
    247         if (!TextUtils.isEmpty(company)) {
    248             if (talkback.length() != 0) {
    249                 talkback.append(", ");
    250             }
    251             talkback.append(company);
    252         }
    253 
    254         if (talkback.length() != 0) {
    255             AccessibilityManager accessibilityManager =
    256                     (AccessibilityManager) this.getSystemService(Context.ACCESSIBILITY_SERVICE);
    257             if (accessibilityManager.isEnabled()) {
    258                 View decorView = getWindow().getDecorView();
    259                 decorView.setContentDescription(talkback);
    260                 decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    261             }
    262         }
    263     }
    264 
    265     private final ContactDetailFragment.Listener mContactDetailFragmentListener =
    266             new ContactDetailFragment.Listener() {
    267         @Override
    268         public void onItemClicked(Intent intent) {
    269             if (intent == null) {
    270                 return;
    271             }
    272             try {
    273                 startActivity(intent);
    274             } catch (ActivityNotFoundException e) {
    275                 Log.e(TAG, "No activity found for intent: " + intent);
    276             }
    277         }
    278 
    279         @Override
    280         public void onCreateRawContactRequested(
    281                 ArrayList<ContentValues> values, AccountWithDataSet account) {
    282             Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
    283                     Toast.LENGTH_LONG).show();
    284             Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
    285                     ContactDetailActivity.this, values, account,
    286                     ContactDetailActivity.class, Intent.ACTION_VIEW);
    287             startService(serviceIntent);
    288 
    289         }
    290     };
    291 
    292     /**
    293      * This interface should be implemented by {@link Fragment}s within this
    294      * activity so that the activity can determine whether the currently
    295      * displayed view is handling the key event or not.
    296      */
    297     public interface FragmentKeyListener {
    298         /**
    299          * Returns true if the key down event will be handled by the implementing class, or false
    300          * otherwise.
    301          */
    302         public boolean handleKeyDown(int keyCode);
    303     }
    304 }
    305