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