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