Merge "Allow setting photo on otherwise-empty local contacts."
diff --git a/res/layout/carousel_about_tab.xml b/res/layout/carousel_about_tab.xml
index 0f93482..f2e504b 100644
--- a/res/layout/carousel_about_tab.xml
+++ b/res/layout/carousel_about_tab.xml
@@ -58,12 +58,4 @@
android:textColor="@color/detail_tab_carousel_tab_label_color"
style="@android:style/Widget.Holo.ActionBar.TabView" />
- <View
- android:id="@+id/touch_intercept_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"/>
</view>
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index e4b61b9..93e6e8f 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -81,13 +81,4 @@
android:textColor="@color/detail_tab_carousel_tab_label_color"
style="@android:style/Widget.Holo.ActionBar.TabView" />
- <View
- android:id="@+id/touch_intercept_overlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"/>
-
</view>
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index bc0f3f4..aed377b 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -412,9 +412,9 @@
/**
* @return true if this is a contact (not group, etc.) with at least one
- * writeable raw-contact, and false otherwise.
+ * writable raw-contact, and false otherwise.
*/
- public boolean isWritableContact(Context context) {
+ public boolean isWritableContact(final Context context) {
if (isDirectoryEntry()) return false;
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
for (Entity rawContact : getEntities()) {
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index 4286293..5b6a1ca 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -582,8 +582,11 @@
view.setImageBitmap(cachedBitmap);
}
- // Put the bitmap in the LRU cache
- mBitmapCache.put(request.getKey(), cachedBitmap);
+ // Put the bitmap in the LRU cache. But only do this for images that are small enough
+ // (we require that at least six of those can be cached at the same time)
+ if (cachedBitmap.getByteCount() < mBitmapCache.maxSize() / 6) {
+ mBitmapCache.put(request.getKey(), cachedBitmap);
+ }
// Soften the reference
holder.bitmap = null;
diff --git a/src/com/android/contacts/calllog/CallLogQueryHandler.java b/src/com/android/contacts/calllog/CallLogQueryHandler.java
index affdd1d..edc631f 100644
--- a/src/com/android/contacts/calllog/CallLogQueryHandler.java
+++ b/src/com/android/contacts/calllog/CallLogQueryHandler.java
@@ -59,7 +59,6 @@
private static final int UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN = 56;
/** The token for the query to mark all missed calls as read after seeing the call log. */
private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 57;
-
/** The token for the query to fetch voicemail status messages. */
private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 58;
@@ -75,6 +74,24 @@
@GuardedBy("this") private Cursor mNewCallsCursor;
/** The cursor containing the old calls, or null if they have not yet been fetched. */
@GuardedBy("this") private Cursor mOldCallsCursor;
+ /**
+ * The identifier of the latest calls request.
+ * <p>
+ * A request for the list of calls requires two queries and hence the two cursor
+ * {@link #mNewCallsCursor} and {@link #mOldCallsCursor} above, corresponding to
+ * {@link #QUERY_NEW_CALLS_TOKEN} and {@link #QUERY_OLD_CALLS_TOKEN}.
+ * <p>
+ * When a new request is about to be started, existing cursors are closed. However, it is
+ * possible that one of the queries completes after the new request has started. This means that
+ * we might merge two cursors that do not correspond to the same request. Moreover, this may
+ * lead to a resource leak if the same query completes and we override the cursor without
+ * closing it first.
+ * <p>
+ * To make sure we only join two cursors from the same request, we use this variable to store
+ * the request id of the latest request and make sure we only process cursors corresponding to
+ * the this request.
+ */
+ @GuardedBy("this") private int mCallsRequestId;
/**
* Simple handler that wraps background calls to catch
@@ -141,9 +158,9 @@
*/
public void fetchAllCalls() {
cancelFetch();
- invalidate();
- fetchCalls(QUERY_NEW_CALLS_TOKEN, true /*isNew*/, false /*voicemailOnly*/);
- fetchCalls(QUERY_OLD_CALLS_TOKEN, false /*isNew*/, false /*voicemailOnly*/);
+ int requestId = newCallsRequest();
+ fetchCalls(QUERY_NEW_CALLS_TOKEN, requestId, true /*isNew*/, false /*voicemailOnly*/);
+ fetchCalls(QUERY_OLD_CALLS_TOKEN, requestId, false /*isNew*/, false /*voicemailOnly*/);
}
/**
@@ -153,9 +170,9 @@
*/
public void fetchVoicemailOnly() {
cancelFetch();
- invalidate();
- fetchCalls(QUERY_NEW_CALLS_TOKEN, true /*isNew*/, true /*voicemailOnly*/);
- fetchCalls(QUERY_OLD_CALLS_TOKEN, false /*isNew*/, true /*voicemailOnly*/);
+ int requestId = newCallsRequest();
+ fetchCalls(QUERY_NEW_CALLS_TOKEN, requestId, true /*isNew*/, true /*voicemailOnly*/);
+ fetchCalls(QUERY_OLD_CALLS_TOKEN, requestId, false /*isNew*/, true /*voicemailOnly*/);
}
@@ -165,7 +182,7 @@
}
/** Fetches the list of calls in the call log, either the new one or the old ones. */
- private void fetchCalls(int token, boolean isNew, boolean voicemailOnly) {
+ private void fetchCalls(int token, int requestId, boolean isNew, boolean voicemailOnly) {
// We need to check for NULL explicitly otherwise entries with where READ is NULL
// may not match either the query or its negation.
// We consider the calls that are not yet consumed (i.e. IS_READ = 0) as "new".
@@ -182,7 +199,7 @@
selection = String.format("(%s) AND (%s = ?)", selection, Calls.TYPE);
selectionArgs.add(Integer.toString(Calls.VOICEMAIL_TYPE));
}
- startQuery(token, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
+ startQuery(token, requestId, Calls.CONTENT_URI_WITH_VOICEMAIL,
CallLogQuery._PROJECTION, selection, selectionArgs.toArray(EMPTY_STRING_ARRAY),
Calls.DEFAULT_SORT_ORDER);
}
@@ -239,25 +256,41 @@
}
/**
- * Invalidate the current list of calls.
+ * Start a new request and return its id. The request id will be used as the cookie for the
+ * background request.
* <p>
- * This method is synchronized because it must close the cursors and reset them atomically.
+ * Closes any open cursor that has not yet been sent to the requester.
*/
- private synchronized void invalidate() {
+ private synchronized int newCallsRequest() {
MoreCloseables.closeQuietly(mNewCallsCursor);
MoreCloseables.closeQuietly(mOldCallsCursor);
mNewCallsCursor = null;
mOldCallsCursor = null;
+ return ++mCallsRequestId;
}
@Override
protected synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
if (token == QUERY_NEW_CALLS_TOKEN) {
+ int requestId = ((Integer) cookie).intValue();
+ if (requestId != mCallsRequestId) {
+ // Ignore this query since it does not correspond to the latest request.
+ return;
+ }
+
// Store the returned cursor.
+ MoreCloseables.closeQuietly(mNewCallsCursor);
mNewCallsCursor = new ExtendedCursor(
cursor, CallLogQuery.SECTION_NAME, CallLogQuery.SECTION_NEW_ITEM);
} else if (token == QUERY_OLD_CALLS_TOKEN) {
+ int requestId = ((Integer) cookie).intValue();
+ if (requestId != mCallsRequestId) {
+ // Ignore this query since it does not correspond to the latest request.
+ return;
+ }
+
// Store the returned cursor.
+ MoreCloseables.closeQuietly(mOldCallsCursor);
mOldCallsCursor = new ExtendedCursor(
cursor, CallLogQuery.SECTION_NAME, CallLogQuery.SECTION_OLD_ITEM);
} else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
diff --git a/src/com/android/contacts/detail/CarouselTab.java b/src/com/android/contacts/detail/CarouselTab.java
index dc564a9..9bdf98b 100644
--- a/src/com/android/contacts/detail/CarouselTab.java
+++ b/src/com/android/contacts/detail/CarouselTab.java
@@ -17,6 +17,7 @@
package com.android.contacts.detail;
import com.android.contacts.R;
+import com.android.contacts.util.ThemeUtils;
import android.content.Context;
import android.util.AttributeSet;
@@ -32,6 +33,8 @@
private static final String TAG = CarouselTab.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
private static final long FADE_TRANSITION_TIME = 150;
private TextView mLabelView;
@@ -50,6 +53,21 @@
public CarouselTab(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ // Programmatically create and initialize touch-interceptor View.
+ mTouchInterceptLayer = new View(context);
+
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ layoutParams.addRule(ALIGN_PARENT_LEFT, TRUE);
+ layoutParams.addRule(ALIGN_PARENT_TOP, TRUE);
+ int background = ThemeUtils.getSelectableItemBackground(context.getTheme());
+
+ mTouchInterceptLayer.setVisibility(GONE);
+ mTouchInterceptLayer.setBackgroundResource(background);
+ mTouchInterceptLayer.setLayoutParams(layoutParams);
+
+ addView(mTouchInterceptLayer);
}
@Override
@@ -59,7 +77,9 @@
mLabelView = (TextView) findViewById(R.id.label);
mLabelBackgroundView = findViewById(R.id.label_background);
mAlphaLayer = findViewById(R.id.alpha_overlay);
- mTouchInterceptLayer = findViewById(R.id.touch_intercept_overlay);
+
+ mTouchInterceptLayer.bringToFront();
+ if (DEBUG) mTouchInterceptLayer.setBackgroundColor(0x4400FF00);
}
public void setLabel(String label) {
@@ -75,18 +95,18 @@
}
@Override
- public void disableTouchInterceptor() {
- if (mTouchInterceptLayer != null) {
- mTouchInterceptLayer.setVisibility(View.GONE);
- }
+ public void setTouchInterceptorListener(OnClickListener listener) {
+ mTouchInterceptLayer.setOnClickListener(listener);
}
@Override
- public void enableTouchInterceptor(OnClickListener clickListener) {
- if (mTouchInterceptLayer != null) {
- mTouchInterceptLayer.setVisibility(View.VISIBLE);
- mTouchInterceptLayer.setOnClickListener(clickListener);
- }
+ public void disableTouchInterceptor() {
+ mTouchInterceptLayer.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void enableTouchInterceptor() {
+ mTouchInterceptLayer.setVisibility(View.VISIBLE);
}
@Override
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index d5b04d9..238055c 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -317,7 +317,8 @@
@Override
public void setAlphaLayerValue(float alpha) {
- // If the alpha layer is not ready yet, store it for later when the view is initialized
+ // If the alpha layer is not ready yet, store it for later when the view
+ // is initialized
if (mAlphaLayer == null) {
mInitialAlphaValue = alpha;
} else {
@@ -327,10 +328,16 @@
}
@Override
- public void enableTouchInterceptor(OnClickListener clickListener) {
+ public void setTouchInterceptorListener(OnClickListener listener) {
+ if (mTouchInterceptLayer != null) {
+ mTouchInterceptLayer.setOnClickListener(listener);
+ }
+ }
+
+ @Override
+ public void enableTouchInterceptor() {
if (mTouchInterceptLayer != null) {
mTouchInterceptLayer.setVisibility(View.VISIBLE);
- mTouchInterceptLayer.setOnClickListener(clickListener);
}
}
@@ -442,7 +449,11 @@
mContext, mContactData, photoView, false);
if (mPhotoTouchOverlay != null) {
mPhotoTouchOverlay.setVisibility(View.VISIBLE);
- mPhotoTouchOverlay.setOnClickListener(listener);
+ if (mContactData.isWritableContact(mContext)) {
+ mPhotoTouchOverlay.setOnClickListener(listener);
+ } else {
+ mPhotoTouchOverlay.setClickable(false);
+ }
}
} else {
mStaticPhotoContainer.setVisibility(View.GONE);
@@ -1363,15 +1374,21 @@
}
@Override
+ public void setTouchInterceptorListener(OnClickListener listener) {
+ if (photoOverlayView != null) {
+ photoOverlayView.setOnClickListener(listener);
+ }
+ }
+
+ @Override
public void setAlphaLayerValue(float alpha) {
// Nothing to do.
}
@Override
- public void enableTouchInterceptor(OnClickListener clickListener) {
+ public void enableTouchInterceptor() {
if (photoOverlayView != null) {
photoOverlayView.setVisibility(View.VISIBLE);
- photoOverlayView.setOnClickListener(clickListener);
}
}
@@ -1495,7 +1512,11 @@
final boolean expandOnClick = !PhoneCapabilityTester.isUsingTwoPanes(mContext);
OnClickListener listener = mPhotoSetter.setupContactPhotoForClick(
mContext, mContactData, viewCache.photoView, expandOnClick);
- viewCache.enableTouchInterceptor(listener);
+
+ if (expandOnClick || mContactData.isWritableContact(mContext)) {
+ viewCache.setTouchInterceptorListener(listener);
+ viewCache.enableTouchInterceptor();
+ }
}
// Set the starred state if it should be displayed
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index f9b057b..a83409f 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -169,6 +169,9 @@
public void setFragments(ViewOverlay aboutFragment, ViewOverlay updatesFragment) {
mAboutFragment = aboutFragment;
mUpdatesFragment = updatesFragment;
+
+ mAboutFragment.setTouchInterceptorListener(mAboutFragTouchInterceptListener);
+ mUpdatesFragment.setTouchInterceptorListener(mUpdatesFragTouchInterceptListener);
}
/**
@@ -216,11 +219,11 @@
// The "about this contact" page has been selected, so disable the touch interceptor
// on this page and enable it for the "updates" page.
mAboutFragment.disableTouchInterceptor();
- mUpdatesFragment.enableTouchInterceptor(mUpdatesFragTouchInterceptListener);
+ mUpdatesFragment.enableTouchInterceptor();
break;
case UPDATES_PAGE:
mUpdatesFragment.disableTouchInterceptor();
- mAboutFragment.enableTouchInterceptor(mAboutFragTouchInterceptListener);
+ mAboutFragment.enableTouchInterceptor();
break;
}
}
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index be07e7a..e5ce961 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -36,7 +36,6 @@
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewPropertyAnimator;
@@ -58,9 +57,6 @@
private final int SINGLE_PANE_FADE_IN_DURATION = 275;
- private static final String TAG = "ContactDetailLayoutController";
- private static final boolean DEBUG = false;
-
/**
* There are 3 possible layouts for the contact detail screen:
* 1. TWO_COLUMN - Tall and wide screen so the 2 pages can be shown side-by-side
@@ -135,13 +131,10 @@
// Determine the layout mode based on the presence of certain views in the layout XML.
if (mViewPager != null) {
mLayoutMode = LayoutMode.VIEW_PAGER_AND_TAB_CAROUSEL;
- if (DEBUG) Log.d(TAG, "set layout mode to VIEW_PAGER_AND_TAB_CAROUSEL");
} else if (mFragmentCarousel != null) {
mLayoutMode = LayoutMode.FRAGMENT_CAROUSEL;
- if (DEBUG) Log.d(TAG, "set layout mode to FRAGMENT_CAROUSEL");
} else {
mLayoutMode = LayoutMode.TWO_COLUMN;
- if (DEBUG) Log.d(TAG, "set layout mode to TWO_COLUMN");
}
initialize(savedState);
@@ -491,9 +484,8 @@
// 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;
- }
+ if (mViewPager.isFakeDragging()) return;
+
int x = (int) ((position + positionOffset) *
mTabCarousel.getAllowedHorizontalScrollLength());
mTabCarousel.scrollTo(x, 0);
@@ -626,34 +618,31 @@
}
};
- private final ContactDetailTabCarousel.Listener mTabCarouselListener =
- new ContactDetailTabCarousel.Listener() {
+ private final 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();
+ // The user just started scrolling the carousel, so begin
+ // "fake dragging" the {@link ViewPager} if it's not already
+ // doing so.
+ if (!mViewPager.isFakeDragging()) 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();
- }
+ // The user just stopped scrolling the carousel, so stop
+ // "fake dragging" the {@link ViewPager} if it 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.
+ // 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);
+ mViewPager.fakeDragBy(oldl - l);
}
}
diff --git a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
index 821b441..13f5970 100644
--- a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
+++ b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
@@ -101,9 +101,7 @@
final ImageView target = getTarget();
if (target == null) return null;
- OnClickListener clickListener = new PhotoClickListener(
+ return new PhotoClickListener(
context, contactData, bitmap, getCompressedImage(), expandPhotoOnClick);
- target.setOnClickListener(clickListener);
- return clickListener;
}
}
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index 21a2c5b..38dcfee 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -21,7 +21,6 @@
import com.android.contacts.detail.ContactDetailPhotoSetter;
import com.android.contacts.util.PhoneCapabilityTester;
-
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
@@ -61,7 +60,6 @@
private ImageView mPhotoView;
private TextView mStatusView;
private ImageView mStatusPhotoView;
- private OnClickListener mPhotoClickListener;
private final ContactDetailPhotoSetter mPhotoSetter = new ContactDetailPhotoSetter();
private Listener mListener;
@@ -85,6 +83,9 @@
private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE;
private int mAllowedVerticalScrollLength = Integer.MIN_VALUE;
+ /** Factor to scale scroll-amount sent to listeners. */
+ private float mScrollScaleFactor = 1.0f;
+
private static final float MAX_ALPHA = 0.5f;
/**
@@ -93,6 +94,7 @@
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);
}
@@ -119,14 +121,13 @@
mTabAndShadowContainer = findViewById(R.id.tab_and_shadow_container);
mAboutTab = (CarouselTab) findViewById(R.id.tab_about);
mAboutTab.setLabel(mContext.getString(R.string.contactDetailAbout));
+ mAboutTab.setTouchInterceptorListener(mAboutTabTouchInterceptListener);
mTabDivider = findViewById(R.id.tab_divider);
mUpdatesTab = (CarouselTab) findViewById(R.id.tab_update);
mUpdatesTab.setLabel(mContext.getString(R.string.contactDetailUpdates));
-
- mAboutTab.enableTouchInterceptor(mAboutTabTouchInterceptListener);
- mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener);
+ mUpdatesTab.setTouchInterceptorListener(mUpdatesTabTouchInterceptListener);
mShadow = findViewById(R.id.shadow);
@@ -138,6 +139,13 @@
// TODO: This should be moved down to mUpdatesTab, so that it hosts its own controls
mStatusView = (TextView) mUpdatesTab.findViewById(R.id.status);
mStatusPhotoView = (ImageView) mUpdatesTab.findViewById(R.id.status_photo);
+
+ // Workaround for framework issue... it shouldn't be necessary to have a
+ // clickable object in the hierarchy, but if not the horizontal scroll
+ // behavior doesn't work. Note: the "About" tab doesn't need this
+ // because we set a real click-handler elsewhere.
+ mStatusView.setClickable(true);
+ mStatusPhotoView.setClickable(true);
}
@Override
@@ -150,13 +158,18 @@
// from the total length of the tabs.
mAllowedHorizontalScrollLength = tabWidth * TAB_COUNT - screenWidth;
+ // Scrolling by mAllowedHorizontalScrollLength causes listeners to
+ // scroll by the entire screen amount; compute the scale-factor
+ // necessary to make this so.
+ mScrollScaleFactor = screenWidth / mAllowedHorizontalScrollLength;
+
int tabHeight = Math.round(screenWidth * mTabHeightScreenWidthFraction) + mTabShadowHeight;
// Set the child {@link LinearLayout} to be TAB_COUNT * the computed tab width so that the
// {@link LinearLayout}'s children (which are the tabs) will evenly split that width.
if (getChildCount() > 0) {
View child = getChildAt(0);
- // add 1 dip of seperation between the tabs
+ // add 1 dip of separation between the tabs
final int seperatorPixels =
(int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
getResources().getDisplayMetrics()) + 0.5f);
@@ -184,23 +197,26 @@
}
}
- private final OnClickListener mAboutTabTouchInterceptListener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCurrentTab == TAB_INDEX_ABOUT && mPhotoClickListener != null) {
- mPhotoClickListener.onClick(v);
- } else {
- mListener.onTabSelected(TAB_INDEX_ABOUT);
- }
- }
- };
+ /** When clicked, selects the corresponding tab. */
+ private class TabClickListener implements OnClickListener {
+ private final int mTab;
- private final OnClickListener mUpdatesTabTouchInterceptListener = new OnClickListener() {
+ public TabClickListener(int tab) {
+ super();
+ mTab = tab;
+ }
+
@Override
public void onClick(View v) {
- mListener.onTabSelected(TAB_INDEX_UPDATES);
+ mListener.onTabSelected(mTab);
}
- };
+ }
+
+ private final TabClickListener mAboutTabTouchInterceptListener =
+ new TabClickListener(TAB_INDEX_ABOUT);
+
+ private final TabClickListener mUpdatesTabTouchInterceptListener =
+ new TabClickListener(TAB_INDEX_UPDATES);
/**
* Does in "appear" animation to allow a seamless transition from
@@ -308,9 +324,18 @@
}
@Override
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- super.onScrollChanged(l, t, oldl, oldt);
- mListener.onScrollChanged(l, t, oldl, oldt);
+ protected void onScrollChanged(int l, int t, int oldL, int oldT) {
+ super.onScrollChanged(l, t, oldL, oldT);
+
+ // Since we never completely scroll the about/updates tabs off-screen,
+ // the draggable range is less than the width of the carousel. Our
+ // listeners don't care about this... if we scroll 75% percent of our
+ // draggable range, they want to scroll 75% of the entire carousel
+ // width, not the same number of pixels that we scrolled.
+ int scaledL = (int) (l * mScrollScaleFactor);
+ int oldScaledL = (int) (oldL * mScrollScaleFactor);
+ mListener.onScrollChanged(scaledL, t, oldScaledL, oldT);
+
mLastScrollPosition = l;
updateAlphaLayers();
}
@@ -388,20 +413,24 @@
* Updates the tab selection.
*/
public void setCurrentTab(int position) {
+ final CarouselTab selected, deselected;
+
switch (position) {
case TAB_INDEX_ABOUT:
- mAboutTab.showSelectedState();
- mUpdatesTab.showDeselectedState();
- mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener);
+ selected = mAboutTab;
+ deselected = mUpdatesTab;
break;
case TAB_INDEX_UPDATES:
- mUpdatesTab.showSelectedState();
- mUpdatesTab.disableTouchInterceptor();
- mAboutTab.showDeselectedState();
+ selected = mUpdatesTab;
+ deselected = mAboutTab;
break;
default:
throw new IllegalStateException("Invalid tab position " + position);
}
+ selected.showSelectedState();
+ selected.disableTouchInterceptor();
+ deselected.showDeselectedState();
+ deselected.enableTouchInterceptor();
mCurrentTab = position;
}
@@ -415,8 +444,17 @@
// TODO: Move this into the {@link CarouselTab} class when the updates
// fragment code is more finalized.
final boolean expandOnClick = !PhoneCapabilityTester.isUsingTwoPanes(mContext);
- mPhotoClickListener = mPhotoSetter.setupContactPhotoForClick(
+ OnClickListener listener = mPhotoSetter.setupContactPhotoForClick(
mContext, contactData, mPhotoView, expandOnClick);
+
+ if (expandOnClick || contactData.isWritableContact(mContext)) {
+ mPhotoView.setOnClickListener(listener);
+ } else {
+ // Work around framework issue... if we instead use
+ // setClickable(false), then we can't swipe horizontally.
+ mPhotoView.setOnClickListener(null);
+ }
+
ContactDetailDisplayUtils.setSocialSnippet(
mContext, contactData, mStatusView, mStatusPhotoView);
}
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index fd59674..1f3ce55 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -177,11 +177,18 @@
}
}
+
@Override
- public void enableTouchInterceptor(OnClickListener clickListener) {
+ public void setTouchInterceptorListener(OnClickListener clickListener) {
+ if (mTouchInterceptLayer != null) {
+ mTouchInterceptLayer.setOnClickListener(clickListener);
+ }
+ }
+
+ @Override
+ public void enableTouchInterceptor() {
if (mTouchInterceptLayer != null) {
mTouchInterceptLayer.setVisibility(View.VISIBLE);
- mTouchInterceptLayer.setOnClickListener(clickListener);
}
}
diff --git a/src/com/android/contacts/detail/ViewOverlay.java b/src/com/android/contacts/detail/ViewOverlay.java
index 58428b8..6b5b02f 100644
--- a/src/com/android/contacts/detail/ViewOverlay.java
+++ b/src/com/android/contacts/detail/ViewOverlay.java
@@ -27,18 +27,25 @@
public interface ViewOverlay {
/**
+ * Sets the callback that will be invoked when the touch-interceptor tapped
+ * while enabled.
+ */
+ public void setTouchInterceptorListener(OnClickListener listener);
+
+ /**
* Sets the alpha value on the alpha layer (if there is one).
*/
public void setAlphaLayerValue(float alpha);
/**
- * Makes the touch intercept layer on this fragment visible (if there is one). Also adds a click
- * listener which is called when there is a touch event on the layer.
+ * Enables the touch intercept layer on this fragment, so that it intercepts
+ * and handles touch events.
*/
- public void enableTouchInterceptor(OnClickListener clickListener);
+ public void enableTouchInterceptor();
/**
- * Makes the touch intercept layer on this fragment gone (if there is one).
+ * Disables the touch intercept layer on this fragment; touch events are
+ * handled normally by the view hierarchy under the overlay.
*/
public void disableTouchInterceptor();
}
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index cbf766e..5f5a855 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -100,7 +100,7 @@
private static final int TONE_RELATIVE_VOLUME = 80;
/** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
- private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+ private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
/**
* View (usually FrameLayout) containing mDigits field. This can be null, in which mDigits
@@ -483,11 +483,7 @@
synchronized (mToneGeneratorLock) {
if (mToneGenerator == null) {
try {
- // we want the user to be able to control the volume of the dial tones
- // outside of a call, so we use the stream type that is also mapped to the
- // volume control keys for this activity
mToneGenerator = new ToneGenerator(DIAL_TONE_STREAM_TYPE, TONE_RELATIVE_VOLUME);
- getActivity().setVolumeControlStream(DIAL_TONE_STREAM_TYPE);
} catch (RuntimeException e) {
Log.w(TAG, "Exception caught while creating local tone generator: " + e);
mToneGenerator = null;