Merge change Ice23b740 into eclair
* changes:
Import revised translations. DO NOT MERGE
diff --git a/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png b/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png
index 6fafcbe..0dcf076 100644
--- a/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png
+++ b/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png b/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png
index efbb1da..2d20076 100644
--- a/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png
+++ b/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png
Binary files differ
diff --git a/res/layout-finger/quickcontact.xml b/res/layout-finger/quickcontact.xml
index a2a94d4..13b5c20 100644
--- a/res/layout-finger/quickcontact.xml
+++ b/res/layout-finger/quickcontact.xml
@@ -18,6 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/quickcontact_shadow_horiz"
+ android:paddingRight="@dimen/quickcontact_shadow_horiz"
android:background="@drawable/quickcontact_drop_shadow">
<FrameLayout
@@ -134,4 +136,13 @@
android:layout_below="@id/footer"
android:src="@drawable/quickcontact_arrow_down" />
+ <ImageView
+ android:id="@+id/arrow_down_stub"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-1dip"
+ android:layout_below="@id/footer_disambig"
+ android:visibility="invisible"
+ android:src="@drawable/quickcontact_arrow_down" />
+
</RelativeLayout>
diff --git a/res/layout-finger/quickcontact_header_large.xml b/res/layout-finger/quickcontact_header_large.xml
index df01f8a..8a11aa2 100644
--- a/res/layout-finger/quickcontact_header_large.xml
+++ b/res/layout-finger/quickcontact_header_large.xml
@@ -29,13 +29,13 @@
android:layout_width="50dip"
android:layout_height="56dip"
android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
style="@*android:style/Widget.QuickContactBadge.WindowLarge" />
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_marginLeft="15dip"
android:paddingRight="8dip"
android:orientation="vertical">
diff --git a/res/layout-finger/quickcontact_item.xml b/res/layout-finger/quickcontact_item.xml
index 819915d..8580ac5 100644
--- a/res/layout-finger/quickcontact_item.xml
+++ b/res/layout-finger/quickcontact_item.xml
@@ -17,11 +17,11 @@
<com.android.contacts.ui.widget.CheckableImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="59dip"
- android:layout_height="52dip"
+ android:layout_height="51dip"
android:paddingLeft="12dip"
android:paddingRight="12dip"
android:paddingTop="8dip"
- android:paddingBottom="9dip"
+ android:paddingBottom="8dip"
android:scaleType="centerInside"
android:focusable="true"
android:clickable="true"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 845c3dc..a82f5a9 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -15,9 +15,12 @@
-->
<resources>
- <dimen name="quickcontact_shadow">37dip</dimen>
+ <dimen name="quickcontact_shadow_horiz">30dip</dimen>
+ <dimen name="quickcontact_shadow_vert">37dip</dimen>
+ <dimen name="quickcontact_shadow_touch">20dip</dimen>
+
<dimen name="edit_photo_size">76dip</dimen>
-
+
<!-- The height of the ScrollingTabWidget -->
<dimen name="tab_height">40dip</dimen>
<dimen name="account_name_height">25dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index dbe8d09..4e311f9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1200,4 +1200,18 @@
<!-- Formatting string for account name -->
<string name="account_name_format">From <xliff:g id="source" example="Gmail">%1$s</xliff:g> account: <xliff:g id="account" example="user@gmail.com">%2$s</xliff:g></string>
+ <!-- String describing which account type a contact came from when editing it -->
+ <string name="account_type_format"><xliff:g id="source" example="Gmail">%1$s</xliff:g> contact</string>
+
+ <!-- String describing which account a contact came from when editing it -->
+ <string name="from_account_format">from <xliff:g id="source" example="user@gmail.com">%1$s</xliff:g></string>
+
+ <!-- Checkbox asking the user if they want to display a particular photo for a contact -->
+ <string name="use_photo_as_primary">Use this photo</string>
+
+ <!-- Text used to explain that a contact cannot be edited since the data is read only -->
+ <string name="contact_read_only"><xliff:g id="source" example="Gmail">%1$s</xliff:g> contact information is not editable on this device.</string>
+
+ <!-- Text describing that a contact has no information available other than name and photo -->
+ <string name="no_contact_details">No additional information for this contact</string>
</resources>
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 0dcfc53..0c929ba 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -2101,8 +2101,13 @@
break;
}
- Bitmap photo = mBitmapCache.get(photoId).get();
+ SoftReference<Bitmap> photoRef = mBitmapCache.get(photoId);
+ if (photoRef == null) {
+ break;
+ }
+ Bitmap photo = photoRef.get();
if (photo == null) {
+ mBitmapCache.remove(photoId);
break;
}
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 3d5ac85..e6dd623 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -823,7 +823,6 @@
Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
- entry.data = PhoneNumberUtils.stripSeparators(entry.data);
entry.isPrimary = isSuperPrimary;
mPhoneEntries.add(entry);
diff --git a/src/com/android/contacts/ui/QuickContactActivity.java b/src/com/android/contacts/ui/QuickContactActivity.java
index 6445664..d17e3be 100644
--- a/src/com/android/contacts/ui/QuickContactActivity.java
+++ b/src/com/android/contacts/ui/QuickContactActivity.java
@@ -41,13 +41,14 @@
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ if (LOGV) Log.d(TAG, "onCreate");
+
this.onNewIntent(getIntent());
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
-
if (LOGV) Log.d(TAG, "onNewIntent");
if (QuickContactWindow.TRACE_LAUNCH) {
@@ -82,14 +83,15 @@
protected void onPause() {
super.onPause();
if (LOGV) Log.d(TAG, "onPause");
+
+ // Dismiss any dialog when pausing
+ mQuickContact.dismiss();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (LOGV) Log.d(TAG, "onDestroy");
-
- mQuickContact.dismiss();
}
/** {@inheritDoc} */
@@ -97,8 +99,6 @@
if (LOGV) Log.d(TAG, "onDismiss");
if (isTaskRoot() && !FORCE_CREATE) {
- if (LOGV) Log.d(TAG, "Moving task to back");
-
// Instead of stopping, simply push this to the back of the stack.
// This is only done when running at the top of the stack;
// otherwise, we have been launched by someone else so need to
diff --git a/src/com/android/contacts/ui/QuickContactWindow.java b/src/com/android/contacts/ui/QuickContactWindow.java
index 45cb73f..ecb5f9d 100644
--- a/src/com/android/contacts/ui/QuickContactWindow.java
+++ b/src/com/android/contacts/ui/QuickContactWindow.java
@@ -26,15 +26,16 @@
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.NotifyingAsyncQueryHandler;
import com.android.internal.policy.PolicyManager;
-import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+import android.app.Activity;
import android.content.ActivityNotFoundException;
-import android.content.ContentValues;
import android.content.ContentUris;
+import android.content.ContentValues;
import android.content.Context;
import android.content.EntityIterator;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -45,7 +46,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Handler;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.QuickContact;
@@ -55,14 +55,9 @@
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.SocialContract.Activities;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
-import android.util.Pool;
-import android.util.Poolable;
-import android.util.PoolableManager;
-import android.util.Pools;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -70,13 +65,12 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
-import android.view.Window.Callback;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -93,7 +87,6 @@
import android.widget.Toast;
import java.lang.ref.SoftReference;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -106,7 +99,8 @@
*/
public class QuickContactWindow implements Window.Callback,
NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
- AbsListView.OnItemClickListener, CompoundButton.OnCheckedChangeListener, KeyEvent.Callback {
+ AbsListView.OnItemClickListener, CompoundButton.OnCheckedChangeListener, KeyEvent.Callback,
+ OnGlobalLayoutListener {
private static final String TAG = "QuickContactWindow";
/**
@@ -124,6 +118,7 @@
private View mDecor;
private final Rect mRect = new Rect();
+ private boolean mDismissed = false;
private boolean mQuerying = false;
private boolean mShowing = false;
@@ -134,7 +129,13 @@
private Uri mLookupUri;
private Rect mAnchor;
- private int mShadowHeight;
+ private int mShadowHoriz;
+ private int mShadowVert;
+ private int mShadowTouch;
+
+ private int mScreenWidth;
+ private int mScreenHeight;
+ private int mRequestedY;
private boolean mHasValidSocial = false;
private boolean mHasData = false;
@@ -226,7 +227,12 @@
mResolveCache = new ResolveCache(mContext);
final Resources res = mContext.getResources();
- mShadowHeight = res.getDimensionPixelSize(R.dimen.quickcontact_shadow);
+ mShadowHoriz = res.getDimensionPixelSize(R.dimen.quickcontact_shadow_horiz);
+ mShadowVert = res.getDimensionPixelSize(R.dimen.quickcontact_shadow_vert);
+ mShadowTouch = res.getDimensionPixelSize(R.dimen.quickcontact_shadow_touch);
+
+ mScreenWidth = mWindowManager.getDefaultDisplay().getWidth();
+ mScreenHeight = mWindowManager.getDefaultDisplay().getHeight();
mTrack = (ViewGroup)mWindow.findViewById(R.id.quickcontact);
mTrackScroll = (HorizontalScrollView)mWindow.findViewById(R.id.scroll);
@@ -290,10 +296,10 @@
* Start showing a dialog for the given {@link Contacts#_ID} pointing
* towards the given location.
*/
- public void show(Uri lookupUri, Rect anchor, int mode, String[] excludeMimes) {
- if (mShowing || mQuerying) {
- Log.w(TAG, "already in process of showing");
- return;
+ public synchronized void show(Uri lookupUri, Rect anchor, int mode, String[] excludeMimes) {
+ if (mQuerying || mShowing) {
+ Log.w(TAG, "dismissing before showing");
+ dismissInternal();
}
if (TRACE_LAUNCH && !android.os.Debug.isMethodTracingActive()) {
@@ -316,7 +322,10 @@
setHeaderImage(R.id.presence, null);
setHeaderImage(R.id.source, null);
+ resetTrack();
+
mHasValidSocial = false;
+ mDismissed = false;
mQuerying = true;
// Start background query for data, but only select photo rows when they
@@ -327,12 +336,12 @@
// Only request photo data when required by mode
if (mMode == QuickContact.MODE_LARGE) {
// Select photos, but only super-primary
- mHandler.startQuery(TOKEN_DATA, null, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
+ ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
} else {
// Exclude all photos from cursor
- mHandler.startQuery(TOKEN_DATA, null, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ "!=?", new String[] { Photo.CONTENT_ITEM_TYPE }, null);
}
}
@@ -380,41 +389,43 @@
*/
private void showInternal() {
mDecor = mWindow.getDecorView();
+ mDecor.getViewTreeObserver().addOnGlobalLayoutListener(this);
WindowManager.LayoutParams l = mWindow.getAttributes();
- l.width = WindowManager.LayoutParams.FILL_PARENT;
+ l.width = mScreenWidth + mShadowHoriz + mShadowHoriz;
l.height = WindowManager.LayoutParams.WRAP_CONTENT;
// Force layout measuring pass so we have baseline numbers
mDecor.measure(l.width, l.height);
-
final int blockHeight = mDecor.getMeasuredHeight();
l.gravity = Gravity.TOP | Gravity.LEFT;
- l.x = 0;
+ l.x = -mShadowHoriz;
if (mAnchor.top > blockHeight) {
// Show downwards callout when enough room, aligning bottom block
// edge with top of anchor area, and adjusting to inset arrow.
showArrow(R.id.arrow_down, mAnchor.centerX());
- l.y = mAnchor.top - blockHeight + mShadowHeight;
+ l.y = mAnchor.top - blockHeight + mShadowVert;
l.windowAnimations = R.style.QuickContactAboveAnimation;
} else {
// Otherwise show upwards callout, aligning block top with bottom of
// anchor area, and adjusting to inset arrow.
showArrow(R.id.arrow_up, mAnchor.centerX());
- l.y = mAnchor.bottom - mShadowHeight;
+ l.y = mAnchor.bottom - mShadowVert;
l.windowAnimations = R.style.QuickContactBelowAnimation;
}
l.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ mRequestedY = l.y;
mWindowManager.addView(mDecor, l);
mShowing = true;
mQuerying = false;
+ mDismissed = false;
mTrack.startAnimation(mTrackAnim);
@@ -425,35 +436,75 @@
}
}
+ /** {@inheritDoc} */
+ public void onGlobalLayout() {
+ layoutInScreen();
+ }
+
+ /**
+ * Adjust vertical {@link WindowManager.LayoutParams} to fit window as best
+ * as possible, shifting up to display content as needed.
+ */
+ private void layoutInScreen() {
+ if (!mShowing) return;
+
+ final WindowManager.LayoutParams l = mWindow.getAttributes();
+ final int originalY = l.y;
+
+ final int blockHeight = mDecor.getHeight();
+
+ l.y = mRequestedY;
+ if (mRequestedY + blockHeight > mScreenHeight) {
+ // Shift up from bottom when overflowing
+ l.y = mScreenHeight - blockHeight;
+ }
+
+ if (originalY != l.y) {
+ // Only update when value is changed
+ mWindow.setAttributes(l);
+ }
+ }
+
/**
* Dismiss this dialog if showing.
*/
- public void dismiss() {
+ public synchronized void dismiss() {
// Notify any listeners that we've been dismissed
if (mDismissListener != null) {
mDismissListener.onDismiss(this);
}
- if (!isShowing()) {
- if (LOGD) Log.d(TAG, "not visible, ignore");
- return;
- }
+ dismissInternal();
+ }
+ private void dismissInternal() {
+ // Remove any attached window decor for recycling
boolean hadDecor = mDecor != null;
if (hadDecor) {
mWindowManager.removeView(mDecor);
+ mWindowRecycled++;
+ mDecor.getViewTreeObserver().removeGlobalOnLayoutListener(this);
mDecor = null;
mWindow.closeAllPanels();
}
-
- // Release reference to last chiclet.
- mLastAction = null;
-
- // Completely hide header from current mode
- mHeader.setVisibility(View.GONE);
+ mShowing = false;
+ mDismissed = true;
// Cancel any pending queries
mHandler.cancelOperation(TOKEN_DATA);
+ mQuerying = false;
+
+ // Completely hide header and reset track
+ mHeader.setVisibility(View.GONE);
+ resetTrack();
+ }
+
+ /**
+ * Reset track to initial state, recycling any chiclets.
+ */
+ private void resetTrack() {
+ // Release reference to last chiclet
+ mLastAction = null;
// Clear track actions and scroll to hard left
mResolveCache.clear();
@@ -468,32 +519,19 @@
mTrackScroll.fullScroll(View.FOCUS_LEFT);
mWasDownArrow = false;
+ // Clear any primary requests
+ mMakePrimary = false;
+ mSetPrimaryCheckBox.setChecked(false);
+
setResolveVisible(false, null);
-
- mQuerying = false;
-
- if (!hadDecor || !mShowing) {
- if (LOGD) Log.d(TAG, "not showing, ignore");
- return;
- }
-
- mShowing = false;
- mWindowRecycled++;
- }
-
- /**
- * Returns true if this dialog is showing or querying.
- */
- public boolean isShowing() {
- return mShowing || mQuerying;
}
/**
* Consider showing this window, which will only call through to
* {@link #showInternal()} when all data items are present.
*/
- private synchronized void considerShowing() {
- if (mHasData && !mShowing) {
+ private void considerShowing() {
+ if (mHasData && !mShowing && !mDismissed) {
if (mMode == QuickContact.MODE_MEDIUM && !mHasValidSocial) {
// Missing valid social, swap medium for small header
mHeader.setVisibility(View.GONE);
@@ -506,7 +544,10 @@
}
/** {@inheritDoc} */
- public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ // Bail early when query is stale
+ if (cookie != mLookupUri) return;
+
if (cursor == null) {
// Problem while running query, so bail without showing
Log.w(TAG, "Missing cursor for token=" + token);
@@ -741,7 +782,7 @@
}
// Always launch as new task, since we're like a launcher
- mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
/** {@inheritDoc} */
@@ -826,7 +867,7 @@
/** {@inheritDoc} */
public Intent getIntent() {
final Intent intent = new Intent(Intent.ACTION_VIEW, mLookupUri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return intent;
}
@@ -887,7 +928,7 @@
if (size == 1) {
bestResolve = matches.get(0);
} else if (size > 1) {
- bestResolve = getBestResolve(matches);
+ bestResolve = getBestResolve(intent, matches);
}
if (bestResolve != null) {
@@ -911,7 +952,18 @@
* displaying in the track, and does not shortcut the system
* {@link Intent} disambiguation dialog.
*/
- protected ResolveInfo getBestResolve(List<ResolveInfo> matches) {
+ protected ResolveInfo getBestResolve(Intent intent, List<ResolveInfo> matches) {
+ // Try finding preferred activity, otherwise detect disambig
+ final ResolveInfo foundResolve = mPackageManager.resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ final boolean foundDisambig = (foundResolve.match &
+ IntentFilter.MATCH_CATEGORY_MASK) == 0;
+
+ if (!foundDisambig) {
+ // Found concrete match, so return directly
+ return foundResolve;
+ }
+
// Accept any package from prefer list, otherwise first system app
ResolveInfo firstSystem = null;
for (ResolveInfo info : matches) {
@@ -920,6 +972,8 @@
final boolean isPrefer = QuickContactWindow.sPreferResolve
.contains(info.activityInfo.applicationInfo.packageName);
+
+
if (isPrefer) return info;
if (isSystem && firstSystem != null) firstSystem = info;
}
@@ -1245,6 +1299,7 @@
if (tag instanceof Action) {
// Incoming tag is concrete intent, so try launching
final Action action = (Action)tag;
+ final boolean makePrimary = mMakePrimary;
try {
mContext.startActivity(action.getIntent());
@@ -1257,7 +1312,7 @@
setResolveVisible(false, actionView);
this.dismiss();
- if (mMakePrimary) {
+ if (makePrimary) {
ContentValues values = new ContentValues(1);
values.put(Data.IS_SUPER_PRIMARY, 1);
final Uri dataUri = action.getDataUri();
@@ -1308,7 +1363,8 @@
});
// Make sure we resize to make room for ListView
- onWindowAttributesChanged(mWindow.getAttributes());
+ mDecor.forceLayout();
+ mDecor.invalidate();
}
}
@@ -1322,6 +1378,8 @@
// it will close the entire dialog.
if (mFooterDisambig.getVisibility() == View.VISIBLE) {
setResolveVisible(false, null);
+ mDecor.forceLayout();
+ mDecor.invalidate();
} else {
dismiss();
}
@@ -1381,8 +1439,8 @@
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// Only try detecting outside events on down-press
mDecor.getHitRect(mRect);
- mRect.top = mRect.top + mDecor.getPaddingTop();
- mRect.bottom = mRect.bottom - mDecor.getPaddingBottom();
+ mRect.top = mRect.top + mShadowTouch;
+ mRect.bottom = mRect.bottom - mShadowTouch;
final int x = (int)event.getX();
final int y = (int)event.getY();
if (!mRect.contains(x, y)) {