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 android.app.Dialog;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.os.Bundle;
23 import android.provider.ContactsContract;
24 import android.provider.ContactsContract.CommonDataKinds.Event;
25 import android.text.TextUtils;
26 import android.util.AttributeSet;
27 import android.view.View;
28 import android.widget.Button;
29 
30 import com.android.contacts.R;
31 import com.android.contacts.datepicker.DatePicker;
32 import com.android.contacts.datepicker.DatePickerDialog;
33 import com.android.contacts.datepicker.DatePickerDialog.OnDateSetListener;
34 import com.android.contacts.common.model.RawContactDelta;
35 import com.android.contacts.common.model.ValuesDelta;
36 import com.android.contacts.common.model.account.AccountType.EditField;
37 import com.android.contacts.common.model.account.AccountType.EventEditType;
38 import com.android.contacts.common.model.dataitem.DataKind;
39 import com.android.contacts.common.util.CommonDateUtils;
40 import com.android.contacts.common.util.DateUtils;
41 
42 import java.text.ParsePosition;
43 import java.util.Calendar;
44 import java.util.Date;
45 import java.util.Locale;
46 
47 /**
48  * Editor that allows editing Events using a {@link DatePickerDialog}
49  */
50 public class EventFieldEditorView extends LabeledEditorView {
51 
52     /**
53      * Default string to show when there is no date selected yet.
54      */
55     private String mNoDateString;
56     private int mPrimaryTextColor;
57     private int mHintTextColor;
58 
59     private Button mDateView;
60 
EventFieldEditorView(Context context)61     public EventFieldEditorView(Context context) {
62         super(context);
63     }
64 
EventFieldEditorView(Context context, AttributeSet attrs)65     public EventFieldEditorView(Context context, AttributeSet attrs) {
66         super(context, attrs);
67     }
68 
EventFieldEditorView(Context context, AttributeSet attrs, int defStyle)69     public EventFieldEditorView(Context context, AttributeSet attrs, int defStyle) {
70         super(context, attrs, defStyle);
71     }
72 
73     /** {@inheritDoc} */
74     @Override
onFinishInflate()75     protected void onFinishInflate() {
76         super.onFinishInflate();
77 
78         Resources resources = getContext().getResources();
79         mPrimaryTextColor = resources.getColor(R.color.primary_text_color);
80         mHintTextColor = resources.getColor(R.color.editor_disabled_text_color);
81         mNoDateString = getContext().getString(R.string.event_edit_field_hint_text);
82 
83         mDateView = (Button) findViewById(R.id.date_view);
84         mDateView.setOnClickListener(new OnClickListener() {
85             @Override
86             public void onClick(View v) {
87                 showDialog(R.id.dialog_event_date_picker);
88             }
89         });
90     }
91 
92     @Override
editNewlyAddedField()93     public void editNewlyAddedField() {
94         showDialog(R.id.dialog_event_date_picker);
95     }
96 
97     @Override
requestFocusForFirstEditField()98     protected void requestFocusForFirstEditField() {
99         mDateView.requestFocus();
100     }
101 
102     @Override
setEnabled(boolean enabled)103     public void setEnabled(boolean enabled) {
104         super.setEnabled(enabled);
105 
106         mDateView.setEnabled(!isReadOnly() && enabled);
107     }
108 
109     @Override
setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly, ViewIdGenerator vig)110     public void setValues(DataKind kind, ValuesDelta entry, RawContactDelta state, boolean readOnly,
111             ViewIdGenerator vig) {
112         if (kind.fieldList.size() != 1) throw new IllegalStateException("kind must have 1 field");
113         super.setValues(kind, entry, state, readOnly, vig);
114 
115         mDateView.setEnabled(isEnabled() && !readOnly);
116 
117         rebuildDateView();
118         updateEmptiness();
119     }
120 
rebuildDateView()121     private void rebuildDateView() {
122         final EditField editField = getKind().fieldList.get(0);
123         final String column = editField.column;
124         String data = DateUtils.formatDate(getContext(), getEntry().getAsString(column),
125                 false /*Use the short DateFormat to ensure that it fits inside the EditText*/);
126         if (TextUtils.isEmpty(data)) {
127             mDateView.setText(mNoDateString);
128             mDateView.setTextColor(mHintTextColor);
129             setDeleteButtonVisible(false);
130         } else {
131             mDateView.setText(data);
132             mDateView.setTextColor(mPrimaryTextColor);
133             setDeleteButtonVisible(true);
134         }
135     }
136 
137     @Override
isEmpty()138     public boolean isEmpty() {
139         final EditField editField = getKind().fieldList.get(0);
140         final String column = editField.column;
141         return TextUtils.isEmpty(getEntry().getAsString(column));
142     }
143 
144     @Override
createDialog(Bundle bundle)145     public Dialog createDialog(Bundle bundle) {
146         if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
147         int dialogId = bundle.getInt(DIALOG_ID_KEY);
148         switch (dialogId) {
149             case R.id.dialog_event_date_picker:
150                 return createDatePickerDialog();
151             default:
152                 return super.createDialog(bundle);
153         }
154     }
155 
156     @Override
getType()157     protected EventEditType getType() {
158         return (EventEditType) super.getType();
159     }
160 
161     @Override
onLabelRebuilt()162     protected void onLabelRebuilt() {
163         // if we changed to a type that requires a year, ensure that it is actually set
164         final String column = getKind().fieldList.get(0).column;
165         final String oldValue = getEntry().getAsString(column);
166         final DataKind kind = getKind();
167 
168         final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE, Locale.US);
169         final int defaultYear = calendar.get(Calendar.YEAR);
170 
171         // Check whether the year is optional
172         final boolean isYearOptional = getType() != null && getType().isYearOptional();
173 
174         if (!isYearOptional && !TextUtils.isEmpty(oldValue)) {
175             final ParsePosition position = new ParsePosition(0);
176             final Date date2 = kind.dateFormatWithoutYear.parse(oldValue, position);
177 
178             // Don't understand the date, lets not change it
179             if (date2 == null) return;
180 
181             // This value is missing the year. Add it now
182             calendar.setTime(date2);
183             calendar.set(defaultYear, calendar.get(Calendar.MONTH),
184                     calendar.get(Calendar.DAY_OF_MONTH), CommonDateUtils.DEFAULT_HOUR, 0, 0);
185 
186             onFieldChanged(column, kind.dateFormatWithYear.format(calendar.getTime()));
187             rebuildDateView();
188         }
189     }
190 
191     /**
192      * Prepare dialog for entering a date
193      */
createDatePickerDialog()194     private Dialog createDatePickerDialog() {
195         final String column = getKind().fieldList.get(0).column;
196         final String oldValue = getEntry().getAsString(column);
197         final DataKind kind = getKind();
198 
199         final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE, Locale.US);
200         final int defaultYear = calendar.get(Calendar.YEAR);
201 
202         // Check whether the year is optional
203         final boolean isYearOptional = getType().isYearOptional();
204 
205         final int oldYear, oldMonth, oldDay;
206 
207         if (TextUtils.isEmpty(oldValue)) {
208             // Default to the current date
209             oldYear = defaultYear;
210             oldMonth = calendar.get(Calendar.MONTH);
211             oldDay = calendar.get(Calendar.DAY_OF_MONTH);
212         } else {
213             // Try parsing with year
214             Calendar cal = DateUtils.parseDate(oldValue, false);
215             if (cal != null) {
216                 if (DateUtils.isYearSet(cal)) {
217                     oldYear = cal.get(Calendar.YEAR);
218                 } else {
219                     //cal.set(Calendar.YEAR, 0);
220                     oldYear = isYearOptional ? DatePickerDialog.NO_YEAR : defaultYear;
221                 }
222                 oldMonth = cal.get(Calendar.MONTH);
223                 oldDay = cal.get(Calendar.DAY_OF_MONTH);
224             } else {
225                 return null;
226             }
227         }
228         final OnDateSetListener callBack = new OnDateSetListener() {
229             @Override
230             public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
231                 if (year == 0 && !isYearOptional) throw new IllegalStateException();
232                 final Calendar outCalendar =
233                         Calendar.getInstance(DateUtils.UTC_TIMEZONE, Locale.US);
234 
235                 // If no year specified, set it to 2000 (we could pick any leap year here).
236                 // The format string will ignore that year.
237                 // For formats other than Exchange, the time of the day is ignored
238                 outCalendar.clear();
239                 outCalendar.set(year == DatePickerDialog.NO_YEAR ? 2000 : year, monthOfYear,
240                         dayOfMonth, CommonDateUtils.DEFAULT_HOUR, 0, 0);
241 
242                 final String resultString;
243                 if (year == 0) {
244                     resultString = kind.dateFormatWithoutYear.format(outCalendar.getTime());
245                 } else {
246                     resultString = kind.dateFormatWithYear.format(outCalendar.getTime());
247                 }
248                 onFieldChanged(column, resultString);
249                 rebuildDateView();
250             }
251         };
252         final DatePickerDialog resultDialog = new DatePickerDialog(getContext(), callBack,
253                 oldYear, oldMonth, oldDay, isYearOptional);
254         return resultDialog;
255     }
256 
257     @Override
clearAllFields()258     public void clearAllFields() {
259         // Update UI
260         mDateView.setText(mNoDateString);
261         mDateView.setTextColor(mHintTextColor);
262 
263         // Update state
264         final String column = getKind().fieldList.get(0).column;
265         onFieldChanged(column, "");
266     }
267 
268     /**
269      * Sets the typeColumn of entry as TYPE_BIRTHDAY and calls rebuildValues() to refresh the view.
270      */
restoreBirthday()271     public void restoreBirthday() {
272         saveValue(getKind().typeColumn, Integer.toString(Event.TYPE_BIRTHDAY));
273         rebuildValues();
274     }
275 
276     /**
277      * EventEditType Birthday:
278      * rawValue=3 labelRes=17039911 secondary=false specificMax=1 customColumn=null
279      * mYearOptional=true
280      */
isBirthdayType()281     public boolean isBirthdayType(){
282         final EventEditType eventType = getType();
283         return eventType.rawValue == Event.TYPE_BIRTHDAY && !eventType.secondary
284                 && eventType.specificMax == 1 && eventType.customColumn == null
285                 && eventType.isYearOptional();
286     }
287 }
288