Back to project page journal.
The source code is released under:
MIT License
If you think the Android project journal listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package cochrane343.journal.dialogs; //from w ww. j a v a 2s. c o m import static cochrane343.journal.Constants.NO_CATEGORY; import static cochrane343.journal.Constants.LOGGING_TAG; import java.math.BigDecimal; import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentUris; import android.content.DialogInterface; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import cochrane343.journal.Constants; import cochrane343.journal.CurrencyHelper; import cochrane343.journal.ExpenseEditListener; import cochrane343.journal.R; import cochrane343.journal.contentprovider.JournalContract.Category; import cochrane343.journal.contentprovider.JournalContract.Expense; import cochrane343.journal.exceptions.IllegalLoaderIdException; import cochrane343.journal.exceptions.MissingFragmentArgumentException; /** * The dialog for creating new expenses and editing existing ones. <br> * Instances of this dialog fragment can be obtained in two ways: <br> * <ul> * <li>Either by invoking {@link #newInstanceForCreate()} to obtain an empty dialog, which * will invoke the callback method {@link ExpenseEditListener#onAddExpense(String, BigDecimal, long) onAddExpense} * upon completion. * <li>Or by invoking {@link #newInstanceForEdit(long)} to obtain a dialog, which is preset * with the current values of the specified expense and will invoke the callback method * {@link ExpenseEditListener#onUpdateExpense(long, String, BigDecimal, long) onUpdateExpense} * upon completion. * </ul> * Any <code>Activity</code>, this fragment is attached to, is expected to implement the callback * interface {@link ExpenseDialogListener}. * * @author cochrane343 * @since 1.0 */ public class ExpenseDialogFragment extends DialogFragment implements LoaderManager.LoaderCallbacks<Cursor>, OnItemSelectedListener { private static final long NO_EXPENSE = -1; private static final int LOADER_ID_CATEGORIES = 0; private static final int LOADER_ID_EXPENSE = 1; private final static String BUNDLE_KEY_EXPENSE_ID = "expenseId"; /** * The category, which is currently selected in the category spinner */ private long selectedCategory = NO_CATEGORY; private EditText descriptionEditText; private EditText costEditText; private Spinner categorySpinner; private CategorySpinnerAdapter categoryAdapter; /* - - - - - Instance Creation - - - - - - - - - - */ /** * Required empty public constructor */ public ExpenseDialogFragment() { // No-op } /** * Creates a new instance of the {@link ExpenseDialogFragment} for creating a new expense. */ public static ExpenseDialogFragment newInstanceForCreate() { final ExpenseDialogFragment expenseDialogFragment = new ExpenseDialogFragment(); final Bundle arguments = new Bundle(); arguments.putLong(BUNDLE_KEY_EXPENSE_ID, NO_EXPENSE); expenseDialogFragment.setArguments(arguments); return expenseDialogFragment; } /** * Creates a new instance of the {@link ExpenseDialogFragment} for editing an existing expense. * @param expenseId the id of the expense to be edited by the newly created dialog instance */ public static ExpenseDialogFragment newInstanceForEdit(final long expenseId) { final ExpenseDialogFragment expenseDialogFragment = new ExpenseDialogFragment(); final Bundle arguments = new Bundle(); arguments.putLong(BUNDLE_KEY_EXPENSE_ID, expenseId); expenseDialogFragment.setArguments(arguments); return expenseDialogFragment; } /* - - - - - Fragment Arguments - - - - - - - - - */ /** * @return the id of the expense to be edited by this dialog or {@link Constants#NO_EXPENSE NO_EXPENSE} if * this dialog is used to create a new expense * @throws MissingFragmentArgumentException if this dialog has no arguments or the respective bundle key * is not included in the arguments */ private long getExpenseId() { final Bundle arguments = getArguments(); if (arguments == null || !arguments.containsKey(BUNDLE_KEY_EXPENSE_ID)) { throw new MissingFragmentArgumentException(BUNDLE_KEY_EXPENSE_ID); } return arguments.getLong(BUNDLE_KEY_EXPENSE_ID); } /* - - - - - Fragment Lifecyle - - - - - - - - - - - - */ @Override public Dialog onCreateDialog(final Bundle savedInstanceState) { final View view = inflateView(); final TextView currencySymbolTextView = (TextView) view.findViewById(R.id.label_currency_symbol); currencySymbolTextView.setText(CurrencyHelper.getCurrencySymbol()); setupCategorySpinner(); final boolean isCreateDialog = (getExpenseId() == NO_EXPENSE); final int positiveButtonLabel; if (isCreateDialog) { positiveButtonLabel = R.string.label_add; } else { positiveButtonLabel = R.string.label_update; } final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setView(view).setTitle(R.string.label_new_expense) .setInverseBackgroundForced( true ) .setPositiveButton(positiveButtonLabel, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int which) { // Pass empty handler in order to get the button instantiated } }) .setNegativeButton(R.string.label_cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ExpenseDialogFragment.this.getDialog().cancel(); } }); return builder.create(); } private View inflateView() { final LayoutInflater inflater = getActivity().getLayoutInflater(); final View view = inflater.inflate(R.layout.new_expense_dialog, null); descriptionEditText = (EditText) view.findViewById(R.id.new_expense_description); costEditText = (EditText) view.findViewById(R.id.new_expense_cost); categorySpinner = (Spinner) view.findViewById(R.id.spinner_category); return view; } private void setupCategorySpinner() { categoryAdapter = new CategorySpinnerAdapter(getActivity(), null); categorySpinner.setAdapter(categoryAdapter); categorySpinner.setOnItemSelectedListener(this); getLoaderManager().initLoader(LOADER_ID_CATEGORIES, null, this); } @Override public void onStart() { super.onStart(); final AlertDialog dialog = (AlertDialog) getDialog(); final Button positiveButton = (Button) dialog.getButton(Dialog.BUTTON_POSITIVE); positiveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { /* - - - - - Read expense description - - - - - */ final String description = descriptionEditText.getText().toString(); if (description.length() == 0) { Toast.makeText(getActivity(), R.string.toast_enter_description, Toast.LENGTH_SHORT).show(); return; } /* - - - - - Read expense cost - - - - - */ final String costInput = costEditText.getText().toString(); if (!CurrencyHelper.isValidInput(costInput)) { if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "User entered an invalid cost: " + costInput); } Toast.makeText(getActivity(), R.string.toast_enter_valid_cost, Toast.LENGTH_SHORT).show(); return; } final long costInCents = CurrencyHelper.parseInput(costInput); final long expenseId = getExpenseId(); final boolean isCreateDialog = (expenseId == NO_EXPENSE); final ExpenseEditListener listenerActivity = (ExpenseEditListener) getActivity(); if (isCreateDialog) { if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Adding new expense '" + description + "' (" + costInCents + ") in category " + selectedCategory); } listenerActivity.onAddExpense(description, costInCents, selectedCategory); } else { if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Updating expense " + expenseId + " with '" + description + "' (" + costInCents + ") in category " + selectedCategory); } listenerActivity.onUpdateExpense(expenseId, description, costInCents, selectedCategory); } dialog.dismiss(); } }); } /* - - - - - Loader Callback - - - - - - - - - - - */ @Override public Loader<Cursor> onCreateLoader(final int loaderId, final Bundle args) { if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Creating loader for loader id: " + loaderId); } switch (loaderId) { case LOADER_ID_CATEGORIES: return createCategoriesLoader(); case LOADER_ID_EXPENSE: return createExpenseLoader(); default: throw new IllegalLoaderIdException(loaderId); } } /** * @return the loader for retrieving the names of all categories to populate * the category spinner */ private Loader<Cursor> createCategoriesLoader() { final Uri uri = Category.CATEGORIES_URI; final String[] projection = new String[]{ Category._ID, Category._NAME }; final String sortOrder = Category._SORT_ORDER + " ASC"; return new CursorLoader(getActivity(), uri, projection, null, null, sortOrder); } /** * @return the loader for retrieving the description, cost and category of * the expense edited in this dialog */ private Loader<Cursor> createExpenseLoader() { final long expenseId = getExpenseId(); final Uri uri = ContentUris.withAppendedId(Expense.EXPENSES_URI, expenseId); final String[] projection = new String[]{ Expense._DESCRIPTION, Expense._COST, Expense._CATEGORY }; return new CursorLoader(getActivity(), uri, projection, null, null, null); } /** * Callback method invoked when a previously created loader has finished its load. * If the finished loader is the categories name loader and this dialog is used to * edit an existing expense, this method initialized the loader for loading the info * on the edited expense. This is necessary in order to prevent the expense loader * from finishing before the category spinner is fully populated. * If the finished loader is the expense loader, this method populates the input fields * of this dialog with the current values of the edited expense. */ @Override public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) { final int loaderId = loader.getId(); if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Finished load of loader with id: " + loaderId); } switch (loaderId) { case LOADER_ID_CATEGORIES: categoryAdapter.swapCursor(data); final boolean isEditDialog = (getExpenseId() != NO_EXPENSE); if (isEditDialog) { getLoaderManager().initLoader(LOADER_ID_EXPENSE, null, this); } break; case LOADER_ID_EXPENSE: if (data.moveToFirst()) { // Set the description edit text final int descriptionIndex = data.getColumnIndex(Expense._DESCRIPTION); final String description = data.getString(descriptionIndex); descriptionEditText.setText(description); // Set the cost edit text final int costIndex = data.getColumnIndex(Expense._COST); final long costInCents = data.getLong(costIndex); final String costInput = CurrencyHelper.toInputString(costInCents); costEditText.setText(costInput); // Set the category spinner final int categoryIndex = data.getColumnIndex(Expense._CATEGORY); final long categoryId = data.getLong(categoryIndex); selectCategory(categoryId); } break; default: throw new IllegalLoaderIdException(loaderId); } } @Override public void onLoaderReset(final Loader<Cursor> loader) { final int loaderId = loader.getId(); if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Reseting loader with id: " + loaderId); } switch (loaderId) { case LOADER_ID_CATEGORIES: categoryAdapter.swapCursor(null); break; case LOADER_ID_EXPENSE: // No-op break; default: throw new IllegalLoaderIdException(loaderId); } } /* - - - - - Category Spinner - - - - - - - - - - - */ /** * Selects the specified category in the category spinner. * @param categoryId the id of the category to select */ private void selectCategory(final long categoryId) { for (int i = 0; i < categorySpinner.getCount(); i++) { long itemIdAtPosition = categorySpinner.getItemIdAtPosition(i); if (itemIdAtPosition == categoryId) { categorySpinner.setSelection(i); break; } }; } /** * Callback method invoked when the user selects a category in the category spinner */ @Override public void onItemSelected(final AdapterView<?> parent, final View view, final int pos, final long categoryId) { if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Selected category " + categoryId); } selectedCategory = categoryId; } /** * Callback method invoked when the user selects nothing in the category spinner */ @Override public void onNothingSelected(final AdapterView<?> parent) { if (Log.isLoggable(LOGGING_TAG, Log.DEBUG)) { Log.d(LOGGING_TAG, "Selected no category"); } selectedCategory = NO_CATEGORY; } }