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.model.DataKind;
     20 import com.android.contacts.model.EntityDelta;
     21 import com.android.contacts.model.EntityDelta.ValuesDelta;
     22 import com.android.contacts.util.NameConverter;
     23 
     24 import android.content.ContentValues;
     25 import android.content.Context;
     26 import android.net.Uri;
     27 import android.os.Parcel;
     28 import android.os.Parcelable;
     29 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     30 import android.text.TextUtils;
     31 import android.util.AttributeSet;
     32 
     33 import java.util.HashMap;
     34 import java.util.Map;
     35 
     36 /**
     37  * A dedicated editor for structured name.  When the user collapses/expands
     38  * the structured name, it will reparse or recompose the name, but only
     39  * if the user has made changes.  This distinction will be particularly
     40  * obvious if the name has a non-standard structure. Consider this structure:
     41  * first name="John Doe", family name="".  As long as the user does not change
     42  * the full name, expand and collapse will preserve this.  However, if the user
     43  * changes "John Doe" to "Jane Doe" and then expands the view, we will reparse
     44  * and show first name="Jane", family name="Doe".
     45  */
     46 public class StructuredNameEditorView extends TextFieldsEditorView {
     47 
     48     private ContentValues mSnapshot;
     49     private boolean mChanged;
     50 
     51     public StructuredNameEditorView(Context context) {
     52         super(context);
     53     }
     54 
     55     public StructuredNameEditorView(Context context, AttributeSet attrs) {
     56         super(context, attrs);
     57     }
     58 
     59     public StructuredNameEditorView(Context context, AttributeSet attrs, int defStyle) {
     60         super(context, attrs, defStyle);
     61     }
     62 
     63     @Override
     64     public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
     65             ViewIdGenerator vig) {
     66         super.setValues(kind, entry, state, readOnly, vig);
     67         if (mSnapshot == null) {
     68             mSnapshot = new ContentValues(getValues().getCompleteValues());
     69             mChanged = entry.isInsert();
     70         } else {
     71             mChanged = false;
     72         }
     73     }
     74 
     75     @Override
     76     public void onFieldChanged(String column, String value) {
     77         if (!isFieldChanged(column, value)) {
     78             return;
     79         }
     80 
     81         // First save the new value for the column.
     82         saveValue(column, value);
     83         mChanged = true;
     84 
     85         // Next make sure the display name and the structured name are synced
     86         if (hasShortAndLongForms()) {
     87             if (areOptionalFieldsVisible()) {
     88                 rebuildFullName(getValues());
     89             } else {
     90                 rebuildStructuredName(getValues());
     91             }
     92         }
     93 
     94         // Then notify the listener, which will rely on the display and structured names to be
     95         // synced (in order to provide aggregate suggestions).
     96         notifyEditorListener();
     97     }
     98 
     99     @Override
    100     protected void onOptionalFieldVisibilityChange() {
    101         if (hasShortAndLongForms()) {
    102             if (areOptionalFieldsVisible()) {
    103                 switchFromFullNameToStructuredName();
    104             } else {
    105                 switchFromStructuredNameToFullName();
    106             }
    107         }
    108 
    109         super.onOptionalFieldVisibilityChange();
    110     }
    111 
    112     private void switchFromFullNameToStructuredName() {
    113         ValuesDelta values = getValues();
    114 
    115         if (!mChanged) {
    116             for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
    117                 values.put(field, mSnapshot.getAsString(field));
    118             }
    119             return;
    120         }
    121 
    122         String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
    123         Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
    124                 getContext(), displayName);
    125         if (!structuredNameMap.isEmpty()) {
    126             eraseFullName(values);
    127             for (String field : structuredNameMap.keySet()) {
    128                 values.put(field, structuredNameMap.get(field));
    129             }
    130         }
    131 
    132         mSnapshot.clear();
    133         mSnapshot.putAll(values.getCompleteValues());
    134         mSnapshot.put(StructuredName.DISPLAY_NAME, displayName);
    135     }
    136 
    137     private void switchFromStructuredNameToFullName() {
    138         ValuesDelta values = getValues();
    139 
    140         if (!mChanged) {
    141             values.put(StructuredName.DISPLAY_NAME,
    142                     mSnapshot.getAsString(StructuredName.DISPLAY_NAME));
    143             return;
    144         }
    145 
    146         Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
    147         String displayName = NameConverter.structuredNameToDisplayName(getContext(),
    148                 structuredNameMap);
    149         if (!TextUtils.isEmpty(displayName)) {
    150             eraseStructuredName(values);
    151             values.put(StructuredName.DISPLAY_NAME, displayName);
    152         }
    153 
    154         mSnapshot.clear();
    155         mSnapshot.put(StructuredName.DISPLAY_NAME, values.getAsString(StructuredName.DISPLAY_NAME));
    156         for (String field : structuredNameMap.keySet()) {
    157             mSnapshot.put(field, structuredNameMap.get(field));
    158         }
    159     }
    160 
    161     private Map<String, String> valuesToStructuredNameMap(ValuesDelta values) {
    162         Map<String, String> structuredNameMap = new HashMap<String, String>();
    163         for (String key : NameConverter.STRUCTURED_NAME_FIELDS) {
    164             structuredNameMap.put(key, values.getAsString(key));
    165         }
    166         return structuredNameMap;
    167     }
    168 
    169     private void eraseFullName(ValuesDelta values) {
    170         values.putNull(StructuredName.DISPLAY_NAME);
    171     }
    172 
    173     private void rebuildFullName(ValuesDelta values) {
    174         Map<String, String> structuredNameMap = valuesToStructuredNameMap(values);
    175         String displayName = NameConverter.structuredNameToDisplayName(getContext(),
    176                 structuredNameMap);
    177         values.put(StructuredName.DISPLAY_NAME, displayName);
    178     }
    179 
    180     private void eraseStructuredName(ValuesDelta values) {
    181         for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
    182             values.putNull(field);
    183         }
    184     }
    185 
    186     private void rebuildStructuredName(ValuesDelta values) {
    187         String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
    188         Map<String, String> structuredNameMap = NameConverter.displayNameToStructuredName(
    189                 getContext(), displayName);
    190         for (String field : structuredNameMap.keySet()) {
    191             values.put(field, structuredNameMap.get(field));
    192         }
    193     }
    194 
    195     private static void appendQueryParameter(Uri.Builder builder, String field, String value) {
    196         if (!TextUtils.isEmpty(value)) {
    197             builder.appendQueryParameter(field, value);
    198         }
    199     }
    200 
    201     @Override
    202     protected Parcelable onSaveInstanceState() {
    203         SavedState state = new SavedState(super.onSaveInstanceState());
    204         state.mChanged = mChanged;
    205         state.mSnapshot = mSnapshot;
    206         return state;
    207     }
    208 
    209     @Override
    210     protected void onRestoreInstanceState(Parcelable state) {
    211         SavedState ss = (SavedState) state;
    212         super.onRestoreInstanceState(ss.mSuperState);
    213 
    214         mChanged = ss.mChanged;
    215         mSnapshot = ss.mSnapshot;
    216     }
    217 
    218     private static class SavedState implements Parcelable {
    219         public boolean mChanged;
    220         public ContentValues mSnapshot;
    221         public Parcelable mSuperState;
    222 
    223         SavedState(Parcelable superState) {
    224             mSuperState = superState;
    225         }
    226 
    227         private SavedState(Parcel in) {
    228             ClassLoader loader = getClass().getClassLoader();
    229             mSuperState = in.readParcelable(loader);
    230 
    231             mChanged = in.readInt() != 0;
    232             mSnapshot = in.readParcelable(loader);
    233         }
    234 
    235         @Override
    236         public void writeToParcel(Parcel out, int flags) {
    237             out.writeParcelable(mSuperState, 0);
    238 
    239             out.writeInt(mChanged ? 1 : 0);
    240             out.writeParcelable(mSnapshot, 0);
    241         }
    242 
    243         @SuppressWarnings({"unused"})
    244         public static final Parcelable.Creator<SavedState> CREATOR
    245                 = new Parcelable.Creator<SavedState>() {
    246             @Override
    247             public SavedState createFromParcel(Parcel in) {
    248                 return new SavedState(in);
    249             }
    250 
    251             @Override
    252             public SavedState[] newArray(int size) {
    253                 return new SavedState[size];
    254             }
    255         };
    256 
    257         @Override
    258         public int describeContents() {
    259             return 0;
    260         }
    261     }
    262 }
    263