com.todoroo.astrid.ui.ReminderControlSet.java Source code

Java tutorial

Introduction

Here is the source code for com.todoroo.astrid.ui.ReminderControlSet.java

Source

/**
 * Copyright (c) 2012 Todoroo Inc
 *
 * See the file "LICENSE" for the full license governing this code.
 */
package com.todoroo.astrid.ui;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

import com.google.common.primitives.Longs;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.alarms.AlarmFields;
import com.todoroo.astrid.alarms.AlarmService;
import com.todoroo.astrid.data.Task;

import org.tasks.R;
import org.tasks.activities.DateAndTimePickerActivity;
import org.tasks.activities.TimePickerActivity;
import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent;
import org.tasks.location.Geofence;
import org.tasks.location.GeofenceService;
import org.tasks.location.PlacePicker;
import org.tasks.preferences.Device;
import org.tasks.preferences.FragmentPermissionRequestor;
import org.tasks.preferences.PermissionRequestor;
import org.tasks.preferences.Preferences;
import org.tasks.ui.HiddenTopArrayAdapter;
import org.tasks.ui.TaskEditControlFragment;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import butterknife.BindView;
import butterknife.OnClick;
import butterknife.OnItemSelected;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static com.todoroo.andlib.utility.DateUtilities.getLongDateStringWithTime;
import static org.tasks.date.DateTimeUtils.newDateTime;

/**
 * Control set dealing with reminder settings
 *
 * @author Tim Su <tim@todoroo.com>
 *
 */
public class ReminderControlSet extends TaskEditControlFragment {

    public static final int TAG = R.string.TEA_ctrl_reminders_pref;

    private static final int REQUEST_NEW_ALARM = 12152;
    private static final int REQUEST_LOCATION_REMINDER = 12153;

    private static final String EXTRA_TASK_ID = "extra_task_id";
    private static final String EXTRA_FLAGS = "extra_flags";
    private static final String EXTRA_RANDOM_REMINDER = "extra_random_reminder";
    private static final String EXTRA_ALARMS = "extra_alarms";
    private static final String EXTRA_GEOFENCES = "extra_geofences";

    @Inject
    AlarmService alarmService;
    @Inject
    GeofenceService geofenceService;
    @Inject
    FragmentPermissionRequestor permissionRequestor;
    @Inject
    Device device;
    @Inject
    Preferences preferences;
    @Inject
    @ForActivity
    Context context;

    @BindView(R.id.alert_container)
    LinearLayout alertContainer;
    @BindView(R.id.reminder_alarm)
    Spinner mode;
    @BindView(R.id.alarms_add_spinner)
    Spinner addSpinner;

    private long taskId;
    private int flags;
    private long randomReminder;
    private int ringMode;

    private RandomReminderControlSet randomControlSet;
    private boolean whenDue;
    private boolean whenOverdue;
    private final List<String> spinnerOptions = new ArrayList<>();
    private ArrayAdapter<String> remindAdapter;
    private final Set<Long> alarms = new LinkedHashSet<>();
    private final Set<Geofence> geofences = new LinkedHashSet<>();

    @OnItemSelected(R.id.reminder_alarm)
    void ringModeSelected(int position) {
        ringMode = position;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = super.onCreateView(inflater, container, savedInstanceState);

        remindAdapter = new HiddenTopArrayAdapter(context, android.R.layout.simple_spinner_item, spinnerOptions);
        String[] modes = getResources().getStringArray(R.array.reminder_ring_modes);
        ArrayAdapter<String> modeAdapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, modes);
        modeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mode.setAdapter(modeAdapter);

        if (savedInstanceState != null) {
            taskId = savedInstanceState.getLong(EXTRA_TASK_ID);
            flags = savedInstanceState.getInt(EXTRA_FLAGS);
            randomReminder = savedInstanceState.getLong(EXTRA_RANDOM_REMINDER);
            List<Geofence> geofences = new ArrayList<>();
            List<Parcelable> geofenceArray = savedInstanceState.getParcelableArrayList(EXTRA_GEOFENCES);
            for (Parcelable geofence : geofenceArray) {
                geofences.add((Geofence) geofence);
            }
            setup(Longs.asList(savedInstanceState.getLongArray(EXTRA_ALARMS)), geofences);
        } else {
            setup(currentAlarms(), geofenceService.getGeofences(taskId));
        }

        addSpinner.setAdapter(remindAdapter);

        return view;
    }

    private List<Long> currentAlarms() {
        final List<Long> alarms = new ArrayList<>();
        alarmService.getAlarms(taskId, entry -> alarms.add(entry.getValue(AlarmFields.TIME)));
        return alarms;
    }

    @OnItemSelected(R.id.alarms_add_spinner)
    void addAlarm(int position) {
        String selected = spinnerOptions.get(position);
        if (selected.equals(getString(R.string.when_due))) {
            addDue();
        } else if (selected.equals(getString(R.string.when_overdue))) {
            addOverdue();
        } else if (selected.equals(getString(R.string.randomly))) {
            addRandomReminder(TimeUnit.DAYS.toMillis(14));
        } else if (selected.equals(getString(R.string.pick_a_date_and_time))) {
            addNewAlarm();
        } else if (selected.equals(getString(R.string.pick_a_location))) {
            if (permissionRequestor.requestFineLocation()) {
                pickLocation();
            }
        }
        if (position != 0) {
            updateSpinner();
        }
    }

    @OnClick(R.id.alarms_add)
    void addAlarm(View view) {
        if (spinnerOptions.size() == 2) {
            addNewAlarm();
        } else {
            addSpinner.performClick();
        }
    }

    @Override
    protected int getLayout() {
        return R.layout.control_set_reminders;
    }

    @Override
    public int getIcon() {
        return R.drawable.ic_notifications_24dp;
    }

    @Override
    public int controlId() {
        return TAG;
    }

    @Override
    public void initialize(boolean isNewTask, Task task) {
        taskId = task.getId();
        flags = task.getReminderFlags();
        randomReminder = task.getReminderPeriod();
    }

    private void setup(List<Long> alarms, List<Geofence> geofences) {
        setValue(flags);

        alertContainer.removeAllViews();
        if (whenDue) {
            addDue();
        }
        if (whenOverdue) {
            addOverdue();
        }
        if (randomReminder > 0) {
            addRandomReminder(randomReminder);
        }
        for (long timestamp : alarms) {
            addAlarmRow(timestamp);
        }
        for (Geofence geofence : geofences) {
            addGeolocationReminder(geofence);
        }
        updateSpinner();
    }

    @Override
    public boolean hasChanges(Task original) {
        return getFlags() != original.getReminderFlags()
                || getRandomReminderPeriod() != original.getReminderPeriod()
                || !newHashSet(currentAlarms()).equals(alarms)
                || !newHashSet(geofenceService.getGeofences(taskId)).equals(geofences);
    }

    @Override
    public void apply(Task task) {
        task.setReminderFlags(getFlags());

        task.setReminderPeriod(getRandomReminderPeriod());

        if (alarmService.synchronizeAlarms(task.getId(), alarms)) {
            task.setModificationDate(DateUtilities.now());
        }
        if (geofenceService.synchronizeGeofences(task.getId(), geofences)) {
            task.setModificationDate(DateUtilities.now());
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putLong(EXTRA_TASK_ID, taskId);
        outState.putInt(EXTRA_FLAGS, getFlags());
        outState.putLong(EXTRA_RANDOM_REMINDER, getRandomReminderPeriod());
        outState.putLongArray(EXTRA_ALARMS, Longs.toArray(alarms));
        outState.putParcelableArrayList(EXTRA_GEOFENCES, newArrayList(geofences));
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_NEW_ALARM) {
            if (resultCode == Activity.RESULT_OK) {
                addAlarmRow(data.getLongExtra(TimePickerActivity.EXTRA_TIMESTAMP, 0L));
            }
        } else if (requestCode == REQUEST_LOCATION_REMINDER) {
            if (resultCode == Activity.RESULT_OK) {
                addGeolocationReminder(PlacePicker.getPlace(context, data, preferences));
            }
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        if (requestCode == PermissionRequestor.REQUEST_LOCATION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                pickLocation();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    private void addAlarmRow(final Long timestamp) {
        addAlarmRow(getLongDateStringWithTime(context, timestamp), v -> alarms.remove(timestamp));
        alarms.add(timestamp);
    }

    private void pickLocation() {
        Intent intent = PlacePicker.getIntent(getActivity());
        if (intent != null) {
            startActivityForResult(intent, REQUEST_LOCATION_REMINDER);
        }
    }

    private void addGeolocationReminder(final Geofence geofence) {
        addAlarmRow(geofence.getName(), v -> geofences.remove(geofence));
        geofences.add(geofence);
    }

    private int getFlags() {
        int value = 0;
        if (whenDue) {
            value |= Task.NOTIFY_AT_DEADLINE;
        }
        if (whenOverdue) {
            value |= Task.NOTIFY_AFTER_DEADLINE;
        }

        value &= ~(Task.NOTIFY_MODE_FIVE | Task.NOTIFY_MODE_NONSTOP);
        if (ringMode == 2) {
            value |= Task.NOTIFY_MODE_NONSTOP;
        } else if (ringMode == 1) {
            value |= Task.NOTIFY_MODE_FIVE;
        }

        return value;
    }

    private long getRandomReminderPeriod() {
        return randomControlSet == null ? 0L : randomControlSet.getReminderPeriod();
    }

    private void addNewAlarm() {
        Intent intent = new Intent(getActivity(), DateAndTimePickerActivity.class);
        intent.putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, newDateTime().startOfDay().getMillis());
        startActivityForResult(intent, REQUEST_NEW_ALARM);
    }

    private View addAlarmRow(String text, final OnClickListener onRemove) {
        final View alertItem = getActivity().getLayoutInflater().inflate(R.layout.alarm_edit_row, null);
        alertContainer.addView(alertItem);
        addAlarmRow(alertItem, text, onRemove);
        return alertItem;
    }

    private void addAlarmRow(final View alertItem, String text, final View.OnClickListener onRemove) {
        TextView display = (TextView) alertItem.findViewById(R.id.alarm_string);
        display.setText(text);
        alertItem.findViewById(R.id.clear).setOnClickListener(v -> {
            alertContainer.removeView(alertItem);
            if (onRemove != null) {
                onRemove.onClick(v);
            }
            updateSpinner();
        });
        updateSpinner();
    }

    private void updateSpinner() {
        addSpinner.setSelection(0);
        spinnerOptions.clear();
        spinnerOptions.add("");
        if (!whenDue) {
            spinnerOptions.add(getString(R.string.when_due));
        }
        if (!whenOverdue) {
            spinnerOptions.add(getString(R.string.when_overdue));
        }
        if (randomControlSet == null) {
            spinnerOptions.add(getString(R.string.randomly));
        }
        if (device.supportsLocationServices()) {
            spinnerOptions.add(getString(R.string.pick_a_location));
        }
        spinnerOptions.add(getString(R.string.pick_a_date_and_time));
        remindAdapter.notifyDataSetChanged();
    }

    private void addDue() {
        whenDue = true;
        addAlarmRow(getString(R.string.when_due), v -> whenDue = false);
    }

    private void addOverdue() {
        whenOverdue = true;
        addAlarmRow(getString(R.string.when_overdue), v -> whenOverdue = false);
    }

    private void addRandomReminder(long reminderPeriod) {
        View alarmRow = addAlarmRow(getString(R.string.randomly_once) + " ", v -> randomControlSet = null);
        randomControlSet = new RandomReminderControlSet(context, alarmRow, reminderPeriod);
    }

    private void setValue(int flags) {
        whenDue = (flags & Task.NOTIFY_AT_DEADLINE) > 0;
        whenOverdue = (flags & Task.NOTIFY_AFTER_DEADLINE) > 0;

        if ((flags & Task.NOTIFY_MODE_NONSTOP) > 0) {
            mode.setSelection(2);
        } else if ((flags & Task.NOTIFY_MODE_FIVE) > 0) {
            mode.setSelection(1);
        } else {
            mode.setSelection(0);
        }
    }

    @Override
    protected void inject(FragmentComponent component) {
        component.inject(this);
    }
}