Contact card with and without social updates
- This is for the phone (landscape and portrait)
- Some tweaks were done to the tablet to prevent regression
but it's not quite ready yet
- No social updates means a single scrolling list of details
- Having social updates means a tab carousel and ViewPager
- Add invisible contact loader fragment
- Now the loader fragment loads the contact --> passes to
ContactDetailActivity --> passes to all necessary fragments /
carousels (no matter the configuration)
- Get rid of ContactDetailAboutFragment and move those changes
into the ContactDetailFragment
Change-Id: I7be55ae7205bbcb8106bf2f2e4ae8dd6ce2c6a78
diff --git a/res/layout-sw580dp/simple_contact_detail_header_view_list_item.xml b/res/layout-sw580dp/simple_contact_detail_header_view_list_item.xml
new file mode 100644
index 0000000..b14d563
--- /dev/null
+++ b/res/layout-sw580dp/simple_contact_detail_header_view_list_item.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This view temporarily holds the extra information that used to be in the
+ original contact detail header view, but now must move into the list because
+ of the new tab carousel. TODO: Integrate this better into the list as provided
+ by the mocks.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="20dip">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:scaleType="centerCrop"
+ android:layout_width="@dimen/detail_contact_photo_size"
+ android:layout_height="@dimen/detail_contact_photo_size" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="36sp" />
+
+ <TextView
+ android:id="@+id/company"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/phonetic_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/attribution"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <CheckBox
+ android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:contentDescription="@string/description_star"
+ style="?android:attr/starStyle" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-w470dp/contact_detail_activity.xml b/res/layout-w470dp/contact_detail_container_with_updates.xml
similarity index 84%
rename from res/layout-w470dp/contact_detail_activity.xml
rename to res/layout-w470dp/contact_detail_container_with_updates.xml
index bf649a2..a8ee0a5 100644
--- a/res/layout-w470dp/contact_detail_activity.xml
+++ b/res/layout-w470dp/contact_detail_container_with_updates.xml
@@ -15,15 +15,13 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contact_detail_view"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<com.android.contacts.detail.ContactDetailFragmentCarousel
android:id="@+id/fragment_carousel"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/contact_detail_activity.xml b/res/layout/contact_detail_activity.xml
index d840d6f..3ab40c3 100644
--- a/res/layout/contact_detail_activity.xml
+++ b/res/layout/contact_detail_activity.xml
@@ -14,24 +14,17 @@
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_detail_view"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <android.support.v4.view.ViewPager
- android:id="@+id/pager"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ <!-- This fragment is an invisible worker fragment that loads the contact details. -->
+ <fragment
+ android:id="@+id/loader_fragment"
+ class="com.android.contacts.detail.ContactLoaderFragment"
+ android:layout_height="0dip"
+ android:layout_width="0dip"
+ android:visibility="gone"/>
- <com.android.contacts.detail.ContactDetailTabCarousel
- android:id="@+id/tab_carousel"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
-</RelativeLayout>
+</FrameLayout>
diff --git a/res/layout/contact_detail_container_with_updates.xml b/res/layout/contact_detail_container_with_updates.xml
new file mode 100644
index 0000000..de7d145
--- /dev/null
+++ b/res/layout/contact_detail_container_with_updates.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <com.android.contacts.detail.ContactDetailTabCarousel
+ android:id="@+id/tab_carousel"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+</RelativeLayout>
diff --git a/res/layout-w470dp/contact_detail_activity.xml b/res/layout/contact_detail_container_without_updates.xml
similarity index 72%
copy from res/layout-w470dp/contact_detail_activity.xml
copy to res/layout/contact_detail_container_without_updates.xml
index bf649a2..884f280 100644
--- a/res/layout-w470dp/contact_detail_activity.xml
+++ b/res/layout/contact_detail_container_without_updates.xml
@@ -14,15 +14,14 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contact_detail_view"
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.contacts.detail.ContactDetailFragmentCarousel
- android:id="@+id/fragment_carousel"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
+ <fragment
+ android:id="@+id/contact_detail_fragment"
+ class="com.android.contacts.detail.ContactDetailFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
diff --git a/res/layout/contact_detail_fragment.xml b/res/layout/contact_detail_fragment.xml
index 70a9a28..d16771c 100644
--- a/res/layout/contact_detail_fragment.xml
+++ b/res/layout/contact_detail_fragment.xml
@@ -15,7 +15,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contact_detail"
+ android:id="@+id/contact_detail_about_fragment"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -25,15 +25,14 @@
android:layout_height="0px"
android:layout_weight="1"
android:background="@color/background_primary"
- android:divider="@null"
- />
+ android:divider="@null"/>
<ScrollView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
- android:visibility="gone"
- >
+ android:visibility="gone">
+
<TextView android:id="@+id/emptyText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -43,8 +42,8 @@
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:paddingTop="10dip"
- android:lineSpacingMultiplier="0.92"
- />
+ android:lineSpacingMultiplier="0.92"/>
+
</ScrollView>
<!-- "Copy to my contacts"- button -->
diff --git a/res/layout/contact_detail_fragment_carousel.xml b/res/layout/contact_detail_fragment_carousel.xml
index 165b6a1..312fdf2 100644
--- a/res/layout/contact_detail_fragment_carousel.xml
+++ b/res/layout/contact_detail_fragment_carousel.xml
@@ -21,7 +21,7 @@
android:scrollbars="none"
android:orientation="horizontal">
- <fragment class="com.android.contacts.detail.ContactDetailAboutFragment"
+ <fragment class="com.android.contacts.detail.ContactDetailFragment"
android:id="@+id/about_fragment"
android:layout_width="@dimen/detail_fragment_carousel_fragment_width"
android:layout_height="match_parent" />
diff --git a/res/layout-w470dp/contact_detail_activity.xml b/res/layout/contact_detail_loader_fragment.xml
similarity index 62%
copy from res/layout-w470dp/contact_detail_activity.xml
copy to res/layout/contact_detail_loader_fragment.xml
index bf649a2..3c4bbef 100644
--- a/res/layout-w470dp/contact_detail_activity.xml
+++ b/res/layout/contact_detail_loader_fragment.xml
@@ -15,15 +15,6 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contact_detail_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.android.contacts.detail.ContactDetailFragmentCarousel
- android:id="@+id/fragment_carousel"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
-</FrameLayout>
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"/>
\ No newline at end of file
diff --git a/res/layout/contact_detail_updates_fragment.xml b/res/layout/contact_detail_updates_fragment.xml
index daa5608..e9c36f3 100644
--- a/res/layout/contact_detail_updates_fragment.xml
+++ b/res/layout/contact_detail_updates_fragment.xml
@@ -15,7 +15,7 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/contact_detail"
+ android:id="@+id/contact_detail_updates_fragment"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/res/layout/favorites_star.xml b/res/layout/favorites_star.xml
index f2afa31..4b859b4 100644
--- a/res/layout/favorites_star.xml
+++ b/res/layout/favorites_star.xml
@@ -20,12 +20,15 @@
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingRight="10dip">
+
<CheckBox
android:id="@+id/star"
+ android:duplicateParentState="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/description_star"
android:visibility="invisible"
style="?android:attr/starStyle"/>
+
</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/simple_contact_detail_header_view_list_item.xml b/res/layout/simple_contact_detail_header_view_list_item.xml
index 1fd9ec5..0c0867f 100644
--- a/res/layout/simple_contact_detail_header_view_list_item.xml
+++ b/res/layout/simple_contact_detail_header_view_list_item.xml
@@ -22,10 +22,15 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="@dimen/detail_tab_carousel_height">
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/detail_tab_carousel_height" />
<TextView
android:id="@+id/phonetic_name"
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index b2f2af1..f1e5736 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -32,4 +32,5 @@
<dimen name="shortcut_icon_size">64dip</dimen>
<dimen name="list_section_height">37dip</dimen>
<dimen name="directory_header_height">56dip</dimen>
+ <dimen name="detail_tab_carousel_height">150dip</dimen>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 95bbb63..d248f5b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -109,6 +109,9 @@
<!-- Margin around the contact's photo on the contact card -->
<dimen name="detail_contact_photo_margin">15dip</dimen>
+ <!-- Width and height of the contact photo on the contact detail page -->
+ <dimen name="detail_contact_photo_size">256dip</dimen>
+
<!-- Left and right padding for a contact detail item -->
<dimen name="detail_item_icon_margin">10dip</dimen>
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index e216d54..d5d0255 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -21,23 +21,29 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
-import com.android.contacts.detail.ContactDetailAboutFragment;
import com.android.contacts.detail.ContactDetailDisplayUtils;
import com.android.contacts.detail.ContactDetailFragment;
import com.android.contacts.detail.ContactDetailFragmentCarousel;
import com.android.contacts.detail.ContactDetailTabCarousel;
import com.android.contacts.detail.ContactDetailUpdatesFragment;
+import com.android.contacts.detail.ContactLoaderFragment;
+import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.util.PhoneCapabilityTester;
import android.accounts.Account;
+import android.app.ActionBar;
+import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
+import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
@@ -46,8 +52,9 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.View;
import android.view.View.OnClickListener;
+import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
@@ -59,12 +66,16 @@
public class ContactDetailActivity extends ContactsActivity {
private static final String TAG = "ContactDetailActivity";
+ private static final String KEY_DETAIL_FRAGMENT_TAG = "detailFragTag";
+ private static final String KEY_UPDATES_FRAGMENT_TAG = "updatesFragTag";
+
public static final int FRAGMENT_COUNT = 2;
private ContactLoader.Result mContactData;
private Uri mLookupUri;
- private ContactDetailAboutFragment mAboutFragment;
+ private ContactLoaderFragment mLoaderFragment;
+ private ContactDetailFragment mDetailFragment;
private ContactDetailUpdatesFragment mUpdatesFragment;
private ContactDetailTabCarousel mTabCarousel;
@@ -72,6 +83,18 @@
private ContactDetailFragmentCarousel mFragmentCarousel;
+ private ViewGroup mRootView;
+ private ViewGroup mContentView;
+ private LayoutInflater mInflater;
+
+ private Handler mHandler = new Handler();
+
+ /**
+ * Whether or not the contact has updates, which dictates whether the
+ * {@link ContactDetailUpdatesFragment} will be shown.
+ */
+ private boolean mContactHasUpdates;
+
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
@@ -93,24 +116,29 @@
}
setContentView(R.layout.contact_detail_activity);
+ mRootView = (ViewGroup) findViewById(R.id.contact_detail_view);
+ mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- // Narrow width screens have a {@link ViewPager} and {@link ContactDetailTabCarousel}
- mViewPager = (ViewPager) findViewById(R.id.pager);
- if (mViewPager != null) {
- mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
- mViewPager.setOnPageChangeListener(mOnPageChangeListener);
- }
+ // Manually remove any {@link ViewPager} fragments if there was an orientation change
+ // because the {@link ViewPager} is not used in both orientations. (If we leave the
+ // fragments around, they'll be around in the {@link FragmentManager} but won't be visible
+ // on screen and the {@link ViewPager} won't ask to initialize them again).
+ if (savedState != null) {
+ String aboutFragmentTag = savedState.getString(KEY_DETAIL_FRAGMENT_TAG);
+ String updatesFragmentTag = savedState.getString(KEY_UPDATES_FRAGMENT_TAG);
- mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
- if (mTabCarousel != null) {
- mTabCarousel.setListener(mTabCarouselListener);
- }
+ FragmentManager fragmentManager = getFragmentManager();
+ mDetailFragment = (ContactDetailFragment) fragmentManager.findFragmentByTag(
+ aboutFragmentTag);
+ mUpdatesFragment = (ContactDetailUpdatesFragment) fragmentManager.findFragmentByTag(
+ updatesFragmentTag);
- // Otherwise, wide width screens have a {@link ContactDetailFragmentCarousel}
- mFragmentCarousel = (ContactDetailFragmentCarousel) findViewById(R.id.fragment_carousel);
- if (mFragmentCarousel != null) {
- if (mAboutFragment != null) mFragmentCarousel.setAboutFragment(mAboutFragment);
- if (mUpdatesFragment != null) mFragmentCarousel.setUpdatesFragment(mUpdatesFragment);
+ if (mDetailFragment != null && mUpdatesFragment != null) {
+ FragmentTransaction ft = fragmentManager.beginTransaction();
+ ft.remove(mDetailFragment);
+ ft.remove(mUpdatesFragment);
+ ft.commit();
+ }
}
Log.i(TAG, getIntent().getData().toString());
@@ -118,13 +146,23 @@
@Override
public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof ContactDetailAboutFragment) {
- mAboutFragment = (ContactDetailAboutFragment) fragment;
- mAboutFragment.setListener(mFragmentListener);
- mAboutFragment.setVerticalScrollListener(mVerticalScrollListener);
- mAboutFragment.loadUri(getIntent().getData());
+ if (fragment instanceof ContactDetailFragment) {
+ mDetailFragment = (ContactDetailFragment) fragment;
+ mDetailFragment.setListener(mFragmentListener);
+ mDetailFragment.setVerticalScrollListener(mVerticalScrollListener);
+ mDetailFragment.setData(mContactData);
+ // If the contact has social updates, then the photo should be shown in the tab
+ // carousel, so don't show the photo again in the scrolling list of contact details.
+ // We also don't want to show the photo if there is a fragment carousel because then
+ // the picture will already be on the left of the list of contact details.
+ mDetailFragment.setShowPhotoInHeader(!mContactHasUpdates && mFragmentCarousel == null);
} else if (fragment instanceof ContactDetailUpdatesFragment) {
mUpdatesFragment = (ContactDetailUpdatesFragment) fragment;
+ mUpdatesFragment.setData(mContactData);
+ } else if (fragment instanceof ContactLoaderFragment) {
+ mLoaderFragment = (ContactLoaderFragment) fragment;
+ mLoaderFragment.setListener(mLoaderFragmentListener);
+ mLoaderFragment.loadUri(getIntent().getData());
}
}
@@ -177,7 +215,7 @@
FragmentKeyListener mCurrentFragment;
switch (getCurrentPage()) {
case 0:
- mCurrentFragment = (FragmentKeyListener) mAboutFragment;
+ mCurrentFragment = (FragmentKeyListener) mDetailFragment;
break;
case 1:
mCurrentFragment = (FragmentKeyListener) mUpdatesFragment;
@@ -191,13 +229,105 @@
}
private int getCurrentPage() {
+ // If the contact doesn't have any social updates, there is only 1 page (detail fragment).
+ if (!mContactHasUpdates) {
+ return 0;
+ }
+ // Otherwise find the current page based on the {@link ViewPager} or fragment carousel.
if (mViewPager != null) {
return mViewPager.getCurrentItem();
} else if (mFragmentCarousel != null) {
return mFragmentCarousel.getCurrentPage();
}
- throw new IllegalStateException("Can't figure out the currently selected page. The activity"
- + "must either have the ViewPager or fragment carousel");
+ throw new IllegalStateException("Can't figure out the currently selected page. If the " +
+ "contact has social updates, there must be a ViewPager or fragment carousel");
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mViewPager != null) {
+ outState.putString(KEY_DETAIL_FRAGMENT_TAG, mDetailFragment.getTag());
+ outState.putString(KEY_UPDATES_FRAGMENT_TAG, mUpdatesFragment.getTag());
+ return;
+ }
+ }
+
+ private final ContactLoaderFragmentListener mLoaderFragmentListener =
+ new ContactLoaderFragmentListener() {
+ @Override
+ public void onDetailsLoaded(final ContactLoader.Result result) {
+ if (result == null) {
+ return;
+ }
+ // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
+ // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
+ // on the main thread to execute later.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mContactData = result;
+ mLookupUri = result.getLookupUri();
+ mContactHasUpdates = result.getSocialSnippet() != null;
+ invalidateOptionsMenu();
+ setupTitle();
+ if (mContactHasUpdates) {
+ setupContactWithUpdates();
+ } else {
+ setupContactWithoutUpdates();
+ }
+ }
+ });
+ }
+ };
+
+ /**
+ * Setup the activity title and subtitle with contact name and company.
+ */
+ private void setupTitle() {
+ CharSequence displayName = ContactDetailDisplayUtils.getDisplayName(this, mContactData);
+ String company = ContactDetailDisplayUtils.getCompany(this, mContactData);
+
+ ActionBar actionBar = getActionBar();
+ actionBar.setTitle(displayName);
+ actionBar.setSubtitle(company);
+ }
+
+ private void setupContactWithUpdates() {
+ if (mContentView == null) {
+ mContentView = (ViewGroup) mInflater.inflate(
+ R.layout.contact_detail_container_with_updates, mRootView, false);
+ mRootView.addView(mContentView);
+ }
+
+ // Narrow width screens have a {@link ViewPager} and {@link ContactDetailTabCarousel}
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ if (mViewPager != null) {
+ mViewPager.removeAllViews();
+ mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
+ mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+ }
+
+ mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
+ if (mTabCarousel != null) {
+ mTabCarousel.setListener(mTabCarouselListener);
+ mTabCarousel.loadData(mContactData);
+ }
+
+ // Otherwise, wide width screens have a {@link ContactDetailFragmentCarousel}
+ mFragmentCarousel = (ContactDetailFragmentCarousel) findViewById(R.id.fragment_carousel);
+ if (mFragmentCarousel != null) {
+ if (mDetailFragment != null) mFragmentCarousel.setAboutFragment(mDetailFragment);
+ if (mUpdatesFragment != null) mFragmentCarousel.setUpdatesFragment(mUpdatesFragment);
+ }
+ }
+
+ private void setupContactWithoutUpdates() {
+ if (mContentView == null) {
+ mContentView = (ViewGroup) mInflater.inflate(
+ R.layout.contact_detail_container_without_updates, mRootView, false);
+ mRootView.addView(mContentView);
+ }
}
private final ContactDetailFragment.Listener mFragmentListener =
@@ -208,19 +338,6 @@
}
@Override
- public void onDetailsLoaded(ContactLoader.Result result) {
- if (result == null) {
- return;
- }
- mContactData = result;
- mLookupUri = result.getLookupUri();
- invalidateOptionsMenu();
- if (mTabCarousel != null) {
- mTabCarousel.loadData(result);
- }
- }
-
- @Override
public void onEditRequested(Uri contactLookupUri) {
startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
}
@@ -262,7 +379,7 @@
public Fragment getItem(int position) {
switch (position) {
case 0:
- return new ContactDetailAboutFragment();
+ return new ContactDetailFragment();
case 1:
return new ContactDetailUpdatesFragment();
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 07c9a48..4b7c360 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -728,11 +728,6 @@
}
@Override
- public void onDetailsLoaded(ContactLoader.Result result) {
- // Nothing needs to be done here
- }
-
- @Override
public void onEditRequested(Uri contactLookupUri) {
startActivityForResult(
new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
diff --git a/src/com/android/contacts/detail/ContactDetailAboutFragment.java b/src/com/android/contacts/detail/ContactDetailAboutFragment.java
deleted file mode 100644
index fc6b9cb..0000000
--- a/src/com/android/contacts/detail/ContactDetailAboutFragment.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.contacts.detail;
-
-import com.android.contacts.ContactLoader;
-import com.android.contacts.R;
-
-import android.app.ActionBar;
-import android.app.Activity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-public class ContactDetailAboutFragment extends ContactDetailFragment {
-
- private static final String TAG = "ContactDetailAboutFragment";
-
- public ContactDetailAboutFragment() {
- // Explicit constructor for inflation
- }
-
- @Override
- protected View createNewHeaderView(ViewGroup parent) {
- ViewGroup headerView = (ViewGroup) inflate(
- R.layout.simple_contact_detail_header_view_list_item, parent, false);
- TextView phoneticNameView = (TextView) headerView.findViewById(R.id.phonetic_name);
- TextView attributionView = (TextView) headerView.findViewById(R.id.attribution);
- ContactDetailDisplayUtils.setPhoneticName(getContext(), getContactData(), phoneticNameView);
- ContactDetailDisplayUtils.setAttribution(getContext(), getContactData(), attributionView);
- return headerView;
- }
-
- @Override
- protected void bindData() {
- ContactLoader.Result contactData = getContactData();
- if (contactData != null) {
- // Setup the activity title and subtitle with contact name and company
- Activity activity = getActivity();
- CharSequence displayName = ContactDetailDisplayUtils.getDisplayName(activity,
- contactData);
- String company = ContactDetailDisplayUtils.getCompany(activity, contactData);
-
- ActionBar actionBar = activity.getActionBar();
- actionBar.setTitle(displayName);
- actionBar.setSubtitle(company);
-
- // Pass the contact loader result to the listener to finish setup
- Listener listener = getListener();
- if (listener != null) {
- listener.onDetailsLoaded(contactData);
- }
- }
-
- super.bindData();
- }
-}
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 0f12de7..357350d 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -209,15 +209,43 @@
*/
public static void setSocialSnippetAndDate(Context context, Result contactData,
TextView statusView, TextView dateView) {
+ if (statusView == null || dateView == null) {
+ return;
+ }
setDataOrHideIfNone(contactData.getSocialSnippet(), statusView);
setDataOrHideIfNone(ContactBadgeUtil.getSocialDate(contactData, context), dateView);
}
/**
+ * Sets the display name of this contact to the given {@link TextView}. If
+ * there is none, then set the view to gone.
+ */
+ public static void setDisplayName(Context context, Result contactData, TextView textView) {
+ if (textView == null) {
+ return;
+ }
+ setDataOrHideIfNone(getDisplayName(context, contactData), textView);
+ }
+
+ /**
+ * Sets the company and job title of this contact to the given {@link TextView}. If
+ * there is none, then set the view to gone.
+ */
+ public static void setCompanyName(Context context, Result contactData, TextView textView) {
+ if (textView == null) {
+ return;
+ }
+ setDataOrHideIfNone(getCompany(context, contactData), textView);
+ }
+
+ /**
* Sets the phonetic name of this contact to the given {@link TextView}. If
* there is none, then set the view to gone.
*/
public static void setPhoneticName(Context context, Result contactData, TextView textView) {
+ if (textView == null) {
+ return;
+ }
setDataOrHideIfNone(getPhoneticName(context, contactData), textView);
}
@@ -226,6 +254,9 @@
* there is none, then set the view to gone.
*/
public static void setAttribution(Context context, Result contactData, TextView textView) {
+ if (textView == null) {
+ return;
+ }
setDataOrHideIfNone(getAttribution(context, contactData), textView);
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index c2aaea4..8ee22a4 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -21,6 +21,7 @@
import com.android.contacts.ContactLoader;
import com.android.contacts.ContactOptionsActivity;
import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsUtils;
import com.android.contacts.GroupMetaData;
import com.android.contacts.NfcHandler;
@@ -40,6 +41,7 @@
import com.android.internal.telephony.ITelephony;
import android.accounts.Account;
+import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
@@ -100,6 +102,7 @@
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
+import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -127,7 +130,7 @@
private NfcHandler mNfcHandler;
private ContactLoader.Result mContactData;
- private ContactDetailHeaderView mHeaderView;
+ private ViewGroup mHeaderView;
private ImageView mPhotoView;
private ListView mListView;
private ViewAdapter mAdapter;
@@ -139,6 +142,7 @@
private int mNumPhoneNumbers = 0;
private String mDefaultCountryIso;
private boolean mContactDataDisplayed;
+ private boolean mContactPhotoDisplayedInHeader = true;
private boolean mOptionsMenuOptions;
private boolean mOptionsMenuEditable;
@@ -268,6 +272,10 @@
});
mView.setVisibility(View.INVISIBLE);
+
+ if (mContactData != null) {
+ bindData();
+ }
return mView;
}
@@ -359,6 +367,19 @@
}
}
+ /**
+ * Sets whether or not the contact photo should be shown in the list of contact details in this
+ * {@link Fragment}.
+ */
+ public void setShowPhotoInHeader(boolean showPhoto) {
+ mContactPhotoDisplayedInHeader = showPhoto;
+ }
+
+ public void setData(ContactLoader.Result result) {
+ mContactData = result;
+ bindData();
+ }
+
protected void bindData() {
if (mView == null) {
return;
@@ -1061,7 +1082,49 @@
if (mHeaderView != null) {
return mHeaderView;
}
- return createNewHeaderView(parent);
+
+ mHeaderView = (ViewGroup) inflate(
+ R.layout.simple_contact_detail_header_view_list_item, parent, false);
+
+ TextView displayNameView = (TextView) mHeaderView.findViewById(R.id.name);
+ TextView companyView = (TextView) mHeaderView.findViewById(R.id.company);
+ TextView phoneticNameView = (TextView) mHeaderView.findViewById(R.id.phonetic_name);
+ TextView attributionView = (TextView) mHeaderView.findViewById(R.id.attribution);
+ ImageView photoView = (ImageView) mHeaderView.findViewById(R.id.photo);
+
+ ContactDetailDisplayUtils.setDisplayName(mContext, mContactData, displayNameView);
+ ContactDetailDisplayUtils.setCompanyName(mContext, mContactData, companyView);
+ ContactDetailDisplayUtils.setPhoneticName(mContext, mContactData, phoneticNameView);
+ ContactDetailDisplayUtils.setAttribution(mContext, mContactData, attributionView);
+
+ // Set the photo if it should be displayed
+ if (mContactPhotoDisplayedInHeader) {
+ ContactDetailDisplayUtils.setPhoto(mContext, mContactData, photoView);
+ } else {
+ // Otherwise hide the view
+ photoView.setVisibility(View.INVISIBLE);
+ }
+
+ // Set the starred state if it should be displayed
+ final CheckBox starredView = (CheckBox) mHeaderView.findViewById(R.id.star);
+ if (starredView != null) {
+ ContactDetailDisplayUtils.setStarred(mContactData, starredView);
+ final Uri lookupUri = mContactData.getLookupUri();
+ starredView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Toggle "starred" state
+ // Make sure there is a contact
+ if (lookupUri != null) {
+ Intent intent = ContactSaveService.createSetStarredIntent(
+ getContext(), lookupUri, starredView.isChecked());
+ getContext().startService(intent);
+ }
+ }
+ });
+ }
+
+ return mHeaderView;
}
private View getSeparatorEntryView(View convertView, ViewGroup parent) {
@@ -1243,16 +1306,6 @@
}
}
- /**
- * Returns a new header view for the top of the list of contact details.
- */
- protected View createNewHeaderView(ViewGroup parent) {
- mHeaderView = (ContactDetailHeaderView) inflate(
- R.layout.contact_detail_header_view_list_item, parent, false);
- mHeaderView.loadData(mContactData);
- return mHeaderView;
- }
-
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.view_contact, menu);
@@ -1504,11 +1557,6 @@
public void onContactNotFound();
/**
- * This contact's details have been loaded.
- */
- public void onDetailsLoaded(ContactLoader.Result result);
-
- /**
* User decided to go to Edit-Mode
*/
public void onEditRequested(Uri lookupUri);
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index b253b25..161ae32 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -154,6 +154,10 @@
* from the outside to fully setup the View
*/
public void loadData(ContactLoader.Result contactData) {
+ if (contactData == null) {
+ return;
+ }
+
View aboutView = findViewById(R.id.tab_about);
View updateView = findViewById(R.id.tab_update);
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index 0c4dc4f..05ae866 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -16,6 +16,7 @@
package com.android.contacts.detail;
+import com.android.contacts.ContactLoader;
import com.android.contacts.R;
import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
@@ -31,6 +32,8 @@
private static final String TAG = "ContactDetailUpdatesFragment";
+ private ContactLoader.Result mContactData;
+
/**
* This optional view adds an alpha layer over the entire fragment.
*/
@@ -57,6 +60,11 @@
return rootView;
}
+ public void setData(ContactLoader.Result result) {
+ mContactData = result;
+ // TODO: Load up the "recent updates" section based on the result
+ }
+
@Override
public void setAlphaLayerValue(float alpha) {
if (mAlphaLayer != null) {
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
new file mode 100644
index 0000000..3ece0ce
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.contacts.detail;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.R;
+import com.android.internal.util.Objects;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.Loader;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This is an invisible worker {@link Fragment} that loads the contact details for the contact card.
+ * The data is then passed to the listener, who can then pass the data to other {@link View}s.
+ */
+public class ContactLoaderFragment extends Fragment {
+
+ private static final String TAG = ContactLoaderFragment.class.getSimpleName();
+
+ /**
+ * This is a listener to the {@link ContactLoaderFragment} and will be notified when the
+ * contact details have finished loading.
+ */
+ public static interface ContactLoaderFragmentListener {
+ public void onDetailsLoaded(ContactLoader.Result result);
+ }
+
+ private static final int LOADER_DETAILS = 1;
+
+ private static final String KEY_CONTACT_URI = "contactUri";
+ private static final String LOADER_ARG_CONTACT_URI = "contactUri";
+
+ private Context mContext;
+ private Uri mLookupUri;
+ private ContactLoaderFragmentListener mListener;
+
+ private ContactLoader.Result mContactData;
+
+ public ContactLoaderFragment() {
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mLookupUri = savedInstanceState.getParcelable(KEY_CONTACT_URI);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_CONTACT_URI, mLookupUri);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mContext = activity;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ // This is an empty view that is set to visibility gone.
+ return inflater.inflate(R.layout.contact_detail_loader_fragment, container, false);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (mLookupUri != null) {
+ Bundle args = new Bundle();
+ args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
+ getLoaderManager().initLoader(LOADER_DETAILS, args, mDetailLoaderListener);
+ }
+ }
+
+ public void loadUri(Uri lookupUri) {
+ if (Objects.equal(lookupUri, mLookupUri)) {
+ // Same URI, no need to load the data again
+ return;
+ }
+
+ mLookupUri = lookupUri;
+ if (mLookupUri == null) {
+ getLoaderManager().destroyLoader(LOADER_DETAILS);
+ mContactData = null;
+ if (mListener != null) {
+ mListener.onDetailsLoaded(mContactData);
+ }
+ } else if (getActivity() != null) {
+ Bundle args = new Bundle();
+ args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
+ getLoaderManager().restartLoader(LOADER_DETAILS, args, mDetailLoaderListener);
+ }
+ }
+
+ public void setListener(ContactLoaderFragmentListener value) {
+ mListener = value;
+ }
+
+ /**
+ * The listener for the detail loader
+ */
+ private final LoaderManager.LoaderCallbacks<ContactLoader.Result> mDetailLoaderListener =
+ new LoaderCallbacks<ContactLoader.Result>() {
+ @Override
+ public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
+ Uri lookupUri = args.getParcelable(LOADER_ARG_CONTACT_URI);
+ return new ContactLoader(mContext, lookupUri, true /* loadGroupMetaData */);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
+ if (!mLookupUri.equals(data.getUri())) {
+ return;
+ }
+
+ if (data != ContactLoader.Result.NOT_FOUND && data != ContactLoader.Result.ERROR) {
+ mContactData = data;
+ } else {
+ Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
+ mContactData = null;
+ }
+
+ if (mListener != null) {
+ mListener.onDetailsLoaded(mContactData);
+ }
+ }
+
+ public void onLoaderReset(Loader<ContactLoader.Result> loader) {
+ mContactData = null;
+ if (mListener != null) {
+ mListener.onDetailsLoaded(mContactData);
+ }
+ }
+ };
+}