com.bydavy.card.receipts.fragments.ReceiptEditFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.bydavy.card.receipts.fragments.ReceiptEditFragment.java

Source

/*
 * Copyright (C) 2012 Davy Leggieri
 * 
 * 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.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package com.bydavy.card.receipts.fragments;

import java.util.Calendar;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.bydavy.card.receipts.R;
import com.bydavy.card.receipts.ShopSuggestionAdapter;
import com.bydavy.card.receipts.providers.Receipts;
import com.bydavy.card.receipts.providers.ReceiptsHelper;
import com.bydavy.card.receipts.utils.CurrencyFormat;
import com.bydavy.card.receipts.utils.LogHelper;
import com.bydavy.card.receipts.utils.LogHelper.ErrorID;
import com.bydavy.card.receipts.utils.MyDateFormat;

public class ReceiptEditFragment extends StateSherlockFragment {
    /** Change first statement of if condition to enable class debug **/
    private static final boolean DEBUG = LogHelper.DEBUG ? false : LogHelper.DEFAULT_DEBUG;
    private static final String DEBUG_PREFIX = ReceiptEditFragment.class.getName();

    public static final String ARG_RECEIPT_ID = "receiptId";
    public static final String ARG_MODE = "mode";

    private static final String STATE_SAVED_DATA = "addReceiptSavedData";
    private static final String STATE_VALID_RECEIPT = "addReceiptValidReceipt";
    private static final String STATE_SHOP = "addReceiptShop";
    private static final String STATE_DATE = "addReceiptDate";
    private static final String STATE_NOTE = "addReceiptNote";
    private static final String STATE_TOTAL = "addReceiptTotal";

    private static final int CURSOR_LOADER_ID = 0;
    private static final String DATE_PICKER_DIALOG_FRAGMENT_TAG = "dateFrag";

    private final MyReceiptLoader mLoaderCallBack = new MyReceiptLoader();

    private MyDateFormat mDateFormat;
    private CurrencyFormat mCurrencyFormat;

    // Data (not updated)
    private String mShop;
    private String mNote;
    private final Calendar mDate = Calendar.getInstance();
    private float mTotal;

    // Views (read here to have the more updated data)
    private AutoCompleteTextView mViewShop;
    private TextView mViewDate;
    private EditText mViewNote;
    private EditText mViewTotal;

    private View mView;

    // Data
    private boolean mValidReceipt;

    public static ReceiptEditFragment newInstance(long receiptId) {
        if (receiptId <= 0) {
            throw new IllegalArgumentException("receipt id must be > 0");
        }

        final ReceiptEditFragment f = new ReceiptEditFragment();

        final Bundle args = new Bundle();
        args.putLong(ARG_RECEIPT_ID, receiptId);
        f.setArguments(args);

        return f;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        mDateFormat = MyDateFormat.getDateFormat(activity);
        mCurrencyFormat = CurrencyFormat.getCurrencyFormat(activity);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_receipt_edit, null);
        mView = view;
        mViewShop = (AutoCompleteTextView) view.findViewById(R.id.shop);
        mViewShop.setAdapter(new ShopSuggestionAdapter(getActivity(), mViewShop));

        mViewDate = (TextView) view.findViewById(R.id.date);
        mViewNote = (EditText) view.findViewById(R.id.note);
        mViewTotal = (EditText) view.findViewById(R.id.total);
        final TextView viewCurrency = (TextView) view.findViewById(R.id.currency);
        viewCurrency.setText(mCurrencyFormat.getCurrencySymbol());

        mViewDate.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showDatePickerDialog();
            }
        });
        mView.setVisibility(View.INVISIBLE);
        return view;
    };

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

        final boolean savedData = (savedInstanceState != null) && savedInstanceState.getBoolean(STATE_SAVED_DATA);
        if (savedData) {
            fromSavedStateToData(savedInstanceState);
            refreshView();
        } else {
            final Bundle bundle = mLoaderCallBack.createLoaderBundle(getReceiptId());
            getLoaderManager().initLoader(CURSOR_LOADER_ID, bundle, mLoaderCallBack);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (getState() == StateFragmentListener.STATE_COMPLETED) {
            outState.putBoolean(STATE_SAVED_DATA, true);
            outState.putBoolean(STATE_VALID_RECEIPT, mValidReceipt);
            if (mValidReceipt) {
                // TODO Save and restore only edited fields (others must come
                // from db)
                outState.putString(STATE_SHOP, mViewShop.getText().toString());
                outState.putLong(STATE_DATE, mDate.getTimeInMillis());
                outState.putString(STATE_NOTE, STATE_NOTE);
                try {
                    outState.putFloat(STATE_TOTAL, Float.valueOf(mViewTotal.getText().toString()));
                } catch (final NumberFormatException e) {
                    /* Nothing */
                }
            }
        }
        super.onSaveInstanceState(outState);
    }

    private void fromSavedStateToData(Bundle savedState) {
        mValidReceipt = savedState.getBoolean(STATE_VALID_RECEIPT);
        if (mValidReceipt) {
            mShop = savedState.getString(STATE_SHOP);
            mDate.setTimeInMillis(savedState.getLong(STATE_DATE));
            mNote = savedState.getString(STATE_NOTE);
            mTotal = savedState.getFloat(STATE_TOTAL);
        }
    }

    private void fromCursorToData(Cursor c) {
        mValidReceipt = c.moveToFirst();
        if (mValidReceipt) {
            final int shopColumnIndex = c.getColumnIndex(Receipts.COLUMN_NAME_SHOP);
            final int noteColumnIndex = c.getColumnIndex(Receipts.COLUMN_NAME_NOTE);
            final int totalColumnIndex = c.getColumnIndex(Receipts.COLUMN_NAME_TOTAL);
            final int dateColumnIndex = c.getColumnIndex(Receipts.COLUMN_NAME_DATE);

            mShop = c.getString(shopColumnIndex);
            mNote = c.getString(noteColumnIndex);
            mTotal = c.getFloat(totalColumnIndex);
            mDate.setTimeInMillis(c.getInt(dateColumnIndex) * 1000L);
        }
    }

    private void refreshView() {
        if (mValidReceipt) {
            mViewShop.setText(mShop);
            mViewNote.setText(mNote);
            mViewTotal.setText(String.valueOf(mTotal));
            mDate.setTimeInMillis(mDate.getTimeInMillis());
            refreshDate();
        } else {
            // TODO provide a screen for this special case
            LogHelper.e(ErrorID.NO_DATA, "No receipt to display", null);
        }
        mView.setVisibility(View.VISIBLE);
    }

    private void refreshDate() {
        mViewDate.setText(mDateFormat.format(mDate.getTime()));
    }

    /*
     * Retrieve the receipt id currently displayed.
     * 
     * This method is thread safe
     * 
     * @return shown receipt id or zero if no receipt shown
     */
    public long getReceiptId() {
        final Bundle args = getArguments();
        if (args != null) {
            return args.getLong(ARG_RECEIPT_ID);
        }
        return 0;
    }

    public void setDate(int year, int monthOfYear, int dayOfMonth) {
        mDate.set(year, monthOfYear, dayOfMonth);
        refreshDate();
    }

    private void showDatePickerDialog() {
        final FragmentTransaction ft = getFragmentManager().beginTransaction();
        final Fragment prev = getFragmentManager().findFragmentByTag(DATE_PICKER_DIALOG_FRAGMENT_TAG);
        if (prev != null) {
            ft.remove(prev);
        }
        ft.addToBackStack(null);

        final DatePickerDialogFragment dialog = DatePickerDialogFragment.newInstance(mDate.get(Calendar.YEAR),
                mDate.get(Calendar.MONTH), mDate.get(Calendar.DAY_OF_MONTH));
        dialog.show(ft, DATE_PICKER_DIALOG_FRAGMENT_TAG);
    }

    public boolean save() {
        if (!isFieldsValid()) {
            return false;
        }
        return updateReceiptInDB();
    }

    private boolean isFieldsValid() {
        final String shop = mViewShop.getText().toString();
        if (TextUtils.isEmpty(shop)) {
            Toast.makeText(getActivity(), getResources().getString(R.string.fragment_receipt_edit__set_shop_msg),
                    Toast.LENGTH_LONG).show();
            return false;
        }
        final String tmpTotal = mViewTotal.getText().toString();
        if (TextUtils.isEmpty(tmpTotal)) {
            Toast.makeText(getActivity(), getResources().getString(R.string.fragment_receipt_edit__set_total_msg),
                    Toast.LENGTH_LONG).show();
            return false;
        }
        try {
            Float.parseFloat(tmpTotal);
        } catch (final NumberFormatException e) {
            Toast.makeText(getActivity(), getResources().getString(R.string.fragment_receipt_edit__set_total_msg),
                    Toast.LENGTH_LONG).show();
            return false;
        }
        return true;
    }

    public boolean updateReceiptInDB() {
        // Edit receipt
        final String shop = mViewShop.getText().toString();
        final String tmpTotal = mViewTotal.getText().toString();
        float total = 0;
        try {
            total = Float.parseFloat(tmpTotal);
            // Should never happend because the view only allow float
        } catch (final NumberFormatException e) {
            /* Nothing */
        }
        // Not required fields
        final String description = mViewNote.getText().toString();
        final long date = mDate.getTimeInMillis() / 1000;

        final ContentValues receipt = ReceiptsHelper.toContentValues(shop, description, date, total, false, null);

        final Uri uri = Uri.withAppendedPath(Receipts.CONTENT_URI, String.valueOf(getReceiptId()));

        try {
            getActivity().getContentResolver().update(uri, receipt, null, null);
        } catch (final Exception e) {
            LogHelper.e(ErrorID.CONTENT_PROVIDER, "Canno't insert receipt in db: " + e, e);
            // TODO advert the user and ask if we can send a debug info to dev'
            return false;
        }
        return true;
    }

    private final class MyReceiptLoader implements LoaderCallbacks<Cursor> {
        private static final String BUNDLE_RECEIPT_ID = "receiptId";

        /* Not possible to create a static method =0/ */
        public Bundle createLoaderBundle(long receiptId) {
            final Bundle bundle = new Bundle();
            bundle.putLong(BUNDLE_RECEIPT_ID, receiptId);
            return bundle;
        }

        /* Dedicated thread */
        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
            switch (id) {
            case CURSOR_LOADER_ID:
                if (bundle == null) {
                    throw new IllegalArgumentException("Must set an id in the loader bundle");
                }

                final Long receiptId = bundle.getLong(BUNDLE_RECEIPT_ID);
                final Uri uriSingleReceipt = Uri.withAppendedPath(Receipts.CONTENT_URI, String.valueOf(receiptId));
                return new CursorLoader(getActivity(), uriSingleReceipt, null, null, null, null);
            default:
                throw new IllegalArgumentException("The cursor loader " + id + "doesn't exist");
            }
        }

        /* UIThread */
        @Override
        public void onLoadFinished(Loader<Cursor> cl, Cursor cursor) {
            // TODO if later we put everything in the cloud,
            // this method could be called (new version) when the user is
            // editing a receipt. We choose to discard updates
            if (getState() != StateFragmentListener.STATE_COMPLETED) {
                fromCursorToData(cursor);
                refreshView();
                setState(StateFragmentListener.STATE_COMPLETED);
            } else {
                LogHelper.i(
                        "Update received for the receipt currently edited (current screen doesn't reflect them)");
            }
        }

        /* UIThread */
        @Override
        public void onLoaderReset(Loader<Cursor> cursor) {
        }
    }
}