Miscellaneous contact-detail fixes.
Fix bug in carousel "Updates" tab, which would turn black when swiping
it all the way to the left. This was caused by a small negative alpha
being computed, and some incautious integer arithmetic to transform it
for use in setBackgroundColor(). Fix clamps the alpha value between 0
and 1 (using newly-introduced MoreMath class).
Fix visual glitch where the "Updates" label displays the "pushed" glow
when tapped.
Bug: 6009463
Change-Id: Iee041ee7793f198f1d51ea5e7c5bbcb07b9a41df
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index e85f5f5..bc0f3f4 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -410,6 +410,23 @@
&& mDirectoryId != Directory.LOCAL_INVISIBLE;
}
+ /**
+ * @return true if this is a contact (not group, etc.) with at least one
+ * writeable raw-contact, and false otherwise.
+ */
+ public boolean isWritableContact(Context context) {
+ if (isDirectoryEntry()) return false;
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ for (Entity rawContact : getEntities()) {
+ final ContentValues rawValues = rawContact.getEntityValues();
+ final String accountType = rawValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ final String dataSet = rawValues.getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
+ if (type != null && type.areContactsWritable()) return true;
+ }
+ return false;
+ }
+
public int getDirectoryExportSupport() {
return mDirectoryExportSupport;
}
diff --git a/src/com/android/contacts/detail/CarouselTab.java b/src/com/android/contacts/detail/CarouselTab.java
index 9331bed..dc564a9 100644
--- a/src/com/android/contacts/detail/CarouselTab.java
+++ b/src/com/android/contacts/detail/CarouselTab.java
@@ -57,10 +57,7 @@
super.onFinishInflate();
mLabelView = (TextView) findViewById(R.id.label);
- mLabelView.setClickable(true);
-
mLabelBackgroundView = findViewById(R.id.label_background);
-
mAlphaLayer = findViewById(R.id.alpha_overlay);
mTouchInterceptLayer = findViewById(R.id.touch_intercept_overlay);
}
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 9f37899..319cba5 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -25,6 +25,7 @@
import com.android.contacts.preference.ContactsPreferences;
import com.android.contacts.util.ContactBadgeUtil;
import com.android.contacts.util.HtmlUtils;
+import com.android.contacts.util.MoreMath;
import com.android.contacts.util.StreamItemEntry;
import com.android.contacts.util.StreamItemPhotoEntry;
import com.google.common.annotations.VisibleForTesting;
@@ -616,7 +617,7 @@
if (view != null) {
// Convert alpha layer to a black background HEX color with an alpha value for better
// performance (i.e. use setBackgroundColor() instead of setAlpha())
- view.setBackgroundColor((int) (alpha * 255) << 24);
+ view.setBackgroundColor((int) (MoreMath.clamp(alpha, 0.0f, 1.0f) * 255) << 24);
}
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index b059ef4..d5b04d9 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.R;
import com.android.contacts.TypePrecedence;
import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
+import com.android.contacts.detail.ContactDetailPhotoSetter;
import com.android.contacts.editor.SelectAccountDialogFragment;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountType.EditType;
@@ -40,7 +41,6 @@
import com.android.contacts.util.Constants;
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.DateUtils;
-import com.android.contacts.util.ImageViewDrawableSetter;
import com.android.contacts.util.PhoneCapabilityTester;
import com.android.contacts.util.StructuredPostalUtils;
import com.android.internal.telephony.ITelephony;
@@ -145,7 +145,7 @@
private Uri mPrimaryPhoneUri = null;
private ViewEntryDimensions mViewEntryDimensions;
- private final ImageViewDrawableSetter mPhotoSetter = new ImageViewDrawableSetter();
+ private final ContactDetailPhotoSetter mPhotoSetter = new ContactDetailPhotoSetter();
private Button mQuickFixButton;
private QuickFix mQuickFix;
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index 0c3e6ac..f9b057b 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -226,10 +226,9 @@
}
private void updateAlphaLayers() {
- mAboutFragment.setAlphaLayerValue(mLastScrollPosition * MAX_ALPHA /
- mAllowedHorizontalScrollLength);
- mUpdatesFragment.setAlphaLayerValue(MAX_ALPHA - mLastScrollPosition * MAX_ALPHA /
- mAllowedHorizontalScrollLength);
+ float alpha = mLastScrollPosition * MAX_ALPHA / mAllowedHorizontalScrollLength;
+ mAboutFragment.setAlphaLayerValue(alpha);
+ mUpdatesFragment.setAlphaLayerValue(MAX_ALPHA - alpha);
}
@Override
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
index a87f97e..be07e7a 100644
--- a/src/com/android/contacts/detail/ContactDetailLayoutController.java
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -17,6 +17,7 @@
package com.android.contacts.detail;
import com.android.contacts.ContactLoader;
+
import com.android.contacts.NfcHandler;
import com.android.contacts.R;
import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
@@ -35,6 +36,7 @@
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;
@@ -56,6 +58,9 @@
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
@@ -130,9 +135,13 @@
// 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 = (mFragmentCarousel != null) ? LayoutMode.FRAGMENT_CAROUSEL :
- LayoutMode.TWO_COLUMN;
+ mLayoutMode = LayoutMode.TWO_COLUMN;
+ if (DEBUG) Log.d(TAG, "set layout mode to TWO_COLUMN");
}
initialize(savedState);
@@ -143,7 +152,7 @@
mDetailFragment = (ContactDetailFragment) mFragmentManager.findFragmentByTag(
ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
mUpdatesFragment = (ContactDetailUpdatesFragment) mFragmentManager.findFragmentByTag(
- ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
+ ContactDetailViewPagerAdapter.UPDATES_FRAGMENT_TAG);
// If the detail fragment was found in the {@link FragmentManager} then we don't need to add
// it again. Otherwise, create the fragments dynamically and remember to add them to the
@@ -190,7 +199,7 @@
transaction.add(R.id.about_fragment_container, mDetailFragment,
ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
- ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
+ ContactDetailViewPagerAdapter.UPDATES_FRAGMENT_TAG);
transaction.commitAllowingStateLoss();
mFragmentManager.executePendingTransactions();
}
@@ -210,7 +219,7 @@
transaction.add(R.id.about_fragment_container, mDetailFragment,
ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
- ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
+ ContactDetailViewPagerAdapter.UPDATES_FRAGMENT_TAG);
transaction.commitAllowingStateLoss();
mFragmentManager.executePendingTransactions();
}
@@ -225,7 +234,7 @@
transaction.add(R.id.about_fragment_container, mDetailFragment,
ContactDetailViewPagerAdapter.ABOUT_FRAGMENT_TAG);
transaction.add(R.id.updates_fragment_container, mUpdatesFragment,
- ContactDetailViewPagerAdapter.UPDTES_FRAGMENT_TAG);
+ ContactDetailViewPagerAdapter.UPDATES_FRAGMENT_TAG);
transaction.commitAllowingStateLoss();
mFragmentManager.executePendingTransactions();
}
diff --git a/src/com/android/contacts/detail/ContactDetailPhotoSetter.java b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
new file mode 100644
index 0000000..821b441
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailPhotoSetter.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2012 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 android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+
+import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.ContactLoader.Result;
+import com.android.contacts.activities.PhotoSelectionActivity;
+import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.util.ImageViewDrawableSetter;
+
+/**
+ * Extends superclass with methods specifically for setting the contact-detail
+ * photo.
+ */
+public class ContactDetailPhotoSetter extends ImageViewDrawableSetter {
+ public OnClickListener setupContactPhotoForClick(Context context, Result contactData,
+ ImageView photoView, boolean expandPhotoOnClick) {
+ setTarget(photoView);
+ Bitmap bitmap = setCompressedImage(contactData.getPhotoBinaryData());
+ return setupClickListener(context, contactData, bitmap, expandPhotoOnClick);
+ }
+
+ private static final class PhotoClickListener implements OnClickListener {
+
+ private final Context mContext;
+ private final Result mContactData;
+ private final Bitmap mPhotoBitmap;
+ private final byte[] mPhotoBytes;
+ private final boolean mExpandPhotoOnClick;
+
+ public PhotoClickListener(Context context, Result contactData, Bitmap photoBitmap,
+ byte[] photoBytes, boolean expandPhotoOnClick) {
+ mContext = context;
+ mContactData = contactData;
+ mPhotoBitmap = photoBitmap;
+ mPhotoBytes = photoBytes;
+ mExpandPhotoOnClick = expandPhotoOnClick;
+ }
+
+ @Override
+ public void onClick(View v) {
+ // Assemble the intent.
+ EntityDeltaList delta = EntityDeltaList.fromIterator(
+ mContactData.getEntities().iterator());
+
+ // Find location and bounds of target view, adjusting based on the
+ // assumed local density.
+ final float appScale =
+ mContext.getResources().getCompatibilityInfo().applicationScale;
+ final int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+
+ final Rect rect = new Rect();
+ rect.left = (int) (pos[0] * appScale + 0.5f);
+ rect.top = (int) (pos[1] * appScale + 0.5f);
+ rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+ rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+
+ Uri photoUri = null;
+ if (mContactData.getPhotoUri() != null) {
+ photoUri = Uri.parse(mContactData.getPhotoUri());
+ }
+ Intent photoSelectionIntent = PhotoSelectionActivity.buildIntent(mContext,
+ photoUri, mPhotoBitmap, mPhotoBytes, rect, delta, mContactData.isUserProfile(),
+ mContactData.isDirectoryEntry(), mExpandPhotoOnClick);
+ // Cache the bitmap directly, so the activity can pull it from the
+ // photo manager.
+ if (mPhotoBitmap != null) {
+ ContactPhotoManager.getInstance(mContext).cacheBitmap(
+ photoUri, mPhotoBitmap, mPhotoBytes);
+ }
+ mContext.startActivity(photoSelectionIntent);
+ }
+ }
+
+ private OnClickListener setupClickListener(Context context, Result contactData, Bitmap bitmap,
+ boolean expandPhotoOnClick) {
+ final ImageView target = getTarget();
+ if (target == null) return null;
+
+ OnClickListener clickListener = 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 008136d..21a2c5b 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -16,10 +16,11 @@
package com.android.contacts.detail;
-import com.android.contacts.ContactLoader;
import com.android.contacts.R;
+import com.android.contacts.ContactLoader;
+import com.android.contacts.detail.ContactDetailPhotoSetter;
import com.android.contacts.util.PhoneCapabilityTester;
-import com.android.contacts.util.ImageViewDrawableSetter;
+
import android.content.Context;
import android.content.res.Resources;
@@ -61,7 +62,7 @@
private TextView mStatusView;
private ImageView mStatusPhotoView;
private OnClickListener mPhotoClickListener;
- private final ImageViewDrawableSetter mPhotoSetter = new ImageViewDrawableSetter();
+ private final ContactDetailPhotoSetter mPhotoSetter = new ContactDetailPhotoSetter();
private Listener mListener;
@@ -301,10 +302,9 @@
}
private void updateAlphaLayers() {
- mAboutTab.setAlphaLayerValue(mLastScrollPosition * MAX_ALPHA /
- mAllowedHorizontalScrollLength);
- mUpdatesTab.setAlphaLayerValue(MAX_ALPHA - mLastScrollPosition * MAX_ALPHA /
- mAllowedHorizontalScrollLength);
+ float alpha = mLastScrollPosition * MAX_ALPHA / mAllowedHorizontalScrollLength;
+ mAboutTab.setAlphaLayerValue(alpha);
+ mUpdatesTab.setAlphaLayerValue(MAX_ALPHA - alpha);
}
@Override
diff --git a/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java b/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java
index c708dc8..4213490 100644
--- a/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java
+++ b/src/com/android/contacts/detail/ContactDetailViewPagerAdapter.java
@@ -29,7 +29,7 @@
public class ContactDetailViewPagerAdapter extends PagerAdapter {
public static final String ABOUT_FRAGMENT_TAG = "view-pager-about-fragment";
- public static final String UPDTES_FRAGMENT_TAG = "view-pager-updates-fragment";
+ public static final String UPDATES_FRAGMENT_TAG = "view-pager-updates-fragment";
private static final int INDEX_ABOUT_FRAGMENT = 0;
private static final int INDEX_UPDATES_FRAGMENT = 1;
diff --git a/src/com/android/contacts/util/ImageViewDrawableSetter.java b/src/com/android/contacts/util/ImageViewDrawableSetter.java
index 412d162..09c6e5c 100644
--- a/src/com/android/contacts/util/ImageViewDrawableSetter.java
+++ b/src/com/android/contacts/util/ImageViewDrawableSetter.java
@@ -61,11 +61,8 @@
Bitmap bitmap = setCompressedImage(contactData.getPhotoBinaryData());
}
- public OnClickListener setupContactPhotoForClick(Context context, Result contactData,
- ImageView photoView, boolean expandPhotoOnClick) {
- setTarget(photoView);
- Bitmap bitmap = setCompressedImage(contactData.getPhotoBinaryData());
- return setupClickListener(context, contactData, bitmap, expandPhotoOnClick);
+ public ImageView getTarget() {
+ return mTarget;
}
/**
@@ -73,7 +70,7 @@
* is set, it will immediately be applied to the target (there will be no
* fade transition).
*/
- private void setTarget(ImageView target) {
+ protected void setTarget(ImageView target) {
if (mTarget != target) {
mTarget = target;
mCompressed = null;
@@ -81,7 +78,11 @@
}
}
- private Bitmap setCompressedImage(byte[] compressed) {
+ protected byte[] getCompressedImage() {
+ return mCompressed;
+ }
+
+ protected Bitmap setCompressedImage(byte[] compressed) {
if (mPreviousDrawable == null) {
// If we don't already have a drawable, skip the exit-early test
// below; otherwise we might not end up setting the default image.
@@ -129,67 +130,6 @@
: ((BitmapDrawable) mPreviousDrawable).getBitmap();
}
- private static final class PhotoClickListener implements OnClickListener {
-
- private final Context mContext;
- private final Result mContactData;
- private final Bitmap mPhotoBitmap;
- private final byte[] mPhotoBytes;
- private final boolean mExpandPhotoOnClick;
- public PhotoClickListener(Context context, Result contactData, Bitmap photoBitmap,
- byte[] photoBytes, boolean expandPhotoOnClick) {
- mContext = context;
- mContactData = contactData;
- mPhotoBitmap = photoBitmap;
- mPhotoBytes = photoBytes;
- mExpandPhotoOnClick = expandPhotoOnClick;
- }
-
- @Override
- public void onClick(View v) {
- // Assemble the intent.
- EntityDeltaList delta = EntityDeltaList.fromIterator(
- mContactData.getEntities().iterator());
-
- // Find location and bounds of target view, adjusting based on the
- // assumed local density.
- final float appScale =
- mContext.getResources().getCompatibilityInfo().applicationScale;
- final int[] pos = new int[2];
- v.getLocationOnScreen(pos);
-
- final Rect rect = new Rect();
- rect.left = (int) (pos[0] * appScale + 0.5f);
- rect.top = (int) (pos[1] * appScale + 0.5f);
- rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
- rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
-
- Uri photoUri = null;
- if (mContactData.getPhotoUri() != null) {
- photoUri = Uri.parse(mContactData.getPhotoUri());
- }
- Intent photoSelectionIntent = PhotoSelectionActivity.buildIntent(mContext,
- photoUri, mPhotoBitmap, mPhotoBytes, rect, delta, mContactData.isUserProfile(),
- mContactData.isDirectoryEntry(), mExpandPhotoOnClick);
- // Cache the bitmap directly, so the activity can pull it from the photo manager.
- if (mPhotoBitmap != null) {
- ContactPhotoManager.getInstance(mContext).cacheBitmap(
- photoUri, mPhotoBitmap, mPhotoBytes);
- }
- mContext.startActivity(photoSelectionIntent);
- }
- }
-
- private OnClickListener setupClickListener(Context context, Result contactData, Bitmap bitmap,
- boolean expandPhotoOnClick) {
- if (mTarget == null) return null;
-
- OnClickListener clickListener = new PhotoClickListener(
- context, contactData, bitmap, mCompressed, expandPhotoOnClick);
- mTarget.setOnClickListener(clickListener);
- return clickListener;
- }
-
/**
* Obtain the default drawable for a contact when no photo is available.
*/
diff --git a/src/com/android/contacts/util/MoreMath.java b/src/com/android/contacts/util/MoreMath.java
new file mode 100644
index 0000000..6f28ccd
--- /dev/null
+++ b/src/com/android/contacts/util/MoreMath.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 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.util;
+
+/**
+ * Useful math functions that aren't in java.lang.Math
+ */
+public class MoreMath {
+ /**
+ * If the input value lies outside of the specified range, return the nearer
+ * bound. Otherwise, return the input value, unchanged.
+ */
+ public static float clamp(float input, float lowerBound, float upperBound) {
+ if (input < lowerBound) return lowerBound;
+ if (input > upperBound) return upperBound;
+ return input;
+ }
+
+ /**
+ * If the input value lies outside of the specified range, return the nearer
+ * bound. Otherwise, return the input value, unchanged.
+ */
+ public static double clamp(double input, double lowerBound, double upperBound) {
+ if (input < lowerBound) return lowerBound;
+ if (input > upperBound) return upperBound;
+ return input;
+ }
+}