felixwiemuth.lincal.ui.CalendarViewFragment.java Source code

Java tutorial

Introduction

Here is the source code for felixwiemuth.lincal.ui.CalendarViewFragment.java

Source

/*
 * Copyright (C) 2015 Felix Wiemuth
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package felixwiemuth.lincal.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;

import java.util.Calendar;

import felixwiemuth.lincal.Calendars;
import felixwiemuth.lincal.Main;
import felixwiemuth.lincal.NotificationService;
import felixwiemuth.lincal.R;
import felixwiemuth.lincal.data.CEntry;
import felixwiemuth.lincal.data.LinCal;
import felixwiemuth.lincal.data.LinCalConfig;
import felixwiemuth.lincal.util.Time;

/**
 * A fragment representing a single Calendar screen with a list of its entries. This fragment is
 * either contained in a {@link CalendarListActivity} in two-pane mode (on tablets) or a {@link
 * CalendarViewActivity} on handsets.
 */
public class CalendarViewFragment extends Fragment {

    /**
     * The fragment argument representing the item ID that this fragment represents.
     */
    public static final String ARG_CALENDAR_POS = "felixwiemuth.lincal.CalendarListActivity.EXTRA_ARG_CALENDAR_POS";

    private LinCal calendar;
    private int calendarPos;
    private CheckBox notificationsEnabled;
    private Time earliestNotificationTime;
    private TextView textViewEarliestNotificationTime;
    private CheckBox earliestNotificationTimeEnabled;
    //private CheckBox onScreenOnEnabled; //TODO implement
    private Spinner entryDisplayModeDate;
    private Spinner entryDisplayModeDescription;
    private Button buttonRemoveCalendar;
    private RecyclerView entryList;

    public static class TimePickerFragment extends DialogFragment implements TimePickerDialog.OnTimeSetListener {
        private int calendarPos;

        @NonNull
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Use the current time as the default values for the picker
            int hour = getArguments().getInt("hour");
            int minute = getArguments().getInt("minute");
            calendarPos = getArguments().getInt("calendarPos");

            // Create a new instance of TimePickerDialog and return it
            return new TimePickerDialog(getActivity(), this, hour, minute, true);
        }

        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            Calendars calendars = Calendars.getInstance(getContext());
            LinCalConfig config = calendars.getConfigByPos(calendarPos);
            Time time = new Time(hourOfDay, minute);
            config.setEarliestNotificationTime(time);
            TextView textViewEarliestNotificationTime = (TextView) getActivity()
                    .findViewById(R.id.setting_earliest_notification_time);
            textViewEarliestNotificationTime.setText(time.toString());
            calendars.save(getContext());
            // have to update the displayed notification times (times might have changed)
            RecyclerView recyclerView = (RecyclerView) getActivity().findViewById(R.id.entry_list_recycler_view);
            recyclerView.getAdapter().notifyDataSetChanged();
            NotificationService.runWithCalendar(getContext(), config.getId()); //TODO reconsider when to call
        }
    }

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon
     * screen orientation changes).
     */
    public CalendarViewFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!getArguments().containsKey(ARG_CALENDAR_POS)) {
            throw new RuntimeException("Missing argument: CalendarViewFragment.ARG_CALENDAR_POS");
        }
        Activity activity = this.getActivity();
        calendarPos = getArguments().getInt(ARG_CALENDAR_POS);
        Calendars calendars = Calendars.getInstance(getContext()); // loads calendar and makes sure all values in config are set (e.g. forceEntryDisplayMode values)
        calendar = calendars.getCalendarByPos(getContext(), calendarPos);
        // If toolbar is present (handset mode), set title to calendar title
        //TODO add toolbar again
        //        CollapsingToolbarLayout appBarLayout = (CollapsingToolbarLayout) activity.findViewById(R.id.toolbar_layout);
        //        if (appBarLayout != null) {
        //            appBarLayout.setTitle(calendars.getConfigByPos(calendarPos).getCalendarTitle());
        //        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.calendar_view, container, false);
        TextView titleView = (TextView) rootView.findViewById(R.id.cal_title);
        TextView authorView = (TextView) rootView.findViewById(R.id.cal_author);

        notificationsEnabled = (CheckBox) rootView.findViewById(R.id.notifications_enabled);
        textViewEarliestNotificationTime = (TextView) rootView
                .findViewById(R.id.setting_earliest_notification_time);
        earliestNotificationTimeEnabled = (CheckBox) rootView
                .findViewById(R.id.setting_earliest_notification_time_enabled);
        entryDisplayModeDate = (Spinner) rootView.findViewById(R.id.setting_entry_display_mode_date);
        entryDisplayModeDescription = (Spinner) rootView.findViewById(R.id.setting_entry_display_mode_description);
        buttonRemoveCalendar = (Button) rootView.findViewById(R.id.button_remove_cal);

        if (calendar == null) {
            titleView.setText(R.string.cal_title_error_loading);
            notificationsEnabled.setEnabled(false);
            earliestNotificationTimeEnabled.setEnabled(false);
            entryDisplayModeDate.setEnabled(false);
            entryDisplayModeDescription.setEnabled(false);
        } else {
            titleView.setText(calendar.getTitle());
            authorView.setText(calendar.getAuthor());
            entryList = (RecyclerView) rootView.findViewById(R.id.entry_list_recycler_view);
            SimpleItemRecyclerViewAdapter adapter = new SimpleItemRecyclerViewAdapter();
            entryList.setAdapter(adapter);
            ((TextView) rootView.findViewById(R.id.cal_descr)).setText(calendar.getDescription());
            ((TextView) rootView.findViewById(R.id.cal_version)).setText(calendar.getVersion());
            ((TextView) rootView.findViewById(R.id.cal_date)).setText(calendar.getDateStr());
            if (calendar.hasForceEntryDisplayModeDate()) {
                entryDisplayModeDate.setEnabled(false);
            }
            if (calendar.hasForceEntryDisplayModeDescription()) {
                entryDisplayModeDescription.setEnabled(false);
            }
        }

        ArrayAdapter<CharSequence> spinnerAdapter = ArrayAdapter.createFromResource(getContext(),
                R.array.setting_entry_display_mode_options, android.R.layout.simple_spinner_item);
        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        entryDisplayModeDate.setAdapter(spinnerAdapter);
        entryDisplayModeDescription.setAdapter(spinnerAdapter);

        loadSettings(); // loading settings before adding listeners prevents them from firing due to initialization (e.g. Spinner)

        if (calendar != null) {
            // Set listeners
            final View.OnClickListener saveSettingsListener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    saveSettings();
                    NotificationService.runWithCalendar(getContext(),
                            Calendars.getInstance(getContext()).getConfigByPos(calendarPos).getId()); //TODO reconsider when to call
                }
            };

            notificationsEnabled.setOnClickListener(saveSettingsListener);
            textViewEarliestNotificationTime.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    loadSettings(); // update earliestNotificationTime
                    DialogFragment dialogFragment = new TimePickerFragment();
                    Bundle arguments = new Bundle();
                    arguments.putInt("hour", earliestNotificationTime.getHour());
                    arguments.putInt("minute", earliestNotificationTime.getMinute());
                    arguments.putInt("calendarPos", calendarPos);
                    dialogFragment.setArguments(arguments);
                    dialogFragment.show(getFragmentManager(), "timePicker");
                }
            });
            earliestNotificationTimeEnabled.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    saveSettingsListener.onClick(v);
                    // have to update the displayed notification times (only show when enabled)
                    entryList.getAdapter().notifyDataSetChanged();
                }
            });
            //TODO implement
            //        onScreenOnEnabled = (CheckBox) rootView.findViewById(R.id.setting_show_notification_on_screen_on);
            //        onScreenOnEnabled.setOnClickListener(saveSettingsListener);
            entryDisplayModeDate.setOnItemSelectedListener(new AdapterViewOnItemSelectedListener(
                    entryDisplayModeDate.getSelectedItemPosition(), saveSettingsListener));
            entryDisplayModeDescription.setOnItemSelectedListener(new AdapterViewOnItemSelectedListener(
                    entryDisplayModeDescription.getSelectedItemPosition(), saveSettingsListener));
        }

        buttonRemoveCalendar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                builder.setTitle(R.string.dialog_remove_cal_title).setMessage(R.string.dialog_remove_cal_msg)
                        .setNegativeButton(R.string.dialog_no, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                            }
                        }).setPositiveButton(R.string.dialog_yes, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // First remove this fragment, so that it cannot be used anymore
                                getActivity().getSupportFragmentManager().beginTransaction()
                                        .remove(CalendarViewFragment.this).commit();
                                Calendars calendars = Calendars.getInstance(getContext());
                                calendars.removeCalendarByPos(getContext(), calendarPos);
                                // Notify calendar list that the calendar with the given position was removed and return to calendar list in case of CalendarViewActivity
                                Activity hostActivity = getActivity();
                                if (hostActivity instanceof CalendarListActivity) {
                                    ((CalendarListActivity) hostActivity).notifyCalendarRemoved(calendarPos);
                                } else if (hostActivity instanceof CalendarViewActivity) {
                                    Intent resultData = new Intent();
                                    resultData.putExtra(CalendarListActivity.EXTRA_RESULT_CAL_REMOVED, calendarPos);
                                    hostActivity.setResult(Activity.RESULT_OK, resultData);
                                    hostActivity.finish();
                                } else {
                                    throw new RuntimeException(
                                            "CalendarViewFragment may only be contained in either CalendarListActivity or CalendarViewActivity.");
                                }
                            }
                        });
                builder.show();
            }
        });

        return rootView;
    }

    // listener used for both {@link Spinner}s
    private class AdapterViewOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
        int selected;
        View.OnClickListener saveSettingsListener;

        public AdapterViewOnItemSelectedListener(int selected, View.OnClickListener saveSettingsListener) {
            this.selected = selected;
            this.saveSettingsListener = saveSettingsListener;
        }

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if (selected != position) {
                saveSettingsListener.onClick(view);
                // have to update the displayed entries
                entryList.getAdapter().notifyDataSetChanged();
                selected = position;
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    }

    public class SimpleItemRecyclerViewAdapter
            extends RecyclerView.Adapter<SimpleItemRecyclerViewAdapter.ViewHolder> {

        public SimpleItemRecyclerViewAdapter() {

        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.entry_list_item, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            final CEntry entry = calendar.get(position);
            String dateStr = entry.getDateTimeStr(); //NOTE if changing expected length, adapt TextView size
            Calendars calendars = Calendars.getInstance(getContext());
            LinCalConfig config = calendars.getConfigByPos(calendarPos);
            Calendar notificationTime = Calendars.calcNotificationTime(entry, config);
            String notificationTimeStr = Main.dfTime.format(notificationTime.getTime());
            if (config.isEarliestNotificationTimeEnabled() && notificationTime.after(entry.getDate())) {
                dateStr += " (" + notificationTimeStr + ")";
            }
            final String descr = entry.getDescription();
            boolean isEntryDue = entry.getDate().getTime().getTime() <= System.currentTimeMillis();
            if (config.getEntryDisplayModeDate() == LinCal.EntryDisplayMode.SHOW_ALL
                    || config.getEntryDisplayModeDate() == LinCal.EntryDisplayMode.HIDE_FUTURE && isEntryDue) {
                holder.dateView.setText(dateStr);
            } else {
                holder.dateView.setText(R.string.entry_hide_date_text);
            }
            if (config.getEntryDisplayModeDescription() == LinCal.EntryDisplayMode.SHOW_ALL
                    || config.getEntryDisplayModeDescription() == LinCal.EntryDisplayMode.HIDE_FUTURE
                            && isEntryDue) {
                holder.descriptionView.setText(descr);
                holder.view.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        showEntryDialog(entry, false);
                    }
                });
            } else {
                holder.descriptionView.setText(getString(R.string.entry_hide_description_text));
            }
        }

        private void showEntryDialog(final CEntry entry, boolean showLink) {
            AlertDialog.Builder builder = new AlertDialog.Builder(CalendarViewFragment.this.getActivity());
            StringBuilder msg = new StringBuilder();
            if (entry.getDescription() != null) {
                msg.append(entry.getDescription());
            }
            if (showLink) {
                if (msg.length() > 0) {
                    msg.append("\n\n");
                }
                msg.append(entry.getLink());
            }
            builder.setTitle(entry.getDateStr()).setMessage(msg.toString());
            final AlertDialog dialog = builder.create();
            if (!showLink) {
                dialog.setButton(AlertDialog.BUTTON_NEUTRAL, s(R.string.dialog_show_link),
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int which) {
                                dialog.dismiss();
                                showEntryDialog(entry, true);
                            }
                        });
            }
            dialog.setButton(AlertDialog.BUTTON_NEGATIVE, s(R.string.dialog_cancel),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int which) {
                            dialogInterface.dismiss();
                        }
                    });
            dialog.setButton(AlertDialog.BUTTON_POSITIVE, s(R.string.dialog_open_link),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int which) {
                            dialogInterface.dismiss();
                            entry.open(getContext());
                        }
                    });
            dialog.show();
        }

        @Override
        public int getItemCount() {
            return calendar.size();
        }

        public class ViewHolder extends RecyclerView.ViewHolder {

            public final View view;
            public final TextView dateView;
            public final TextView descriptionView;

            public ViewHolder(View view) {
                super(view);
                this.view = view;
                dateView = (TextView) view.findViewById(R.id.date);
                descriptionView = (TextView) view.findViewById(R.id.description);
            }

            @Override
            public String toString() {
                return super.toString() + " '" + descriptionView.getText() + "'";
            }
        }
    }

    //NOTE it does not matter to save everything at each single change as the whole file is rewritten anyway
    private void saveSettings() {
        Calendars calendars = Calendars.getInstance(getContext());
        LinCalConfig config = calendars.getConfigByPos(calendarPos);
        config.setNotificationsEnabled(notificationsEnabled.isChecked());
        config.setEarliestNotificationTimeEnabled(earliestNotificationTimeEnabled.isChecked());
        //config.setOnScreenOn(onScreenOnEnabled.isChecked()); //TODO implement
        config.setEntryDisplayModeDate(
                LinCal.EntryDisplayMode.values()[entryDisplayModeDate.getSelectedItemPosition()]);
        config.setEntryDisplayModeDescription(
                LinCal.EntryDisplayMode.values()[entryDisplayModeDescription.getSelectedItemPosition()]);
        calendars.save(getContext());
    }

    private void loadSettings() {
        Calendars calendars = Calendars.getInstance(getContext());
        LinCalConfig config = calendars.getConfigByPos(calendarPos);
        notificationsEnabled.setChecked(config.isNotificationsEnabled());
        earliestNotificationTimeEnabled.setChecked(config.isEarliestNotificationTimeEnabled());
        earliestNotificationTime = config.getEarliestNotificationTime();
        textViewEarliestNotificationTime.setText(earliestNotificationTime.toString());
        //onScreenOnEnabled.setChecked(config.isOnScreenOn()); //TODO implement
        entryDisplayModeDate.setSelection(config.getEntryDisplayModeDate().ordinal());
        entryDisplayModeDescription.setSelection(config.getEntryDisplayModeDescription().ordinal());
    }

    private String s(int resId) {
        return getContext().getString(resId);
    }
}