summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--res/layout/carousel_about_tab.xml66
-rw-r--r--res/layout/carousel_updates_tab.xml66
-rw-r--r--res/layout/contact_detail_activity.xml21
-rw-r--r--res/layout/contact_detail_tab_carousel.xml39
-rw-r--r--res/layout/contact_detail_updates_fragment.xml33
-rw-r--r--res/layout/simple_contact_detail_header_view_list_item.xml43
-rw-r--r--res/values/colors.xml3
-rw-r--r--res/values/dimens.xml15
-rw-r--r--res/values/strings.xml6
-rw-r--r--src/com/android/contacts/activities/ContactDetailActivity.java166
-rw-r--r--src/com/android/contacts/activities/PeopleActivity.java6
-rw-r--r--src/com/android/contacts/detail/ContactDetailAboutFragment.java78
-rw-r--r--src/com/android/contacts/detail/ContactDetailDisplayUtils.java246
-rw-r--r--src/com/android/contacts/detail/ContactDetailFragment.java54
-rw-r--r--src/com/android/contacts/detail/ContactDetailHeaderView.java1
-rw-r--r--src/com/android/contacts/detail/ContactDetailTabCarousel.java230
-rw-r--r--src/com/android/contacts/detail/ContactDetailUpdatesFragment.java45
18 files changed, 1090 insertions, 32 deletions
diff --git a/Android.mk b/Android.mk
index 1c9dcc475..c6bea9d49 100644
--- a/Android.mk
+++ b/Android.mk
@@ -9,7 +9,9 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
com.android.phone.common \
com.android.vcard \
android-common \
- guava
+ guava \
+ android-support-v13 \
+ android-support-v4
LOCAL_PACKAGE_NAME := Contacts
LOCAL_CERTIFICATE := shared
diff --git a/res/layout/carousel_about_tab.xml b/res/layout/carousel_about_tab.xml
new file mode 100644
index 000000000..f1ed4f1b3
--- /dev/null
+++ b/res/layout/carousel_about_tab.xml
@@ -0,0 +1,66 @@
+<?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="@dimen/detail_tab_carousel_tab_width"
+ android:layout_height="@dimen/detail_tab_carousel_height"
+ android:background="@color/detail_tab_background">
+
+ <ImageView android:id="@+id/photo"
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"/>
+
+ <!-- Transparent view to overlay on the contact's photo
+ (to allow white text to appear over a white photo). -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:background="@android:color/black"
+ android:alpha=".25"/>
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:paddingLeft="@dimen/detail_item_side_margin"
+ android:singleLine="true"
+ android:gravity="left|center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/detail_header_view_text_color"
+ style="@android:style/Widget.Holo.ActionBar.TabView" />
+
+ <CheckBox
+ android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/description_star"
+ android:visibility="invisible"
+ style="?android:attr/starStyle"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
new file mode 100644
index 000000000..9deb2f7ce
--- /dev/null
+++ b/res/layout/carousel_updates_tab.xml
@@ -0,0 +1,66 @@
+<?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="@dimen/detail_tab_carousel_tab_width"
+ android:layout_height="@dimen/detail_tab_carousel_height"
+ android:background="@color/detail_tab_background">
+
+ <!-- Transparent view to overlay on the contact's photo
+ (to allow white text to appear over a white photo). -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:background="@android:color/black"
+ android:alpha=".25"/>
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/detail_tab_carousel_tab_label_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:paddingLeft="@dimen/detail_item_side_margin"
+ android:singleLine="true"
+ android:gravity="left|center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="@color/detail_header_view_text_color"
+ style="@android:style/Widget.Holo.ActionBar.TabView" />
+
+ <TextView android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_marginTop="@dimen/detail_update_tab_vertical_margin"
+ android:paddingLeft="@dimen/detail_update_tab_side_padding"
+ android:paddingRight="@dimen/detail_update_tab_side_padding"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="3"/>
+
+ <TextView android:id="@+id/status_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/status"
+ android:layout_marginBottom="@dimen/detail_update_tab_vertical_margin"
+ android:paddingLeft="@dimen/detail_update_tab_side_padding"
+ android:paddingRight="@dimen/detail_update_tab_side_padding"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/contact_detail_activity.xml b/res/layout/contact_detail_activity.xml
index 9408f89bc..744f3436c 100644
--- a/res/layout/contact_detail_activity.xml
+++ b/res/layout/contact_detail_activity.xml
@@ -14,12 +14,19 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout 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">
- <fragment class="com.android.contacts.detail.ContactDetailFragment"
- android:id="@+id/contact_detail_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-</FrameLayout>
+ <com.android.contacts.detail.ContactDetailTabCarousel
+ android:id="@+id/tab_carousel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</LinearLayout>
diff --git a/res/layout/contact_detail_tab_carousel.xml b/res/layout/contact_detail_tab_carousel.xml
new file mode 100644
index 000000000..a7321ee91
--- /dev/null
+++ b/res/layout/contact_detail_tab_carousel.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<HorizontalScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="none">
+
+ <LinearLayout
+ android:id="@+id/tab_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <include
+ android:id="@+id/tab_about"
+ layout="@layout/carousel_about_tab" />
+
+ <include
+ android:id="@+id/tab_update"
+ layout="@layout/carousel_updates_tab" />
+
+ </LinearLayout>
+
+</HorizontalScrollView> \ No newline at end of file
diff --git a/res/layout/contact_detail_updates_fragment.xml b/res/layout/contact_detail_updates_fragment.xml
new file mode 100644
index 000000000..7baba42a5
--- /dev/null
+++ b/res/layout/contact_detail_updates_fragment.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/contact_detail"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView android:id="@+id/emptyText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/no_contact_details"
+ android:textSize="20sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:paddingTop="10dip"/>
+</LinearLayout>
+
diff --git a/res/layout/simple_contact_detail_header_view_list_item.xml b/res/layout/simple_contact_detail_header_view_list_item.xml
new file mode 100644
index 000000000..117aef1a8
--- /dev/null
+++ b/res/layout/simple_contact_detail_header_view_list_item.xml
@@ -0,0 +1,43 @@
+<?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="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/phonetic_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/attribution"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 9a6081504..60873b17d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -49,6 +49,9 @@
<!-- Color of the text indicating the type of entry (e.g. Home, Work etc) -->
<color name="detail_header_view_text_color">#FFFFFF</color>
+ <!-- Color of the background of the tabs on the contact detail page -->
+ <color name="detail_tab_background">#DBDBDB</color>
+
<!-- Color of the text foreground and background of Regular Sized ContactTile -->
<color name="contact_tile_regular_text">#2B1B17</color>
<color name="contact_tile_regular_text_background">#FFFFFF</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 68655d833..f36975271 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -70,6 +70,21 @@
<!-- Font size for the entries in a spinner in the contact editor. -->
<dimen name="editor_field_spinner_text_size">10sp</dimen>
+ <!-- Height of the tab carousel on the contact detail page -->
+ <dimen name="detail_tab_carousel_height">150dip</dimen>
+
+ <!-- Width of a tab in the tab carousel on the contact detail page -->
+ <dimen name="detail_tab_carousel_tab_width">240dip</dimen>
+
+ <!-- Height of the tab text label in the tab carousel on the contact detail page -->
+ <dimen name="detail_tab_carousel_tab_label_height">40dip</dimen>
+
+ <!-- Vertical margin of the text within the update tab in the tab carousel -->
+ <dimen name="detail_update_tab_vertical_margin">20dip</dimen>
+
+ <!-- Left and right padding of the text within the update tab in the tab carousel -->
+ <dimen name="detail_update_tab_side_padding">10dip</dimen>
+
<!-- Left and right padding for a contact detail item -->
<dimen name="detail_item_icon_margin">10dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f259ea702..c9b278761 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -78,6 +78,12 @@
creating a new contact. This string represents the built in way to create the contact. -->
<string name="insertContactDescription">Create contact</string>
+ <!-- The tab label for the contact detail activity that displays information about the contact [CHAR LIMIT=11] -->
+ <string name="contactDetailAbout">About</string>
+
+ <!-- The tab label for the contact detail activity that displays information about the contact [CHAR LIMIT=11] -->
+ <string name="contactDetailUpdates">Updates</string>
+
<!-- Hint text in the search box when the user hits the Search key while in the contacts app -->
<string name="searchHint">Search contacts</string>
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 5992042a2..0dbc8dfe9 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -16,22 +16,36 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactLoader;
import com.android.contacts.ContactSaveService;
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.ContactDetailFragment;
+import com.android.contacts.detail.ContactDetailHeaderView;
+import com.android.contacts.detail.ContactDetailTabCarousel;
+import com.android.contacts.detail.ContactDetailUpdatesFragment;
import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.list.ContactBrowseListFragment;
import com.android.contacts.util.PhoneCapabilityTester;
import android.accounts.Account;
+import android.app.Fragment;
+import android.app.FragmentManager;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
import android.widget.Toast;
import java.util.ArrayList;
@@ -39,7 +53,15 @@ import java.util.ArrayList;
public class ContactDetailActivity extends ContactsActivity {
private static final String TAG = "ContactDetailActivity";
- private ContactDetailFragment mFragment;
+ public static final int FRAGMENT_COUNT = 2;
+
+ private ContactDetailAboutFragment mAboutFragment;
+ private ContactDetailUpdatesFragment mUpdatesFragment;
+
+ private ContactDetailTabCarousel mTabCarousel;
+ private ViewPager mViewPager;
+
+ private Uri mUri;
@Override
public void onCreate(Bundle savedState) {
@@ -64,14 +86,29 @@ public class ContactDetailActivity extends ContactsActivity {
setContentView(R.layout.contact_detail_activity);
- mFragment = (ContactDetailFragment) getFragmentManager().findFragmentById(
- R.id.contact_detail_fragment);
- mFragment.setListener(mFragmentListener);
- mFragment.loadUri(getIntent().getData());
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
+ mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+
+ mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
+ mTabCarousel.setListener(mTabCarouselListener);
+ mUri = getIntent().getData();
Log.i(TAG, getIntent().getData().toString());
}
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ if (fragment instanceof ContactDetailAboutFragment) {
+ mAboutFragment = (ContactDetailAboutFragment) fragment;
+ mAboutFragment.setListener(mFragmentListener);
+ mAboutFragment.loadUri(mUri);
+ } else if (fragment instanceof ContactDetailUpdatesFragment) {
+ mUpdatesFragment = (ContactDetailUpdatesFragment) fragment;
+ }
+ }
+
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
boolean globalSearch) {
@@ -84,7 +121,18 @@ public class ContactDetailActivity extends ContactsActivity {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (mFragment.handleKeyDown(keyCode)) return true;
+ FragmentKeyListener mCurrentFragment;
+ switch (mViewPager.getCurrentItem()) {
+ case 0:
+ mCurrentFragment = (FragmentKeyListener) mAboutFragment;
+ break;
+ case 1:
+ mCurrentFragment = (FragmentKeyListener) mUpdatesFragment;
+ break;
+ default:
+ throw new IllegalStateException("Invalid current item for ViewPager");
+ }
+ if (mCurrentFragment.handleKeyDown(keyCode)) return true;
return super.onKeyDown(keyCode, event);
}
@@ -97,6 +145,11 @@ public class ContactDetailActivity extends ContactsActivity {
}
@Override
+ public void onDetailsLoaded(ContactLoader.Result result) {
+ mTabCarousel.loadData(result);
+ }
+
+ @Override
public void onEditRequested(Uri contactLookupUri) {
startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
}
@@ -127,4 +180,105 @@ public class ContactDetailActivity extends ContactsActivity {
}
};
+
+ public class ViewPagerAdapter extends FragmentPagerAdapter{
+
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case 0:
+ return new ContactDetailAboutFragment();
+ case 1:
+ return new ContactDetailUpdatesFragment();
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ @Override
+ public int getCount() {
+ return FRAGMENT_COUNT;
+ }
+ }
+
+ private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ // The user is horizontally dragging the {@link ViewPager}, so send
+ // these scroll changes to the tab carousel. Ignore these events though if the carousel
+ // is actually controlling the {@link ViewPager} scrolls because it will already be
+ // in the correct position.
+ if (mViewPager.isFakeDragging()) {
+ return;
+ }
+ int x = (int) ((position + positionOffset) * mTabCarousel.getAllowedScrollLength());
+ mTabCarousel.scrollTo(x, 0);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // Since a new page has been selected by the {@link ViewPager},
+ // update the tab selection in the carousel.
+ mTabCarousel.setCurrentTab(position);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+
+ };
+
+ private ContactDetailTabCarousel.Listener mTabCarouselListener =
+ new ContactDetailTabCarousel.Listener() {
+
+ @Override
+ public void onTouchDown() {
+ // The user just started scrolling the carousel, so begin "fake dragging" the
+ // {@link ViewPager} if it's not already doing so.
+ if (mViewPager.isFakeDragging()) {
+ return;
+ }
+ mViewPager.beginFakeDrag();
+ }
+
+ @Override
+ public void onTouchUp() {
+ // The user just stopped scrolling the carousel, so stop "fake dragging" the
+ // {@link ViewPager} if was doing so before.
+ if (mViewPager.isFakeDragging()) {
+ mViewPager.endFakeDrag();
+ }
+ }
+
+ @Override
+ public void onScrollChanged(int l, int t, int oldl, int oldt) {
+ // The user is scrolling the carousel, so send the scroll deltas to the
+ // {@link ViewPager} so it can move in sync.
+ if (mViewPager.isFakeDragging()) {
+ mViewPager.fakeDragBy(oldl-l);
+ }
+ }
+
+ @Override
+ public void onTabSelected(int position) {
+ // The user selected a tab, so update the {@link ViewPager}
+ mViewPager.setCurrentItem(position);
+ }
+ };
+
+ /**
+ * This interface should be implemented by {@link Fragment}s within this
+ * activity so that the activity can determine whether the currently
+ * displayed view is handling the key event or not.
+ */
+ public interface FragmentKeyListener {
+ /**
+ * Returns true if the key down event will be handled by the implementing class, or false
+ * otherwise.
+ */
+ public boolean handleKeyDown(int keyCode);
+ }
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 53dd306f9..c247d4328 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactLoader;
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
@@ -679,6 +680,11 @@ public class PeopleActivity extends ContactsActivity
}
@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
new file mode 100644
index 000000000..a1377e83d
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailAboutFragment.java
@@ -0,0 +1,78 @@
+/*
+ * 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.accounts.Account;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+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
new file mode 100644
index 000000000..0f12de72e
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -0,0 +1,246 @@
+/*
+ * 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.ContactLoader.Result;
+import com.android.contacts.R;
+import com.android.contacts.format.FormatUtils;
+import com.android.contacts.preference.ContactsPreferences;
+import com.android.contacts.util.ContactBadgeUtil;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.content.Entity.NamedContentValues;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * This class contains utility methods to bind high-level contact details
+ * (meaning name, phonetic name, job, and attribution) from a
+ * {@link ContactLoader.Result} data object to appropriate {@link View}s.
+ */
+public class ContactDetailDisplayUtils {
+
+ private static final int PHOTO_FADE_IN_ANIMATION_DURATION_MILLIS = 100;
+
+ private ContactDetailDisplayUtils() {
+ // Disallow explicit creation of this class.
+ }
+
+ /**
+ * Returns the display name of the contact. Depending on the preference for
+ * display name ordering, the contact's first name may be bolded if
+ * possible. Returns empty string if there is no display name.
+ */
+ public static CharSequence getDisplayName(Context context, Result contactData) {
+ CharSequence displayName = contactData.getDisplayName();
+ CharSequence altDisplayName = contactData.getAltDisplayName();
+ ContactsPreferences prefs = new ContactsPreferences(context);
+ CharSequence styledName = "";
+ if (!TextUtils.isEmpty(displayName) && !TextUtils.isEmpty(altDisplayName)) {
+ if (prefs.getDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+ int overlapPoint = FormatUtils.overlapPoint(
+ displayName.toString(), altDisplayName.toString());
+ if (overlapPoint > 0) {
+ styledName = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+ displayName, 0, overlapPoint, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ styledName = displayName;
+ }
+ } else {
+ // Displaying alternate display name.
+ int overlapPoint = FormatUtils.overlapPoint(
+ altDisplayName.toString(), displayName.toString());
+ if (overlapPoint > 0) {
+ styledName = FormatUtils.applyStyleToSpan(Typeface.BOLD,
+ altDisplayName, overlapPoint, altDisplayName.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ styledName = altDisplayName;
+ }
+ }
+ }
+ return styledName;
+ }
+
+ /**
+ * Returns the phonetic name of the contact or null if there isn't one.
+ */
+ public static String getPhoneticName(Context context, Result contactData) {
+ String phoneticName = contactData.getPhoneticName();
+ if (!TextUtils.isEmpty(phoneticName)) {
+ return phoneticName;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the attribution string for the contact. This could either specify
+ * that this is a joined contact or specify the contact directory that the
+ * contact came from. Returns null if there is none applicable.
+ */
+ public static String getAttribution(Context context, Result contactData) {
+ // Check if this is a joined contact
+ if (contactData.getEntities().size() > 1) {
+ return context.getString(R.string.indicator_joined_contact);
+ } else if (contactData.isDirectoryEntry()) {
+ // This contact is from a directory
+ String directoryDisplayName = contactData.getDirectoryDisplayName();
+ String directoryType = contactData.getDirectoryType();
+ String displayName = !TextUtils.isEmpty(directoryDisplayName)
+ ? directoryDisplayName
+ : directoryType;
+ return context.getString(R.string.contact_directory_description, displayName);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the organization of the contact. If several organizations are given,
+ * the first one is used. Returns null if not applicable.
+ */
+ public static String getCompany(Context context, Result contactData) {
+ final boolean displayNameIsOrganization = contactData.getDisplayNameSource()
+ == DisplayNameSources.ORGANIZATION;
+ for (Entity entity : contactData.getEntities()) {
+ for (NamedContentValues subValue : entity.getSubValues()) {
+ final ContentValues entryValues = subValue.values;
+ final String mimeType = entryValues.getAsString(Data.MIMETYPE);
+
+ if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final String company = entryValues.getAsString(Organization.COMPANY);
+ final String title = entryValues.getAsString(Organization.TITLE);
+ final String combined;
+ // We need to show company and title in a combined string. However, if the
+ // DisplayName is already the organization, it mirrors company or (if company
+ // is empty title). Make sure we don't show what's already shown as DisplayName
+ if (TextUtils.isEmpty(company)) {
+ combined = displayNameIsOrganization ? null : title;
+ } else {
+ if (TextUtils.isEmpty(title)) {
+ combined = displayNameIsOrganization ? null : company;
+ } else {
+ if (displayNameIsOrganization) {
+ combined = title;
+ } else {
+ combined = context.getString(
+ R.string.organization_company_and_title,
+ company, title);
+ }
+ }
+ }
+
+ if (!TextUtils.isEmpty(combined)) {
+ return combined;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Sets the contact photo to display in the given {@link ImageView}. If bitmap is null, the
+ * default placeholder image is shown.
+ */
+ public static void setPhoto(Context context, Result contactData, ImageView photoView) {
+ if (contactData.isLoadingPhoto()) {
+ photoView.setImageBitmap(null);
+ return;
+ }
+ byte[] photo = contactData.getPhotoBinaryData();
+ Bitmap bitmap = photo != null ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
+ : ContactBadgeUtil.loadPlaceholderPhoto(context);
+ boolean fadeIn = contactData.isDirectoryEntry();
+ if (photoView.getDrawable() == null && fadeIn) {
+ AlphaAnimation animation = new AlphaAnimation(0, 1);
+ animation.setDuration(PHOTO_FADE_IN_ANIMATION_DURATION_MILLIS);
+ animation.setInterpolator(new AccelerateInterpolator());
+ photoView.startAnimation(animation);
+ }
+ photoView.setImageBitmap(bitmap);
+ }
+
+ /**
+ * Sets the starred state of this contact.
+ */
+ public static void setStarred(Result contactData, CheckBox starredView) {
+ // Check if the starred state should be visible
+ if (!contactData.isDirectoryEntry()) {
+ starredView.setVisibility(View.VISIBLE);
+ starredView.setChecked(contactData.getStarred());
+ } else {
+ starredView.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Set the social snippet text and date. If there isn't one, then set the view to gone.
+ */
+ public static void setSocialSnippetAndDate(Context context, Result contactData,
+ TextView statusView, TextView dateView) {
+ setDataOrHideIfNone(contactData.getSocialSnippet(), statusView);
+ setDataOrHideIfNone(ContactBadgeUtil.getSocialDate(contactData, context), dateView);
+ }
+
+ /**
+ * 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) {
+ setDataOrHideIfNone(getPhoneticName(context, contactData), textView);
+ }
+
+ /**
+ * Sets the attribution contact to the given {@link TextView}. If
+ * there is none, then set the view to gone.
+ */
+ public static void setAttribution(Context context, Result contactData, TextView textView) {
+ setDataOrHideIfNone(getAttribution(context, contactData), textView);
+ }
+
+ /**
+ * Helper function to display the given text in the {@link TextView} or
+ * hides the {@link TextView} if the text is empty or null.
+ */
+ private static void setDataOrHideIfNone(CharSequence textToDisplay, TextView textView) {
+ if (!TextUtils.isEmpty(textToDisplay)) {
+ textView.setText(textToDisplay);
+ textView.setVisibility(View.VISIBLE);
+ } else {
+ textView.setText(null);
+ textView.setVisibility(View.GONE);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 2a0085a27..599a12e11 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -26,6 +26,7 @@ import com.android.contacts.GroupMetaData;
import com.android.contacts.NfcHandler;
import com.android.contacts.R;
import com.android.contacts.TypePrecedence;
+import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
import com.android.contacts.editor.SelectAccountDialogFragment;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountType.EditType;
@@ -107,7 +108,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-public class ContactDetailFragment extends Fragment implements
+public class ContactDetailFragment extends Fragment implements FragmentKeyListener,
OnItemClickListener, OnItemLongClickListener, SelectAccountDialogFragment.Listener {
private static final String TAG = "ContactDetailFragment";
@@ -250,10 +251,26 @@ public class ContactDetailFragment extends Fragment implements
return mView;
}
+ protected View inflate(int resource, ViewGroup root, boolean attachToRoot) {
+ return mInflater.inflate(resource, root, attachToRoot);
+ }
+
public void setListener(Listener value) {
mListener = value;
}
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected Listener getListener() {
+ return mListener;
+ }
+
+ protected ContactLoader.Result getContactData() {
+ return mContactData;
+ }
+
public Uri getUri() {
return mLookupUri;
}
@@ -289,7 +306,7 @@ public class ContactDetailFragment extends Fragment implements
}
}
- private void bindData() {
+ protected void bindData() {
if (mView == null) {
return;
}
@@ -986,11 +1003,7 @@ public class ContactDetailFragment extends Fragment implements
if (mHeaderView != null) {
return mHeaderView;
}
- mHeaderView = (ContactDetailHeaderView) mInflater.inflate(
- R.layout.contact_detail_header_view_list_item, parent, false);
- mHeaderView.setListener(mHeaderViewListener);
- mHeaderView.loadData(mContactData);
- return mHeaderView;
+ return createNewHeaderView(parent);
}
private View getSeparatorEntryView(View convertView, ViewGroup parent) {
@@ -1172,6 +1185,16 @@ public class ContactDetailFragment extends Fragment implements
}
}
+ /**
+ * 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);
@@ -1336,6 +1359,7 @@ public class ContactDetailFragment extends Fragment implements
return true;
}
+ @Override
public boolean handleKeyDown(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL: {
@@ -1414,17 +1438,6 @@ public class ContactDetailFragment extends Fragment implements
}
};
- private ContactDetailHeaderView.Listener mHeaderViewListener =
- new ContactDetailHeaderView.Listener() {
- @Override
- public void onDisplayNameClick(View view) {
- }
-
- @Override
- public void onPhotoClick(View view) {
- }
- };
-
public static interface Listener {
/**
* Contact was not found, so somehow close this fragment. This is raised after a contact
@@ -1433,6 +1446,11 @@ public class ContactDetailFragment extends Fragment implements
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/ContactDetailHeaderView.java b/src/com/android/contacts/detail/ContactDetailHeaderView.java
index 795ed62bf..63f8fbe90 100644
--- a/src/com/android/contacts/detail/ContactDetailHeaderView.java
+++ b/src/com/android/contacts/detail/ContactDetailHeaderView.java
@@ -56,6 +56,7 @@ import android.widget.Toast;
* Header for displaying a title bar with contact info. You
* can bind specific values by calling
* {@link ContactDetailHeaderView#loadData(com.android.contacts.ContactLoader.Result)}
+ * TODO: Refactor to use {@link ContactDetailDisplayUtils}
*/
public class ContactDetailHeaderView extends FrameLayout
implements View.OnClickListener, View.OnLongClickListener {
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
new file mode 100644
index 000000000..a8803f525
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -0,0 +1,230 @@
+/*
+ * 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.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.activities.ContactDetailActivity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.CheckBox;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * This is a horizontally scrolling carousel with 2 tabs: one to see info about the contact and
+ * one to see updates from the contact.
+ * TODO: Create custom views for the tabs so their width can be programatically set as 2/3 of the
+ * screen width.
+ */
+public class ContactDetailTabCarousel extends HorizontalScrollView
+ implements View.OnClickListener, OnTouchListener {
+ private static final String TAG = "ContactDetailTabCarousel";
+
+ private CheckBox mStarredView;
+ private ImageView mPhotoView;
+ private TextView mStatusView;
+ private TextView mStatusDateView;
+
+ private Uri mContactUri;
+ private Listener mListener;
+
+ private View[] mTabs = new View[2];
+
+ private int mAllowedScrollLength;
+
+ /**
+ * Interface for callbacks invoked when the user interacts with the carousel.
+ */
+ public interface Listener {
+ public void onTouchDown();
+ public void onTouchUp();
+ public void onScrollChanged(int l, int t, int oldl, int oldt);
+ public void onTabSelected(int position);
+ }
+
+ public ContactDetailTabCarousel(Context context) {
+ this(context, null);
+ }
+
+ public ContactDetailTabCarousel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ContactDetailTabCarousel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final LayoutInflater inflater =
+ (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.contact_detail_tab_carousel, this);
+
+ setOnTouchListener(this);
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ mListener.onScrollChanged(l, t, oldl, oldt);
+ }
+
+ /**
+ * Returns the number of pixels that this view can be scrolled.
+ */
+ public int getAllowedScrollLength() {
+ if (mAllowedScrollLength == 0) {
+ // Find the total length of two tabs side-by-side
+ int totalLength = 0;
+ for (int i=0; i < mTabs.length; i++) {
+ totalLength += mTabs[i].getWidth();
+ }
+ // Find the allowed scrolling length by subtracting the current visible screen width
+ // from the total length of the tabs.
+ mAllowedScrollLength = totalLength - getWidth();
+ }
+ return mAllowedScrollLength;
+ }
+
+ /**
+ * Updates the tab selection.
+ */
+ public void setCurrentTab(int position) {
+ if (position < 0 || position > mTabs.length) {
+ throw new IllegalStateException("Invalid position in array of tabs: " + position);
+ }
+ // TODO: Handle device rotation (saving and restoring state of the selected tab)
+ // This will take more work because there is no tab carousel in phone landscape
+ if (mTabs[position] == null) {
+ return;
+ }
+ mTabs[position].setSelected(true);
+ unselectAllOtherTabs(position);
+ }
+
+ private void unselectAllOtherTabs(int position) {
+ for (int i = 0; i < mTabs.length; i++) {
+ if (position != i) {
+ mTabs[i].setSelected(false);
+ }
+ }
+ }
+
+ /**
+ * Loads the data from the Loader-Result. This is the only function that has to be called
+ * from the outside to fully setup the View
+ */
+ public void loadData(ContactLoader.Result contactData) {
+ mContactUri = contactData.getLookupUri();
+
+ View aboutView = findViewById(R.id.tab_about);
+ View updateView = findViewById(R.id.tab_update);
+
+ TextView aboutTab = (TextView) aboutView.findViewById(R.id.label);
+ aboutTab.setText(mContext.getString(R.string.contactDetailAbout));
+ aboutTab.setClickable(true);
+ aboutTab.setSelected(true);
+
+ TextView updatesTab = (TextView) updateView.findViewById(R.id.label);
+ updatesTab.setText(mContext.getString(R.string.contactDetailUpdates));
+ updatesTab.setClickable(true);
+
+ aboutTab.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mListener.onTabSelected(0);
+ }
+ });
+ updatesTab.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mListener.onTabSelected(1);
+ }
+ });
+
+ mTabs[0] = aboutTab;
+ mTabs[1] = updatesTab;
+
+ // Retrieve the photo and star views for the "about" tab
+ mPhotoView = (ImageView) aboutView.findViewById(R.id.photo);
+ mStarredView = (CheckBox) aboutView.findViewById(R.id.star);
+ mStarredView.setOnClickListener(this);
+
+ // Retrieve the social update views for the "updates" tab
+ mStatusView = (TextView) updateView.findViewById(R.id.status);
+ mStatusDateView = (TextView) updateView.findViewById(R.id.status_date);
+
+ ContactDetailDisplayUtils.setPhoto(mContext, contactData, mPhotoView);
+ ContactDetailDisplayUtils.setStarred(contactData, mStarredView);
+ ContactDetailDisplayUtils.setSocialSnippetAndDate(mContext, contactData, mStatusView,
+ mStatusDateView);
+ }
+
+ /**
+ * Set the given {@link Listener} to handle carousel events.
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ // TODO: The starred icon needs to move to the action bar.
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.star: {
+ // Toggle "starred" state
+ // Make sure there is a contact
+ if (mContactUri != null) {
+ Intent intent = ContactSaveService.createSetStarredIntent(
+ getContext(), mContactUri, mStarredView.isChecked());
+ getContext().startService(intent);
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mListener.onTouchDown();
+ return true;
+ case MotionEvent.ACTION_UP:
+ mListener.onTouchUp();
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ boolean interceptTouch = super.onInterceptTouchEvent(ev);
+ if (interceptTouch) {
+ mListener.onTouchDown();
+ }
+ return interceptTouch;
+ }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
new file mode 100644
index 000000000..02678de45
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -0,0 +1,45 @@
+/*
+ * 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.R;
+import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ContactDetailUpdatesFragment extends Fragment implements FragmentKeyListener {
+
+ private static final String TAG = "ContactDetailUpdatesFragment";
+
+ public ContactDetailUpdatesFragment() {
+ // Explicit constructor for inflation
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ return inflater.inflate(R.layout.contact_detail_updates_fragment, container, false);
+ }
+
+ @Override
+ public boolean handleKeyDown(int keyCode) {
+ return false;
+ }
+}