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