blob: 5996fa6b62a3487e7f862129bc37c0fd56362df2 [file] [log] [blame]
Daniel Lehmann392ccec2010-11-01 20:50:03 -07001/*
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
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080017package com.android.contacts.editor;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070018
Daniel Lehmann392ccec2010-11-01 20:50:03 -070019import android.app.AlertDialog;
20import android.app.Dialog;
21import android.content.Context;
22import android.content.DialogInterface;
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -070023import android.content.DialogInterface.OnShowListener;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070024import android.os.Bundle;
Daniel Lehmann79c9c5a2010-11-11 16:05:27 -080025import android.os.Handler;
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -070026import android.text.Editable;
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -080027import android.text.TextUtils;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070028import android.text.TextWatcher;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070029import android.util.AttributeSet;
Brian Attwell043fba62014-10-30 11:11:56 -070030import android.util.TypedValue;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080031import android.view.LayoutInflater;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070032import android.view.View;
33import android.view.ViewGroup;
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -070034import android.view.WindowManager;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070035import android.view.inputmethod.EditorInfo;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080036import android.widget.AdapterView;
37import android.widget.AdapterView.OnItemSelectedListener;
38import android.widget.ArrayAdapter;
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -070039import android.widget.Button;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070040import android.widget.EditText;
Katherine Kuan12a93632011-05-05 13:38:01 -070041import android.widget.ImageView;
Katherine Kuan63ffb902011-04-26 19:53:40 -070042import android.widget.LinearLayout;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080043import android.widget.Spinner;
44import android.widget.TextView;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070045
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070046import com.android.contacts.R;
Yorke Leecd321f62013-10-28 15:20:15 -070047import com.android.contacts.common.model.RawContactDelta;
48import com.android.contacts.common.ContactsUtils;
Chiao Cheng738ff862012-11-30 12:06:03 -080049import com.android.contacts.common.model.ValuesDelta;
Yorke Leecd321f62013-10-28 15:20:15 -070050import com.android.contacts.common.model.RawContactModifier;
Chiao Cheng428f0082012-11-13 18:38:56 -080051import com.android.contacts.common.model.account.AccountType.EditType;
52import com.android.contacts.common.model.dataitem.DataKind;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070053import com.android.contacts.util.DialogManager;
54import com.android.contacts.util.DialogManager.DialogShowingView;
55
Dmitri Plotnikov14918c02010-12-07 17:20:27 -080056import java.util.List;
57
Daniel Lehmann392ccec2010-11-01 20:50:03 -070058/**
Katherine Kuan63ffb902011-04-26 19:53:40 -070059 * Base class for editors that handles labels and values. Uses
Maurice Chu851222a2012-06-21 11:43:08 -070060 * {@link ValuesDelta} to read any existing {@link RawContact} values, and to
Katherine Kuan63ffb902011-04-26 19:53:40 -070061 * correctly write any changes values.
Daniel Lehmann392ccec2010-11-01 20:50:03 -070062 */
Katherine Kuan63ffb902011-04-26 19:53:40 -070063public abstract class LabeledEditorView extends LinearLayout implements Editor, DialogShowingView {
Daniel Lehmann392ccec2010-11-01 20:50:03 -070064 protected static final String DIALOG_ID_KEY = "dialog_id";
65 private static final int DIALOG_ID_CUSTOM = 1;
66
67 private static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
68 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
69
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080070 private Spinner mLabel;
71 private EditTypeAdapter mEditTypeAdapter;
Katherine Kuan63ffb902011-04-26 19:53:40 -070072 private View mDeleteContainer;
Katherine Kuan12a93632011-05-05 13:38:01 -070073 private ImageView mDelete;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070074
75 private DataKind mKind;
76 private ValuesDelta mEntry;
Maurice Chu851222a2012-06-21 11:43:08 -070077 private RawContactDelta mState;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070078 private boolean mReadOnly;
Katherine Kuan25914362011-04-29 19:42:01 -070079 private boolean mWasEmpty = true;
Katherine Kuan5e330ed2011-07-15 15:06:06 -070080 private boolean mIsDeletable = true;
Katherine Kuan2293e552011-07-21 20:25:44 -070081 private boolean mIsAttachedToWindow;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070082
83 private EditType mType;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070084
85 private ViewIdGenerator mViewIdGenerator;
86 private DialogManager mDialogManager = null;
87 private EditorListener mListener;
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -080088 protected int mMinLineItemHeight;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070089
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080090 /**
91 * A marker in the spinner adapter of the currently selected custom type.
92 */
93 public static final EditType CUSTOM_SELECTION = new EditType(0, 0);
94
95 private OnItemSelectedListener mSpinnerListener = new OnItemSelectedListener() {
96
97 @Override
98 public void onItemSelected(
99 AdapterView<?> parent, View view, int position, long id) {
100 onTypeSelectionChange(position);
101 }
102
103 @Override
104 public void onNothingSelected(AdapterView<?> parent) {
105 }
106 };
107
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700108 public LabeledEditorView(Context context) {
109 super(context);
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800110 init(context);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700111 }
112
113 public LabeledEditorView(Context context, AttributeSet attrs) {
114 super(context, attrs);
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800115 init(context);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700116 }
117
118 public LabeledEditorView(Context context, AttributeSet attrs, int defStyle) {
119 super(context, attrs, defStyle);
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800120 init(context);
121 }
122
Walter Jangd35e5ef2015-02-24 09:18:16 -0800123 public Long getRawContactId() {
124 return mState == null ? null : mState.getRawContactId();
125 }
126
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800127 private void init(Context context) {
128 mMinLineItemHeight = context.getResources().getDimensionPixelSize(
129 R.dimen.editor_min_line_item_height);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700130 }
131
Katherine Kuan63ffb902011-04-26 19:53:40 -0700132 /** {@inheritDoc} */
133 @Override
134 protected void onFinishInflate() {
135
Katherine Kuan63ffb902011-04-26 19:53:40 -0700136 mLabel = (Spinner) findViewById(R.id.spinner);
Daniel Lehmann67713b32012-02-21 21:57:09 -0800137 // Turn off the Spinner's own state management. We do this ourselves on rotation
138 mLabel.setId(View.NO_ID);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700139 mLabel.setOnItemSelectedListener(mSpinnerListener);
140
Katherine Kuan12a93632011-05-05 13:38:01 -0700141 mDelete = (ImageView) findViewById(R.id.delete_button);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700142 mDeleteContainer = findViewById(R.id.delete_button_container);
Katherine Kuan12a93632011-05-05 13:38:01 -0700143 mDeleteContainer.setOnClickListener(new OnClickListener() {
Katherine Kuan63ffb902011-04-26 19:53:40 -0700144 @Override
145 public void onClick(View v) {
146 // defer removal of this button so that the pressed state is visible shortly
147 new Handler().post(new Runnable() {
148 @Override
149 public void run() {
Katherine Kuan2293e552011-07-21 20:25:44 -0700150 // Don't do anything if the view is no longer attached to the window
151 // (This check is needed because when this {@link Runnable} is executed,
152 // we can't guarantee the view is still valid.
153 if (!mIsAttachedToWindow) {
154 return;
155 }
156 // Send the delete request to the listener (which will in turn call
157 // deleteEditor() on this view if the deletion is valid - i.e. this is not
158 // the last {@link Editor} in the section).
Katherine Kuan63ffb902011-04-26 19:53:40 -0700159 if (mListener != null) {
Katherine Kuan2293e552011-07-21 20:25:44 -0700160 mListener.onDeleteRequested(LabeledEditorView.this);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700161 }
162 }
163 });
164 }
165 });
Brian Attwell043fba62014-10-30 11:11:56 -0700166
167 setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
168 (int) getResources().getDimension(R.dimen.editor_padding_between_editor_views));
Katherine Kuan63ffb902011-04-26 19:53:40 -0700169 }
170
Katherine Kuan2293e552011-07-21 20:25:44 -0700171 @Override
172 protected void onAttachedToWindow() {
173 super.onAttachedToWindow();
174 // Keep track of when the view is attached or detached from the window, so we know it's
175 // safe to remove views (in case the user requests to delete this editor).
176 mIsAttachedToWindow = true;
177 }
178
179 @Override
180 protected void onDetachedFromWindow() {
181 super.onDetachedFromWindow();
182 mIsAttachedToWindow = false;
183 }
184
185 @Override
186 public void deleteEditor() {
187 // Keep around in model, but mark as deleted
188 mEntry.markDeleted();
189
190 // Remove the view
Daniel Lehmannca87e9c2012-03-06 14:02:31 -0800191 EditorAnimator.getInstance().removeEditorView(this);
Katherine Kuan2293e552011-07-21 20:25:44 -0700192 }
193
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800194 public boolean isReadOnly() {
195 return mReadOnly;
196 }
197
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800198 public int getBaseline(int row) {
199 if (row == 0 && mLabel != null) {
200 return mLabel.getBaseline();
201 }
202 return -1;
203 }
204
205 /**
Katherine Kuana94859f2011-07-20 13:38:27 -0700206 * Configures the visibility of the type label button and enables or disables it properly.
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700207 */
208 private void setupLabelButton(boolean shouldExist) {
Katherine Kuan63ffb902011-04-26 19:53:40 -0700209 if (shouldExist) {
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800210 mLabel.setEnabled(!mReadOnly && isEnabled());
Katherine Kuan63ffb902011-04-26 19:53:40 -0700211 mLabel.setVisibility(View.VISIBLE);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700212 } else {
213 mLabel.setVisibility(View.GONE);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700214 }
215 }
216
217 /**
Katherine Kuana94859f2011-07-20 13:38:27 -0700218 * Configures the visibility of the "delete" button and enables or disables it properly.
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700219 */
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700220 private void setupDeleteButton() {
221 if (mIsDeletable) {
Katherine Kuan63ffb902011-04-26 19:53:40 -0700222 mDeleteContainer.setVisibility(View.VISIBLE);
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800223 mDelete.setEnabled(!mReadOnly && isEnabled());
Katherine Kuan63ffb902011-04-26 19:53:40 -0700224 } else {
225 mDeleteContainer.setVisibility(View.GONE);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700226 }
227 }
228
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700229 public void setDeleteButtonVisible(boolean visible) {
230 if (mIsDeletable) {
Katherine Kuana94859f2011-07-20 13:38:27 -0700231 mDeleteContainer.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700232 }
233 }
234
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700235 protected void onOptionalFieldVisibilityChange() {
236 if (mListener != null) {
237 mListener.onRequest(EditorListener.EDITOR_FORM_CHANGED);
238 }
239 }
240
241 @Override
242 public void setEditorListener(EditorListener listener) {
243 mListener = listener;
244 }
245
Brian Attwelld690dff2014-12-02 15:04:56 -0800246 protected EditorListener getEditorListener(){
247 return mListener;
248 }
249
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700250 @Override
251 public void setDeletable(boolean deletable) {
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700252 mIsDeletable = deletable;
253 setupDeleteButton();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700254 }
255
256 @Override
257 public void setEnabled(boolean enabled) {
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800258 super.setEnabled(enabled);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700259 mLabel.setEnabled(!mReadOnly && enabled);
260 mDelete.setEnabled(!mReadOnly && enabled);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700261 }
262
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800263 public Spinner getLabel() {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700264 return mLabel;
265 }
266
Katherine Kuan12a93632011-05-05 13:38:01 -0700267 public ImageView getDelete() {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700268 return mDelete;
269 }
270
271 protected DataKind getKind() {
272 return mKind;
273 }
274
275 protected ValuesDelta getEntry() {
276 return mEntry;
277 }
278
279 protected EditType getType() {
280 return mType;
281 }
282
283 /**
284 * Build the current label state based on selected {@link EditType} and
285 * possible custom label string.
286 */
Brian Attwelld690dff2014-12-02 15:04:56 -0800287 public void rebuildLabel() {
Brian Attwellf1402272014-12-16 16:00:08 -0800288 mEditTypeAdapter = new EditTypeAdapter(getContext());
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800289 mLabel.setAdapter(mEditTypeAdapter);
290 if (mEditTypeAdapter.hasCustomSelection()) {
291 mLabel.setSelection(mEditTypeAdapter.getPosition(CUSTOM_SELECTION));
292 } else {
293 mLabel.setSelection(mEditTypeAdapter.getPosition(mType));
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700294 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700295 }
296
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700297 @Override
298 public void onFieldChanged(String column, String value) {
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -0800299 if (!isFieldChanged(column, value)) {
Dmitri Plotnikov7d4a8dd2010-12-07 20:49:25 -0800300 return;
301 }
302
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700303 // Field changes are saved directly
Katherine Kuane55faef2011-12-06 17:58:43 -0800304 saveValue(column, value);
305
306 // Notify listener if applicable
307 notifyEditorListener();
Brian Attwelld690dff2014-12-02 15:04:56 -0800308
309 rebuildLabel();
Katherine Kuane55faef2011-12-06 17:58:43 -0800310 }
311
312 protected void saveValue(String column, String value) {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700313 mEntry.put(column, value);
Katherine Kuane55faef2011-12-06 17:58:43 -0800314 }
315
Brian Attwell8f9d84f2014-11-03 23:17:04 -0800316 /**
317 * Sub classes should call this at the end of {@link #setValues} once they finish changing
318 * isEmpty(). This is needed to fix b/18194655.
319 */
320 protected final void updateEmptiness() {
321 mWasEmpty = isEmpty();
322 }
323
Katherine Kuane55faef2011-12-06 17:58:43 -0800324 protected void notifyEditorListener() {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700325 if (mListener != null) {
326 mListener.onRequest(EditorListener.FIELD_CHANGED);
327 }
Katherine Kuan25914362011-04-29 19:42:01 -0700328
329 boolean isEmpty = isEmpty();
330 if (mWasEmpty != isEmpty) {
331 if (isEmpty) {
Katherine Kuan37bddc22011-07-09 17:36:26 -0700332 if (mListener != null) {
333 mListener.onRequest(EditorListener.FIELD_TURNED_EMPTY);
334 }
Katherine Kuana94859f2011-07-20 13:38:27 -0700335 if (mIsDeletable) mDeleteContainer.setVisibility(View.INVISIBLE);
Katherine Kuan25914362011-04-29 19:42:01 -0700336 } else {
Katherine Kuan37bddc22011-07-09 17:36:26 -0700337 if (mListener != null) {
338 mListener.onRequest(EditorListener.FIELD_TURNED_NON_EMPTY);
339 }
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700340 if (mIsDeletable) mDeleteContainer.setVisibility(View.VISIBLE);
Katherine Kuan25914362011-04-29 19:42:01 -0700341 }
342 mWasEmpty = isEmpty;
343 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700344 }
345
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -0800346 protected boolean isFieldChanged(String column, String value) {
Daniel Lehmann60b239c2011-03-07 20:01:45 -0800347 final String dbValue = mEntry.getAsString(column);
348 // nullable fields (e.g. Middle Name) are usually represented as empty columns,
349 // so lets treat null and empty space equivalently here
350 final String dbValueNoNull = dbValue == null ? "" : dbValue;
351 final String valueNoNull = value == null ? "" : value;
352 return !TextUtils.equals(dbValueNoNull, valueNoNull);
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -0800353 }
354
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700355 protected void rebuildValues() {
356 setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
357 }
358
359 /**
Brian Attwell8f9d84f2014-11-03 23:17:04 -0800360 * Prepare this editor using the given {@link DataKind} for defining structure and
361 * {@link ValuesDelta} describing the content to edit. When overriding this, be careful
362 * to call {@link #updateEmptiness} at the end.
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700363 */
364 @Override
Maurice Chu851222a2012-06-21 11:43:08 -0700365 public void setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly,
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700366 ViewIdGenerator vig) {
367 mKind = kind;
368 mEntry = entry;
369 mState = state;
370 mReadOnly = readOnly;
371 mViewIdGenerator = vig;
372 setId(vig.getId(state, kind, entry, ViewIdGenerator.NO_VIEW_INDEX));
373
374 if (!entry.isVisible()) {
375 // Hide ourselves entirely if deleted
376 setVisibility(View.GONE);
377 return;
378 }
379 setVisibility(View.VISIBLE);
380
381 // Display label selector if multiple types available
Maurice Chu851222a2012-06-21 11:43:08 -0700382 final boolean hasTypes = RawContactModifier.hasEditTypes(kind);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700383 setupLabelButton(hasTypes);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700384 mLabel.setEnabled(!readOnly && isEnabled());
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700385 if (hasTypes) {
Maurice Chu851222a2012-06-21 11:43:08 -0700386 mType = RawContactModifier.getCurrentType(entry, kind);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700387 rebuildLabel();
388 }
389 }
390
391 public ValuesDelta getValues() {
392 return mEntry;
393 }
394
395 /**
396 * Prepare dialog for entering a custom label. The input value is trimmed: white spaces before
397 * and after the input text is removed.
398 * <p>
399 * If the final value is empty, this change request is ignored;
400 * no empty text is allowed in any custom label.
401 */
402 private Dialog createCustomDialog() {
Brian Attwellf1402272014-12-16 16:00:08 -0800403 final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700404 final LayoutInflater layoutInflater = LayoutInflater.from(builder.getContext());
Adam Powelleb765ac2011-11-22 12:28:17 -0800405 builder.setTitle(R.string.customLabelPickerTitle);
406
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700407 final View view = layoutInflater.inflate(R.layout.contact_editor_label_name_dialog, null);
408 final EditText editText = (EditText) view.findViewById(R.id.custom_dialog_content);
409 editText.setInputType(INPUT_TYPE_CUSTOM);
410 editText.setSaveEnabled(true);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700411
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700412 builder.setView(view);
413 editText.requestFocus();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700414
415 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
416 @Override
417 public void onClick(DialogInterface dialog, int which) {
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700418 final String customText = editText.getText().toString().trim();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700419 if (ContactsUtils.isGraphic(customText)) {
Dmitri Plotnikov14918c02010-12-07 17:20:27 -0800420 final List<EditType> allTypes =
Maurice Chu851222a2012-06-21 11:43:08 -0700421 RawContactModifier.getValidTypes(mState, mKind, null);
Dmitri Plotnikov14918c02010-12-07 17:20:27 -0800422 mType = null;
423 for (EditType editType : allTypes) {
424 if (editType.customColumn != null) {
425 mType = editType;
426 break;
427 }
428 }
429 if (mType == null) return;
430
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700431 mEntry.put(mKind.typeColumn, mType.rawValue);
432 mEntry.put(mType.customColumn, customText);
433 rebuildLabel();
434 requestFocusForFirstEditField();
435 onLabelRebuilt();
436 }
437 }
438 });
439
440 builder.setNegativeButton(android.R.string.cancel, null);
441
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700442 final AlertDialog dialog = builder.create();
443 dialog.setOnShowListener(new OnShowListener() {
444 @Override
445 public void onShow(DialogInterface dialogInterface) {
446 updateCustomDialogOkButtonState(dialog, editText);
447 }
448 });
449 editText.addTextChangedListener(new TextWatcher() {
450 @Override
451 public void onTextChanged(CharSequence s, int start, int before, int count) {
452 }
453
454 @Override
455 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
456 }
457
458 @Override
459 public void afterTextChanged(Editable s) {
460 updateCustomDialogOkButtonState(dialog, editText);
461 }
462 });
463 dialog.getWindow().setSoftInputMode(
464 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
465
466 return dialog;
467 }
468
469 /* package */ void updateCustomDialogOkButtonState(AlertDialog dialog, EditText editText) {
470 final Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
471 okButton.setEnabled(!TextUtils.isEmpty(editText.getText().toString().trim()));
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700472 }
473
474 /**
475 * Called after the label has changed (either chosen from the list or entered in the Dialog)
476 */
477 protected void onLabelRebuilt() {
478 }
479
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800480 protected void onTypeSelectionChange(int position) {
481 EditType selected = mEditTypeAdapter.getItem(position);
482 // See if the selection has in fact changed
483 if (mEditTypeAdapter.hasCustomSelection() && selected == CUSTOM_SELECTION) {
484 return;
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700485 }
486
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800487 if (mType == selected && mType.customColumn == null) {
488 return;
489 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700490
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800491 if (selected.customColumn != null) {
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800492 showDialog(DIALOG_ID_CUSTOM);
493 } else {
494 // User picked type, and we're sure it's ok to actually write the entry.
495 mType = selected;
496 mEntry.put(mKind.typeColumn, mType.rawValue);
497 rebuildLabel();
498 requestFocusForFirstEditField();
499 onLabelRebuilt();
500 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700501 }
502
503 /* package */
504 void showDialog(int bundleDialogId) {
505 Bundle bundle = new Bundle();
506 bundle.putInt(DIALOG_ID_KEY, bundleDialogId);
507 getDialogManager().showDialogInView(this, bundle);
508 }
509
510 private DialogManager getDialogManager() {
511 if (mDialogManager == null) {
512 Context context = getContext();
513 if (!(context instanceof DialogManager.DialogShowingViewActivity)) {
514 throw new IllegalStateException(
515 "View must be hosted in an Activity that implements " +
516 "DialogManager.DialogShowingViewActivity");
517 }
518 mDialogManager = ((DialogManager.DialogShowingViewActivity)context).getDialogManager();
519 }
520 return mDialogManager;
521 }
522
523 @Override
524 public Dialog createDialog(Bundle bundle) {
525 if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
526 int dialogId = bundle.getInt(DIALOG_ID_KEY);
527 switch (dialogId) {
528 case DIALOG_ID_CUSTOM:
529 return createCustomDialog();
530 default:
531 throw new IllegalArgumentException("Invalid dialogId: " + dialogId);
532 }
533 }
534
535 protected abstract void requestFocusForFirstEditField();
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800536
537 private class EditTypeAdapter extends ArrayAdapter<EditType> {
538 private final LayoutInflater mInflater;
539 private boolean mHasCustomSelection;
Brian Attwelld690dff2014-12-02 15:04:56 -0800540 private int mTextColorHintUnfocused;
541 private int mTextColorDark;
542 private int mTextColorSecondary;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800543
544 public EditTypeAdapter(Context context) {
545 super(context, 0);
546 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Brian Attwelld690dff2014-12-02 15:04:56 -0800547 mTextColorHintUnfocused = context.getResources().getColor(
548 R.color.editor_disabled_text_color);
549 mTextColorSecondary = context.getResources().getColor(R.color.secondary_text_color);
550 mTextColorDark = context.getResources().getColor(R.color.primary_text_color);
551
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800552
Dmitri Plotnikov4fa9cd02010-12-10 17:40:38 -0800553 if (mType != null && mType.customColumn != null) {
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800554
555 // Use custom label string when present
556 final String customText = mEntry.getAsString(mType.customColumn);
557 if (customText != null) {
558 add(CUSTOM_SELECTION);
559 mHasCustomSelection = true;
560 }
561 }
562
Maurice Chu851222a2012-06-21 11:43:08 -0700563 addAll(RawContactModifier.getValidTypes(mState, mKind, mType));
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800564 }
565
566 public boolean hasCustomSelection() {
567 return mHasCustomSelection;
568 }
569
570 @Override
571 public View getView(int position, View convertView, ViewGroup parent) {
Brian Attwelld690dff2014-12-02 15:04:56 -0800572 final TextView view = createViewFromResource(
Brian Attwell043fba62014-10-30 11:11:56 -0700573 position, convertView, parent, R.layout.edit_simple_spinner_item);
574 // We don't want any background on this view. The background would obscure
575 // the spinner's background.
576 view.setBackground(null);
Brian Attwelld690dff2014-12-02 15:04:56 -0800577 // The text color should be a very light hint color when unfocused and empty. When
578 // focused and empty, use a less light hint color. When non-empty, use a dark non-hint
579 // color.
580 if (!LabeledEditorView.this.isEmpty()) {
581 view.setTextColor(mTextColorDark);
582 } else if (LabeledEditorView.this.hasFocus()) {
583 view.setTextColor(mTextColorSecondary);
584 } else {
585 view.setTextColor(mTextColorHintUnfocused);
586 }
Brian Attwell043fba62014-10-30 11:11:56 -0700587 return view;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800588 }
589
590 @Override
591 public View getDropDownView(int position, View convertView, ViewGroup parent) {
592 return createViewFromResource(
593 position, convertView, parent, android.R.layout.simple_spinner_dropdown_item);
594 }
595
Brian Attwelld690dff2014-12-02 15:04:56 -0800596 private TextView createViewFromResource(int position, View convertView, ViewGroup parent,
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800597 int resource) {
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800598 TextView textView;
599
600 if (convertView == null) {
Katherine Kuanfd709032011-08-02 14:18:16 -0700601 textView = (TextView) mInflater.inflate(resource, parent, false);
Brian Attwell043fba62014-10-30 11:11:56 -0700602 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(
603 R.dimen.editor_form_text_size));
Brian Attwelld690dff2014-12-02 15:04:56 -0800604 textView.setTextColor(mTextColorDark);
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800605 } else {
Katherine Kuanfd709032011-08-02 14:18:16 -0700606 textView = (TextView) convertView;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800607 }
608
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800609 EditType type = getItem(position);
610 String text;
611 if (type == CUSTOM_SELECTION) {
612 text = mEntry.getAsString(mType.customColumn);
613 } else {
614 text = getContext().getString(type.labelRes);
615 }
Katherine Kuanfd709032011-08-02 14:18:16 -0700616 textView.setText(text);
617 return textView;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800618 }
619 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700620}