/******************************************************************************* * Copyright (c) 2010 Denis Solonenko. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Denis Solonenko - initial API and implementation ******************************************************************************/ package ru.orangesoftware.financisto.activity; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.SpinnerAdapter; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; import java.text.DateFormat; import java.util.Calendar; import ru.orangesoftware.financisto.R; import ru.orangesoftware.financisto.datetime.DateUtils; import ru.orangesoftware.financisto.utils.LocalizableEnum; import ru.orangesoftware.financisto.utils.MyPreferences; import ru.orangesoftware.financisto.utils.PinProtection; import ru.orangesoftware.financisto.utils.RecurUtils; import ru.orangesoftware.financisto.utils.RecurUtils.DayOfWeek; import ru.orangesoftware.financisto.utils.RecurUtils.EveryXDay; import ru.orangesoftware.financisto.utils.RecurUtils.Layoutable; import ru.orangesoftware.financisto.utils.RecurUtils.Recur; import ru.orangesoftware.financisto.utils.RecurUtils.RecurInterval; import ru.orangesoftware.financisto.utils.RecurUtils.RecurPeriod; import ru.orangesoftware.financisto.utils.RecurUtils.SemiMonthly; import ru.orangesoftware.financisto.utils.RecurUtils.Weekly; import ru.orangesoftware.financisto.utils.Utils; import static ru.orangesoftware.financisto.activity.UiUtils.applyTheme; public class RecurActivity extends Activity { public static final String EXTRA_RECUR = "recur"; private static final RecurPeriod[] periods = RecurPeriod.values(); private Spinner sInterval; private Spinner sPeriod; private LinearLayout layoutInterval; private LinearLayout layoutRecur; private Button bStartDate; private final Calendar startDate = Calendar.getInstance(); private final Calendar stopsOnDate = Calendar.getInstance(); private DateFormat df; @Override protected void attachBaseContext(Context base) { super.attachBaseContext(MyPreferences.switchLocale(base)); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_LEFT_ICON); setContentView(R.layout.recur); setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.ic_dialog_time); df = DateUtils.getLongDateFormat(this); stopsOnDate.add(Calendar.YEAR, 1); sInterval = findViewById(R.id.intervalSpinner); sPeriod = findViewById(R.id.recurSpinner); layoutInterval = findViewById(R.id.layoutInterval); layoutRecur = findViewById(R.id.recurInterval); bStartDate = findViewById(R.id.bStartDate); bStartDate.setOnClickListener(v -> { final Calendar c = startDate; DatePickerDialog dialog = DatePickerDialog.newInstance( (view, year, monthOfYear, dayOfMonth) -> { c.set(Calendar.YEAR, year); c.set(Calendar.MONTH, monthOfYear); c.set(Calendar.DAY_OF_MONTH, dayOfMonth); DateUtils.startOfDay(c); editStartDate(c.getTimeInMillis()); }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH) ); applyTheme(this, dialog); dialog.show(getFragmentManager(), "DatePickerDialog"); }); addSpinnerItems(sInterval, new RecurInterval[]{RecurInterval.NO_RECUR, RecurInterval.WEEKLY, RecurInterval.MONTHLY}); addSpinnerItems(sPeriod, periods); LayoutInflater inflater = getLayoutInflater(); //addLayouts(inflater, layoutInterval, intervals); addLayouts(inflater, layoutRecur, periods); Recur recur = RecurUtils.createDefaultRecur(); Intent intent = getIntent(); if (intent != null) { String extra = intent.getStringExtra(EXTRA_RECUR); if (extra != null) { recur = RecurUtils.createFromExtraString(extra); } } editRecur(recur); sInterval.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { RecurInterval interval = getRecurInterval(sInterval.getSelectedItem()); selectInterval(interval); } @Override public void onNothingSelected(AdapterView<?> arg0) { } }); sPeriod.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { RecurPeriod period = periods[position]; selectPeriod(period); } @Override public void onNothingSelected(AdapterView<?> arg0) { } }); Button bOk = findViewById(R.id.bOK); bOk.setOnClickListener(view -> { RecurInterval interval = getRecurInterval(sInterval.getSelectedItem()); RecurPeriod period = periods[sPeriod.getSelectedItemPosition()]; Recur r = RecurUtils.createRecur(interval); r.startDate = startDate.getTimeInMillis(); r.period = period; if (updateInterval(r) && updatePeriod(r)) { Intent data = new Intent(); data.putExtra(EXTRA_RECUR, r.toString()); setResult(RESULT_OK, data); finish(); } }); Button bCancel = findViewById(R.id.bCancel); bCancel.setOnClickListener(view -> { setResult(RESULT_CANCELED, null); finish(); }); } private static class SpinnerItem { public final String title; public final String value; public SpinnerItem(String title, String value) { super(); this.title = title; this.value = value; } @Override public String toString() { return title; } } private void addSpinnerItems(Spinner spinner, LocalizableEnum[] a) { int length = a.length; SpinnerItem[] items = new SpinnerItem[length]; for (int i = 0; i < length; i++) { LocalizableEnum x = a[i]; String title = getString(x.getTitleId()); String value = x.name(); items[i] = new SpinnerItem(title, value); } ArrayAdapter<SpinnerItem> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, items); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); } protected RecurInterval getRecurInterval(Object item) { return RecurInterval.valueOf(((SpinnerItem) item).value); } protected boolean updateInterval(Recur r) { RecurInterval interval = r.interval; View v = selectInterval(interval); switch (interval) { case EVERY_X_DAY: return updateEveryXDay(v, r); case WEEKLY: return updateWeekly(v, r); case SEMI_MONTHLY: return updateSemiMonthly(v, r); } return true; } protected boolean updatePeriod(Recur r) { RecurPeriod period = r.period; View v = selectPeriod(period); switch (period) { case EXACTLY_TIMES: return updateExactlyTimes(v, r); case STOPS_ON_DATE: return updateStopsOnDate(v, r); } return true; } private void addLayouts(LayoutInflater inflater, LinearLayout layout, Layoutable[] items) { for (Layoutable i : items) { int layoutId = i.getLayoutId(); if (layoutId != 0) { final View v = inflater.inflate(layoutId, null); v.setTag(i); v.setVisibility(View.INVISIBLE); if (i == RecurPeriod.STOPS_ON_DATE) { Button b = v.findViewById(R.id.bStopsOnDate); final Calendar c = this.stopsOnDate; editStopsOnDate(v, c.getTimeInMillis()); b.setOnClickListener(view -> { DatePickerDialog dialog = DatePickerDialog.newInstance( (view1, year, monthOfYear, dayOfMonth) -> { c.set(Calendar.YEAR, year); c.set(Calendar.MONTH, monthOfYear); c.set(Calendar.DAY_OF_MONTH, dayOfMonth); DateUtils.endOfDay(c); editStopsOnDate(v, c.getTimeInMillis()); }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH) ); applyTheme(RecurActivity.this, dialog); dialog.show(getFragmentManager(), "DatePickerDialog"); }); } layout.addView(v, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); } } } private void editRecur(Recur recur) { editStartDate(recur.startDate); RecurInterval interval = recur.interval; SpinnerAdapter adapter = sInterval.getAdapter(); int count = adapter.getCount(); for (int i = 0; i < count; i++) { SpinnerItem item = (SpinnerItem) adapter.getItem(i); if (interval == RecurInterval.valueOf(item.value)) { sInterval.setSelection(i); break; } } View v = selectInterval(interval); switch (interval) { case EVERY_X_DAY: editEveryXDay(v, recur); break; case WEEKLY: editWeekly(v, recur); break; case SEMI_MONTHLY: editSemiMonthly(v, recur); break; } RecurPeriod period = recur.period; sPeriod.setSelection(period.ordinal()); v = selectPeriod(period); switch (period) { case EXACTLY_TIMES: editExactlyTimes(v, recur.periodParam); break; case STOPS_ON_DATE: editStopsOnDate(v, recur.periodParam); break; } } private void editEveryXDay(View v, Recur recur) { EveryXDay x = (EveryXDay) recur; EditText t = v.findViewById(R.id.edEveryXDays); t.setText(String.valueOf(x.days)); } private void editWeekly(View v, Recur recur) { return; } private void editSemiMonthly(View v, Recur recur) { SemiMonthly sm = (SemiMonthly) recur; EditText t1 = v.findViewById(R.id.edFirstDay); t1.setText(String.valueOf(sm.firstDay)); EditText t2 = v.findViewById(R.id.edSecondDay); t2.setText(String.valueOf(sm.secondDay)); } private boolean updateEveryXDay(View v, Recur r) { EveryXDay x = (EveryXDay) r; EditText t = v.findViewById(R.id.edEveryXDays); if (Utils.isEmpty(t)) { showError(t, R.string.recur_error_specify_days); return false; } x.days = Integer.parseInt(Utils.text(t)); return true; } private void showError(EditText t, int messageId) { t.setError(getString(messageId)); } private boolean updateWeekly(View v, Recur r) { Weekly w = (Weekly) r; int i = startDate.get(Calendar.DAY_OF_WEEK); DayOfWeek[] days = DayOfWeek.values(); for (DayOfWeek d : days) { w.unset(d); } w.set(days[i - 1]); return true; } private boolean updateSemiMonthly(View v, Recur r) { SemiMonthly sm = (SemiMonthly) r; EditText t1 = v.findViewById(R.id.edFirstDay); if (Utils.isEmpty(t1)) { showError(t1, R.string.recur_error_specify_first_day); return false; } sm.firstDay = Integer.parseInt(Utils.text(t1)); EditText t2 = v.findViewById(R.id.edSecondDay); if (Utils.isEmpty(t2)) { showError(t2, R.string.recur_error_specify_second_day); return false; } sm.secondDay = Integer.parseInt(Utils.text(t2)); return true; } private void editExactlyTimes(View v, long times) { EditText e = v.findViewById(R.id.edTimes); e.setText(times > 0 ? String.valueOf(times) : "1"); } private void editStartDate(long date) { Calendar c = startDate; c.setTimeInMillis(date); bStartDate.setText(df.format(c.getTime())); } private void editStopsOnDate(View v, long date) { Calendar c = stopsOnDate; c.setTimeInMillis(date); Button b = v.findViewById(R.id.bStopsOnDate); b.setText(df.format(c.getTime())); } private boolean updateExactlyTimes(View v, Recur r) { EditText e = v.findViewById(R.id.edTimes); if (Utils.isEmpty(e)) { showError(e, R.string.recur_error_specify_times); return false; } r.periodParam = Long.parseLong(Utils.text(e)); return true; } private boolean updateStopsOnDate(View v, Recur r) { r.periodParam = stopsOnDate.getTimeInMillis(); return true; } protected View selectInterval(RecurInterval interval) { if (interval == RecurInterval.NO_RECUR) { sPeriod.setSelection(RecurPeriod.STOPS_ON_DATE.ordinal()); sPeriod.setEnabled(false); } else { sPeriod.setEnabled(true); } return selectInLayout(layoutInterval, interval); } protected View selectPeriod(RecurPeriod period) { return selectInLayout(layoutRecur, period); } private View selectInLayout(LinearLayout layout, Object tag) { View selected = null; int count = layout.getChildCount(); for (int i = 0; i < count; i++) { View v = layout.getChildAt(i); if (tag == v.getTag()) { selected = v; } else { v.setVisibility(View.GONE); } } if (selected != null) { selected.setVisibility(View.VISIBLE); } return selected; } @Override protected void onPause() { super.onPause(); PinProtection.lock(this); } @Override protected void onResume() { super.onResume(); PinProtection.unlock(this); } }