• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedState)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
onAttachFragment(Fragment fragment)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)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
onKeyDown(int keyCode, KeyEvent event)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
onSaveInstanceState(Bundle outState)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      */
setupTitle()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          */
handleKeyDown(int keyCode)302         public boolean handleKeyDown(int keyCode);
303     }
304 }
305