OSDN Git Service

auto import from //depot/cupcake/@136594
authorThe Android Open Source Project <initial-contribution@android.com>
Thu, 5 Mar 2009 22:34:37 +0000 (14:34 -0800)
committerThe Android Open Source Project <initial-contribution@android.com>
Thu, 5 Mar 2009 22:34:37 +0000 (14:34 -0800)
res/layout-finger/contacts_list_item_photo.xml [new file with mode: 0644]
res/layout-finger/edit_separator.xml
res/layout-finger/edit_separator_alone.xml
src/com/android/contacts/ContactsListActivity.java
src/com/android/contacts/EditContactActivity.java

diff --git a/res/layout-finger/contacts_list_item_photo.xml b/res/layout-finger/contacts_list_item_photo.xml
new file mode 100644 (file)
index 0000000..d2821e1
--- /dev/null
@@ -0,0 +1,92 @@
+<?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>
index f638ade..1fe799d 100644 (file)
@@ -43,6 +43,9 @@
             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"
index c42af1c..7d4aa88 100644 (file)
@@ -36,6 +36,9 @@
         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"
index 6e72fbd..21b9ec0 100644 (file)
@@ -33,6 +33,7 @@ import android.content.SharedPreferences;
 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;
@@ -48,6 +49,7 @@ import android.provider.Contacts.Presence;
 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;
@@ -67,6 +69,7 @@ import android.widget.ResourceCursorAdapter;
 import android.widget.SectionIndexer;
 import android.widget.TextView;
 
+import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
@@ -104,6 +107,8 @@ public final class ContactsListActivity extends ListActivity
     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;
@@ -118,7 +123,7 @@ public final class ContactsListActivity extends ListActivity
     /** 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 */
@@ -184,7 +189,8 @@ public final class ContactsListActivity extends ListActivity
         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[] {
@@ -215,6 +221,7 @@ public final class ContactsListActivity extends ListActivity
     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;
@@ -696,7 +703,7 @@ public final class ContactsListActivity extends ListActivity
      * 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
@@ -1307,16 +1314,18 @@ public final class ContactsListActivity extends ListActivity
         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);
@@ -1334,6 +1343,12 @@ public final class ContactsListActivity extends ListActivity
                             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>>();
+            }
         }
 
         /**
@@ -1373,7 +1388,7 @@ public final class ContactsListActivity extends ListActivity
                 }
             }
         }
-        
+
         @Override
         public View newView(Context context, Cursor cursor, ViewGroup parent) {
             final View view = super.newView(context, cursor, parent);
@@ -1383,6 +1398,7 @@ public final class ContactsListActivity extends ListActivity
             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;
@@ -1459,12 +1475,53 @@ public final class ContactsListActivity extends ListActivity
                     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) {
index 49569e3..44bdef5 100644 (file)
@@ -162,6 +162,7 @@ public final class EditContactActivity extends Activity implements View.OnClickL
     
     private EditText mNameView;
     private ImageView mPhotoImageView;
+    private ViewGroup mContentView;
     private LinearLayout mLayout;
     private LayoutInflater mInflater;
     private MenuItem mPhotoMenuItem;
@@ -335,7 +336,10 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         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);
@@ -348,8 +352,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         view = findViewById(R.id.discardButton);
         view.setOnClickListener(this);
 
-        mInflater = getLayoutInflater();
-
         // Resolve the intent
         mState = STATE_UNKNOWN;
         Intent intent = getIntent();
@@ -401,6 +403,48 @@ public final class EditContactActivity extends Activity implements View.OnClickL
     
     @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);
@@ -447,6 +491,17 @@ public final class EditContactActivity extends Activity implements View.OnClickL
 
         // 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
@@ -1572,6 +1627,19 @@ public final class EditContactActivity extends Activity implements View.OnClickL
                 }
             }
         }
+        
+        // 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) {
@@ -1656,6 +1724,19 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         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
         }
@@ -2082,7 +2163,8 @@ public final class EditContactActivity extends Activity implements View.OnClickL
             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;
         }
     }