Java tutorial
/* * 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) { } } }