OSDN Git Service

New edit UI, now without tabs.
authorJeff Hamilton <jham@android.com>
Thu, 1 Oct 2009 09:22:33 +0000 (02:22 -0700)
committerJeff Hamilton <jham@android.com>
Thu, 1 Oct 2009 12:43:16 +0000 (05:43 -0700)
Make ExternalSource derive from FallbackSource
and add photo and name to the list of sources
since they're not optional.

Change-Id: I043db076a001a711e56dd6e5e6ee32c4c0c9477a

14 files changed:
AndroidManifest.xml
res/layout/act_edit.xml
res/layout/item_contact_editor.xml
res/layout/item_generic_editor.xml
res/menu/edit.xml
src/com/android/contacts/ViewContactActivity.java
src/com/android/contacts/model/ContactsSource.java
src/com/android/contacts/model/ExchangeSource.java
src/com/android/contacts/model/ExternalSource.java
src/com/android/contacts/model/FallbackSource.java
src/com/android/contacts/model/GoogleSource.java
src/com/android/contacts/ui/EditContactActivity.java
src/com/android/contacts/ui/widget/ContactEditorView.java
tests/src/com/android/contacts/EntityModifierTests.java

index bd0a769..9242b1b 100644 (file)
         <!-- Edit or insert details for a contact -->
         <activity
             android:name=".ui.EditContactActivity"
-            android:theme="@style/TallTitleBarTheme"
+            android:label="@string/editContactDescription"
             android:windowSoftInputMode="stateVisible|adjustResize">
 
             <intent-filter android:label="@string/editContactDescription">
index 250a0e1..e56ec20 100644 (file)
@@ -4,9 +4,9 @@
      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.
      limitations under the License.
 -->
 
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
-    android:fillViewport="true">
+    android:orientation="vertical"
+>
 
-    <LinearLayout
-        android:id="@+id/panel"
+    <ScrollView 
         android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:orientation="vertical"
-        android:fillViewport="true">
+        android:layout_height="1px"
+        android:layout_weight="1"
+        android:fillViewport="true"
+    >
 
-        <com.android.internal.widget.ContactHeaderWidget
-            android:id="@+id/contact_header_widget"
+        <LinearLayout android:id="@+id/editors"
             android:layout_width="fill_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="fill_parent"
+            android:orientation="vertical"
+        />
 
-        <com.android.contacts.ScrollingTabWidget
-            android:id="@+id/tab_widget"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content" />
+    </ScrollView>
 
-        <include
-            android:id="@android:id/tabcontent"
-            android:layout_width="fill_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-            android:fillViewport="true"
-            android:visibility="invisible"
-            layout="@layout/item_contact_editor" />
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        style="@android:style/ButtonBar"
+    >
 
-        <LinearLayout
-            android:layout_width="fill_parent"
+        <Button android:id="@+id/btn_done"
+            android:layout_width="0dip"
             android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            style="@android:style/ButtonBar">
-
-            <Button
-                android:id="@+id/btn_done"
-                android:layout_width="0dip"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:text="@string/menu_done" />
-
-            <Button
-                android:id="@+id/btn_discard"
-                android:layout_width="0dip"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:text="@string/menu_doNotSave" />
+            android:layout_weight="1"
+            android:text="@string/menu_done"
+        />
 
-        </LinearLayout>
+        <Button android:id="@+id/btn_discard"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/menu_doNotSave"
+        />
 
     </LinearLayout>
 
-</ScrollView>
+</LinearLayout>
index 24d612a..cea4bdb 100644 (file)
 <com.android.contacts.ui.widget.ContactEditorView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:fillViewport="true">
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+>
 
-    <TextView
-        android:id="@+id/edit_read_only"
-        android:layout_width="fill_parent"
+    <!-- Left side color bar -->
+    <ImageView
+        android:id="@+id/color_bar"
+        android:layout_width="15dip"
+        android:layout_height="fill_parent"
+    />
+
+    <!-- The content -->
+    <RelativeLayout
+        android:layout_width="0dip"
         android:layout_height="wrap_content"
-        android:gravity="center|center_vertical"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:paddingTop="5dip"
-        android:paddingBottom="5dip"
-        android:text="@string/edit_read_only"/>
-
-    <FrameLayout
-        android:id="@+id/stub_photo"
-        android:layout_width="100dip"
-        android:layout_height="96dip"
-        android:layout_below="@id/edit_read_only"
-        android:layout_alignWithParentIfMissing="true"
-        android:paddingLeft="12dip"
-        android:paddingTop="10dip">
+        android:layout_weight="1"
+    >
+
+        <!-- Account info header -->
+        <RelativeLayout android:id="@+id/header"
+            android:layout_height="64dip"
+            android:layout_width="fill_parent"
+        >
+
+            <ImageView android:id="@+id/header_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="7dip"
+                android:layout_marginRight="7dip"
+                android:layout_centerVertical="true"
+            />
+
+            <TextView android:id="@+id/header_account_type"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@+id/header_icon"
+                android:layout_alignTop="@id/header_icon"
+                android:layout_marginTop="-4dip"
+
+                android:textSize="24pt"
+                android:textStyle="bold"
+                android:textColor="?android:attr/textColorPrimary"
+            />
+
+            <TextView android:id="@+id/header_account_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@+id/header_icon"
+                android:layout_alignBottom="@+id/header_icon"
+                android:layout_marginBottom="2dip"
+
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="?android:attr/textColorPrimary"
+            />
+
+        </RelativeLayout>
+
+        <FrameLayout
+            android:id="@+id/stub_photo"
+            android:layout_width="100dip"
+            android:layout_height="96dip"
+            android:layout_below="@id/header"
+            android:layout_alignWithParentIfMissing="true"
+            android:paddingLeft="12dip"
+            android:paddingTop="10dip">
+
+            <include
+                android:id="@+id/edit_photo"
+                layout="@layout/item_photo_editor" />
+
+        </FrameLayout>
 
         <include
-            android:id="@+id/edit_photo"
-            layout="@layout/item_photo_editor" />
+            android:id="@+id/edit_name"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/stub_photo"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="4dip"
+            android:layout_alignWithParentIfMissing="true"
+            layout="@layout/item_generic_editor" />
 
-    </FrameLayout>
+        <LinearLayout
+            android:id="@+id/sect_general"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/edit_name"
+            android:orientation="vertical"
+        />
 
-    <include
-        android:id="@+id/edit_name"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/edit_read_only"
-        android:layout_toRightOf="@id/stub_photo"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="4dip"
-        android:layout_alignWithParentIfMissing="true"
-        layout="@layout/item_generic_editor" />
-
-    <LinearLayout
-        android:id="@+id/sect_general"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/edit_name"
-        android:orientation="vertical" />
+        <View android:id="@+id/head_secondary_divider"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/sect_general"
+            android:background="?android:attr/listDivider" />
 
-    <TextView
-        android:id="@+id/head_secondary"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/sect_general"
-        android:gravity="center_vertical"
-        android:minHeight="?android:attr/listPreferredItemHeight"
-        android:background="@color/sect_secondary"
-        android:text="@string/edit_secondary_collapse"
-        android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textColor="@color/kind_title"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:focusable="true"
-        android:clickable="true"
-        android:paddingLeft="10dip"
-        android:drawablePadding="10dip" />
-
-    <LinearLayout
-        android:id="@+id/sect_secondary"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@+id/head_secondary"
-        android:background="@color/sect_secondary"
-        android:orientation="vertical" />
+        <TextView
+            android:id="@+id/head_secondary"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/head_secondary_divider"
+
+            android:gravity="center_vertical"
+            android:minHeight="?android:attr/listPreferredItemHeight"
+            android:text="@string/edit_secondary_collapse"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/kind_title"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:focusable="true"
+            android:clickable="true"
+            android:paddingLeft="10dip"
+            android:drawablePadding="10dip" />
+
+        <LinearLayout
+            android:id="@+id/sect_secondary"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/head_secondary"
+            android:orientation="vertical" />
+
+        <TextView
+            android:id="@+id/edit_read_only"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/edit_name"
+            android:gravity="center_horizontal"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:paddingTop="5dip"
+            android:paddingBottom="5dip"
+            android:text="@string/edit_read_only"/>
+
+    </RelativeLayout>
 
 </com.android.contacts.ui.widget.ContactEditorView>
index 3c1426d..01fa980 100644 (file)
@@ -16,7 +16,7 @@
 
 <com.android.contacts.ui.widget.GenericEditorView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:baselineAligned="false"
     android:paddingRight="?android:attr/scrollbarSize">
index 658a567..5ecc652 100644 (file)
         android:title="@string/menu_deleteContact" />
 
     <item
-        android:id="@+id/menu_photo_add"
-        android:icon="@drawable/ic_menu_add_picture"
-        android:title="@string/addPicture" />
-
-    <item
-        android:id="@+id/menu_photo_remove"
-        android:icon="@android:drawable/ic_menu_delete"
-        android:title="@string/removePicture" />
-
-    <item
         android:id="@+id/menu_split"
         android:icon="@drawable/ic_menu_merge"
         android:title="@string/menu_splitAggregate" />
index 6e993ba..ab8e931 100644 (file)
@@ -17,8 +17,6 @@
 package com.android.contacts;
 
 import com.android.contacts.Collapser.Collapsible;
-import com.android.contacts.ScrollingTabWidget.OnTabSelectionChangedListener;
-import com.android.contacts.SplitAggregateView.OnContactSelectedListener;
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.Sources;
 import com.android.contacts.model.ContactsSource.DataKind;
@@ -40,7 +38,6 @@ import android.content.DialogInterface;
 import android.content.Entity;
 import android.content.EntityIterator;
 import android.content.Intent;
-import android.content.DialogInterface.OnClickListener;
 import android.content.Entity.NamedContentValues;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -62,9 +59,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.ContextMenu;
-import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -74,9 +69,6 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.animation.Animation;
-import android.view.animation.TranslateAnimation;
-import android.view.animation.Animation.AnimationListener;
 import android.widget.AdapterView;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -551,7 +543,7 @@ public class ViewContactActivity extends Activity
         } else if (requestCode == REQUEST_EDIT_CONTACT) {
             if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
                 finish();
-            } else {
+            } else if (resultCode == Activity.RESULT_OK) {
                 mLookupUri = intent.getData();
                 if (mLookupUri == null) {
                     finish();
index f0c21e3..f82f8a1 100644 (file)
@@ -138,6 +138,10 @@ public abstract class ContactsSource {
         }
     }
 
+    abstract public int getHeaderColor(Context context);
+
+    abstract public int getSideBarColor(Context context);
+    
     /**
      * {@link Comparator} to sort by {@link DataKind#weight}.
      */
index 6d4e357..5161499 100644 (file)
@@ -272,4 +272,14 @@ public class ExchangeSource extends FallbackSource {
 
         return kind;
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff876b47;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffc6ab8c;
+    }
 }
index 0786e28..11e06c3 100644 (file)
@@ -77,7 +77,7 @@ import java.util.List;
  * <p>
  * In the future this may be inflated from XML defined by a data source.
  */
-public class ExternalSource extends ContactsSource {
+public class ExternalSource extends FallbackSource {
     private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
     private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
 
@@ -110,6 +110,10 @@ public class ExternalSource extends ContactsSource {
             inflate(context, parser);
         }
 
+        // Bring in name and photo from fallback source, which are non-optional
+        inflateStructuredName(inflateLevel);
+        inflatePhoto(inflateLevel);
+
         setInflatedLevel(inflateLevel);
     }
 
@@ -260,4 +264,14 @@ public class ExternalSource extends ContactsSource {
             return mPublishedMode ? inflatePublished(status.published) : status.title;
         }
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff7f93bc;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffbdc7d8;
+    }
 }
index ebef42b..ca405eb 100644 (file)
@@ -621,4 +621,14 @@ public class FallbackSource extends ContactsSource {
             }
         }
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff7f93bc;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffbdc7b8;
+    }
 }
index 010982c..e8f7f46 100644 (file)
@@ -260,4 +260,14 @@ public class GoogleSource extends FallbackSource {
             cursor.close();
         }
     }
+
+    @Override
+    public int getHeaderColor(Context context) {
+        return 0xff000000;
+    }
+
+    @Override
+    public int getSideBarColor(Context context) {
+        return 0xffffffff;
+    }
 }
index e6bb4cf..50532a3 100644 (file)
 
 package com.android.contacts.ui;
 
+import com.android.contacts.ContactsListActivity;
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.ScrollingTabWidget;
+import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.Editor;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.model.EntitySet;
+import com.android.contacts.model.GoogleSource;
+import com.android.contacts.model.Sources;
+import com.android.contacts.model.Editor.EditorListener;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.ui.widget.ContactEditorView;
+import com.android.contacts.util.EmptyService;
+import com.android.contacts.util.WeakAsyncTask;
+import com.android.internal.widget.ContactHeaderWidget;
+import com.google.android.collect.Lists;
+
 import android.accounts.Account;
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -34,8 +53,6 @@ import android.content.Intent;
 import android.content.OperationApplicationException;
 import android.content.ContentProviderOperation.Builder;
 import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -45,12 +62,9 @@ import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts.Data;
-import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -59,38 +73,19 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
 import android.widget.ListAdapter;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.contacts.ContactsListActivity;
-import com.android.contacts.ContactsUtils;
-import com.android.contacts.R;
-import com.android.contacts.ScrollingTabWidget;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Editor;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
-import com.android.contacts.model.GoogleSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.widget.ContactEditorView;
-import com.android.contacts.util.EmptyService;
-import com.android.contacts.util.WeakAsyncTask;
-import com.android.internal.widget.ContactHeaderWidget;
-import com.google.android.collect.Lists;
-
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
  * Activity for editing or inserting a contact.
  */
-public final class EditContactActivity extends Activity implements View.OnClickListener,
-        ScrollingTabWidget.OnTabSelectionChangedListener,
-        ContactHeaderWidget.ContactHeaderListener, EditorListener {
+public final class EditContactActivity extends Activity
+        implements View.OnClickListener, EditorListener {
     private static final String TAG = "EditContactActivity";
 
     /** The launch code when picking a photo and the raw data is returned */
@@ -100,7 +95,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
     private static final int REQUEST_JOIN_CONTACT = 3022;
 
     private static final String KEY_EDIT_STATE = "state";
-    private static final String KEY_SELECTED_RAW_CONTACT = "selected";
 
     /** The result code when view activity should close after edit returns */
     public static final int RESULT_CLOSE_VIEW_ACTIVITY = 777;
@@ -110,46 +104,27 @@ public final class EditContactActivity extends Activity implements View.OnClickL
     public static final int SAVE_MODE_JOIN = 2;
 
 
-    private String mQuerySelection;
-
-    private ScrollingTabWidget mTabWidget;
-    private ContactHeaderWidget mHeader;
-    private TextView mAccountName;
+    String mQuerySelection;
 
-    private ContactEditorView mEditor;
-
-    private EntitySet mState;
     private long mContactIdForJoin;
+    EntitySet mState;
 
+    /** The linear layout holding the ContactEditorViews */
+    LinearLayout mContent;
+    
     private ArrayList<Dialog> mManagedDialogs = Lists.newArrayList();
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        final Context context = this;
-        final LayoutInflater inflater = this.getLayoutInflater();
-
         final Intent intent = getIntent();
         final String action = intent.getAction();
-        final Bundle extras = intent.getExtras();
 
         setContentView(R.layout.act_edit);
 
-        // Header bar is filled later after queries finish
-        mHeader = (ContactHeaderWidget)this.findViewById(R.id.contact_header_widget);
-        mHeader.setContactHeaderListener(this);
-        mHeader.showStar(false);
-        mHeader.enableClickListeners();
-
-        mTabWidget = (ScrollingTabWidget)this.findViewById(R.id.tab_widget);
-        mTabWidget.setTabSelectionListener(this);
-        mAccountName = (TextView)mTabWidget.findViewById(R.id.account_name);
-
         // Build editor and listen for photo requests
-        mEditor = (ContactEditorView)this.findViewById(android.R.id.tabcontent);
-        mEditor.getPhotoEditor().setEditorListener(this);
-        mEditor.setNameEditorListener(this);
+        mContent = (LinearLayout) findViewById(R.id.editors);
 
         findViewById(R.id.btn_done).setOnClickListener(this);
         findViewById(R.id.btn_discard).setOnClickListener(this);
@@ -160,12 +135,9 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         if (Intent.ACTION_EDIT.equals(action) && !hasIncomingState) {
             // Read initial state from database
             new QueryEntitiesTask(this).execute(intent);
-            mHeader.showStar(true);
-            mHeader.setContactUri(intent.getData(), false);
         } else if (Intent.ACTION_INSERT.equals(action) && !hasIncomingState) {
             // Trigger dialog to pick account type
             doAddAction();
-            mHeader.showStar(false);
         }
     }
 
@@ -227,8 +199,7 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         @Override
         protected void onPostExecute(EditContactActivity target, Void result) {
             // Bind UI to new background state
-            target.bindTabs();
-            target.bindHeader();
+            target.bindEditors();
         }
     }
 
@@ -239,7 +210,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         if (hasValidState()) {
             // Store entities with modifications
             outState.putParcelable(KEY_EDIT_STATE, mState);
-            outState.putLong(KEY_SELECTED_RAW_CONTACT, getSelectedRawContactId());
         }
 
         super.onSaveInstanceState(outState);
@@ -250,15 +220,8 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         // Read modifications from instance
         mState = savedInstanceState.<EntitySet> getParcelable(KEY_EDIT_STATE);
 
-        bindTabs();
-        bindHeader();
+        bindEditors();
 
-        if (hasValidState()) {
-            final Long selectedId = savedInstanceState.getLong(KEY_SELECTED_RAW_CONTACT);
-            setSelectedRawContactId(selectedId);
-        }
-
-        // Restore selected tab and any focus
         super.onRestoreInstanceState(savedInstanceState);
     }
 
@@ -285,47 +248,12 @@ public final class EditContactActivity extends Activity implements View.OnClickL
     /**
      * Show this {@link Dialog} and manage with the {@link Activity}.
      */
-    private void showAndManageDialog(Dialog dialog) {
+    void showAndManageDialog(Dialog dialog) {
         startManagingDialog(dialog);
         dialog.show();
     }
 
     /**
-     * Return the {@link RawContacts#_ID} of the currently selected tab.
-     */
-    protected Long getSelectedRawContactId() {
-        final int tabIndex = mTabWidget.getCurrentTab();
-        return this.mTabRawContacts.get(tabIndex);
-    }
-
-    /**
-     * Return the {@link EntityDelta} for the currently selected tab.
-     */
-    protected EntityDelta getSelectedEntityDelta() {
-        final Long rawContactId = getSelectedRawContactId();
-        return mState.getByRawContactId(rawContactId);
-    }
-
-    /**
-     * Set the selected tab based on the given {@link RawContacts#_ID}.
-     */
-    protected void setSelectedRawContactId(Long rawContactId) {
-        int tabIndex = 0;
-
-        // Find index of requested contact
-        final int size = mTabRawContacts.size();
-        for (int i = 0; i < size; i++) {
-            if (mTabRawContacts.valueAt(i) == rawContactId) {
-                tabIndex = i;
-                break;
-            }
-        }
-
-        mTabWidget.setCurrentTab(tabIndex);
-        this.onTabSelectionChanged(tabIndex, false);
-    }
-
-    /**
      * Check if our internal {@link #mState} is valid, usually checked before
      * performing user actions.
      */
@@ -336,128 +264,46 @@ public final class EditContactActivity extends Activity implements View.OnClickL
 
 
     /**
-     * Map from {@link #mTabWidget} indexes to {@link RawContacts#_ID}, usually
-     * used when mapping to {@link #mState}.
+     * An array of the raw contacts in the order they appear in the list.
      */
-    private SparseArray<Long> mTabRawContacts = new SparseArray<Long>();
-
+    private EntityDelta[] mEntities;
 
     /**
-     * Rebuild tabs to match our underlying {@link #mState} object, usually
+     * Rebuild the editors to match our underlying {@link #mState} object, usually
      * called once we've parsed {@link Entity} data or have inserted a new
      * {@link RawContacts}.
      */
-    protected void bindTabs() {
+    protected void bindEditors() {
         if (!hasValidState()) return;
 
+        final LayoutInflater inflater = (LayoutInflater) getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
         final Sources sources = Sources.getInstance(this);
-        final Long selectedRawContactId = this.getSelectedRawContactId();
 
-        // Remove any existing tabs and rebuild any visible
-        mTabWidget.removeAllTabs();
-        mTabRawContacts.clear();
-        for (EntityDelta entity : mState) {
+        // Remove any existing editors and rebuild any visible
+
+        mContent.removeAllViews();
+        mEntities = new EntityDelta[mState.size()];
+        int size = mState.size();
+        for (int i = 0; i < size; i++) {
+            // TODO ensure proper ordering of entities in the list
+            EntityDelta entity = mState.get(i);
             final ValuesDelta values = entity.getValues();
             if (!values.isVisible()) continue;
 
             final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
-            final Long rawContactId = values.getAsLong(RawContacts._ID);
             final ContactsSource source = sources.getInflatedSource(accountType,
                     ContactsSource.LEVEL_CONSTRAINTS);
 
-            final int tabIndex = mTabWidget.getTabCount();
-            final View tabView = ContactsUtils.createTabIndicatorView(
-                    mTabWidget.getTabParent(), source);
-            mTabWidget.addTab(tabView);
-            mTabRawContacts.put(tabIndex, rawContactId);
-        }
-
-        final boolean hasActiveTabs = mTabWidget.getTabCount() > 0;
-        if (hasActiveTabs) {
-            // Focus on last selected contact
-            this.setSelectedRawContactId(selectedRawContactId);
-        } else {
-            // Nothing remains to edit, save and bail entirely
-            this.doSaveAction(SAVE_MODE_DEFAULT);
+            ContactEditorView editor = (ContactEditorView) inflater.inflate(
+                    R.layout.item_contact_editor, mContent, false);
+            mContent.addView(editor);
+            editor.setState(entity, source);
+            mEntities[i] = entity;
         }
 
         // Show editor now that we've loaded state
-        mEditor.setVisibility(View.VISIBLE);
-    }
-
-    /**
-     * Bind our header based on {@link #mState}, which include any edits.
-     * Usually called once {@link Entity} data has been loaded, or after a
-     * primary {@link Data} change.
-     */
-    protected void bindHeader() {
-        if (!hasValidState()) return;
-
-        boolean starred = false;
-
-        ValuesDelta photoDelta = mState.getSuperPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
-        if (photoDelta != null) {
-            final byte[] photoBytes = photoDelta.getAsByteArray(Photo.PHOTO);
-            if (photoBytes != null) {
-                Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
-                        photoBytes.length);
-                mHeader.setPhoto(photo);
-            }
-        }
-
-        ValuesDelta nameDelta = mState.getSuperPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
-        if (nameDelta != null) {
-            String visibleName = getVisibleName(nameDelta);
-            if (visibleName != null) {
-                mHeader.setDisplayName(visibleName, null);
-            }
-        }
-
-        for (EntityDelta delta : mState) {
-            Long isCurrStarred = delta.getValues().getAsLong(RawContacts.STARRED);
-            starred = starred || (isCurrStarred != null && isCurrStarred != 0);
-        }
-        mHeader.setStared(starred);
-    }
-
-    private static String getVisibleName(ValuesDelta nameDelta) {
-        final String givenName = nameDelta.getAsString(StructuredName.GIVEN_NAME);
-        final String familyName = nameDelta.getAsString(StructuredName.FAMILY_NAME);
-        final boolean hasGiven = !TextUtils.isEmpty(givenName);
-        final boolean hasFamily = !TextUtils.isEmpty(familyName);
-
-        if (hasGiven && hasFamily) {
-            return givenName + " " + familyName;
-        } else if (hasFamily) {
-            return familyName;
-        } else if (hasGiven) {
-            return givenName;
-        } else {
-            return null;
-        }
-    }
-
-    /** {@inheritDoc} */
-    public void onTabSelectionChanged(int tabIndex, boolean clicked) {
-        if (!hasValidState()) return;
-
-        // Find entity and source for selected tab
-        final EntityDelta entity = this.getSelectedEntityDelta();
-        if (entity == null) return;
-
-        final Sources sources = Sources.getInstance(this);
-        final ValuesDelta values = entity.getValues();
-        final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
-        final String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
-        final ContactsSource source = sources.getInflatedSource(accountType,
-                ContactsSource.LEVEL_CONSTRAINTS);
-
-        mAccountName.setText(getString(R.string.account_name_format,
-                source.getDisplayLabel(this), accountName));
-        mAccountName.setVisibility(View.VISIBLE);
-
-        // Assign editor state based on entity and source
-        mEditor.setState(entity, source);
+        mContent.setVisibility(View.VISIBLE);
     }
 
     /** {@inheritDoc} */
@@ -498,12 +344,13 @@ public final class EditContactActivity extends Activity implements View.OnClickL
 
         switch (requestCode) {
             case PHOTO_PICKED_WITH_DATA: {
+/*
                 // When reaching this point, we've already inflated our tab
                 // state and returned to the last-visible tab.
                 final Bitmap photo = data.getParcelableExtra("data");
                 mEditor.setPhotoBitmap(photo);
-                bindHeader();
                 break;
+*/
             }
 
             case REQUEST_JOIN_CONTACT: {
@@ -525,17 +372,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
     }
 
     @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        final boolean hasPhotoEditor = mEditor.hasPhotoEditor();
-        final boolean hasSetPhoto = mEditor.hasSetPhoto();
-
-        menu.findItem(R.id.menu_photo_add).setVisible(hasPhotoEditor);
-        menu.findItem(R.id.menu_photo_remove).setVisible(hasSetPhoto);
-
-        return true;
-    }
-
-    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case R.id.menu_done:
@@ -546,10 +382,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
                 return doAddAction();
             case R.id.menu_delete:
                 return doDeleteAction();
-            case R.id.menu_photo_add:
-                return doPickPhotoAction();
-            case R.id.menu_photo_remove:
-                return doRemovePhotoAction();
             case R.id.menu_split:
                 return doSplitContactAction();
             case R.id.menu_join:
@@ -692,7 +524,7 @@ public final class EditContactActivity extends Activity implements View.OnClickL
      * Saves or creates the contact based on the mode, and if successful
      * finishes the activity.
      */
-    private boolean doSaveAction(int saveMode) {
+    boolean doSaveAction(int saveMode) {
         if (!hasValidState()) return false;
 
         final PersistTask task = new PersistTask(this, saveMode);
@@ -858,17 +690,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         return true;
     }
 
-    /**
-     * Clear any existing photo under the currently selected tab.
-     */
-    public boolean doRemovePhotoAction() {
-        if (!hasValidState()) return false;
-
-        // Remove photo from selected contact
-        mEditor.setPhotoBitmap(null);
-        return true;
-    }
-
     /** {@inheritDoc} */
     public void onDeleted(Editor editor) {
         // Ignore any editor deletes
@@ -884,7 +705,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
                 break;
             }
             case EditorListener.FIELD_CHANGED: {
-                bindHeader();
                 break;
             }
         }
@@ -976,8 +796,7 @@ public final class EditContactActivity extends Activity implements View.OnClickL
                     // Update the UI.
                     EditContactActivity target = mTarget.get();
                     if (target != null) {
-                        target.bindTabs();
-                        target.bindHeader();
+                        target.bindEditors();
                     }
                 }
             };
@@ -1061,8 +880,7 @@ public final class EditContactActivity extends Activity implements View.OnClickL
                 // Account was auto-selected on the background thread,
                 // but we need to update the UI still in the
                 // now-current UI thread.
-                target.bindTabs();
-                target.bindHeader();
+                target.bindEditors();
             }
         }
     }
@@ -1076,13 +894,14 @@ public final class EditContactActivity extends Activity implements View.OnClickL
         builder.setMessage(R.string.deleteConfirmation);
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface dialog, int which) {
-                // Mark the currently selected contact for deletion
-                final EntityDelta delta = getSelectedEntityDelta();
-                if (delta == null) return;
-                delta.markDeleted();
+                // Mark all raw contacts for deletion
+                for (EntityDelta delta : mState) {
+                    delta.markDeleted();
+                }
 
-                bindTabs();
-                bindHeader();
+                // Save the deletes
+                doSaveAction(SAVE_MODE_DEFAULT);
+                finish();
             }
         });
         builder.setNegativeButton(android.R.string.cancel, null);
@@ -1138,9 +957,6 @@ public final class EditContactActivity extends Activity implements View.OnClickL
                 final ValuesDelta structuredName = allNames.get(which);
                 structuredName.put(Data.IS_PRIMARY, 1);
                 structuredName.put(Data.IS_SUPER_PRIMARY, 1);
-
-                // Update header based on edited values
-                bindHeader();
             }
         };
 
index c723f6f..f89ee31 100644 (file)
@@ -35,12 +35,14 @@ import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.View.OnClickListener;
-import android.widget.RelativeLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 /**
@@ -54,7 +56,7 @@ import android.widget.TextView;
  * adding {@link Data} rows or changing {@link EditType}, are performed through
  * {@link EntityModifier} to ensure that {@link ContactsSource} are enforced.
  */
-public class ContactEditorView extends RelativeLayout implements OnClickListener {
+public class ContactEditorView extends LinearLayout implements OnClickListener {
     private LayoutInflater mInflater;
 
     private TextView mReadOnly;
@@ -72,6 +74,12 @@ public class ContactEditorView extends RelativeLayout implements OnClickListener
     private Drawable mSecondaryOpen;
     private Drawable mSecondaryClosed;
 
+    private View mHeader;
+    private View mSideBar;
+    private ImageView mHeaderIcon;
+    private TextView mHeaderAccountType;
+    private TextView mHeaderAccountName;
+    
     public ContactEditorView(Context context) {
         super(context);
     }
@@ -101,6 +109,12 @@ public class ContactEditorView extends RelativeLayout implements OnClickListener
         mGeneral = (ViewGroup)findViewById(R.id.sect_general);
         mSecondary = (ViewGroup)findViewById(R.id.sect_secondary);
 
+        mHeader = findViewById(R.id.header);
+        mSideBar = findViewById(R.id.color_bar);
+        mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
+        mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
+        mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
+        
         mSecondaryHeader = (TextView)findViewById(R.id.head_secondary);
         mSecondaryHeader.setOnClickListener(this);
 
@@ -170,6 +184,19 @@ public class ContactEditorView extends RelativeLayout implements OnClickListener
         // Make sure we have StructuredName
         EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
 
+        // Fill in the header info
+        mHeader.setBackgroundColor(source.getHeaderColor(mContext));
+        mSideBar.setBackgroundColor(source.getSideBarColor(mContext));
+        ValuesDelta values = state.getValues();
+        String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+        if (TextUtils.isEmpty(accountName)) {
+            // TODO get from resource
+            accountName = "Local contact";
+        }
+        mHeaderAccountName.setText(accountName);
+        mHeaderAccountType.setText(source.getDisplayLabel(mContext));
+        mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext));
+
         // Show photo editor when supported
         EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE);
         mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null);
@@ -177,8 +204,15 @@ public class ContactEditorView extends RelativeLayout implements OnClickListener
         mPhoto.setEnabled(!source.readOnly);
         mName.setEnabled(!source.readOnly);
 
-        mReadOnly.setVisibility(source.readOnly ? View.VISIBLE : View.GONE);
-
+        boolean readOnly = source.readOnly;
+        if (readOnly) {
+            mGeneral.setVisibility(View.GONE);
+            mSecondary.setVisibility(View.GONE);
+            mSecondaryHeader.setVisibility(View.GONE);
+        } else {
+            mReadOnly.setVisibility(View.GONE);
+        }
+    
         // Create editor sections for each possible data kind
         for (DataKind kind : source.getSortedDataKinds()) {
             // Skip kind of not editable
@@ -193,7 +227,7 @@ public class ContactEditorView extends RelativeLayout implements OnClickListener
                 // Handle special case editor for photos
                 final ValuesDelta primary = state.getPrimaryEntry(mimeType);
                 mPhoto.setValues(kind, primary, state, source.readOnly);
-            } else {
+            } else if (!readOnly) {
                 // Otherwise use generic section-based editors
                 if (kind.fieldList == null) continue;
                 final ViewGroup parent = kind.secondary ? mSecondary : mGeneral;
index 1de3936..5f7e0ef 100644 (file)
@@ -90,6 +90,16 @@ public class EntityModifierTests extends AndroidTestCase {
 
             addKind(kind);
         }
+
+        @Override
+        public int getHeaderColor(Context context) {
+            return 0;
+        }
+
+        @Override
+        public int getSideBarColor(Context context) {
+            return 0xffffff;
+        }
     }
 
     /**