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