Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.contacts.ui.widget;
     18 
     19 import com.android.contacts.R;
     20 import com.android.contacts.model.ContactsSource;
     21 import com.android.contacts.model.EntityDelta;
     22 import com.android.contacts.model.EntityModifier;
     23 import com.android.contacts.model.ContactsSource.DataKind;
     24 import com.android.contacts.model.ContactsSource.EditType;
     25 import com.android.contacts.model.Editor.EditorListener;
     26 import com.android.contacts.model.EntityDelta.ValuesDelta;
     27 import com.android.contacts.ui.ViewIdGenerator;
     28 
     29 import android.content.Context;
     30 import android.content.Entity;
     31 import android.content.res.Resources;
     32 import android.graphics.drawable.Drawable;
     33 import android.os.Parcel;
     34 import android.os.Parcelable;
     35 import android.provider.ContactsContract.Contacts;
     36 import android.provider.ContactsContract.Data;
     37 import android.provider.ContactsContract.RawContacts;
     38 import android.provider.ContactsContract.CommonDataKinds.Photo;
     39 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     40 import android.text.TextUtils;
     41 import android.util.AttributeSet;
     42 import android.view.LayoutInflater;
     43 import android.view.View;
     44 import android.view.ViewGroup;
     45 import android.view.View.OnClickListener;
     46 import android.widget.ImageView;
     47 import android.widget.TextView;
     48 
     49 /**
     50  * Custom view that provides all the editor interaction for a specific
     51  * {@link Contacts} represented through an {@link EntityDelta}. Callers can
     52  * reuse this view and quickly rebuild its contents through
     53  * {@link #setState(EntityDelta, ContactsSource)}.
     54  * <p>
     55  * Internal updates are performed against {@link ValuesDelta} so that the
     56  * source {@link Entity} can be swapped out. Any state-based changes, such as
     57  * adding {@link Data} rows or changing {@link EditType}, are performed through
     58  * {@link EntityModifier} to ensure that {@link ContactsSource} are enforced.
     59  */
     60 public class ContactEditorView extends BaseContactEditorView implements OnClickListener {
     61     private TextView mReadOnly;
     62     private TextView mReadOnlyName;
     63 
     64     private View mPhotoStub;
     65     private GenericEditorView mName;
     66 
     67     private boolean mIsSourceReadOnly;
     68     private ViewGroup mGeneral;
     69     private ViewGroup mSecondary;
     70     private boolean mSecondaryVisible;
     71 
     72     private TextView mSecondaryHeader;
     73 
     74     private Drawable mSecondaryOpen;
     75     private Drawable mSecondaryClosed;
     76 
     77     private View mHeaderColorBar;
     78     private View mSideBar;
     79     private ImageView mHeaderIcon;
     80     private TextView mHeaderAccountType;
     81     private TextView mHeaderAccountName;
     82 
     83     private long mRawContactId = -1;
     84 
     85     public ContactEditorView(Context context) {
     86         super(context);
     87     }
     88 
     89     public ContactEditorView(Context context, AttributeSet attrs) {
     90         super(context, attrs);
     91     }
     92 
     93     /** {@inheritDoc} */
     94     @Override
     95     protected void onFinishInflate() {
     96         super.onFinishInflate();
     97 
     98         mInflater = (LayoutInflater)getContext().getSystemService(
     99                 Context.LAYOUT_INFLATER_SERVICE);
    100 
    101         mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
    102         mPhotoStub = findViewById(R.id.stub_photo);
    103 
    104         final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size);
    105 
    106         mReadOnly = (TextView)findViewById(R.id.edit_read_only);
    107 
    108         mName = (GenericEditorView)findViewById(R.id.edit_name);
    109         mName.setMinimumHeight(photoSize);
    110         mName.setDeletable(false);
    111 
    112         mReadOnlyName = (TextView) findViewById(R.id.read_only_name);
    113 
    114         mGeneral = (ViewGroup)findViewById(R.id.sect_general);
    115         mSecondary = (ViewGroup)findViewById(R.id.sect_secondary);
    116 
    117         mHeaderColorBar = findViewById(R.id.header_color_bar);
    118         mSideBar = findViewById(R.id.color_bar);
    119         mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
    120         mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
    121         mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
    122 
    123         mSecondaryHeader = (TextView)findViewById(R.id.head_secondary);
    124         mSecondaryHeader.setOnClickListener(this);
    125 
    126         final Resources res = getResources();
    127         mSecondaryOpen = res.getDrawable(com.android.internal.R.drawable.expander_ic_maximized);
    128         mSecondaryClosed = res.getDrawable(com.android.internal.R.drawable.expander_ic_minimized);
    129 
    130         this.setSecondaryVisible(false);
    131     }
    132 
    133     /** {@inheritDoc} */
    134     public void onClick(View v) {
    135         // Toggle visibility of secondary kinds
    136         final boolean makeVisible = mSecondary.getVisibility() != View.VISIBLE;
    137         this.setSecondaryVisible(makeVisible);
    138     }
    139 
    140     /**
    141      * Set the visibility of secondary sections, along with header icon.
    142      *
    143      * <p>If the source is read-only and there's no secondary fields, the entire secondary section
    144      * will be hidden.
    145      */
    146     private void setSecondaryVisible(boolean makeVisible) {
    147         mSecondaryVisible = makeVisible;
    148 
    149         if (!mIsSourceReadOnly && mSecondary.getChildCount() > 0) {
    150             mSecondaryHeader.setVisibility(View.VISIBLE);
    151             mSecondaryHeader.setCompoundDrawablesWithIntrinsicBounds(
    152                     makeVisible ? mSecondaryOpen : mSecondaryClosed, null, null, null);
    153             mSecondary.setVisibility(makeVisible ? View.VISIBLE : View.GONE);
    154         } else {
    155             mSecondaryHeader.setVisibility(View.GONE);
    156             mSecondary.setVisibility(View.GONE);
    157         }
    158     }
    159 
    160     /**
    161      * Set the internal state for this view, given a current
    162      * {@link EntityDelta} state and the {@link ContactsSource} that
    163      * apply to that state.
    164      */
    165     @Override
    166     public void setState(EntityDelta state, ContactsSource source, ViewIdGenerator vig) {
    167         // Remove any existing sections
    168         mGeneral.removeAllViews();
    169         mSecondary.removeAllViews();
    170 
    171         // Bail if invalid state or source
    172         if (state == null || source == null) return;
    173 
    174         setId(vig.getId(state, null, null, ViewIdGenerator.NO_VIEW_INDEX));
    175 
    176         mIsSourceReadOnly = source.readOnly;
    177 
    178         // Make sure we have StructuredName
    179         EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
    180 
    181         // Fill in the header info
    182         ValuesDelta values = state.getValues();
    183         String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
    184         CharSequence accountType = source.getDisplayLabel(mContext);
    185         if (TextUtils.isEmpty(accountType)) {
    186             accountType = mContext.getString(R.string.account_phone);
    187         }
    188         if (!TextUtils.isEmpty(accountName)) {
    189             mHeaderAccountName.setText(
    190                     mContext.getString(R.string.from_account_format, accountName));
    191         }
    192         mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
    193         mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext));
    194 
    195         mRawContactId = values.getAsLong(RawContacts._ID);
    196 
    197         // Show photo editor when supported
    198         EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE);
    199         mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null);
    200         mPhoto.setVisibility(mHasPhotoEditor ? View.VISIBLE : View.GONE);
    201         mPhoto.setEnabled(!mIsSourceReadOnly);
    202         mName.setEnabled(!mIsSourceReadOnly);
    203 
    204         // Show and hide the appropriate views
    205         if (mIsSourceReadOnly) {
    206             mGeneral.setVisibility(View.GONE);
    207             mName.setVisibility(View.GONE);
    208             mReadOnly.setVisibility(View.VISIBLE);
    209             mReadOnly.setText(mContext.getString(R.string.contact_read_only, accountType));
    210             mReadOnlyName.setVisibility(View.VISIBLE);
    211         } else {
    212             mGeneral.setVisibility(View.VISIBLE);
    213             mName.setVisibility(View.VISIBLE);
    214             mReadOnly.setVisibility(View.GONE);
    215             mReadOnlyName.setVisibility(View.GONE);
    216         }
    217 
    218         boolean anySecondaryFieldFilled = false;
    219         // Create editor sections for each possible data kind
    220         for (DataKind kind : source.getSortedDataKinds()) {
    221             // Skip kind of not editable
    222             if (!kind.editable) continue;
    223 
    224             final String mimeType = kind.mimeType;
    225             if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
    226                 // Handle special case editor for structured name
    227                 final ValuesDelta primary = state.getPrimaryEntry(mimeType);
    228                 if (!mIsSourceReadOnly) {
    229                     mName.setValues(kind, primary, state, mIsSourceReadOnly, vig);
    230                 } else {
    231                     String displayName = primary.getAsString(StructuredName.DISPLAY_NAME);
    232                     mReadOnlyName.setText(displayName);
    233                 }
    234             } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
    235                 // Handle special case editor for photos
    236                 final ValuesDelta primary = state.getPrimaryEntry(mimeType);
    237                 mPhoto.setValues(kind, primary, state, mIsSourceReadOnly, vig);
    238                 if (mIsSourceReadOnly && !mPhoto.hasSetPhoto()) {
    239                     mPhotoStub.setVisibility(View.GONE);
    240                 } else {
    241                     mPhotoStub.setVisibility(View.VISIBLE);
    242                 }
    243             } else if (!mIsSourceReadOnly) {
    244                 // Otherwise use generic section-based editors
    245                 if (kind.fieldList == null) continue;
    246                 final ViewGroup parent = kind.secondary ? mSecondary : mGeneral;
    247                 final KindSectionView section = (KindSectionView)mInflater.inflate(
    248                         R.layout.item_kind_section, parent, false);
    249                 section.setState(kind, state, mIsSourceReadOnly, vig);
    250                 if (kind.secondary && section.isAnyEditorFilledOut()) {
    251                     anySecondaryFieldFilled = true;
    252                 }
    253                 parent.addView(section);
    254             }
    255         }
    256 
    257         setSecondaryVisible(anySecondaryFieldFilled);
    258     }
    259 
    260     /**
    261      * Sets the {@link EditorListener} on the name field
    262      */
    263     @Override
    264     public void setNameEditorListener(EditorListener listener) {
    265         mName.setEditorListener(listener);
    266     }
    267 
    268     @Override
    269     public long getRawContactId() {
    270         return mRawContactId;
    271     }
    272 
    273     private static class SavedState extends BaseSavedState {
    274         public boolean mSecondaryVisible;
    275 
    276         SavedState(Parcelable superState) {
    277             super(superState);
    278         }
    279 
    280         private SavedState(Parcel in) {
    281             super(in);
    282             mSecondaryVisible = (in.readInt() == 0 ? false : true);
    283         }
    284 
    285         @Override
    286         public void writeToParcel(Parcel out, int flags) {
    287             super.writeToParcel(out, flags);
    288             out.writeInt(mSecondaryVisible ? 1 : 0);
    289         }
    290 
    291         public static final Parcelable.Creator<SavedState> CREATOR
    292                 = new Parcelable.Creator<SavedState>() {
    293             public SavedState createFromParcel(Parcel in) {
    294                 return new SavedState(in);
    295             }
    296 
    297             public SavedState[] newArray(int size) {
    298                 return new SavedState[size];
    299             }
    300         };
    301     }
    302 
    303     /**
    304      * Saves the visibility of the secondary field.
    305      */
    306     @Override
    307     protected Parcelable onSaveInstanceState() {
    308         Parcelable superState = super.onSaveInstanceState();
    309         SavedState ss = new SavedState(superState);
    310 
    311         ss.mSecondaryVisible = mSecondaryVisible;
    312         return ss;
    313     }
    314 
    315     /**
    316      * Restores the visibility of the secondary field.
    317      */
    318     @Override
    319     protected void onRestoreInstanceState(Parcelable state) {
    320         SavedState ss = (SavedState) state;
    321         super.onRestoreInstanceState(ss.getSuperState());
    322 
    323         setSecondaryVisible(ss.mSecondaryVisible);
    324     }
    325 }
    326