| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.calendar.recurrencepicker; |
| |
| import android.app.Activity; |
| import android.app.DialogFragment; |
| import android.content.Context; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.os.Bundle; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.text.Editable; |
| import android.text.TextUtils; |
| import android.text.TextWatcher; |
| import android.text.format.DateUtils; |
| import android.text.format.Time; |
| import android.util.Log; |
| import android.util.TimeFormatException; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| import android.view.ViewGroup.LayoutParams; |
| import android.view.ViewGroup; |
| import android.view.Window; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemSelectedListener; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.CompoundButton; |
| import android.widget.CompoundButton.OnCheckedChangeListener; |
| import android.widget.EditText; |
| import android.widget.LinearLayout; |
| import android.widget.RadioButton; |
| import android.widget.RadioGroup; |
| import android.widget.Spinner; |
| import android.widget.Switch; |
| import android.widget.TableLayout; |
| import android.widget.TextView; |
| import android.widget.Toast; |
| import android.widget.ToggleButton; |
| |
| import com.android.calendar.R; |
| import com.android.calendar.Utils; |
| import com.android.calendar.CalendarController.ViewType; |
| import com.android.calendarcommon2.EventRecurrence; |
| import com.android.datetimepicker.date.DatePickerDialog; |
| |
| import java.text.DateFormatSymbols; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| |
| public class RecurrencePickerDialog extends DialogFragment implements OnItemSelectedListener, |
| OnCheckedChangeListener, OnClickListener, |
| android.widget.RadioGroup.OnCheckedChangeListener, DatePickerDialog.OnDateSetListener { |
| |
| private static final String TAG = "RecurrencePickerDialog"; |
| |
| // in dp's |
| private static final int MIN_SCREEN_WIDTH_FOR_SINGLE_ROW_WEEK = 450; |
| |
| // Update android:maxLength in EditText as needed |
| private static final int INTERVAL_MAX = 99; |
| private static final int INTERVAL_DEFAULT = 1; |
| // Update android:maxLength in EditText as needed |
| private static final int COUNT_MAX = 730; |
| private static final int COUNT_DEFAULT = 5; |
| |
| private DatePickerDialog mDatePickerDialog; |
| |
| private class RecurrenceModel implements Parcelable { |
| |
| // Should match EventRecurrence.DAILY, etc |
| static final int FREQ_DAILY = 0; |
| static final int FREQ_WEEKLY = 1; |
| static final int FREQ_MONTHLY = 2; |
| static final int FREQ_YEARLY = 3; |
| |
| static final int END_NEVER = 0; |
| static final int END_BY_DATE = 1; |
| static final int END_BY_COUNT = 2; |
| |
| static final int MONTHLY_BY_DATE = 0; |
| static final int MONTHLY_BY_NTH_DAY_OF_WEEK = 1; |
| |
| static final int STATE_NO_RECURRENCE = 0; |
| static final int STATE_RECURRENCE = 1; |
| |
| int recurrenceState; |
| |
| /** |
| * FREQ: Repeat pattern |
| * |
| * @see FREQ_DAILY |
| * @see FREQ_WEEKLY |
| * @see FREQ_MONTHLY |
| * @see FREQ_YEARLY |
| */ |
| int freq = FREQ_WEEKLY; |
| |
| /** |
| * INTERVAL: Every n days/weeks/months/years. n >= 1 |
| */ |
| int interval = INTERVAL_DEFAULT; |
| |
| /** |
| * UNTIL and COUNT: How does the the event end? |
| * |
| * @see END_NEVER |
| * @see END_BY_DATE |
| * @see END_BY_COUNT |
| * @see untilDate |
| * @see untilCount |
| */ |
| int end; |
| |
| /** |
| * UNTIL: Date of the last recurrence. Used when until == END_BY_DATE |
| */ |
| Time endDate; |
| |
| /** |
| * COUNT: Times to repeat. Use when until == END_BY_COUNT |
| */ |
| int endCount = COUNT_DEFAULT; |
| |
| /** |
| * BYDAY: Days of the week to be repeated. Sun = 0, Mon = 1, etc |
| */ |
| boolean[] weeklyByDayOfWeek = new boolean[7]; |
| |
| /** |
| * BYDAY AND BYMONTHDAY: How to repeat monthly events? Same date of the |
| * month or Same nth day of week. |
| * |
| * @see MONTHLY_BY_DATE |
| * @see MONTHLY_BY_NTH_DAY_OF_WEEK |
| */ |
| int monthlyRepeat; |
| |
| /** |
| * Day of the month to repeat. Used when monthlyRepeat == |
| * MONTHLY_BY_DATE |
| */ |
| int monthlyByMonthDay; |
| |
| /** |
| * Day of the week to repeat. Used when monthlyRepeat == |
| * MONTHLY_BY_NTH_DAY_OF_WEEK |
| */ |
| int monthlyByDayOfWeek; |
| |
| /** |
| * Nth day of the week to repeat. Used when monthlyRepeat == |
| * MONTHLY_BY_NTH_DAY_OF_WEEK 0=undefined, 1=1st, 2=2nd, etc |
| */ |
| int monthlyByNthDayOfWeek; |
| |
| /* |
| * (generated method) |
| */ |
| @Override |
| public String toString() { |
| return "Model [freq=" + freq + ", interval=" + interval + ", end=" + end + ", endDate=" |
| + endDate + ", endCount=" + endCount + ", weeklyByDayOfWeek=" |
| + Arrays.toString(weeklyByDayOfWeek) + ", monthlyRepeat=" + monthlyRepeat |
| + ", monthlyByMonthDay=" + monthlyByMonthDay + ", monthlyByDayOfWeek=" |
| + monthlyByDayOfWeek + ", monthlyByNthDayOfWeek=" + monthlyByNthDayOfWeek + "]"; |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| public RecurrenceModel() { |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(freq); |
| dest.writeInt(interval); |
| dest.writeInt(end); |
| dest.writeInt(endDate.year); |
| dest.writeInt(endDate.month); |
| dest.writeInt(endDate.monthDay); |
| dest.writeInt(endCount); |
| dest.writeBooleanArray(weeklyByDayOfWeek); |
| dest.writeInt(monthlyRepeat); |
| dest.writeInt(monthlyByMonthDay); |
| dest.writeInt(monthlyByDayOfWeek); |
| dest.writeInt(monthlyByNthDayOfWeek); |
| dest.writeInt(recurrenceState); |
| } |
| } |
| |
| class minMaxTextWatcher implements TextWatcher { |
| private int mMin; |
| private int mMax; |
| private int mDefault; |
| |
| public minMaxTextWatcher(int min, int defaultInt, int max) { |
| mMin = min; |
| mMax = max; |
| mDefault = defaultInt; |
| } |
| |
| @Override |
| public void afterTextChanged(Editable s) { |
| |
| boolean updated = false; |
| int value; |
| try { |
| value = Integer.parseInt(s.toString()); |
| } catch (NumberFormatException e) { |
| value = mDefault; |
| } |
| |
| if (value < mMin) { |
| value = mMin; |
| updated = true; |
| } else if (value > mMax) { |
| updated = true; |
| value = mMax; |
| } |
| |
| // Update UI |
| if (updated) { |
| s.clear(); |
| s.append(Integer.toString(value)); |
| } |
| |
| updateDoneButtonState(); |
| onChange(value); |
| } |
| |
| /** Override to be called after each key stroke */ |
| void onChange(int value) { |
| } |
| |
| @Override |
| public void beforeTextChanged(CharSequence s, int start, int count, int after) { |
| } |
| |
| @Override |
| public void onTextChanged(CharSequence s, int start, int before, int count) { |
| } |
| } |
| |
| private Resources mResources; |
| private EventRecurrence mRecurrence = new EventRecurrence(); |
| private Time mTime = new Time(); // TODO timezone? |
| private RecurrenceModel mModel = new RecurrenceModel(); |
| private Toast mToast; |
| |
| private final int[] TIME_DAY_TO_CALENDAR_DAY = new int[] { |
| Calendar.SUNDAY, |
| Calendar.MONDAY, |
| Calendar.TUESDAY, |
| Calendar.WEDNESDAY, |
| Calendar.THURSDAY, |
| Calendar.FRIDAY, |
| Calendar.SATURDAY, |
| }; |
| |
| // Call mStringBuilder.setLength(0) before formatting any string or else the |
| // formatted text will accumulate. |
| // private final StringBuilder mStringBuilder = new StringBuilder(); |
| // private Formatter mFormatter = new Formatter(mStringBuilder); |
| |
| private View mView; |
| |
| private Spinner mFreqSpinner; |
| private static final int[] mFreqModelToEventRecurrence = { |
| EventRecurrence.DAILY, |
| EventRecurrence.WEEKLY, |
| EventRecurrence.MONTHLY, |
| EventRecurrence.YEARLY |
| }; |
| |
| public static final String BUNDLE_START_TIME_MILLIS = "bundle_event_start_time"; |
| public static final String BUNDLE_TIME_ZONE = "bundle_event_time_zone"; |
| public static final String BUNDLE_RRULE = "bundle_event_rrule"; |
| |
| private static final String BUNDLE_MODEL = "bundle_model"; |
| private static final String BUNDLE_END_COUNT_HAS_FOCUS = "bundle_end_count_has_focus"; |
| |
| private static final String FRAG_TAG_DATE_PICKER = "tag_date_picker_frag"; |
| |
| private Switch mRepeatSwitch; |
| |
| private EditText mInterval; |
| private TextView mIntervalPreText; |
| private TextView mIntervalPostText; |
| |
| private int mIntervalResId = -1; |
| |
| private Spinner mEndSpinner; |
| private TextView mEndDateTextView; |
| private EditText mEndCount; |
| private TextView mPostEndCount; |
| private boolean mHidePostEndCount; |
| |
| private ArrayList<CharSequence> mEndSpinnerArray = new ArrayList<CharSequence>(3); |
| private EndSpinnerAdapter mEndSpinnerAdapter; |
| private String mEndNeverStr; |
| private String mEndDateLabel; |
| private String mEndCountLabel; |
| |
| /** Hold toggle buttons in the order per user's first day of week preference */ |
| private LinearLayout mWeekGroup; |
| private LinearLayout mWeekGroup2; |
| // Sun = 0 |
| private ToggleButton[] mWeekByDayButtons = new ToggleButton[7]; |
| /** A double array of Strings to hold the 7x5 list of possible strings of the form: |
| * "on every [Nth] [DAY_OF_WEEK]", e.g. "on every second Monday", |
| * where [Nth] can be [first, second, third, fourth, last] */ |
| private String[][] mMonthRepeatByDayOfWeekStrs; |
| |
| private LinearLayout mMonthGroup; |
| private RadioGroup mMonthRepeatByRadioGroup; |
| private RadioButton mRepeatMonthlyByNthDayOfWeek; |
| private RadioButton mRepeatMonthlyByNthDayOfMonth; |
| private String mMonthRepeatByDayOfWeekStr; |
| |
| private Button mDone; |
| |
| private OnRecurrenceSetListener mRecurrenceSetListener; |
| |
| public RecurrencePickerDialog() { |
| } |
| |
| static public boolean canHandleRecurrenceRule(EventRecurrence er) { |
| switch (er.freq) { |
| case EventRecurrence.DAILY: |
| case EventRecurrence.MONTHLY: |
| case EventRecurrence.YEARLY: |
| case EventRecurrence.WEEKLY: |
| break; |
| default: |
| return false; |
| } |
| |
| if (er.count > 0 && !TextUtils.isEmpty(er.until)) { |
| return false; |
| } |
| |
| // Weekly: For "repeat by day of week", the day of week to repeat is in |
| // er.byday[] |
| |
| /* |
| * Monthly: For "repeat by nth day of week" the day of week to repeat is |
| * in er.byday[] and the "nth" is stored in er.bydayNum[]. Currently we |
| * can handle only one and only in monthly |
| */ |
| int numOfByDayNum = 0; |
| for (int i = 0; i < er.bydayCount; i++) { |
| if (er.bydayNum[i] > 0) { |
| ++numOfByDayNum; |
| } |
| } |
| |
| if (numOfByDayNum > 1) { |
| return false; |
| } |
| |
| if (numOfByDayNum > 0 && er.freq != EventRecurrence.MONTHLY) { |
| return false; |
| } |
| |
| // The UI only handle repeat by one day of month i.e. not 9th and 10th |
| // of every month |
| if (er.bymonthdayCount > 1) { |
| return false; |
| } |
| |
| if (er.freq == EventRecurrence.MONTHLY) { |
| if (er.bydayCount > 1) { |
| return false; |
| } |
| if (er.bydayCount > 0 && er.bymonthdayCount > 0) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // TODO don't lose data when getting data that our UI can't handle |
| static private void copyEventRecurrenceToModel(final EventRecurrence er, |
| RecurrenceModel model) { |
| // Freq: |
| switch (er.freq) { |
| case EventRecurrence.DAILY: |
| model.freq = RecurrenceModel.FREQ_DAILY; |
| break; |
| case EventRecurrence.MONTHLY: |
| model.freq = RecurrenceModel.FREQ_MONTHLY; |
| break; |
| case EventRecurrence.YEARLY: |
| model.freq = RecurrenceModel.FREQ_YEARLY; |
| break; |
| case EventRecurrence.WEEKLY: |
| model.freq = RecurrenceModel.FREQ_WEEKLY; |
| break; |
| default: |
| throw new IllegalStateException("freq=" + er.freq); |
| } |
| |
| // Interval: |
| if (er.interval > 0) { |
| model.interval = er.interval; |
| } |
| |
| // End: |
| // End by count: |
| model.endCount = er.count; |
| if (model.endCount > 0) { |
| model.end = RecurrenceModel.END_BY_COUNT; |
| } |
| |
| // End by date: |
| if (!TextUtils.isEmpty(er.until)) { |
| if (model.endDate == null) { |
| model.endDate = new Time(); |
| } |
| |
| try { |
| model.endDate.parse(er.until); |
| } catch (TimeFormatException e) { |
| model.endDate = null; |
| } |
| |
| // LIMITATION: The UI can only handle END_BY_DATE or END_BY_COUNT |
| if (model.end == RecurrenceModel.END_BY_COUNT && model.endDate != null) { |
| throw new IllegalStateException("freq=" + er.freq); |
| } |
| |
| model.end = RecurrenceModel.END_BY_DATE; |
| } |
| |
| // Weekly: repeat by day of week or Monthly: repeat by nth day of week |
| // in the month |
| Arrays.fill(model.weeklyByDayOfWeek, false); |
| if (er.bydayCount > 0) { |
| int count = 0; |
| for (int i = 0; i < er.bydayCount; i++) { |
| int dayOfWeek = EventRecurrence.day2TimeDay(er.byday[i]); |
| model.weeklyByDayOfWeek[dayOfWeek] = true; |
| |
| if (model.freq == RecurrenceModel.FREQ_MONTHLY && er.bydayNum[i] > 0) { |
| // LIMITATION: Can handle only (one) weekDayNum and only |
| // when |
| // monthly |
| model.monthlyByDayOfWeek = dayOfWeek; |
| model.monthlyByNthDayOfWeek = er.bydayNum[i]; |
| model.monthlyRepeat = RecurrenceModel.MONTHLY_BY_NTH_DAY_OF_WEEK; |
| count++; |
| } |
| } |
| |
| if (model.freq == RecurrenceModel.FREQ_MONTHLY) { |
| if (er.bydayCount != 1) { |
| // Can't handle 1st Monday and 2nd Wed |
| throw new IllegalStateException("Can handle only 1 byDayOfWeek in monthly"); |
| } |
| if (count != 1) { |
| throw new IllegalStateException( |
| "Didn't specify which nth day of week to repeat for a monthly"); |
| } |
| } |
| } |
| |
| // Monthly by day of month |
| if (model.freq == RecurrenceModel.FREQ_MONTHLY) { |
| if (er.bymonthdayCount == 1) { |
| if (model.monthlyRepeat == RecurrenceModel.MONTHLY_BY_NTH_DAY_OF_WEEK) { |
| throw new IllegalStateException( |
| "Can handle only by monthday or by nth day of week, not both"); |
| } |
| model.monthlyByMonthDay = er.bymonthday[0]; |
| model.monthlyRepeat = RecurrenceModel.MONTHLY_BY_DATE; |
| } else if (er.bymonthCount > 1) { |
| // LIMITATION: Can handle only one month day |
| throw new IllegalStateException("Can handle only one bymonthday"); |
| } |
| } |
| } |
| |
| static private void copyModelToEventRecurrence(final RecurrenceModel model, |
| EventRecurrence er) { |
| if (model.recurrenceState == RecurrenceModel.STATE_NO_RECURRENCE) { |
| throw new IllegalStateException("There's no recurrence"); |
| } |
| |
| // Freq |
| er.freq = mFreqModelToEventRecurrence[model.freq]; |
| |
| // Interval |
| if (model.interval <= 1) { |
| er.interval = 0; |
| } else { |
| er.interval = model.interval; |
| } |
| |
| // End |
| switch (model.end) { |
| case RecurrenceModel.END_BY_DATE: |
| if (model.endDate != null) { |
| model.endDate.switchTimezone(Time.TIMEZONE_UTC); |
| model.endDate.normalize(false); |
| er.until = model.endDate.format2445(); |
| er.count = 0; |
| } else { |
| throw new IllegalStateException("end = END_BY_DATE but endDate is null"); |
| } |
| break; |
| case RecurrenceModel.END_BY_COUNT: |
| er.count = model.endCount; |
| er.until = null; |
| if (er.count <= 0) { |
| throw new IllegalStateException("count is " + er.count); |
| } |
| break; |
| default: |
| er.count = 0; |
| er.until = null; |
| break; |
| } |
| |
| // Weekly && monthly repeat patterns |
| er.bydayCount = 0; |
| er.bymonthdayCount = 0; |
| |
| switch (model.freq) { |
| case RecurrenceModel.FREQ_MONTHLY: |
| if (model.monthlyRepeat == RecurrenceModel.MONTHLY_BY_DATE) { |
| if (model.monthlyByMonthDay > 0) { |
| if (er.bymonthday == null || er.bymonthdayCount < 1) { |
| er.bymonthday = new int[1]; |
| } |
| er.bymonthday[0] = model.monthlyByMonthDay; |
| er.bymonthdayCount = 1; |
| } |
| } else if (model.monthlyRepeat == RecurrenceModel.MONTHLY_BY_NTH_DAY_OF_WEEK) { |
| if (model.monthlyByNthDayOfWeek <= 0) { |
| throw new IllegalStateException("month repeat by nth week but n is " |
| + model.monthlyByNthDayOfWeek); |
| } |
| int count = 1; |
| if (er.bydayCount < count || er.byday == null || er.bydayNum == null) { |
| er.byday = new int[count]; |
| er.bydayNum = new int[count]; |
| } |
| er.bydayCount = count; |
| er.byday[0] = EventRecurrence.timeDay2Day(model.monthlyByDayOfWeek); |
| er.bydayNum[0] = model.monthlyByNthDayOfWeek; |
| } |
| break; |
| case RecurrenceModel.FREQ_WEEKLY: |
| int count = 0; |
| for (int i = 0; i < 7; i++) { |
| if (model.weeklyByDayOfWeek[i]) { |
| count++; |
| } |
| } |
| |
| if (er.bydayCount < count || er.byday == null || er.bydayNum == null) { |
| er.byday = new int[count]; |
| er.bydayNum = new int[count]; |
| } |
| er.bydayCount = count; |
| |
| for (int i = 6; i >= 0; i--) { |
| if (model.weeklyByDayOfWeek[i]) { |
| er.bydayNum[--count] = 0; |
| er.byday[count] = EventRecurrence.timeDay2Day(i); |
| } |
| } |
| break; |
| } |
| |
| if (!canHandleRecurrenceRule(er)) { |
| throw new IllegalStateException("UI generated recurrence that it can't handle. ER:" |
| + er.toString() + " Model: " + model.toString()); |
| } |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| mRecurrence.wkst = EventRecurrence.timeDay2Day(Utils.getFirstDayOfWeek(getActivity())); |
| |
| getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); |
| |
| boolean endCountHasFocus = false; |
| if (savedInstanceState != null) { |
| RecurrenceModel m = (RecurrenceModel) savedInstanceState.get(BUNDLE_MODEL); |
| if (m != null) { |
| mModel = m; |
| } |
| endCountHasFocus = savedInstanceState.getBoolean(BUNDLE_END_COUNT_HAS_FOCUS); |
| } else { |
| Bundle b = getArguments(); |
| if (b != null) { |
| mTime.set(b.getLong(BUNDLE_START_TIME_MILLIS)); |
| |
| String tz = b.getString(BUNDLE_TIME_ZONE); |
| if (!TextUtils.isEmpty(tz)) { |
| mTime.timezone = tz; |
| } |
| mTime.normalize(false); |
| |
| // Time days of week: Sun=0, Mon=1, etc |
| mModel.weeklyByDayOfWeek[mTime.weekDay] = true; |
| String rrule = b.getString(BUNDLE_RRULE); |
| if (!TextUtils.isEmpty(rrule)) { |
| mModel.recurrenceState = RecurrenceModel.STATE_RECURRENCE; |
| mRecurrence.parse(rrule); |
| copyEventRecurrenceToModel(mRecurrence, mModel); |
| // Leave today's day of week as checked by default in weekly view. |
| if (mRecurrence.bydayCount == 0) { |
| mModel.weeklyByDayOfWeek[mTime.weekDay] = true; |
| } |
| } |
| |
| } else { |
| mTime.setToNow(); |
| } |
| } |
| |
| mResources = getResources(); |
| mView = inflater.inflate(R.layout.recurrencepicker, container, true); |
| |
| final Activity activity = getActivity(); |
| final Configuration config = activity.getResources().getConfiguration(); |
| |
| mRepeatSwitch = (Switch) mView.findViewById(R.id.repeat_switch); |
| mRepeatSwitch.setChecked(mModel.recurrenceState == RecurrenceModel.STATE_RECURRENCE); |
| mRepeatSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { |
| |
| @Override |
| public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
| mModel.recurrenceState = isChecked ? RecurrenceModel.STATE_RECURRENCE |
| : RecurrenceModel.STATE_NO_RECURRENCE; |
| togglePickerOptions(); |
| } |
| }); |
| |
| mFreqSpinner = (Spinner) mView.findViewById(R.id.freqSpinner); |
| mFreqSpinner.setOnItemSelectedListener(this); |
| ArrayAdapter<CharSequence> freqAdapter = ArrayAdapter.createFromResource(getActivity(), |
| R.array.recurrence_freq, R.layout.recurrencepicker_freq_item); |
| freqAdapter.setDropDownViewResource(R.layout.recurrencepicker_freq_item); |
| mFreqSpinner.setAdapter(freqAdapter); |
| |
| mInterval = (EditText) mView.findViewById(R.id.interval); |
| mInterval.addTextChangedListener(new minMaxTextWatcher(1, INTERVAL_DEFAULT, INTERVAL_MAX) { |
| @Override |
| void onChange(int v) { |
| if (mIntervalResId != -1 && mInterval.getText().toString().length() > 0) { |
| mModel.interval = v; |
| updateIntervalText(); |
| mInterval.requestLayout(); |
| } |
| } |
| }); |
| mIntervalPreText = (TextView) mView.findViewById(R.id.intervalPreText); |
| mIntervalPostText = (TextView) mView.findViewById(R.id.intervalPostText); |
| |
| mEndNeverStr = mResources.getString(R.string.recurrence_end_continously); |
| mEndDateLabel = mResources.getString(R.string.recurrence_end_date_label); |
| mEndCountLabel = mResources.getString(R.string.recurrence_end_count_label); |
| |
| mEndSpinnerArray.add(mEndNeverStr); |
| mEndSpinnerArray.add(mEndDateLabel); |
| mEndSpinnerArray.add(mEndCountLabel); |
| mEndSpinner = (Spinner) mView.findViewById(R.id.endSpinner); |
| mEndSpinner.setOnItemSelectedListener(this); |
| mEndSpinnerAdapter = new EndSpinnerAdapter(getActivity(), mEndSpinnerArray, |
| R.layout.recurrencepicker_freq_item, R.layout.recurrencepicker_end_text); |
| mEndSpinnerAdapter.setDropDownViewResource(R.layout.recurrencepicker_freq_item); |
| mEndSpinner.setAdapter(mEndSpinnerAdapter); |
| |
| mEndCount = (EditText) mView.findViewById(R.id.endCount); |
| mEndCount.addTextChangedListener(new minMaxTextWatcher(1, COUNT_DEFAULT, COUNT_MAX) { |
| @Override |
| void onChange(int v) { |
| if (mModel.endCount != v) { |
| mModel.endCount = v; |
| updateEndCountText(); |
| mEndCount.requestLayout(); |
| } |
| } |
| }); |
| mPostEndCount = (TextView) mView.findViewById(R.id.postEndCount); |
| |
| mEndDateTextView = (TextView) mView.findViewById(R.id.endDate); |
| mEndDateTextView.setOnClickListener(this); |
| if (mModel.endDate == null) { |
| mModel.endDate = new Time(mTime); |
| switch (mModel.freq) { |
| case RecurrenceModel.FREQ_DAILY: |
| case RecurrenceModel.FREQ_WEEKLY: |
| mModel.endDate.month += 1; |
| break; |
| case RecurrenceModel.FREQ_MONTHLY: |
| mModel.endDate.month += 3; |
| break; |
| case RecurrenceModel.FREQ_YEARLY: |
| mModel.endDate.year += 3; |
| break; |
| } |
| mModel.endDate.normalize(false); |
| } |
| |
| mWeekGroup = (LinearLayout) mView.findViewById(R.id.weekGroup); |
| mWeekGroup2 = (LinearLayout) mView.findViewById(R.id.weekGroup2); |
| |
| // In Calendar.java day of week order e.g Sun = 1 ... Sat = 7 |
| String[] dayOfWeekString = new DateFormatSymbols().getWeekdays(); |
| |
| mMonthRepeatByDayOfWeekStrs = new String[7][]; |
| // from Time.SUNDAY as 0 through Time.SATURDAY as 6 |
| mMonthRepeatByDayOfWeekStrs[0] = mResources.getStringArray(R.array.repeat_by_nth_sun); |
| mMonthRepeatByDayOfWeekStrs[1] = mResources.getStringArray(R.array.repeat_by_nth_mon); |
| mMonthRepeatByDayOfWeekStrs[2] = mResources.getStringArray(R.array.repeat_by_nth_tues); |
| mMonthRepeatByDayOfWeekStrs[3] = mResources.getStringArray(R.array.repeat_by_nth_wed); |
| mMonthRepeatByDayOfWeekStrs[4] = mResources.getStringArray(R.array.repeat_by_nth_thurs); |
| mMonthRepeatByDayOfWeekStrs[5] = mResources.getStringArray(R.array.repeat_by_nth_fri); |
| mMonthRepeatByDayOfWeekStrs[6] = mResources.getStringArray(R.array.repeat_by_nth_sat); |
| |
| // In Time.java day of week order e.g. Sun = 0 |
| int idx = Utils.getFirstDayOfWeek(getActivity()); |
| |
| // In Calendar.java day of week order e.g Sun = 1 ... Sat = 7 |
| dayOfWeekString = new DateFormatSymbols().getShortWeekdays(); |
| |
| int numOfButtonsInRow1; |
| int numOfButtonsInRow2; |
| |
| if (mResources.getConfiguration().screenWidthDp > MIN_SCREEN_WIDTH_FOR_SINGLE_ROW_WEEK) { |
| numOfButtonsInRow1 = 7; |
| numOfButtonsInRow2 = 0; |
| mWeekGroup2.setVisibility(View.GONE); |
| mWeekGroup2.getChildAt(3).setVisibility(View.GONE); |
| } else { |
| numOfButtonsInRow1 = 4; |
| numOfButtonsInRow2 = 3; |
| |
| mWeekGroup2.setVisibility(View.VISIBLE); |
| // Set rightmost button on the second row invisible so it takes up |
| // space and everything centers properly |
| mWeekGroup2.getChildAt(3).setVisibility(View.INVISIBLE); |
| } |
| |
| /* First row */ |
| for (int i = 0; i < 7; i++) { |
| if (i >= numOfButtonsInRow1) { |
| mWeekGroup.getChildAt(i).setVisibility(View.GONE); |
| continue; |
| } |
| |
| mWeekByDayButtons[idx] = (ToggleButton) mWeekGroup.getChildAt(i); |
| mWeekByDayButtons[idx].setTextOff(dayOfWeekString[TIME_DAY_TO_CALENDAR_DAY[idx]]); |
| mWeekByDayButtons[idx].setTextOn(dayOfWeekString[TIME_DAY_TO_CALENDAR_DAY[idx]]); |
| mWeekByDayButtons[idx].setOnCheckedChangeListener(this); |
| |
| if (++idx >= 7) { |
| idx = 0; |
| } |
| } |
| |
| /* 2nd Row */ |
| for (int i = 0; i < 3; i++) { |
| if (i >= numOfButtonsInRow2) { |
| mWeekGroup2.getChildAt(i).setVisibility(View.GONE); |
| continue; |
| } |
| mWeekByDayButtons[idx] = (ToggleButton) mWeekGroup2.getChildAt(i); |
| mWeekByDayButtons[idx].setTextOff(dayOfWeekString[TIME_DAY_TO_CALENDAR_DAY[idx]]); |
| mWeekByDayButtons[idx].setTextOn(dayOfWeekString[TIME_DAY_TO_CALENDAR_DAY[idx]]); |
| mWeekByDayButtons[idx].setOnCheckedChangeListener(this); |
| |
| if (++idx >= 7) { |
| idx = 0; |
| } |
| } |
| |
| mMonthGroup = (LinearLayout) mView.findViewById(R.id.monthGroup); |
| mMonthRepeatByRadioGroup = (RadioGroup) mView.findViewById(R.id.monthGroup); |
| mMonthRepeatByRadioGroup.setOnCheckedChangeListener(this); |
| mRepeatMonthlyByNthDayOfWeek = (RadioButton) mView |
| .findViewById(R.id.repeatMonthlyByNthDayOfTheWeek); |
| mRepeatMonthlyByNthDayOfMonth = (RadioButton) mView |
| .findViewById(R.id.repeatMonthlyByNthDayOfMonth); |
| |
| mDone = (Button) mView.findViewById(R.id.done); |
| mDone.setOnClickListener(this); |
| |
| togglePickerOptions(); |
| updateDialog(); |
| if (endCountHasFocus) { |
| mEndCount.requestFocus(); |
| } |
| return mView; |
| } |
| |
| private void togglePickerOptions() { |
| if (mModel.recurrenceState == RecurrenceModel.STATE_NO_RECURRENCE) { |
| mFreqSpinner.setEnabled(false); |
| mEndSpinner.setEnabled(false); |
| mIntervalPreText.setEnabled(false); |
| mInterval.setEnabled(false); |
| mIntervalPostText.setEnabled(false); |
| mMonthRepeatByRadioGroup.setEnabled(false); |
| mEndCount.setEnabled(false); |
| mPostEndCount.setEnabled(false); |
| mEndDateTextView.setEnabled(false); |
| mRepeatMonthlyByNthDayOfWeek.setEnabled(false); |
| mRepeatMonthlyByNthDayOfMonth.setEnabled(false); |
| for (Button button : mWeekByDayButtons) { |
| button.setEnabled(false); |
| } |
| } else { |
| mView.findViewById(R.id.options).setEnabled(true); |
| mFreqSpinner.setEnabled(true); |
| mEndSpinner.setEnabled(true); |
| mIntervalPreText.setEnabled(true); |
| mInterval.setEnabled(true); |
| mIntervalPostText.setEnabled(true); |
| mMonthRepeatByRadioGroup.setEnabled(true); |
| mEndCount.setEnabled(true); |
| mPostEndCount.setEnabled(true); |
| mEndDateTextView.setEnabled(true); |
| mRepeatMonthlyByNthDayOfWeek.setEnabled(true); |
| mRepeatMonthlyByNthDayOfMonth.setEnabled(true); |
| for (Button button : mWeekByDayButtons) { |
| button.setEnabled(true); |
| } |
| } |
| updateDoneButtonState(); |
| } |
| |
| private void updateDoneButtonState() { |
| if (mModel.recurrenceState == RecurrenceModel.STATE_NO_RECURRENCE) { |
| mDone.setEnabled(true); |
| return; |
| } |
| |
| if (mInterval.getText().toString().length() == 0) { |
| mDone.setEnabled(false); |
| return; |
| } |
| |
| if (mEndCount.getVisibility() == View.VISIBLE && |
| mEndCount.getText().toString().length() == 0) { |
| mDone.setEnabled(false); |
| return; |
| } |
| |
| if (mModel.freq == RecurrenceModel.FREQ_WEEKLY) { |
| for (CompoundButton b : mWeekByDayButtons) { |
| if (b.isChecked()) { |
| mDone.setEnabled(true); |
| return; |
| } |
| } |
| mDone.setEnabled(false); |
| return; |
| } |
| |
| mDone.setEnabled(true); |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putParcelable(BUNDLE_MODEL, mModel); |
| if (mEndCount.hasFocus()) { |
| outState.putBoolean(BUNDLE_END_COUNT_HAS_FOCUS, true); |
| } |
| } |
| |
| public void updateDialog() { |
| // Interval |
| // Checking before setting because this causes infinite recursion |
| // in afterTextWatcher |
| final String intervalStr = Integer.toString(mModel.interval); |
| if (!intervalStr.equals(mInterval.getText().toString())) { |
| mInterval.setText(intervalStr); |
| } |
| |
| mFreqSpinner.setSelection(mModel.freq); |
| mWeekGroup.setVisibility(mModel.freq == RecurrenceModel.FREQ_WEEKLY ? View.VISIBLE : View.GONE); |
| mWeekGroup2.setVisibility(mModel.freq == RecurrenceModel.FREQ_WEEKLY ? View.VISIBLE : View.GONE); |
| mMonthGroup.setVisibility(mModel.freq == RecurrenceModel.FREQ_MONTHLY ? View.VISIBLE : View.GONE); |
| |
| switch (mModel.freq) { |
| case RecurrenceModel.FREQ_DAILY: |
| mIntervalResId = R.plurals.recurrence_interval_daily; |
| break; |
| |
| case RecurrenceModel.FREQ_WEEKLY: |
| mIntervalResId = R.plurals.recurrence_interval_weekly; |
| for (int i = 0; i < 7; i++) { |
| mWeekByDayButtons[i].setChecked(mModel.weeklyByDayOfWeek[i]); |
| } |
| break; |
| |
| case RecurrenceModel.FREQ_MONTHLY: |
| mIntervalResId = R.plurals.recurrence_interval_monthly; |
| |
| if (mModel.monthlyRepeat == RecurrenceModel.MONTHLY_BY_DATE) { |
| mMonthRepeatByRadioGroup.check(R.id.repeatMonthlyByNthDayOfMonth); |
| } else if (mModel.monthlyRepeat == RecurrenceModel.MONTHLY_BY_NTH_DAY_OF_WEEK) { |
| mMonthRepeatByRadioGroup.check(R.id.repeatMonthlyByNthDayOfTheWeek); |
| } |
| |
| if (mMonthRepeatByDayOfWeekStr == null) { |
| if (mModel.monthlyByNthDayOfWeek == 0) { |
| mModel.monthlyByNthDayOfWeek = (mTime.monthDay + 6) / 7; |
| mModel.monthlyByDayOfWeek = mTime.weekDay; |
| } |
| |
| String[] monthlyByNthDayOfWeekStrs = |
| mMonthRepeatByDayOfWeekStrs[mModel.monthlyByDayOfWeek]; |
| mMonthRepeatByDayOfWeekStr = |
| monthlyByNthDayOfWeekStrs[mModel.monthlyByNthDayOfWeek - 1]; |
| mRepeatMonthlyByNthDayOfWeek.setText(mMonthRepeatByDayOfWeekStr); |
| } |
| break; |
| |
| case RecurrenceModel.FREQ_YEARLY: |
| mIntervalResId = R.plurals.recurrence_interval_yearly; |
| break; |
| } |
| updateIntervalText(); |
| updateDoneButtonState(); |
| |
| mEndSpinner.setSelection(mModel.end); |
| if (mModel.end == RecurrenceModel.END_BY_DATE) { |
| final String dateStr = DateUtils.formatDateTime(getActivity(), |
| mModel.endDate.toMillis(false), DateUtils.FORMAT_NUMERIC_DATE); |
| mEndDateTextView.setText(dateStr); |
| } else { |
| if (mModel.end == RecurrenceModel.END_BY_COUNT) { |
| // Checking before setting because this causes infinite |
| // recursion |
| // in afterTextWatcher |
| final String countStr = Integer.toString(mModel.endCount); |
| if (!countStr.equals(mEndCount.getText().toString())) { |
| mEndCount.setText(countStr); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param endDateString |
| */ |
| private void setEndSpinnerEndDateStr(final String endDateString) { |
| mEndSpinnerArray.set(1, endDateString); |
| mEndSpinnerAdapter.notifyDataSetChanged(); |
| } |
| |
| private void doToast() { |
| Log.e(TAG, "Model = " + mModel.toString()); |
| String rrule; |
| if (mModel.recurrenceState == RecurrenceModel.STATE_NO_RECURRENCE) { |
| rrule = "Not repeating"; |
| } else { |
| copyModelToEventRecurrence(mModel, mRecurrence); |
| rrule = mRecurrence.toString(); |
| } |
| |
| if (mToast != null) { |
| mToast.cancel(); |
| } |
| mToast = Toast.makeText(getActivity(), rrule, |
| Toast.LENGTH_LONG); |
| mToast.show(); |
| } |
| |
| // TODO Test and update for Right-to-Left |
| private void updateIntervalText() { |
| if (mIntervalResId == -1) { |
| return; |
| } |
| |
| final String INTERVAL_COUNT_MARKER = "%d"; |
| String intervalString = mResources.getQuantityString(mIntervalResId, mModel.interval); |
| int markerStart = intervalString.indexOf(INTERVAL_COUNT_MARKER); |
| |
| if (markerStart != -1) { |
| if (markerStart == 0) { |
| mIntervalPreText.setText(""); |
| } else { |
| int postTextStart = markerStart + INTERVAL_COUNT_MARKER.length(); |
| if (postTextStart < intervalString.length() && |
| intervalString.charAt(postTextStart) == ' ') { |
| postTextStart++; |
| } |
| mIntervalPostText.setText(intervalString.subSequence(postTextStart, |
| intervalString.length())); |
| |
| if (intervalString.charAt(markerStart - 1) == ' ') { |
| markerStart--; |
| } |
| mIntervalPreText.setText(intervalString.subSequence(0, markerStart)); |
| } |
| } |
| } |
| |
| /** |
| * Update the "Repeat for N events" end option with the proper string values |
| * based on the value that has been entered for N. |
| */ |
| private void updateEndCountText() { |
| final String END_COUNT_MARKER = "%d"; |
| String endString = mResources.getQuantityString(R.plurals.recurrence_end_count, |
| mModel.endCount); |
| int markerStart = endString.indexOf(END_COUNT_MARKER); |
| |
| if (markerStart != -1) { |
| if (markerStart == 0) { |
| Log.e(TAG, "No text to put in to recurrence's end spinner."); |
| } else { |
| int postTextStart = markerStart + END_COUNT_MARKER.length(); |
| if (postTextStart < endString.length() && |
| endString.charAt(postTextStart) == ' ') { |
| postTextStart++; |
| } |
| mPostEndCount.setText(endString.subSequence(postTextStart, |
| endString.length())); |
| } |
| } |
| } |
| |
| // Implements OnItemSelectedListener interface |
| // Freq spinner |
| // End spinner |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |
| if (parent == mFreqSpinner) { |
| mModel.freq = position; |
| } else if (parent == mEndSpinner) { |
| switch (position) { |
| case RecurrenceModel.END_NEVER: |
| mModel.end = RecurrenceModel.END_NEVER; |
| break; |
| case RecurrenceModel.END_BY_DATE: |
| mModel.end = RecurrenceModel.END_BY_DATE; |
| break; |
| case RecurrenceModel.END_BY_COUNT: |
| mModel.end = RecurrenceModel.END_BY_COUNT; |
| |
| if (mModel.endCount <= 1) { |
| mModel.endCount = 1; |
| } else if (mModel.endCount > COUNT_MAX) { |
| mModel.endCount = COUNT_MAX; |
| } |
| updateEndCountText(); |
| break; |
| } |
| mEndCount.setVisibility(mModel.end == RecurrenceModel.END_BY_COUNT ? View.VISIBLE |
| : View.GONE); |
| mEndDateTextView.setVisibility(mModel.end == RecurrenceModel.END_BY_DATE ? View.VISIBLE |
| : View.GONE); |
| mPostEndCount.setVisibility( |
| mModel.end == RecurrenceModel.END_BY_COUNT && !mHidePostEndCount? |
| View.VISIBLE : View.GONE); |
| |
| } |
| updateDialog(); |
| } |
| |
| // Implements OnItemSelectedListener interface |
| @Override |
| public void onNothingSelected(AdapterView<?> arg0) { |
| } |
| |
| @Override |
| public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) { |
| if (mModel.endDate == null) { |
| mModel.endDate = new Time(mTime.timezone); |
| mModel.endDate.hour = mModel.endDate.minute = mModel.endDate.second = 0; |
| } |
| mModel.endDate.year = year; |
| mModel.endDate.month = monthOfYear; |
| mModel.endDate.monthDay = dayOfMonth; |
| mModel.endDate.normalize(false); |
| updateDialog(); |
| } |
| |
| // Implements OnCheckedChangeListener interface |
| // Week repeat by day of week |
| @Override |
| public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
| int itemIdx = -1; |
| for (int i = 0; i < 7; i++) { |
| if (itemIdx == -1 && buttonView == mWeekByDayButtons[i]) { |
| itemIdx = i; |
| mModel.weeklyByDayOfWeek[i] = isChecked; |
| } |
| } |
| updateDialog(); |
| } |
| |
| // Implements android.widget.RadioGroup.OnCheckedChangeListener interface |
| // Month repeat by radio buttons |
| @Override |
| public void onCheckedChanged(RadioGroup group, int checkedId) { |
| if (checkedId == R.id.repeatMonthlyByNthDayOfMonth) { |
| mModel.monthlyRepeat = RecurrenceModel.MONTHLY_BY_DATE; |
| } else if (checkedId == R.id.repeatMonthlyByNthDayOfTheWeek) { |
| mModel.monthlyRepeat = RecurrenceModel.MONTHLY_BY_NTH_DAY_OF_WEEK; |
| } |
| updateDialog(); |
| } |
| |
| // Implements OnClickListener interface |
| // EndDate button |
| // Done button |
| @Override |
| public void onClick(View v) { |
| if (mEndDateTextView == v) { |
| if (mDatePickerDialog != null) { |
| mDatePickerDialog.dismiss(); |
| } |
| mDatePickerDialog = DatePickerDialog.newInstance(this, mModel.endDate.year, |
| mModel.endDate.month, mModel.endDate.monthDay); |
| mDatePickerDialog.setFirstDayOfWeek(Utils.getFirstDayOfWeekAsCalendar(getActivity())); |
| mDatePickerDialog.setYearRange(Utils.YEAR_MIN, Utils.YEAR_MAX); |
| mDatePickerDialog.show(getFragmentManager(), FRAG_TAG_DATE_PICKER); |
| } else if (mDone == v) { |
| String rrule; |
| if (mModel.recurrenceState == RecurrenceModel.STATE_NO_RECURRENCE) { |
| rrule = null; |
| } else { |
| copyModelToEventRecurrence(mModel, mRecurrence); |
| rrule = mRecurrence.toString(); |
| } |
| mRecurrenceSetListener.onRecurrenceSet(rrule); |
| dismiss(); |
| } |
| } |
| |
| @Override |
| public void onActivityCreated(Bundle savedInstanceState) { |
| super.onActivityCreated(savedInstanceState); |
| mDatePickerDialog = (DatePickerDialog) getFragmentManager() |
| .findFragmentByTag(FRAG_TAG_DATE_PICKER); |
| if (mDatePickerDialog != null) { |
| mDatePickerDialog.setOnDateSetListener(this); |
| } |
| } |
| |
| public interface OnRecurrenceSetListener { |
| void onRecurrenceSet(String rrule); |
| } |
| |
| public void setOnRecurrenceSetListener(OnRecurrenceSetListener l) { |
| mRecurrenceSetListener = l; |
| } |
| |
| private class EndSpinnerAdapter extends ArrayAdapter<CharSequence> { |
| final String END_DATE_MARKER = "%s"; |
| final String END_COUNT_MARKER = "%d"; |
| |
| private LayoutInflater mInflater; |
| private int mItemResourceId; |
| private int mTextResourceId; |
| private ArrayList<CharSequence> mStrings; |
| private String mEndDateString; |
| private boolean mUseFormStrings; |
| |
| /** |
| * @param context |
| * @param textViewResourceId |
| * @param objects |
| */ |
| public EndSpinnerAdapter(Context context, ArrayList<CharSequence> strings, |
| int itemResourceId, int textResourceId) { |
| super(context, itemResourceId, strings); |
| mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
| mItemResourceId = itemResourceId; |
| mTextResourceId = textResourceId; |
| mStrings = strings; |
| mEndDateString = getResources().getString(R.string.recurrence_end_date); |
| |
| // If either date or count strings don't translate well, such that we aren't assured |
| // to have some text available to be placed in the spinner, then we'll have to use |
| // the more form-like versions of both strings instead. |
| int markerStart = mEndDateString.indexOf(END_DATE_MARKER); |
| if (markerStart <= 0) { |
| // The date string does not have any text before the "%s" so we'll have to use the |
| // more form-like strings instead. |
| mUseFormStrings = true; |
| } else { |
| String countEndStr = getResources().getQuantityString( |
| R.plurals.recurrence_end_count, 1); |
| markerStart = countEndStr.indexOf(END_COUNT_MARKER); |
| if (markerStart <= 0) { |
| // The count string does not have any text before the "%d" so we'll have to use |
| // the more form-like strings instead. |
| mUseFormStrings = true; |
| } |
| } |
| |
| if (mUseFormStrings) { |
| // We'll have to set the layout for the spinner to be weight=0 so it doesn't |
| // take up too much space. |
| mEndSpinner.setLayoutParams( |
| new TableLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)); |
| } |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| View v; |
| // Check if we can recycle the view |
| if (convertView == null) { |
| v = mInflater.inflate(mTextResourceId, parent, false); |
| } else { |
| v = convertView; |
| } |
| |
| TextView item = (TextView) v.findViewById(R.id.spinner_item); |
| int markerStart; |
| switch (position) { |
| case RecurrenceModel.END_NEVER: |
| item.setText(mStrings.get(RecurrenceModel.END_NEVER)); |
| break; |
| case RecurrenceModel.END_BY_DATE: |
| markerStart = mEndDateString.indexOf(END_DATE_MARKER); |
| |
| if (markerStart != -1) { |
| if (mUseFormStrings || markerStart == 0) { |
| // If we get here, the translation of "Until" doesn't work correctly, |
| // so we'll just set the whole "Until a date" string. |
| item.setText(mEndDateLabel); |
| } else { |
| if (mEndDateString.charAt(markerStart - 1) == ' ') { |
| markerStart--; |
| } |
| item.setText(mEndDateString.subSequence(0, markerStart)); |
| } |
| } |
| break; |
| case RecurrenceModel.END_BY_COUNT: |
| String endString = mResources.getQuantityString(R.plurals.recurrence_end_count, |
| mModel.endCount); |
| markerStart = endString.indexOf(END_COUNT_MARKER); |
| |
| if (markerStart != -1) { |
| if (mUseFormStrings || markerStart == 0) { |
| // If we get here, the translation of "For" doesn't work correctly, |
| // so we'll just set the whole "For a number of events" string. |
| item.setText(mEndCountLabel); |
| // Also, we'll hide the " events" that would have been at the end. |
| mPostEndCount.setVisibility(View.GONE); |
| // Use this flag so the onItemSelected knows whether to show it later. |
| mHidePostEndCount = true; |
| } else { |
| int postTextStart = markerStart + END_COUNT_MARKER.length(); |
| if (endString.charAt(postTextStart) == ' ') { |
| postTextStart++; |
| } |
| mPostEndCount.setText(endString.subSequence(postTextStart, |
| endString.length())); |
| // In case it's a recycled view that wasn't visible. |
| if (mModel.end == RecurrenceModel.END_BY_COUNT) { |
| mPostEndCount.setVisibility(View.VISIBLE); |
| } |
| if (endString.charAt(markerStart - 1) == ' ') { |
| markerStart--; |
| } |
| item.setText(endString.subSequence(0, markerStart)); |
| } |
| } |
| break; |
| default: |
| v = null; |
| break; |
| } |
| |
| return v; |
| } |
| |
| @Override |
| public View getDropDownView(int position, View convertView, ViewGroup parent) { |
| View v; |
| // Check if we can recycle the view |
| if (convertView == null) { |
| v = mInflater.inflate(mItemResourceId, parent, false); |
| } else { |
| v = convertView; |
| } |
| |
| TextView item = (TextView) v.findViewById(R.id.spinner_item); |
| item.setText(mStrings.get(position)); |
| |
| return v; |
| } |
| } |
| } |