Home | History | Annotate | Download | only in editor
      1 /*
      2  * Copyright (C) 2010 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.editor;
     18 
     19 import com.android.contacts.R;
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     25 import android.text.TextUtils;
     26 import android.util.AttributeSet;
     27 import android.view.View;
     28 import android.widget.ImageView;
     29 import android.widget.LinearLayout;
     30 import android.widget.TextView;
     31 
     32 import com.android.contacts.common.model.RawContactDelta;
     33 import com.android.contacts.common.model.ValuesDelta;
     34 import com.android.contacts.common.model.account.AccountType;
     35 import com.android.contacts.common.model.dataitem.DataItem;
     36 import com.android.contacts.common.model.dataitem.DataKind;
     37 import com.android.contacts.common.util.NameConverter;
     38 import com.android.contacts.common.model.dataitem.StructuredNameDataItem;
     39 
     40 import java.util.HashMap;
     41 import java.util.Map;
     42 
     43 /**
     44  * A dedicated editor for structured name.  When the user collapses/expands
     45  * the structured name, it will reparse or recompose the name, but only
     46  * if the user has made changes.  This distinction will be particularly
     47  * obvious if the name has a non-standard structure. Consider this structure:
     48  * first name="John Doe", family name="".  As long as the user does not change
     49  * the full name, expand and collapse will preserve this.  However, if the user
     50  * changes "John Doe" to "Jane Doe" and then expands the view, we will reparse
     51  * and show first name="Jane", family name="Doe".
     52  */
     53 public class StructuredNameEditorView extends TextFieldsEditorView {
     54 
     55     private StructuredNameDataItem mSnapshot;
     56     private boolean mChanged;
     57 
     58     public StructuredNameEditorView(Context context) {
     59         super(context);
     60     }
     61 
     62     public StructuredNameEditorView(Context context, AttributeSet attrs) {
     63         super(context, attrs);
     64     }
     65 
     66     public StructuredNameEditorView(Context context, AttributeSet attrs, int defStyle) {
     67         super(context, attrs, defStyle);
     68     }
     69 
     70     @Override
     71     public void setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly,
     72             ViewIdGenerator vig) {
     73         super.setValues(kind, entry, state, readOnly, vig);
     74         if (mSnapshot == null) {
     75             mSnapshot = (StructuredNameDataItem) DataItem.createFrom(
     76                     new ContentValues(getValues().getCompleteValues()));
     77             mChanged = entry.isInsert();
     78         } else {
     79             mChanged = false;
     80         }
     81         updateEmptiness();
     82     }
     83 
     84     /**
     85      * Displays the icon and name for the given account under the name name input fields.
     86      */
     87     public void setAccountType(AccountType accountType) {
     88         final LinearLayout layout = (LinearLayout) findViewById(R.id.account_type);
     89         layout.setVisibility(View.VISIBLE);
     90         final ImageView imageView = (ImageView) layout.findViewById(R.id.account_type_icon);
     91         imageView.setImageDrawable(accountType.getDisplayIcon(getContext()));
     92         final TextView textView = (TextView) layout.findViewById(R.id.account_type_name);
     93         textView.setText(accountType.getDisplayLabel(getContext()));
     94     }
     95 
     96     @Override
     97     public void onFieldChanged(String column, String value) {
     98         if (!isFieldChanged(column, value)) {
     99             return;
    100         }
    101 
    102         // First save the new value for the column.
    103         saveValue(column, value);
    104         mChanged = true;
    105 
    106         // Next make sure the display name and the structured name are synced
    107         if (hasShortAndLongForms()) {
    108             if (areOptionalFieldsVisible()) {
    109                 rebuildFullName(getValues());
    110             } else {
    111                 rebuildStructuredName(getValues());
    112             }
    113         }
    114 
    115         // Then notify the listener, which will rely on the display and structured names to be
    116         // synced (in order to provide aggregate suggestions).
    117         notifyEditorListener();
    118     }
    119 
    120     @Override
    121     protected void onOptionalFieldVisibilityChange() {
    122         if (hasShortAndLongForms()) {
    123             if (areOptionalFieldsVisible()) {
    124                 switchFromFullNameToStructuredName();
    125             } else {
    126                 switchFromStructuredNameToFullName();
    127             }
    128         }
    129 
    130         super.onOptionalFieldVisibilityChange();
    131     }
    132 
    133     private void switchFromFullNameToStructuredName() {
    134         ValuesDelta values = getValues();
    135 
    136         if (!mChanged) {
    137             for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
    138                 values.put(field, mSnapshot.getContentValues().getAsString(field));
    139             }
    140             return;
    141         }
    142 
    143         String displayName = values.getDisplayName();
    144         Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
    145                 getContext(), displayName);
    146         if (!structuredNameMap.isEmpty()) {
    147             eraseFullName(values);
    148             for (String field : structuredNameMap.keySet()) {
    149                 values.put(field, structuredNameMap.get(field));
    150             }
    151         }
    152 
    153         mSnapshot.getContentValues().clear();
    154         mSnapshot.getContentValues().putAll(values.getCompleteValues());
    155         mSnapshot.setDisplayName(displayName);
    156     }
    157 
    158     private void switchFromStructuredNameToFullName() {
    159         ValuesDelta values = getValues();
    160 
    161         if (!mChanged) {
    162             values.setDisplayName(mSnapshot.getDisplayName());
    163             return;
    164         }
    165 
    166         Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
    167         String displayName = NameConverter.structuredNameToDisplayName(getContext(),
    168                 structuredNameMap);
    169         if (!TextUtils.isEmpty(displayName)) {
    170             eraseStructuredName(values);
    171             values.put(StructuredName.DISPLAY_NAME, displayName);
    172         }
    173 
    174         mSnapshot.getContentValues().clear();
    175         mSnapshot.setDisplayName(values.getDisplayName());
    176         mSnapshot.setMimeType(StructuredName.CONTENT_ITEM_TYPE);
    177         for (String field : structuredNameMap.keySet()) {
    178             mSnapshot.getContentValues().put(field, structuredNameMap.get(field));
    179         }
    180     }
    181 
    182     private Map<String, String> valuesToStructuredNameMap(ValuesDelta values) {
    183         Map<String, String> structuredNameMap = new HashMap<String, String>();
    184         for (String key : NameConverter.STRUCTURED_NAME_FIELDS) {
    185             structuredNameMap.put(key, values.getAsString(key));
    186         }
    187         return structuredNameMap;
    188     }
    189 
    190     private void eraseFullName(ValuesDelta values) {
    191         values.setDisplayName(null);
    192     }
    193 
    194     private void rebuildFullName(ValuesDelta values) {
    195         Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
    196         String displayName = NameConverter.structuredNameToDisplayName(getContext(),
    197                 structuredNameMap);
    198         values.setDisplayName(displayName);
    199     }
    200 
    201     private void eraseStructuredName(ValuesDelta values) {
    202         for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
    203             values.putNull(field);
    204         }
    205     }
    206 
    207     private void rebuildStructuredName(ValuesDelta values) {
    208         String displayName = values.getDisplayName();
    209         Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
    210                 getContext(), displayName);
    211         for (String field : structuredNameMap.keySet()) {
    212             values.put(field, structuredNameMap.get(field));
    213         }
    214     }
    215 
    216     /**
    217      * Set the display name onto the text field directly.  This does not affect the underlying
    218      * data structure so it is similar to the user typing the value in on the field directly.
    219      *
    220      * @param name The name to set on the text field.
    221      */
    222     public void setDisplayName(String name) {
    223         // For now, assume the first text field is the name.
    224         // TODO: Find a better way to get a hold of the name field,
    225         // including given_name and family_name.
    226         super.setValue(0, name);
    227         getValues().setDisplayName(name);
    228         rebuildStructuredName(getValues());
    229         super.setValue(1, getValues().getAsString(StructuredName.GIVEN_NAME));
    230         super.setValue(3, getValues().getAsString(StructuredName.FAMILY_NAME));
    231     }
    232 
    233     /**
    234      * Returns the display name currently displayed in the editor.
    235      */
    236     public String getDisplayName() {
    237         final ValuesDelta valuesDelta = getValues();
    238         rebuildFullName(valuesDelta);
    239         if (hasShortAndLongForms() && areOptionalFieldsVisible()) {
    240             final Map<String, String> structuredNameMap = valuesToStructuredNameMap(valuesDelta);
    241             final String displayName = NameConverter.structuredNameToDisplayName(
    242                     getContext(), structuredNameMap);
    243             if (!TextUtils.isEmpty(displayName)) {
    244                 return displayName;
    245             }
    246         }
    247         return valuesDelta.getDisplayName();
    248     }
    249 
    250     @Override
    251     protected Parcelable onSaveInstanceState() {
    252         SavedState state = new SavedState(super.onSaveInstanceState());
    253         state.mChanged = mChanged;
    254         state.mSnapshot = mSnapshot.getContentValues();
    255         return state;
    256     }
    257 
    258     @Override
    259     protected void onRestoreInstanceState(Parcelable state) {
    260         SavedState ss = (SavedState) state;
    261         super.onRestoreInstanceState(ss.mSuperState);
    262 
    263         mChanged = ss.mChanged;
    264         mSnapshot = (StructuredNameDataItem) DataItem.createFrom(ss.mSnapshot);
    265     }
    266 
    267     private static class SavedState implements Parcelable {
    268         public boolean mChanged;
    269         public ContentValues mSnapshot;
    270         public Parcelable mSuperState;
    271 
    272         SavedState(Parcelable superState) {
    273             mSuperState = superState;
    274         }
    275 
    276         private SavedState(Parcel in) {
    277             ClassLoader loader = getClass().getClassLoader();
    278             mSuperState = in.readParcelable(loader);
    279 
    280             mChanged = in.readInt() != 0;
    281             mSnapshot = in.readParcelable(loader);
    282         }
    283 
    284         @Override
    285         public void writeToParcel(Parcel out, int flags) {
    286             out.writeParcelable(mSuperState, 0);
    287 
    288             out.writeInt(mChanged ? 1 : 0);
    289             out.writeParcelable(mSnapshot, 0);
    290         }
    291 
    292         @SuppressWarnings({"unused"})
    293         public static final Parcelable.Creator<SavedState> CREATOR
    294                 = new Parcelable.Creator<SavedState>() {
    295             @Override
    296             public SavedState createFromParcel(Parcel in) {
    297                 return new SavedState(in);
    298             }
    299 
    300             @Override
    301             public SavedState[] newArray(int size) {
    302                 return new SavedState[size];
    303             }
    304         };
    305 
    306         @Override
    307         public int describeContents() {
    308             return 0;
    309         }
    310     }
    311 }
    312