--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2009, 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="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="0dip"
+ android:paddingRight="5dip"
+>
+
+ <ImageView android:id="@+id/presence"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="5dip"
+ android:layout_centerVertical="true"
+
+ android:gravity="center"
+ android:scaleType="centerInside"
+ />
+
+ <ImageView android:id="@+id/photo"
+ android:layout_width="64dip"
+ android:layout_height="64dip"
+ android:layout_alignParentLeft="true"
+ android:layout_marginRight="9dip"
+
+ android:gravity="center"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/photo"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="8dip"
+ android:layout_marginTop="-8dip"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ />
+
+ <TextView android:id="@+id/number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dip"
+ android:layout_toRightOf="@id/label"
+ android:layout_alignBaseline="@id/label"
+ android:layout_toLeftOf="@id/presence"
+ android:layout_alignWithParentIfMissing="true"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView android:id="@+id/name"
+ android:layout_width="0dip"
+ android:layout_height="0dip"
+ android:layout_above="@id/label"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/photo"
+ android:layout_toLeftOf="@id/presence"
+ android:layout_marginBottom="1dip"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:gravity="center_vertical|left"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+</RelativeLayout>
android:layout_weight="1"
android:layout_marginBottom="8dip"
android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
/>
<ImageView android:id="@+id/add"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:duplicateParentState="true"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
/>
<ImageView android:id="@+id/add"
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Contacts.Intents.UI;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.KeyEvent;
import android.widget.SectionIndexer;
import android.widget.TextView;
+import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
static final int MODE_MASK_NO_FILTER = 0x20000000;
/** Mask for having a "create new contact" header in the list */
static final int MODE_MASK_CREATE_NEW = 0x10000000;
+ /** Mask for showing photos in the list */
+ static final int MODE_MASK_SHOW_PHOTOS = 0x08000000;
/** Unknown mode */
static final int MODE_UNKNOWN = 0;
/** Show frequently contacted contacts */
static final int MODE_FREQUENT = 30;
/** Show starred and the frequent */
- static final int MODE_STREQUENT = 35;
+ static final int MODE_STREQUENT = 35 | MODE_MASK_SHOW_PHOTOS;
/** Show all contacts and pick them when clicking */
static final int MODE_PICK_CONTACT = 40 | MODE_MASK_PICKER;
/** Show all contacts as well as the option to create a new one */
People.PRIMARY_PHONE_ID, // 6
People.PRIMARY_EMAIL_ID, // 7
People.PRESENCE_STATUS, // 8
- People.TIMES_CONTACTED, // 9 (not displayed, but required for the order by to work)
+ "photo_data", // 9
+ People.TIMES_CONTACTED, // 10 (not displayed, but required for the order by to work)
};
static final String[] PHONES_PROJECTION = new String[] {
static final int PRIMARY_PHONE_ID_COLUMN_INDEX = 6;
static final int PRIMARY_EMAIL_ID_COLUMN_INDEX = 7;
static final int SERVER_STATUS_COLUMN_INDEX = 8;
+ static final int PHOTO_COLUMN_INDEX = 9;
static final int DISPLAY_GROUP_INDEX_ALL_CONTACTS = 0;
* Implements the handler for display group selection.
*/
public void onClick(DialogInterface dialogInterface, int which) {
- if (which == DialogInterface.BUTTON1) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
// The OK button was pressed
if (mDisplayGroupOriginalSelection != mDisplayGroupCurrentSelection) {
// Set the group to display
public TextView numberView;
public CharArrayBuffer numberBuffer = new CharArrayBuffer(128);
public ImageView presenceView;
+ public ImageView photoView;
}
private final class ContactItemListAdapter extends ResourceCursorAdapter
implements SectionIndexer {
-
private AlphabetIndexer mIndexer;
private String mAlphabet;
private boolean mLoading = true;
private CharSequence mUnknownNameText;
private CharSequence[] mLocalizedLabels;
+ private boolean mDisplayPhotos = false;
+ private SparseArray<SoftReference<Bitmap>> mBitmapCache = null;
public ContactItemListAdapter(Context context) {
super(context, R.layout.contacts_list_item, null, false);
Contacts.KIND_PHONE);
break;
}
+
+ if ((mMode & MODE_MASK_SHOW_PHOTOS) == MODE_MASK_SHOW_PHOTOS) {
+ mDisplayPhotos = true;
+ setViewResource(R.layout.contacts_list_item_photo);
+ mBitmapCache = new SparseArray<SoftReference<Bitmap>>();
+ }
}
/**
}
}
}
-
+
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final View view = super.newView(context, cursor, parent);
cache.labelView = (TextView) view.findViewById(R.id.label);
cache.numberView = (TextView) view.findViewById(R.id.number);
cache.presenceView = (ImageView) view.findViewById(R.id.presence);
+ cache.photoView = (ImageView) view.findViewById(R.id.photo);
view.setTag(cache);
return view;
presenceView.setVisibility(View.GONE);
}
}
+
+ // Set the photo, if requested
+ if (mDisplayPhotos) {
+ Bitmap photo = null;
+
+ // Look for the cached bitmap
+ int pos = cursor.getPosition();
+ SoftReference<Bitmap> ref = mBitmapCache.get(pos);
+ if (ref != null) {
+ photo = ref.get();
+ }
+
+ if (photo == null) {
+ // Bitmap cache miss, decode it from the cursor
+ if (!cursor.isNull(PHOTO_COLUMN_INDEX)) {
+ try {
+ byte[] photoData = cursor.getBlob(PHOTO_COLUMN_INDEX);
+ photo = BitmapFactory.decodeByteArray(photoData, 0,
+ photoData.length);
+ mBitmapCache.put(pos, new SoftReference<Bitmap>(photo));
+ } catch (OutOfMemoryError e) {
+ // Not enough memory for the photo, use the default one instead
+ photo = null;
+ }
+ }
+ }
+
+ // Bind the photo, or use the fallback no photo resource
+ if (photo != null) {
+ cache.photoView.setImageBitmap(photo);
+ } else {
+ cache.photoView.setImageResource(R.drawable.ic_contact_picture);
+ }
+ }
}
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
+
+ // Update the indexer for the fast scroll widget
updateIndexer(cursor);
+
+ // Clear the photo bitmap cache, if there is one
+ if (mBitmapCache != null) {
+ mBitmapCache.clear();
+ }
}
private void updateIndexer(Cursor cursor) {
private EditText mNameView;
private ImageView mPhotoImageView;
+ private ViewGroup mContentView;
private LinearLayout mLayout;
private LayoutInflater mInflater;
private MenuItem mPhotoMenuItem;
setupSections();
// Load the UI
- setContentView(R.layout.edit_contact);
+ mInflater = getLayoutInflater();
+ mContentView = (ViewGroup)mInflater.inflate(R.layout.edit_contact, null);
+ setContentView(mContentView);
+
mLayout = (LinearLayout) findViewById(R.id.list);
mNameView = (EditText) findViewById(R.id.name);
mPhotoImageView = (ImageView) findViewById(R.id.photoImage);
view = findViewById(R.id.discardButton);
view.setOnClickListener(this);
- mInflater = getLayoutInflater();
-
// Resolve the intent
mState = STATE_UNKNOWN;
Intent intent = getIntent();
@Override
protected void onSaveInstanceState(Bundle outState) {
+
+ // To store current focus between config changes, follow focus down the
+ // view tree, keeping track of any parents with EditEntry tags
+ View focusedChild = mContentView.getFocusedChild();
+ EditEntry focusedEntry = null;
+ while (focusedChild != null) {
+ Object tag = focusedChild.getTag();
+ if (tag instanceof EditEntry) {
+ focusedEntry = (EditEntry) tag;
+ }
+
+ // Keep going deeper until child isn't a group
+ if (focusedChild instanceof ViewGroup) {
+ View deeperFocus = ((ViewGroup) focusedChild).getFocusedChild();
+ if (deeperFocus != null) {
+ focusedChild = deeperFocus;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (focusedChild != null) {
+ int requestFocusId = focusedChild.getId();
+ int requestCursor = 0;
+ if (focusedChild instanceof EditText) {
+ requestCursor = ((EditText) focusedChild).getSelectionStart();
+ }
+
+ // Store focus values in EditEntry if found, otherwise store as
+ // generic values
+ if (focusedEntry != null) {
+ focusedEntry.requestFocusId = requestFocusId;
+ focusedEntry.requestCursor = requestCursor;
+ } else {
+ outState.putInt("requestFocusId", requestFocusId);
+ outState.putInt("requestCursor", requestCursor);
+ }
+ }
+
outState.putParcelableArrayList("phoneEntries", mPhoneEntries);
outState.putParcelableArrayList("emailEntries", mEmailEntries);
outState.putParcelableArrayList("imEntries", mImEntries);
// Now that everything is restored, build the view
buildViews();
+
+ // Try restoring any generally requested focus
+ int requestFocusId = inState.getInt("requestFocusId", View.NO_ID);
+ View focusedChild = mContentView.findViewById(requestFocusId);
+ if (focusedChild != null) {
+ focusedChild.requestFocus();
+ if (focusedChild instanceof EditText) {
+ int requestCursor = inState.getInt("requestCursor", 0);
+ ((EditText) focusedChild).setSelection(requestCursor);
+ }
+ }
}
@Override
}
}
}
+
+ // Give focus to children as requested, possibly after a configuration change
+ View focusChild = view.findViewById(entry.requestFocusId);
+ if (focusChild != null) {
+ focusChild.requestFocus();
+ if (focusChild instanceof EditText) {
+ ((EditText) focusChild).setSelection(entry.requestCursor);
+ }
+ }
+
+ // Reset requested focus values
+ entry.requestFocusId = View.NO_ID;
+ entry.requestCursor = 0;
// Connect listeners up to watch for changed values.
if (data instanceof EditText) {
public boolean isStaticLabel = false;
public boolean syncDataWithView = true;
+ /**
+ * Request focus on the child of this {@link EditEntry} found using
+ * {@link View#findViewById(int)}. This value should be reset to
+ * {@link View#NO_ID} after each use.
+ */
+ public int requestFocusId = View.NO_ID;
+
+ /**
+ * If the {@link #requestFocusId} is an {@link EditText}, this value
+ * indicates the requested cursor position placement.
+ */
+ public int requestCursor = 0;
+
private EditEntry() {
// only used by CREATOR
}
entry.column = ContactMethods.DATA;
entry.contentDirectory = People.ContactMethods.CONTENT_DIRECTORY;
entry.kind = Contacts.KIND_IM;
- entry.contentType = EditorInfo.TYPE_CLASS_TEXT;
+ entry.contentType = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
return entry;
}
}