ContactDetailActivity.java revision 942cec2d8a248b1a22e95f4d573d235e281ee91a
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.ContactsSearchManager; 23import com.android.contacts.R; 24import com.android.contacts.detail.ContactDetailAboutFragment; 25import com.android.contacts.detail.ContactDetailDisplayUtils; 26import com.android.contacts.detail.ContactDetailFragment; 27import com.android.contacts.detail.ContactDetailFragmentCarousel; 28import com.android.contacts.detail.ContactDetailTabCarousel; 29import com.android.contacts.detail.ContactDetailUpdatesFragment; 30import com.android.contacts.interactions.ContactDeletionInteraction; 31import com.android.contacts.util.PhoneCapabilityTester; 32 33import android.accounts.Account; 34import android.app.Fragment; 35import android.app.FragmentManager; 36import android.content.ActivityNotFoundException; 37import android.content.ContentValues; 38import android.content.Intent; 39import android.net.Uri; 40import android.os.Bundle; 41import android.support.v13.app.FragmentPagerAdapter; 42import android.support.v4.view.ViewPager; 43import android.support.v4.view.ViewPager.OnPageChangeListener; 44import android.util.Log; 45import android.view.KeyEvent; 46import android.view.Menu; 47import android.view.MenuInflater; 48import android.view.MenuItem; 49import android.view.View; 50import android.view.View.OnClickListener; 51import android.view.ViewGroup; 52import android.widget.AbsListView; 53import android.widget.AbsListView.OnScrollListener; 54import android.widget.CheckBox; 55import android.widget.Toast; 56 57import java.util.ArrayList; 58 59public class ContactDetailActivity extends ContactsActivity { 60 private static final String TAG = "ContactDetailActivity"; 61 62 public static final int FRAGMENT_COUNT = 2; 63 64 private ContactLoader.Result mContactData; 65 private Uri mLookupUri; 66 67 private ContactDetailAboutFragment mAboutFragment; 68 private ContactDetailUpdatesFragment mUpdatesFragment; 69 70 private ContactDetailTabCarousel mTabCarousel; 71 private ViewPager mViewPager; 72 73 private ContactDetailFragmentCarousel mFragmentCarousel; 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 setContentView(R.layout.contact_detail_activity); 96 97 // Narrow width screens have a {@link ViewPager} and {@link ContactDetailTabCarousel} 98 mViewPager = (ViewPager) findViewById(R.id.pager); 99 if (mViewPager != null) { 100 mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager())); 101 mViewPager.setOnPageChangeListener(mOnPageChangeListener); 102 } 103 104 mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel); 105 if (mTabCarousel != null) { 106 mTabCarousel.setListener(mTabCarouselListener); 107 } 108 109 // Otherwise, wide width screens have a {@link ContactDetailFragmentCarousel} 110 mFragmentCarousel = (ContactDetailFragmentCarousel) findViewById(R.id.fragment_carousel); 111 if (mFragmentCarousel != null) { 112 if (mAboutFragment != null) mFragmentCarousel.setAboutFragment(mAboutFragment); 113 if (mUpdatesFragment != null) mFragmentCarousel.setUpdatesFragment(mUpdatesFragment); 114 } 115 116 Log.i(TAG, getIntent().getData().toString()); 117 } 118 119 @Override 120 public void onAttachFragment(Fragment fragment) { 121 if (fragment instanceof ContactDetailAboutFragment) { 122 mAboutFragment = (ContactDetailAboutFragment) fragment; 123 mAboutFragment.setListener(mFragmentListener); 124 mAboutFragment.setVerticalScrollListener(mVerticalScrollListener); 125 mAboutFragment.loadUri(getIntent().getData()); 126 } else if (fragment instanceof ContactDetailUpdatesFragment) { 127 mUpdatesFragment = (ContactDetailUpdatesFragment) fragment; 128 } 129 } 130 131 @Override 132 public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, 133 boolean globalSearch) { 134 if (globalSearch) { 135 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch); 136 } else { 137 ContactsSearchManager.startSearch(this, initialQuery); 138 } 139 } 140 141 @Override 142 public boolean onCreateOptionsMenu(Menu menu) { 143 super.onCreateOptionsMenu(menu); 144 MenuInflater inflater = getMenuInflater(); 145 inflater.inflate(R.menu.star, menu); 146 return true; 147 } 148 149 @Override 150 public boolean onPrepareOptionsMenu(Menu menu) { 151 MenuItem starredMenuItem = menu.findItem(R.id.menu_star); 152 ViewGroup starredContainer = (ViewGroup) getLayoutInflater().inflate( 153 R.layout.favorites_star, null, false); 154 final CheckBox starredView = (CheckBox) starredContainer.findViewById(R.id.star); 155 starredView.setOnClickListener(new OnClickListener() { 156 @Override 157 public void onClick(View v) { 158 // Toggle "starred" state 159 // Make sure there is a contact 160 if (mLookupUri != null) { 161 Intent intent = ContactSaveService.createSetStarredIntent( 162 ContactDetailActivity.this, mLookupUri, starredView.isChecked()); 163 ContactDetailActivity.this.startService(intent); 164 } 165 } 166 }); 167 // If there is contact data, update the starred state 168 if (mContactData != null) { 169 ContactDetailDisplayUtils.setStarred(mContactData, starredView); 170 } 171 starredMenuItem.setActionView(starredContainer); 172 return true; 173 } 174 175 @Override 176 public boolean onKeyDown(int keyCode, KeyEvent event) { 177 FragmentKeyListener mCurrentFragment; 178 switch (getCurrentPage()) { 179 case 0: 180 mCurrentFragment = (FragmentKeyListener) mAboutFragment; 181 break; 182 case 1: 183 mCurrentFragment = (FragmentKeyListener) mUpdatesFragment; 184 break; 185 default: 186 throw new IllegalStateException("Invalid current item for ViewPager"); 187 } 188 if (mCurrentFragment.handleKeyDown(keyCode)) return true; 189 190 return super.onKeyDown(keyCode, event); 191 } 192 193 private int getCurrentPage() { 194 if (mViewPager != null) { 195 return mViewPager.getCurrentItem(); 196 } else if (mFragmentCarousel != null) { 197 return mFragmentCarousel.getCurrentPage(); 198 } 199 throw new IllegalStateException("Can't figure out the currently selected page. The activity" 200 + "must either have the ViewPager or fragment carousel"); 201 } 202 203 private final ContactDetailFragment.Listener mFragmentListener = 204 new ContactDetailFragment.Listener() { 205 @Override 206 public void onContactNotFound() { 207 finish(); 208 } 209 210 @Override 211 public void onDetailsLoaded(ContactLoader.Result result) { 212 if (result == null) { 213 return; 214 } 215 mContactData = result; 216 mLookupUri = result.getLookupUri(); 217 invalidateOptionsMenu(); 218 if (mTabCarousel != null) { 219 mTabCarousel.loadData(result); 220 } 221 } 222 223 @Override 224 public void onEditRequested(Uri contactLookupUri) { 225 startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri)); 226 } 227 228 @Override 229 public void onItemClicked(Intent intent) { 230 try { 231 startActivity(intent); 232 } catch (ActivityNotFoundException e) { 233 Log.e(TAG, "No activity found for intent: " + intent); 234 } 235 } 236 237 @Override 238 public void onDeleteRequested(Uri contactUri) { 239 ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true); 240 } 241 242 @Override 243 public void onCreateRawContactRequested( 244 ArrayList<ContentValues> values, Account account) { 245 Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy, 246 Toast.LENGTH_LONG).show(); 247 Intent serviceIntent = ContactSaveService.createNewRawContactIntent( 248 ContactDetailActivity.this, values, account, 249 ContactDetailActivity.class, Intent.ACTION_VIEW); 250 startService(serviceIntent); 251 252 } 253 }; 254 255 public class ViewPagerAdapter extends FragmentPagerAdapter{ 256 257 public ViewPagerAdapter(FragmentManager fm) { 258 super(fm); 259 } 260 261 @Override 262 public Fragment getItem(int position) { 263 switch (position) { 264 case 0: 265 return new ContactDetailAboutFragment(); 266 case 1: 267 return new ContactDetailUpdatesFragment(); 268 } 269 throw new IllegalStateException("No fragment at position " + position); 270 } 271 272 @Override 273 public int getCount() { 274 return FRAGMENT_COUNT; 275 } 276 } 277 278 private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() { 279 280 @Override 281 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 282 // The user is horizontally dragging the {@link ViewPager}, so send 283 // these scroll changes to the tab carousel. Ignore these events though if the carousel 284 // is actually controlling the {@link ViewPager} scrolls because it will already be 285 // in the correct position. 286 if (mViewPager.isFakeDragging()) { 287 return; 288 } 289 int x = (int) ((position + positionOffset) * 290 mTabCarousel.getAllowedHorizontalScrollLength()); 291 mTabCarousel.scrollTo(x, 0); 292 } 293 294 @Override 295 public void onPageSelected(int position) { 296 // Since a new page has been selected by the {@link ViewPager}, 297 // update the tab selection in the carousel. 298 mTabCarousel.setCurrentTab(position); 299 } 300 301 @Override 302 public void onPageScrollStateChanged(int state) {} 303 304 }; 305 306 private ContactDetailTabCarousel.Listener mTabCarouselListener = 307 new ContactDetailTabCarousel.Listener() { 308 309 @Override 310 public void onTouchDown() { 311 // The user just started scrolling the carousel, so begin "fake dragging" the 312 // {@link ViewPager} if it's not already doing so. 313 if (mViewPager.isFakeDragging()) { 314 return; 315 } 316 mViewPager.beginFakeDrag(); 317 } 318 319 @Override 320 public void onTouchUp() { 321 // The user just stopped scrolling the carousel, so stop "fake dragging" the 322 // {@link ViewPager} if was doing so before. 323 if (mViewPager.isFakeDragging()) { 324 mViewPager.endFakeDrag(); 325 } 326 } 327 328 @Override 329 public void onScrollChanged(int l, int t, int oldl, int oldt) { 330 // The user is scrolling the carousel, so send the scroll deltas to the 331 // {@link ViewPager} so it can move in sync. 332 if (mViewPager.isFakeDragging()) { 333 mViewPager.fakeDragBy(oldl-l); 334 } 335 } 336 337 @Override 338 public void onTabSelected(int position) { 339 // The user selected a tab, so update the {@link ViewPager} 340 mViewPager.setCurrentItem(position); 341 } 342 }; 343 344 private OnScrollListener mVerticalScrollListener = new OnScrollListener() { 345 346 @Override 347 public void onScroll( 348 AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 349 if (mTabCarousel == null) { 350 return; 351 } 352 // Only re-position the tab carousel vertically if the FIRST item is still visible on 353 // the screen, otherwise the carousel should be in the correct place (pinned at the 354 // top). 355 if (firstVisibleItem != 0) { 356 return; 357 } 358 View topView = view.getChildAt(firstVisibleItem); 359 if (topView == null) { 360 return; 361 } 362 int amtToScroll = Math.max((int) view.getChildAt(firstVisibleItem).getY(), 363 -mTabCarousel.getAllowedVerticalScrollLength()); 364 mTabCarousel.setY(amtToScroll); 365 } 366 367 @Override 368 public void onScrollStateChanged(AbsListView view, int scrollState) {} 369 370 }; 371 372 /** 373 * This interface should be implemented by {@link Fragment}s within this 374 * activity so that the activity can determine whether the currently 375 * displayed view is handling the key event or not. 376 */ 377 public interface FragmentKeyListener { 378 /** 379 * Returns true if the key down event will be handled by the implementing class, or false 380 * otherwise. 381 */ 382 public boolean handleKeyDown(int keyCode); 383 } 384} 385