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