• 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.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
onCreate(Bundle savedState)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
onAttachFragment(Fragment fragment)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)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
onKeyDown(int keyCode, KeyEvent event)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
onSaveInstanceState(Bundle outState)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      */
setupTitle()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          */
handleKeyDown(int keyCode)322         public boolean handleKeyDown(int keyCode);
323     }
324 }
325