Android Open Source - android-kidspend Spend Totals






From Project

Back to project page android-kidspend.

License

The source code is released under:

GNU General Public License

If you think the Android project android-kidspend 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

package com.manangatangy.kidspend;
// ww  w.ja v  a2 s  .  co  m
/**
 * Created with IntelliJ IDEA.
 * User: david
 * Date: 24/11/14
 * Time: 3:25 PM
 * To change this template use File | Settings | File Templates.
 */
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
import android.widget.TextView;

import com.manangatangy.kidspend.SpendProviderMetaData.SpendsTableMetaData;

/**
 * Modelled on ref: http://developer.android.com/guide/topics/fundamentals/loaders.html
 *
 *
 */
public class SpendTotals extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    public static final String TAG = "SpendTotals";

    private static final float DAYS_PER_DAY = (float) 1.0;
    private static final float DAYS_PER_WEEK = (float) 7.0;
    private static final float DAYS_PER_MONTH = (float) (365.24/12.0);

    private static final String KEY_DAY = "day";
    private static final String KEY_WEEK = "week";
    private static final String KEY_MONTH = "month";
    private static final String KEY_TOTAL = "total";

    // The fields and itemIds are used in the adapter to map from the cursor to the view.
    private static final String[] fields = new String[] {
            SpendsTableMetaData.SPEND_TYPE,
            SpendsTableMetaData.SPEND_AMOUNT
    };
    private static final int[] itemIds = new int[] {
            R.id.totalEntryTypeText,
            R.id.totalEntryAmountText
    };

    class Formatter {
        private String[] map;
        private String fullTitle;
        private String summaryItemTitle;

        private int totalPrecision;
        private float periodCount;

        public String toString() {
            return "{Formatter summaryItemTitle:" + summaryItemTitle + ", periodCount:" + periodCount + ", totalPrecision:" + totalPrecision + "}";
        }

        /**
         * Generates various strings based on the period and dayCount.
         * @param title is the main title (the first part of the full title)
         * @param periodName is used to format the second part of the full title.
         * @param periodDuration in days
         * @param periodCountPrecision is the number of decimal places in the second part of the full title.
         * @param dayCount for the entire duration
         * @param totalPrecision is the number of decimal places in the amount column.
         */
        public Formatter(String title, String periodName, float periodDuration, String periodCountPrecision,
                         float dayCount, int totalPrecision) {
            // Note that periodDuration will be zero for the "Totals" if there are no spend records (ie dayCount is zero)
            periodCount = dayCount / (periodDuration == 0 ? 1 : periodDuration);
            map = new String[] {
                    SpendsTableMetaData._ID,
                    SpendsTableMetaData.SPEND_TYPE,
                    String.format("ROUND(TOTAL(%s)/%f,%d) as %s",
                            SpendsTableMetaData.SPEND_AMOUNT, periodCount, totalPrecision, SpendsTableMetaData.SPEND_AMOUNT)
            };

            summaryItemTitle = "TOTAL per " + periodName;

            // For the total counts (entire period), show number of days, not number of entire periods (which would be 1)
            float pCount = periodCount;
            if ("Totals".equalsIgnoreCase(title)) {
                pCount = dayCount;
                summaryItemTitle = "TOTAL";
            }
            fullTitle = String.format("%s (%." + periodCountPrecision + "f %ss)", title, pCount, periodName);

            this.totalPrecision = totalPrecision;
        }
        public final String[] getMap() {
            return map;
        }
        public String getFullTitle(String accountName) {
            return accountName + ":" + fullTitle;
        }
        public String getTotalAmountPerPeriod() {
            // Note that periodCount (below) will be 0 when there are no spend records (ie dayCount is zero)
            return String.format("%." + totalPrecision + "f", totalAmount/(periodCount == 0 ? 1 : periodCount));
        }
        public String getSummaryItemTitle() {
            return summaryItemTitle;
        }
    }

    // This is the Adapter being used to display the list's data.
    private SimpleCursorAdapter mAdapter;
    // This specifies which averaging period to use.
    private String formatsIndex = KEY_DAY;
    private String currentAccount = "";
    private float totalAmount = (float) 0.0;

    private String[] accountArray = {};
    private HashMap<String,Formatter> formats = new HashMap<String,Formatter>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // Set a custom title view
        // ref: http://stackoverflow.com/questions/10189037/android-custom-title-how-to-follow-the-system-theme
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
        setContentView(R.layout.totals_manager);
        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.title_totals);

        accountArray = getResources().getStringArray(R.array.accounts_array);

        Bundle extras = getIntent().getExtras();
        if (extras != null)
            currentAccount = extras.getString("account");

        // Can't calculate these two until the current account is set.
        long dayCount = getDayCount();
        totalAmount = getTotalAmount();

        formats.put(KEY_DAY, new Formatter("Daily", "day", DAYS_PER_DAY, "0", dayCount, 1));
        formats.put(KEY_WEEK, new Formatter("Weekly", "week", DAYS_PER_WEEK, "1", dayCount, 1));
        formats.put(KEY_MONTH, new Formatter("Monthly", "month", DAYS_PER_MONTH, "1", dayCount, 0));
        formats.put(KEY_TOTAL, new Formatter("Totals", "day", dayCount, "0", dayCount, 0));

        // Buttons to change the average period.
        findViewById(R.id.totalByDayButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                formatsIndex = KEY_DAY;
                setActivityTitle();
                getSupportLoaderManager().restartLoader(0, null, SpendTotals.this);
            }
        });
        findViewById(R.id.totalByWeekButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                formatsIndex = KEY_WEEK;
                setActivityTitle();
                getSupportLoaderManager().restartLoader(0, null, SpendTotals.this);
            }
        });
        findViewById(R.id.totalByMonthButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                formatsIndex = KEY_MONTH;
                setActivityTitle();
                getSupportLoaderManager().restartLoader(0, null, SpendTotals.this);
            }
        });
        findViewById(R.id.totalCompleteButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                formatsIndex = KEY_TOTAL;
                setActivityTitle();
                getSupportLoaderManager().restartLoader(0, null, SpendTotals.this);
            }
        });

        ListView mSpendList = (ListView) findViewById(R.id.totalsList);
        mAdapter = new SimpleCursorAdapter(this, R.layout.totals_entry, null, fields, itemIds, 0);
        mSpendList.setAdapter(mAdapter);

        formatsIndex = KEY_TOTAL;
        setActivityTitle();

        getSupportLoaderManager().initLoader(0, null, this);    // Assigns loader callbacks here.

        // Register for long click on the title - changes the account.
        findViewById(R.id.titleTotalTypeText).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AlertDialog.Builder(SpendTotals.this)
                        .setTitle("Select the Account")
                        .setItems(accountArray, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int item) {
                                // Selected was items[item]
                                currentAccount = accountArray[item];
                                totalAmount = getTotalAmount();
                                setActivityTitle();
                                getSupportLoaderManager().restartLoader(0, null, SpendTotals.this);
                            }
                        }).show();
            }
        });


    }

    private long getDayCount() {
        // Determine how many days are in the complete period.
        // Assume that the items in the list are ordered chronologically, take first and last.
        String date1 = null;
        String date2 = null;
        Uri spendsUri = SpendProviderMetaData.SpendsTableMetaData.SPEND_CONTENT_URI;

        String where = "WHERE " + SpendsTableMetaData.SPEND_ACCOUNT + " ='" + currentAccount + "'";

        Cursor cursor = managedQuery(spendsUri,
                new String[] {SpendProviderMetaData.SpendsTableMetaData.SPEND_DATE},
                " _id in (select min(_id) from spends " + where + " union select max(_id) from spends " + where + " )",
                null, null);
        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
            int index = cursor.getColumnIndex(SpendProviderMetaData.SpendsTableMetaData.SPEND_DATE);
            String value = cursor.getString(index);
            Log.v(TAG, "value=" + value);
            if (TextUtils.isEmpty(date1))
                date1 = value;
            else if (TextUtils.isEmpty(date2))
                date2 = value;
        }
        long dayCount = 0;
        if (!TextUtils.isEmpty(date1) && !TextUtils.isEmpty(date2))
            dayCount =  Math.abs(getTimeInMillis(date1) - getTimeInMillis(date2)) / (24 * 60 * 60 * 1000) + 1;
        Log.v(TAG, "dayCount:" + dayCount);
        return dayCount;
    }

    private float getTotalAmount() {
        // Query for the total amount.
        Uri spendsUri = SpendProviderMetaData.SpendsTableMetaData.SPEND_CONTENT_URI;
        Cursor cursor = managedQuery(spendsUri, new String[] {String.format("TOTAL(%s) as total_amount", SpendsTableMetaData.SPEND_AMOUNT)},
                SpendsTableMetaData.SPEND_ACCOUNT + " ='" + currentAccount + "'", null, null);
        float totalAmount = 0;
        if (cursor.moveToFirst()) {
            int index = cursor.getColumnIndex("total_amount");
            String value = cursor.getString(index);
            totalAmount = Float.parseFloat(value);
        }
        return totalAmount;
    }

    private void setActivityTitle() {
        // formatsIndex is the period type; total, month, week, day
        TextView typeView = (TextView)findViewById(R.id.titleTotalTypeText);
        TextView amountView = (TextView)findViewById(R.id.titleTotalAmountText);

        Log.v(TAG, "formatsIndex:" + formatsIndex + " --> " + formats.get(formatsIndex));

        typeView.setText(formats.get(formatsIndex).getFullTitle(currentAccount));
        amountView.setText(formats.get(formatsIndex).getTotalAmountPerPeriod());
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // This is called when a new Loader needs to be created.
        Uri uri = SpendProviderMetaData.SpendsTableMetaData.SPEND_CONTENT_URI;
        uri = Uri.withAppendedPath(uri, "groupBy");
        uri = Uri.withAppendedPath(uri, SpendsTableMetaData.SPEND_TYPE);
        return new CursorLoader(this, uri, formats.get(formatsIndex).getMap(),
                SpendsTableMetaData.SPEND_ACCOUNT + " ='" + currentAccount + "'",
                null, SpendProviderMetaData.SpendsTableMetaData.SPEND_MAX_SORT_ORDER);
        // ref: http://developer.android.com/reference/android/content/ContentResolver.html#query%28android.net.Uri,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String%29
        // The above explains the meanings of the parameters.
    }


    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)

        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // This is called when the last Cursor provided to onLoadFinished()
        // above is about to be closed.  We need to make sure we are no
        // longer using it.
        mAdapter.swapCursor(null);
    }

    static Calendar cal = Calendar.getInstance();

    // dateText is formatted like "10 MAY 2012" or like "2012-05-25"
    public static long getTimeInMillis(String dateText) {
        if (dateText == null)
            return 0;

        int month;  // jan = 0.
        String[] parts;
        if (dateText.contains("-")) {
            parts = dateText.split("-");
            // Swap year and month parts;
            String temp = parts[0];
            parts[0] = parts[2];
            parts[2] = temp;
            month = Integer.parseInt(parts[1]) - 1;
        } else {
            parts = dateText.split(" ");
            for (month = 0; month < 12; month++) {
                if (SpendAdder.monthNames[month].equalsIgnoreCase(parts[1]))
                    break;
            }
        }
        int day = Integer.parseInt(parts[0]);
        int year = Integer.parseInt(parts[2]);
        cal.set(year, month, day);
        return cal.getTimeInMillis();
    }

}




Java Source Code List

com.manangatangy.kidspend.EditableSpinner.java
com.manangatangy.kidspend.RepeatManager.java
com.manangatangy.kidspend.SpendAdder.java
com.manangatangy.kidspend.SpendManager.java
com.manangatangy.kidspend.SpendProviderMetaData.java
com.manangatangy.kidspend.SpendProvider.java
com.manangatangy.kidspend.SpendTotals.java
com.manangatangy.kidspend.SpinnerWithPrefs.java