Android Open Source - timesheet Time Entries Activity






From Project

Back to project page timesheet.

License

The source code is released under:

GNU General Public License

If you think the Android project timesheet listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (c) 2009-2010 Tasty Cactus Software, LLC
 * //from  ww  w  . j av  a 2s .  c  o  m
 * 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 2
 * 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.
 *
 * Aaron Brice <aaron@tastycactus.com>
 *
 */

package com.tastycactus.timesheet;

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TabActivity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;

import android.database.Cursor;
import android.os.Bundle;

import android.preference.PreferenceManager;

import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;
import android.widget.TabHost;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import com.tastycactus.timesheet.TimesheetAppWidgetProvider;
import com.tastycactus.timesheet.TimesheetDatabase;
import com.tastycactus.timesheet.MergeAdapter;

public class TimeEntriesActivity extends TabActivity
{
    class TimeEntriesWeeklyData 
    {
        TimesheetDatabase m_db;
        int m_year, m_month, m_day, m_start_of_week;
        String[] m_headers;
        boolean m_weekly_billable_only;

        // In perl: $data[$day][$i] = { _id => $id, title => $title, duration => $duration }
        Vector<Vector<HashMap<String, String>>> m_data = new Vector<Vector<HashMap<String,String>>>();
        Vector<HashMap<String, String>> m_totals = new Vector<HashMap<String, String>>();

        private final String DAY_LABEL[] = 
            new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

        public TimeEntriesWeeklyData(Context ctx, TimesheetDatabase db, int year, int month, int day) {
            m_db = db;
            m_year = year;
            m_month = month;
            m_day = day;
            for (int i=0; i < 7; ++i) {
                m_data.add(i, new Vector<HashMap<String, String>>());
            }

            m_headers = new String[7];

            SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(ctx);
            m_start_of_week = new Integer(prefs.getString("week_start", "2"));
            m_weekly_billable_only = prefs.getBoolean("weekly_billable_only", true);

            adjustDate();
            requery();
        }

        public void setDate(int year, int month, int day) {
            m_year = year;
            m_month = month;
            m_day = day;
            adjustDate();
        }

        private void adjustDate() {
            Calendar c = Calendar.getInstance();
            c.set(m_year, m_month - 1, m_day);

            // Rewind the calendar to the start of the week
            while (c.get(Calendar.DAY_OF_WEEK) != m_start_of_week) {
                c.add(Calendar.DAY_OF_YEAR, -1);
            }

            m_year = c.get(Calendar.YEAR);
            m_month = c.get(Calendar.MONTH) + 1;
            m_day = c.get(Calendar.DAY_OF_MONTH);
        }

        public void requery() {
            for (int i=0; i < 7; ++i) {
                m_data.get(i).clear();
            }
            m_totals.clear();

            Cursor c = m_db.getWeekEntries(m_year, m_month, m_day);

            HashMap<String, Float> total_map = new HashMap<String, Float>();
            while (!c.isAfterLast()) {
                HashMap<String, String> row_data = new HashMap<String, String>();
                int billable = c.getInt(c.getColumnIndex("billable"));
                if (billable == 1 || !m_weekly_billable_only) {
                    int day = c.getInt(c.getColumnIndex("day"));
                    row_data.put("_id", c.getString(c.getColumnIndex("_id")));
                    String title = c.getString(c.getColumnIndex("title"));
                    row_data.put("title", title);
                    String comment = ": " + c.getString(c.getColumnIndex("comment"));
                    row_data.put("comment", comment);
                    float duration = c.getFloat(c.getColumnIndex("duration"));
                    row_data.put("duration", String.format("%1.2f", duration));
                    m_data.get(day).add(row_data);

                    // Track the total durations
                    if (total_map.containsKey(title)) {
                        total_map.put(title, total_map.get(title) + duration);
                    } else {
                        total_map.put(title, duration);
                    }
                }

                c.moveToNext();
            }
            for (Map.Entry<String, Float> entry : total_map.entrySet()) {
                HashMap<String, String> row = new HashMap<String, String>();
                row.put("title", entry.getKey());
                row.put("duration", String.format("%1.2f", entry.getValue()));
                m_totals.add(row);
            }
            c.close();
            headers();
        }

        public Vector<HashMap<String, String>> entries(int idx) {
            int day = (idx + m_start_of_week - 1) % 7;
            return m_data.get(day);
        }

        public Vector<HashMap<String, String>> totals() {
            return m_totals;
        }

        public String[] headers() {
            Calendar c = Calendar.getInstance();
            c.set(m_year, m_month - 1, m_day);

            for (int i=0; i < 7; ++i) {
                m_headers[i] = String.format("%04d-%02d-%02d - %s",
                    c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH), 
                    DAY_LABEL[c.get(Calendar.DAY_OF_WEEK) - 1]);
                c.add(Calendar.DAY_OF_YEAR, 1);
            }

            return m_headers;
        }
    }

    TimeEntriesWeeklyData m_week_data;
    TimesheetDatabase m_db;
    Cursor m_day_cursor;
    TabHost m_tab_host;
    SimpleCursorAdapter m_day_ca;
    SimpleAdapter m_week_adapters[] = new SimpleAdapter[7];
    MergeAdapter m_merge_adapter;
    SimpleAdapter m_totals_adapter;
    Button m_day_button, m_week_button;

    public static final int ADD_TIME_ENTRY_MENU_ITEM    = Menu.FIRST;
    public static final int DELETE_TIME_ENTRY_MENU_ITEM = Menu.FIRST + 1;
    public static final int EDIT_TIME_ENTRY_MENU_ITEM   = Menu.FIRST + 2;
    public static final int EXPORT_MENU_ITEM            = Menu.FIRST + 3;
    public static final int EMAIL_MENU_ITEM             = Menu.FIRST + 4;

    private static final int SELECT_DAY_DIALOG_ID = 0;
    private static final int SELECT_WEEK_DIALOG_ID = 1;

    private static final int ACTIVITY_CREATE = 0;
    private static final int ACTIVITY_EDIT   = 1;

    private DatePickerDialog.OnDateSetListener m_day_listener =
        new DatePickerDialog.OnDateSetListener() {
            public void onDateSet(DatePicker view, int year, int month, int day) {
                m_day_cursor.close();
                m_day_cursor = m_db.getTimeEntries(year, month + 1, day);
                m_day_ca.changeCursor(m_day_cursor);
                m_day_button.setText(String.format("%04d-%02d-%02d", year, month + 1, day));
            }
        };

    private DatePickerDialog.OnDateSetListener m_week_listener =
        new DatePickerDialog.OnDateSetListener() {
            public void onDateSet(DatePicker view, int year, int month, int day) {
                m_week_data.setDate(year, month + 1, day);
                m_week_data.requery();
                m_merge_adapter.notifyDataSetChanged();
                m_totals_adapter.notifyDataSetChanged();
                m_week_button.setText(String.format("Week of %04d-%02d-%02d", year, month + 1, day));
            }
        };

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.entries);

        m_tab_host = getTabHost();
        m_tab_host.addTab(m_tab_host.newTabSpec("tab_byday").setIndicator("By Day").setContent(R.id.byday_content));
        m_tab_host.addTab(m_tab_host.newTabSpec("tab_byweek").setIndicator("By Week").setContent(R.id.byweek_content));
        m_tab_host.setCurrentTab(0);

        m_db = new TimesheetDatabase(this);

        setupDayTab();
        setupWeekTab();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        m_day_cursor.requery();
        m_day_ca.notifyDataSetChanged();
        m_week_data.requery();
        m_merge_adapter.notifyDataSetChanged();
        m_totals_adapter.notifyDataSetChanged();
    }

    @Override
    public void onDestroy()
    {
        m_day_cursor.close();
        m_db.close();
        super.onDestroy();
    }

    protected void setupDayTab() 
    {
        m_day_cursor = m_db.getTimeEntries();

        ListView time_entry_list = (ListView) findViewById(R.id.entries_byday);
        m_day_ca = new SimpleCursorAdapter(this,
                R.layout.time_entry, 
                m_day_cursor,
                new String[] {"title", "comment", "start_time", "end_time", "duration"},
                new int[] {R.id.time_entry_title, R.id.time_entry_comment, R.id.time_entry_start, R.id.time_entry_end, R.id.time_entry_duration});
        time_entry_list.setAdapter(m_day_ca);
        time_entry_list.setChoiceMode(ListView.CHOICE_MODE_NONE);

        time_entry_list.setOnItemClickListener( new AdapterView.OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent i = new Intent(parent.getContext(), TimeEntryEditActivity.class);
                i.putExtra("_id", id);
                startActivityForResult(i, ACTIVITY_EDIT);
            }
        });

        m_day_button = (Button) findViewById(R.id.day_selection_button);
        m_day_button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                showDialog(SELECT_DAY_DIALOG_ID);
            }
        });
        final Calendar c = Calendar.getInstance();
        m_day_button.setText(String.format("%04d-%02d-%02d", 
                    c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH)));

        registerForContextMenu(time_entry_list);
    }

    protected void setupWeekTab() 
    {
        final Calendar c = Calendar.getInstance();
        m_week_data = new TimeEntriesWeeklyData(this, m_db, 
                c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH));

        ListView week_list = (ListView) findViewById(R.id.entries_byweek);

        for (int i=0; i < 7; ++i) {
            m_week_adapters[i] = new SimpleAdapter(this,
                    m_week_data.entries(i),
                    R.layout.week_entry,
                    new String[] {"title", "duration"},
                    new int[] {R.id.week_entry_title, R.id.week_entry_duration});
        }

        m_merge_adapter = new MergeAdapter(this, R.layout.header, R.id.header, m_week_adapters, m_week_data.headers());
        week_list.setAdapter(m_merge_adapter);
        week_list.setChoiceMode(ListView.CHOICE_MODE_NONE);
        week_list.setItemsCanFocus(false);

        ListView total_view = (ListView) findViewById(R.id.entries_week_totals);
        m_totals_adapter = new SimpleAdapter(this,
                m_week_data.totals(),
                R.layout.week_entry,
                new String[] {"title", "duration"},
                new int[] {R.id.week_entry_title, R.id.week_entry_duration});
        total_view.setAdapter(m_totals_adapter);
        total_view.setChoiceMode(ListView.CHOICE_MODE_NONE);
        total_view.setItemsCanFocus(false);

        m_week_button = (Button) findViewById(R.id.week_selection_button);
        m_week_button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                showDialog(SELECT_WEEK_DIALOG_ID);
            }
        });
        m_week_button.setText(String.format("Week of %04d-%02d-%02d", 
                    c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH)));

    }

    @Override
    protected Dialog onCreateDialog(int id) 
    {
        final Calendar c = Calendar.getInstance();
        switch (id) {
            case SELECT_DAY_DIALOG_ID:
                return new DatePickerDialog(this, m_day_listener, 
                        c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
            case SELECT_WEEK_DIALOG_ID:
                return new DatePickerDialog(this, m_week_listener, 
                        c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
        }
        return null;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
    {
        boolean result = super.onCreateOptionsMenu(menu);
        menu.add(Menu.NONE, ADD_TIME_ENTRY_MENU_ITEM, Menu.NONE, "Add Time Entry").setIcon(android.R.drawable.ic_menu_add);
        menu.add(Menu.NONE, EXPORT_MENU_ITEM, Menu.NONE, "Export to CSV").setIcon(android.R.drawable.ic_menu_save);
        menu.add(Menu.NONE, EMAIL_MENU_ITEM, Menu.NONE, "Send Email").setIcon(android.R.drawable.ic_menu_send);
        return result;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
    {
        Intent i;
        switch (item.getItemId()) {
            case ADD_TIME_ENTRY_MENU_ITEM:
                i = new Intent(this, TimeEntryEditActivity.class);
                startActivityForResult(i, ACTIVITY_CREATE);
                return true;
            case EXPORT_MENU_ITEM:
                i = new Intent(this, ExportActivity.class);
                i.putExtra("type", "csv");
                startActivityForResult(i, ACTIVITY_CREATE);
                return true;
            case EMAIL_MENU_ITEM:
                i = new Intent(this, ExportActivity.class);
                i.putExtra("type", "email");
                startActivityForResult(i, ACTIVITY_CREATE);
                return true;
        }
        return false;
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) 
    {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.add(Menu.NONE, DELETE_TIME_ENTRY_MENU_ITEM, Menu.NONE, "Delete Time Entry");
        menu.add(Menu.NONE, EDIT_TIME_ENTRY_MENU_ITEM, Menu.NONE, "Edit Time Entry");
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) 
    {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        switch (item.getItemId()) {
            case DELETE_TIME_ENTRY_MENU_ITEM:
                m_db.deleteTimeEntry(info.id);
                m_day_cursor.requery();
                m_week_data.requery();
                m_merge_adapter.notifyDataSetChanged();
                m_totals_adapter.notifyDataSetChanged();
                // Update the App Widget in case we deleted the currently-active
                // time entry
                startService(new Intent(this, TimesheetAppWidgetProvider.UpdateService.class));
                return true;
            case EDIT_TIME_ENTRY_MENU_ITEM:
                Intent i = new Intent(this, TimeEntryEditActivity.class);
                i.putExtra("_id", info.id);
                startActivityForResult(i, ACTIVITY_EDIT);
                return true;
        }
        return false;
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            case ACTIVITY_CREATE:
                if (resultCode == RESULT_OK) {
                    m_day_cursor.requery();
                    m_week_data.requery();
                    m_merge_adapter.notifyDataSetChanged();
                    m_totals_adapter.notifyDataSetChanged();
                }
                break;
            case ACTIVITY_EDIT:
                if (resultCode == RESULT_OK) {
                    m_day_cursor.requery();
                    m_week_data.requery();
                    m_merge_adapter.notifyDataSetChanged();
                    m_totals_adapter.notifyDataSetChanged();
                }
                break;
        }
    }
}




Java Source Code List

com.tastycactus.timesheet.ExportActivity.java
com.tastycactus.timesheet.MergeAdapter.java
com.tastycactus.timesheet.TaskEditActivity.java
com.tastycactus.timesheet.TimeEntriesActivity.java
com.tastycactus.timesheet.TimeEntryEditActivity.java
com.tastycactus.timesheet.TimesheetActivity.java
com.tastycactus.timesheet.TimesheetAppWidgetProvider.java
com.tastycactus.timesheet.TimesheetDatabase.java
com.tastycactus.timesheet.TimesheetPreferences.java