blob: df4c5f9924fec114192fbbfdf8125d65a7440211 [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;
Gary Mai186d7912016-08-10 14:41:45 -070040import android.widget.CheckedTextView;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070041import android.widget.EditText;
Katherine Kuan12a93632011-05-05 13:38:01 -070042import android.widget.ImageView;
Katherine Kuan63ffb902011-04-26 19:53:40 -070043import android.widget.LinearLayout;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080044import android.widget.Spinner;
45import android.widget.TextView;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070046
Gary Mai0a49afa2016-12-05 15:53:58 -080047import com.android.contacts.ContactsUtils;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070048import com.android.contacts.R;
Gary Mai69c182a2016-12-05 13:07:03 -080049import com.android.contacts.model.RawContactDelta;
50import com.android.contacts.model.RawContactModifier;
51import com.android.contacts.model.ValuesDelta;
52import com.android.contacts.model.account.AccountType.EditType;
53import com.android.contacts.model.dataitem.DataKind;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070054import com.android.contacts.util.DialogManager;
55import com.android.contacts.util.DialogManager.DialogShowingView;
56
Dmitri Plotnikov14918c02010-12-07 17:20:27 -080057import java.util.List;
58
Daniel Lehmann392ccec2010-11-01 20:50:03 -070059/**
Katherine Kuan63ffb902011-04-26 19:53:40 -070060 * Base class for editors that handles labels and values. Uses
Maurice Chu851222a2012-06-21 11:43:08 -070061 * {@link ValuesDelta} to read any existing {@link RawContact} values, and to
Katherine Kuan63ffb902011-04-26 19:53:40 -070062 * correctly write any changes values.
Daniel Lehmann392ccec2010-11-01 20:50:03 -070063 */
Katherine Kuan63ffb902011-04-26 19:53:40 -070064public abstract class LabeledEditorView extends LinearLayout implements Editor, DialogShowingView {
Daniel Lehmann392ccec2010-11-01 20:50:03 -070065 protected static final String DIALOG_ID_KEY = "dialog_id";
66 private static final int DIALOG_ID_CUSTOM = 1;
67
68 private static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
69 | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
70
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080071 private Spinner mLabel;
72 private EditTypeAdapter mEditTypeAdapter;
avipuleda7e562017-02-16 16:41:44 -080073 protected View mDeleteContainer;
Katherine Kuan12a93632011-05-05 13:38:01 -070074 private ImageView mDelete;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070075
76 private DataKind mKind;
77 private ValuesDelta mEntry;
Maurice Chu851222a2012-06-21 11:43:08 -070078 private RawContactDelta mState;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070079 private boolean mReadOnly;
Katherine Kuan25914362011-04-29 19:42:01 -070080 private boolean mWasEmpty = true;
Katherine Kuan5e330ed2011-07-15 15:06:06 -070081 private boolean mIsDeletable = true;
Katherine Kuan2293e552011-07-21 20:25:44 -070082 private boolean mIsAttachedToWindow;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070083
84 private EditType mType;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070085
86 private ViewIdGenerator mViewIdGenerator;
87 private DialogManager mDialogManager = null;
88 private EditorListener mListener;
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -080089 protected int mMinLineItemHeight;
Gary Mai186d7912016-08-10 14:41:45 -070090 private int mSelectedLabelIndex;
Daniel Lehmann392ccec2010-11-01 20:50:03 -070091
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -080092 /**
93 * A marker in the spinner adapter of the currently selected custom type.
94 */
95 public static final EditType CUSTOM_SELECTION = new EditType(0, 0);
96
97 private OnItemSelectedListener mSpinnerListener = new OnItemSelectedListener() {
98
99 @Override
100 public void onItemSelected(
101 AdapterView<?> parent, View view, int position, long id) {
102 onTypeSelectionChange(position);
103 }
104
105 @Override
106 public void onNothingSelected(AdapterView<?> parent) {
107 }
108 };
109
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700110 public LabeledEditorView(Context context) {
111 super(context);
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800112 init(context);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700113 }
114
115 public LabeledEditorView(Context context, AttributeSet attrs) {
116 super(context, attrs);
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800117 init(context);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700118 }
119
120 public LabeledEditorView(Context context, AttributeSet attrs, int defStyle) {
121 super(context, attrs, defStyle);
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800122 init(context);
123 }
124
Walter Jangd35e5ef2015-02-24 09:18:16 -0800125 public Long getRawContactId() {
126 return mState == null ? null : mState.getRawContactId();
127 }
128
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800129 private void init(Context context) {
130 mMinLineItemHeight = context.getResources().getDimensionPixelSize(
131 R.dimen.editor_min_line_item_height);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700132 }
133
Katherine Kuan63ffb902011-04-26 19:53:40 -0700134 /** {@inheritDoc} */
135 @Override
136 protected void onFinishInflate() {
137
Katherine Kuan63ffb902011-04-26 19:53:40 -0700138 mLabel = (Spinner) findViewById(R.id.spinner);
Daniel Lehmann67713b32012-02-21 21:57:09 -0800139 // Turn off the Spinner's own state management. We do this ourselves on rotation
140 mLabel.setId(View.NO_ID);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700141 mLabel.setOnItemSelectedListener(mSpinnerListener);
Wenyi Wang843de772015-11-10 10:24:04 -0800142 ViewSelectedFilter.suppressViewSelectedEvent(mLabel);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700143
Katherine Kuan12a93632011-05-05 13:38:01 -0700144 mDelete = (ImageView) findViewById(R.id.delete_button);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700145 mDeleteContainer = findViewById(R.id.delete_button_container);
Katherine Kuan12a93632011-05-05 13:38:01 -0700146 mDeleteContainer.setOnClickListener(new OnClickListener() {
Katherine Kuan63ffb902011-04-26 19:53:40 -0700147 @Override
148 public void onClick(View v) {
149 // defer removal of this button so that the pressed state is visible shortly
150 new Handler().post(new Runnable() {
151 @Override
152 public void run() {
Katherine Kuan2293e552011-07-21 20:25:44 -0700153 // Don't do anything if the view is no longer attached to the window
154 // (This check is needed because when this {@link Runnable} is executed,
155 // we can't guarantee the view is still valid.
156 if (!mIsAttachedToWindow) {
157 return;
158 }
159 // Send the delete request to the listener (which will in turn call
160 // deleteEditor() on this view if the deletion is valid - i.e. this is not
161 // the last {@link Editor} in the section).
Katherine Kuan63ffb902011-04-26 19:53:40 -0700162 if (mListener != null) {
Katherine Kuan2293e552011-07-21 20:25:44 -0700163 mListener.onDeleteRequested(LabeledEditorView.this);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700164 }
165 }
166 });
167 }
168 });
Brian Attwell043fba62014-10-30 11:11:56 -0700169
170 setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
171 (int) getResources().getDimension(R.dimen.editor_padding_between_editor_views));
Katherine Kuan63ffb902011-04-26 19:53:40 -0700172 }
173
Katherine Kuan2293e552011-07-21 20:25:44 -0700174 @Override
175 protected void onAttachedToWindow() {
176 super.onAttachedToWindow();
177 // Keep track of when the view is attached or detached from the window, so we know it's
178 // safe to remove views (in case the user requests to delete this editor).
179 mIsAttachedToWindow = true;
180 }
181
182 @Override
183 protected void onDetachedFromWindow() {
184 super.onDetachedFromWindow();
185 mIsAttachedToWindow = false;
186 }
187
188 @Override
Walter Jang23a34d12015-07-08 09:41:05 -0700189 public void markDeleted() {
Katherine Kuan2293e552011-07-21 20:25:44 -0700190 // Keep around in model, but mark as deleted
191 mEntry.markDeleted();
Walter Jang23a34d12015-07-08 09:41:05 -0700192 }
193
194 @Override
195 public void deleteEditor() {
196 markDeleted();
Katherine Kuan2293e552011-07-21 20:25:44 -0700197
198 // Remove the view
Daniel Lehmannca87e9c2012-03-06 14:02:31 -0800199 EditorAnimator.getInstance().removeEditorView(this);
Katherine Kuan2293e552011-07-21 20:25:44 -0700200 }
201
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800202 public boolean isReadOnly() {
203 return mReadOnly;
204 }
205
Dmitri Plotnikov91d8e892010-12-07 20:36:51 -0800206 public int getBaseline(int row) {
207 if (row == 0 && mLabel != null) {
208 return mLabel.getBaseline();
209 }
210 return -1;
211 }
212
213 /**
Katherine Kuana94859f2011-07-20 13:38:27 -0700214 * Configures the visibility of the type label button and enables or disables it properly.
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700215 */
216 private void setupLabelButton(boolean shouldExist) {
Katherine Kuan63ffb902011-04-26 19:53:40 -0700217 if (shouldExist) {
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800218 mLabel.setEnabled(!mReadOnly && isEnabled());
Katherine Kuan63ffb902011-04-26 19:53:40 -0700219 mLabel.setVisibility(View.VISIBLE);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700220 } else {
221 mLabel.setVisibility(View.GONE);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700222 }
223 }
224
225 /**
Katherine Kuana94859f2011-07-20 13:38:27 -0700226 * Configures the visibility of the "delete" button and enables or disables it properly.
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700227 */
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700228 private void setupDeleteButton() {
229 if (mIsDeletable) {
Katherine Kuan63ffb902011-04-26 19:53:40 -0700230 mDeleteContainer.setVisibility(View.VISIBLE);
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800231 mDelete.setEnabled(!mReadOnly && isEnabled());
Katherine Kuan63ffb902011-04-26 19:53:40 -0700232 } else {
avipuleda7e562017-02-16 16:41:44 -0800233 mDeleteContainer.setVisibility(View.INVISIBLE);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700234 }
235 }
236
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700237 public void setDeleteButtonVisible(boolean visible) {
238 if (mIsDeletable) {
Katherine Kuana94859f2011-07-20 13:38:27 -0700239 mDeleteContainer.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700240 }
241 }
242
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700243 protected void onOptionalFieldVisibilityChange() {
244 if (mListener != null) {
245 mListener.onRequest(EditorListener.EDITOR_FORM_CHANGED);
246 }
247 }
248
249 @Override
250 public void setEditorListener(EditorListener listener) {
251 mListener = listener;
252 }
253
Brian Attwelld690dff2014-12-02 15:04:56 -0800254 protected EditorListener getEditorListener(){
255 return mListener;
256 }
257
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700258 @Override
259 public void setDeletable(boolean deletable) {
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700260 mIsDeletable = deletable;
261 setupDeleteButton();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700262 }
263
264 @Override
265 public void setEnabled(boolean enabled) {
Dmitri Plotnikov02bb1252010-12-06 09:39:05 -0800266 super.setEnabled(enabled);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700267 mLabel.setEnabled(!mReadOnly && enabled);
268 mDelete.setEnabled(!mReadOnly && enabled);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700269 }
270
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800271 public Spinner getLabel() {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700272 return mLabel;
273 }
274
Katherine Kuan12a93632011-05-05 13:38:01 -0700275 public ImageView getDelete() {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700276 return mDelete;
277 }
278
279 protected DataKind getKind() {
280 return mKind;
281 }
282
283 protected ValuesDelta getEntry() {
284 return mEntry;
285 }
286
287 protected EditType getType() {
288 return mType;
289 }
290
291 /**
292 * Build the current label state based on selected {@link EditType} and
293 * possible custom label string.
294 */
Brian Attwelld690dff2014-12-02 15:04:56 -0800295 public void rebuildLabel() {
Brian Attwellf1402272014-12-16 16:00:08 -0800296 mEditTypeAdapter = new EditTypeAdapter(getContext());
Gary Mai186d7912016-08-10 14:41:45 -0700297 mEditTypeAdapter.setSelectedIndex(mSelectedLabelIndex);
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800298 mLabel.setAdapter(mEditTypeAdapter);
299 if (mEditTypeAdapter.hasCustomSelection()) {
300 mLabel.setSelection(mEditTypeAdapter.getPosition(CUSTOM_SELECTION));
Wenyi Wangdaf8a412015-11-06 12:59:57 -0800301 mDeleteContainer.setContentDescription(
302 getContext().getString(R.string.editor_delete_view_description,
303 mEntry.getAsString(mType.customColumn),
304 getContext().getString(mKind.titleRes)));
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800305 } else {
Walter Jang155299e2016-08-25 10:38:52 -0700306 if (mType != null && mType.labelRes > 0 && mKind.titleRes > 0) {
Tingting Wangcfa628c2016-07-13 17:31:06 -0700307 mLabel.setSelection(mEditTypeAdapter.getPosition(mType));
Wenyi Wangdaf8a412015-11-06 12:59:57 -0800308 mDeleteContainer.setContentDescription(
309 getContext().getString(R.string.editor_delete_view_description,
310 getContext().getString(mType.labelRes),
311 getContext().getString(mKind.titleRes)));
Walter Jang155299e2016-08-25 10:38:52 -0700312 } else if (mKind.titleRes > 0) {
Wenyi Wangdaf8a412015-11-06 12:59:57 -0800313 mDeleteContainer.setContentDescription(
314 getContext().getString(R.string.editor_delete_view_description_short,
315 getContext().getString(mKind.titleRes)));
316 }
317
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700318 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700319 }
320
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700321 @Override
322 public void onFieldChanged(String column, String value) {
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -0800323 if (!isFieldChanged(column, value)) {
Dmitri Plotnikov7d4a8dd2010-12-07 20:49:25 -0800324 return;
325 }
326
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700327 // Field changes are saved directly
Katherine Kuane55faef2011-12-06 17:58:43 -0800328 saveValue(column, value);
329
330 // Notify listener if applicable
331 notifyEditorListener();
332 }
333
yoichi kakimoto5ed462a2017-09-04 19:20:13 +0900334 /** {@inheritDoc} */
335 @Override
336 public void updatePhonetic(String column, String value) {
337 }
338
339 /** {@inheritDoc} */
340 @Override
341 public String getPhonetic(String column){
342 return "";
343 }
344
Katherine Kuane55faef2011-12-06 17:58:43 -0800345 protected void saveValue(String column, String value) {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700346 mEntry.put(column, value);
Katherine Kuane55faef2011-12-06 17:58:43 -0800347 }
348
Brian Attwell8f9d84f2014-11-03 23:17:04 -0800349 /**
350 * Sub classes should call this at the end of {@link #setValues} once they finish changing
351 * isEmpty(). This is needed to fix b/18194655.
352 */
353 protected final void updateEmptiness() {
354 mWasEmpty = isEmpty();
355 }
356
Katherine Kuane55faef2011-12-06 17:58:43 -0800357 protected void notifyEditorListener() {
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700358 if (mListener != null) {
359 mListener.onRequest(EditorListener.FIELD_CHANGED);
360 }
Katherine Kuan25914362011-04-29 19:42:01 -0700361
362 boolean isEmpty = isEmpty();
363 if (mWasEmpty != isEmpty) {
364 if (isEmpty) {
Katherine Kuan37bddc22011-07-09 17:36:26 -0700365 if (mListener != null) {
366 mListener.onRequest(EditorListener.FIELD_TURNED_EMPTY);
367 }
Katherine Kuana94859f2011-07-20 13:38:27 -0700368 if (mIsDeletable) mDeleteContainer.setVisibility(View.INVISIBLE);
Katherine Kuan25914362011-04-29 19:42:01 -0700369 } else {
Katherine Kuan37bddc22011-07-09 17:36:26 -0700370 if (mListener != null) {
371 mListener.onRequest(EditorListener.FIELD_TURNED_NON_EMPTY);
372 }
Katherine Kuan5e330ed2011-07-15 15:06:06 -0700373 if (mIsDeletable) mDeleteContainer.setVisibility(View.VISIBLE);
Katherine Kuan25914362011-04-29 19:42:01 -0700374 }
375 mWasEmpty = isEmpty;
Walter Jang23942c02015-09-25 10:53:41 -0700376
377 // Update the label text color
Walter Jang4f5594a2015-10-06 18:40:31 -0700378 if (mEditTypeAdapter != null) {
379 mEditTypeAdapter.notifyDataSetChanged();
380 }
Katherine Kuan25914362011-04-29 19:42:01 -0700381 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700382 }
383
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -0800384 protected boolean isFieldChanged(String column, String value) {
Daniel Lehmann60b239c2011-03-07 20:01:45 -0800385 final String dbValue = mEntry.getAsString(column);
386 // nullable fields (e.g. Middle Name) are usually represented as empty columns,
387 // so lets treat null and empty space equivalently here
388 final String dbValueNoNull = dbValue == null ? "" : dbValue;
389 final String valueNoNull = value == null ? "" : value;
390 return !TextUtils.equals(dbValueNoNull, valueNoNull);
Dmitri Plotnikov11bb73b2011-02-25 20:39:58 -0800391 }
392
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700393 protected void rebuildValues() {
394 setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
395 }
396
397 /**
Brian Attwell8f9d84f2014-11-03 23:17:04 -0800398 * Prepare this editor using the given {@link DataKind} for defining structure and
399 * {@link ValuesDelta} describing the content to edit. When overriding this, be careful
400 * to call {@link #updateEmptiness} at the end.
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700401 */
402 @Override
Maurice Chu851222a2012-06-21 11:43:08 -0700403 public void setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly,
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700404 ViewIdGenerator vig) {
405 mKind = kind;
406 mEntry = entry;
407 mState = state;
408 mReadOnly = readOnly;
409 mViewIdGenerator = vig;
410 setId(vig.getId(state, kind, entry, ViewIdGenerator.NO_VIEW_INDEX));
411
412 if (!entry.isVisible()) {
413 // Hide ourselves entirely if deleted
414 setVisibility(View.GONE);
415 return;
416 }
417 setVisibility(View.VISIBLE);
418
419 // Display label selector if multiple types available
Walter Jangf7dfa542015-10-26 14:27:27 -0700420 final boolean hasTypes = RawContactModifier.hasEditTypes(kind);
421 setupLabelButton(hasTypes);
Katherine Kuan63ffb902011-04-26 19:53:40 -0700422 mLabel.setEnabled(!readOnly && isEnabled());
Walter Jang155299e2016-08-25 10:38:52 -0700423 if (mKind.titleRes > 0) {
424 mLabel.setContentDescription(getContext().getResources().getString(mKind.titleRes));
425 }
Tingting Wangcfa628c2016-07-13 17:31:06 -0700426 mType = RawContactModifier.getCurrentType(entry, kind);
427 rebuildLabel();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700428 }
429
430 public ValuesDelta getValues() {
431 return mEntry;
432 }
433
434 /**
435 * Prepare dialog for entering a custom label. The input value is trimmed: white spaces before
436 * and after the input text is removed.
437 * <p>
438 * If the final value is empty, this change request is ignored;
439 * no empty text is allowed in any custom label.
440 */
441 private Dialog createCustomDialog() {
Brian Attwellf1402272014-12-16 16:00:08 -0800442 final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700443 final LayoutInflater layoutInflater = LayoutInflater.from(builder.getContext());
Adam Powelleb765ac2011-11-22 12:28:17 -0800444 builder.setTitle(R.string.customLabelPickerTitle);
445
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700446 final View view = layoutInflater.inflate(R.layout.contact_editor_label_name_dialog, null);
447 final EditText editText = (EditText) view.findViewById(R.id.custom_dialog_content);
448 editText.setInputType(INPUT_TYPE_CUSTOM);
449 editText.setSaveEnabled(true);
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700450
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700451 builder.setView(view);
452 editText.requestFocus();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700453
454 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
455 @Override
456 public void onClick(DialogInterface dialog, int which) {
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700457 final String customText = editText.getText().toString().trim();
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700458 if (ContactsUtils.isGraphic(customText)) {
Dmitri Plotnikov14918c02010-12-07 17:20:27 -0800459 final List<EditType> allTypes =
Tingting Wang6062cec2015-12-10 14:54:59 -0800460 RawContactModifier.getValidTypes(mState, mKind, null, true, null, true);
Dmitri Plotnikov14918c02010-12-07 17:20:27 -0800461 mType = null;
462 for (EditType editType : allTypes) {
463 if (editType.customColumn != null) {
464 mType = editType;
465 break;
466 }
467 }
468 if (mType == null) return;
469
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700470 mEntry.put(mKind.typeColumn, mType.rawValue);
471 mEntry.put(mType.customColumn, customText);
472 rebuildLabel();
473 requestFocusForFirstEditField();
474 onLabelRebuilt();
475 }
476 }
477 });
478
479 builder.setNegativeButton(android.R.string.cancel, null);
480
Daniel Lehmanna04dc9c2012-04-14 16:09:48 -0700481 final AlertDialog dialog = builder.create();
482 dialog.setOnShowListener(new OnShowListener() {
483 @Override
484 public void onShow(DialogInterface dialogInterface) {
485 updateCustomDialogOkButtonState(dialog, editText);
486 }
487 });
488 editText.addTextChangedListener(new TextWatcher() {
489 @Override
490 public void onTextChanged(CharSequence s, int start, int before, int count) {
491 }
492
493 @Override
494 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
495 }
496
497 @Override
498 public void afterTextChanged(Editable s) {
499 updateCustomDialogOkButtonState(dialog, editText);
500 }
501 });
502 dialog.getWindow().setSoftInputMode(
503 WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
504
505 return dialog;
506 }
507
508 /* package */ void updateCustomDialogOkButtonState(AlertDialog dialog, EditText editText) {
509 final Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
510 okButton.setEnabled(!TextUtils.isEmpty(editText.getText().toString().trim()));
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700511 }
512
513 /**
514 * Called after the label has changed (either chosen from the list or entered in the Dialog)
515 */
516 protected void onLabelRebuilt() {
517 }
518
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800519 protected void onTypeSelectionChange(int position) {
520 EditType selected = mEditTypeAdapter.getItem(position);
521 // See if the selection has in fact changed
522 if (mEditTypeAdapter.hasCustomSelection() && selected == CUSTOM_SELECTION) {
523 return;
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700524 }
525
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800526 if (mType == selected && mType.customColumn == null) {
527 return;
528 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700529
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800530 if (selected.customColumn != null) {
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800531 showDialog(DIALOG_ID_CUSTOM);
532 } else {
533 // User picked type, and we're sure it's ok to actually write the entry.
534 mType = selected;
535 mEntry.put(mKind.typeColumn, mType.rawValue);
Gary Mai186d7912016-08-10 14:41:45 -0700536 mSelectedLabelIndex = position;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800537 rebuildLabel();
538 requestFocusForFirstEditField();
539 onLabelRebuilt();
540 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700541 }
542
543 /* package */
544 void showDialog(int bundleDialogId) {
545 Bundle bundle = new Bundle();
546 bundle.putInt(DIALOG_ID_KEY, bundleDialogId);
547 getDialogManager().showDialogInView(this, bundle);
548 }
549
550 private DialogManager getDialogManager() {
551 if (mDialogManager == null) {
552 Context context = getContext();
553 if (!(context instanceof DialogManager.DialogShowingViewActivity)) {
554 throw new IllegalStateException(
555 "View must be hosted in an Activity that implements " +
556 "DialogManager.DialogShowingViewActivity");
557 }
558 mDialogManager = ((DialogManager.DialogShowingViewActivity)context).getDialogManager();
559 }
560 return mDialogManager;
561 }
562
563 @Override
564 public Dialog createDialog(Bundle bundle) {
565 if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
566 int dialogId = bundle.getInt(DIALOG_ID_KEY);
567 switch (dialogId) {
568 case DIALOG_ID_CUSTOM:
569 return createCustomDialog();
570 default:
571 throw new IllegalArgumentException("Invalid dialogId: " + dialogId);
572 }
573 }
574
575 protected abstract void requestFocusForFirstEditField();
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800576
577 private class EditTypeAdapter extends ArrayAdapter<EditType> {
578 private final LayoutInflater mInflater;
579 private boolean mHasCustomSelection;
Brian Attwelld690dff2014-12-02 15:04:56 -0800580 private int mTextColorHintUnfocused;
581 private int mTextColorDark;
Gary Mai186d7912016-08-10 14:41:45 -0700582 private int mSelectedIndex;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800583
584 public EditTypeAdapter(Context context) {
585 super(context, 0);
586 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Brian Attwelld690dff2014-12-02 15:04:56 -0800587 mTextColorHintUnfocused = context.getResources().getColor(
588 R.color.editor_disabled_text_color);
Brian Attwelld690dff2014-12-02 15:04:56 -0800589 mTextColorDark = context.getResources().getColor(R.color.primary_text_color);
590
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800591
Dmitri Plotnikov4fa9cd02010-12-10 17:40:38 -0800592 if (mType != null && mType.customColumn != null) {
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800593
594 // Use custom label string when present
595 final String customText = mEntry.getAsString(mType.customColumn);
596 if (customText != null) {
597 add(CUSTOM_SELECTION);
598 mHasCustomSelection = true;
599 }
600 }
601
Tingting Wang6062cec2015-12-10 14:54:59 -0800602 addAll(RawContactModifier.getValidTypes(mState, mKind, mType, true, null, false));
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800603 }
604
605 public boolean hasCustomSelection() {
606 return mHasCustomSelection;
607 }
608
609 @Override
610 public View getView(int position, View convertView, ViewGroup parent) {
Brian Attwelld690dff2014-12-02 15:04:56 -0800611 final TextView view = createViewFromResource(
Brian Attwell043fba62014-10-30 11:11:56 -0700612 position, convertView, parent, R.layout.edit_simple_spinner_item);
613 // We don't want any background on this view. The background would obscure
614 // the spinner's background.
615 view.setBackground(null);
Brian Attwelld690dff2014-12-02 15:04:56 -0800616 // The text color should be a very light hint color when unfocused and empty. When
617 // focused and empty, use a less light hint color. When non-empty, use a dark non-hint
618 // color.
619 if (!LabeledEditorView.this.isEmpty()) {
620 view.setTextColor(mTextColorDark);
Brian Attwelld690dff2014-12-02 15:04:56 -0800621 } else {
622 view.setTextColor(mTextColorHintUnfocused);
623 }
Brian Attwell043fba62014-10-30 11:11:56 -0700624 return view;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800625 }
626
627 @Override
628 public View getDropDownView(int position, View convertView, ViewGroup parent) {
Gary Mai186d7912016-08-10 14:41:45 -0700629 final CheckedTextView dropDownView = (CheckedTextView) createViewFromResource(
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800630 position, convertView, parent, android.R.layout.simple_spinner_dropdown_item);
Gary Mai186d7912016-08-10 14:41:45 -0700631 dropDownView.setBackground(getContext().getDrawable(R.drawable.drawer_item_background));
632 dropDownView.setChecked(position == mSelectedIndex);
633 return dropDownView;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800634 }
635
Brian Attwelld690dff2014-12-02 15:04:56 -0800636 private TextView createViewFromResource(int position, View convertView, ViewGroup parent,
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800637 int resource) {
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800638 TextView textView;
639
640 if (convertView == null) {
Katherine Kuanfd709032011-08-02 14:18:16 -0700641 textView = (TextView) mInflater.inflate(resource, parent, false);
Brian Attwell043fba62014-10-30 11:11:56 -0700642 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(
643 R.dimen.editor_form_text_size));
Brian Attwelld690dff2014-12-02 15:04:56 -0800644 textView.setTextColor(mTextColorDark);
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800645 } else {
Katherine Kuanfd709032011-08-02 14:18:16 -0700646 textView = (TextView) convertView;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800647 }
648
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800649 EditType type = getItem(position);
650 String text;
651 if (type == CUSTOM_SELECTION) {
652 text = mEntry.getAsString(mType.customColumn);
653 } else {
654 text = getContext().getString(type.labelRes);
655 }
Katherine Kuanfd709032011-08-02 14:18:16 -0700656 textView.setText(text);
657 return textView;
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800658 }
Gary Mai186d7912016-08-10 14:41:45 -0700659
660 public void setSelectedIndex(int selectedIndex) {
661 mSelectedIndex = selectedIndex;
662 }
Dmitri Plotnikov2fcfa492010-12-07 16:10:33 -0800663 }
Daniel Lehmann392ccec2010-11-01 20:50:03 -0700664}