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.View; 47 import android.view.View.OnClickListener; 48 import android.view.ViewGroup; 49 import android.view.accessibility.AccessibilityEvent; 50 import android.view.accessibility.AccessibilityManager; 51 import android.widget.CheckBox; 52 import android.widget.Toast; 53 54 import java.util.ArrayList; 55 56 public class ContactDetailActivity extends ContactsActivity { 57 private static final String TAG = "ContactDetailActivity"; 58 59 /** 60 * Intent key for a boolean that specifies whether the "up" afforance in this activity should 61 * behave as default (return user back to {@link PeopleActivity}) or whether the activity should 62 * instead be finished. 63 */ 64 public static final String INTENT_KEY_IGNORE_DEFAULT_UP_BEHAVIOR = "ignoreDefaultUpBehavior"; 65 66 private ContactLoader.Result mContactData; 67 private Uri mLookupUri; 68 private boolean mIgnoreDefaultUpBehavior; 69 70 private ContactDetailLayoutController mContactDetailLayoutController; 71 private ContactLoaderFragment mLoaderFragment; 72 73 private Handler mHandler = new Handler(); 74 75 @Override 76 public void onCreate(Bundle savedState) { 77 super.onCreate(savedState); 78 if (PhoneCapabilityTester.isUsingTwoPanes(this)) { 79 // This activity must not be shown. We have to select the contact in the 80 // PeopleActivity instead ==> Create a forward intent and finish 81 final Intent originalIntent = getIntent(); 82 Intent intent = new Intent(); 83 intent.setAction(originalIntent.getAction()); 84 intent.setDataAndType(originalIntent.getData(), originalIntent.getType()); 85 intent.setFlags( 86 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_FORWARD_RESULT 87 | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); 88 89 intent.setClass(this, PeopleActivity.class); 90 startActivity(intent); 91 finish(); 92 return; 93 } 94 95 mIgnoreDefaultUpBehavior = getIntent().getBooleanExtra( 96 INTENT_KEY_IGNORE_DEFAULT_UP_BEHAVIOR, false); 97 98 setContentView(R.layout.contact_detail_activity); 99 100 mContactDetailLayoutController = new ContactDetailLayoutController(this, savedState, 101 getFragmentManager(), 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 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 127 public boolean onCreateOptionsMenu(Menu menu) { 128 super.onCreateOptionsMenu(menu); 129 MenuInflater inflater = getMenuInflater(); 130 inflater.inflate(R.menu.star, menu); 131 return true; 132 } 133 134 @Override 135 public boolean onPrepareOptionsMenu(Menu menu) { 136 MenuItem starredMenuItem = menu.findItem(R.id.menu_star); 137 ViewGroup starredContainer = (ViewGroup) getLayoutInflater().inflate( 138 R.layout.favorites_star, null, false); 139 final CheckBox starredView = (CheckBox) starredContainer.findViewById(R.id.star); 140 starredView.setOnClickListener(new OnClickListener() { 141 @Override 142 public void onClick(View v) { 143 // Toggle "starred" state 144 // Make sure there is a contact 145 if (mLookupUri != null) { 146 Intent intent = ContactSaveService.createSetStarredIntent( 147 ContactDetailActivity.this, mLookupUri, starredView.isChecked()); 148 ContactDetailActivity.this.startService(intent); 149 } 150 } 151 }); 152 // If there is contact data, update the starred state 153 if (mContactData != null) { 154 ContactDetailDisplayUtils.setStarred(mContactData, starredView); 155 } 156 starredMenuItem.setActionView(starredContainer); 157 return true; 158 } 159 160 @Override 161 public boolean onKeyDown(int keyCode, KeyEvent event) { 162 // First check if the {@link ContactLoaderFragment} can handle the key 163 if (mLoaderFragment != null && mLoaderFragment.handleKeyDown(keyCode)) return true; 164 165 // Otherwise find the correct fragment to handle the event 166 FragmentKeyListener mCurrentFragment = mContactDetailLayoutController.getCurrentPage(); 167 if (mCurrentFragment != null && mCurrentFragment.handleKeyDown(keyCode)) return true; 168 169 // In the last case, give the key event to the superclass. 170 return super.onKeyDown(keyCode, event); 171 } 172 173 @Override 174 protected void onSaveInstanceState(Bundle outState) { 175 super.onSaveInstanceState(outState); 176 if (mContactDetailLayoutController != null) { 177 mContactDetailLayoutController.onSaveInstanceState(outState); 178 } 179 } 180 181 private final ContactLoaderFragmentListener mLoaderFragmentListener = 182 new ContactLoaderFragmentListener() { 183 @Override 184 public void onContactNotFound() { 185 finish(); 186 } 187 188 @Override 189 public void onDetailsLoaded(final ContactLoader.Result result) { 190 if (result == null) { 191 return; 192 } 193 // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the 194 // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler} 195 // on the main thread to execute later. 196 mHandler.post(new Runnable() { 197 @Override 198 public void run() { 199 // If the activity is destroyed (or will be destroyed soon), don't update the UI 200 if (isFinishing()) { 201 return; 202 } 203 mContactData = result; 204 mLookupUri = result.getLookupUri(); 205 invalidateOptionsMenu(); 206 setupTitle(); 207 mContactDetailLayoutController.setContactData(mContactData); 208 } 209 }); 210 } 211 212 @Override 213 public void onEditRequested(Uri contactLookupUri) { 214 startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri)); 215 finish(); 216 } 217 218 @Override 219 public void onDeleteRequested(Uri contactUri) { 220 ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true); 221 } 222 }; 223 224 /** 225 * Setup the activity title and subtitle with contact name and company. 226 */ 227 private void setupTitle() { 228 CharSequence displayName = ContactDetailDisplayUtils.getDisplayName(this, mContactData); 229 String company = ContactDetailDisplayUtils.getCompany(this, mContactData); 230 231 ActionBar actionBar = getActionBar(); 232 actionBar.setTitle(displayName); 233 actionBar.setSubtitle(company); 234 235 if (!TextUtils.isEmpty(displayName) && 236 AccessibilityManager.getInstance(this).isEnabled()) { 237 View decorView = getWindow().getDecorView(); 238 decorView.setContentDescription(displayName); 239 decorView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 240 } 241 } 242 243 private final ContactDetailFragment.Listener mContactDetailFragmentListener = 244 new ContactDetailFragment.Listener() { 245 @Override 246 public void onItemClicked(Intent intent) { 247 if (intent == null) { 248 return; 249 } 250 try { 251 startActivity(intent); 252 } catch (ActivityNotFoundException e) { 253 Log.e(TAG, "No activity found for intent: " + intent); 254 } 255 } 256 257 @Override 258 public void onCreateRawContactRequested( 259 ArrayList<ContentValues> values, AccountWithDataSet account) { 260 Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy, 261 Toast.LENGTH_LONG).show(); 262 Intent serviceIntent = ContactSaveService.createNewRawContactIntent( 263 ContactDetailActivity.this, values, account, 264 ContactDetailActivity.class, Intent.ACTION_VIEW); 265 startService(serviceIntent); 266 267 } 268 }; 269 270 /** 271 * This interface should be implemented by {@link Fragment}s within this 272 * activity so that the activity can determine whether the currently 273 * displayed view is handling the key event or not. 274 */ 275 public interface FragmentKeyListener { 276 /** 277 * Returns true if the key down event will be handled by the implementing class, or false 278 * otherwise. 279 */ 280 public boolean handleKeyDown(int keyCode); 281 } 282 283 @Override 284 public boolean onOptionsItemSelected(MenuItem item) { 285 286 switch (item.getItemId()) { 287 case android.R.id.home: 288 if (mIgnoreDefaultUpBehavior) { 289 finish(); 290 return true; 291 } 292 Intent intent = new Intent(this, PeopleActivity.class); 293 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 294 startActivity(intent); 295 finish(); 296 return true; 297 default: 298 break; 299 } 300 return super.onOptionsItemSelected(item); 301 } 302 } 303