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