Java tutorial
/* * The MIT License (MIT) * Copyright 2016 Steve Guidetti * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software?), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS?, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.ultramegasoft.flavordex2.fragment; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.RatingBar; import com.ultramegasoft.flavordex2.FlavordexApp; import com.ultramegasoft.flavordex2.R; import com.ultramegasoft.flavordex2.provider.Tables; import com.ultramegasoft.flavordex2.util.EntryFormHelper; import com.ultramegasoft.flavordex2.widget.DateInputWidget; import com.ultramegasoft.flavordex2.widget.EntryHolder; import com.ultramegasoft.flavordex2.widget.ExtraFieldHolder; import java.util.Date; import java.util.LinkedHashMap; /** * Fragment for editing details for a new or existing journal entry. * * @author Steve Guidetti */ public class EditInfoFragment extends LoadingProgressFragment implements LoaderManager.LoaderCallbacks { /** * Keys for the Fragment arguments */ public static final String ARG_ENTRY_ID = "entry_id"; /** * Loader IDs */ private static final int LOADER_MAIN = 0; /** * Keys for the saved state */ private static final String STATE_EXTRAS = "extras"; /** * The Views for the form fields */ private DateInputWidget mDateInputWidget; private RatingBar mRatingBar; /** * The category ID for the entry being added */ private long mCatId; /** * The entry ID to edit */ private long mEntryId; /** * True while data is loading */ private boolean mIsLoading; /** * The EntryFormHelper */ private EntryFormHelper mFormHelper; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); final Bundle args = getArguments(); if (args != null) { mCatId = args.getLong(AddEntryFragment.ARG_CAT_ID); mEntryId = args.getLong(ARG_ENTRY_ID); } } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState == null) { getLoaderManager().initLoader(LOADER_MAIN, null, this).forceLoad(); } else { //noinspection unchecked final LinkedHashMap<String, ExtraFieldHolder> extras = (LinkedHashMap<String, ExtraFieldHolder>) savedInstanceState .getSerializable(STATE_EXTRAS); if (extras != null) { mFormHelper.setExtras(extras); } hideLoadingIndicator(false); } } @NonNull @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View root = super.onCreateView(inflater, container, savedInstanceState); mFormHelper = createHelper(root); mDateInputWidget = root.findViewById(R.id.entry_date); mRatingBar = root.findViewById(R.id.entry_rating); final Date date = new Date(); mDateInputWidget.setDate(date); mDateInputWidget.setMaxDate(date); final Activity activity = getActivity(); if (activity != null) { mFormHelper.mTxtLocation.setText(((FlavordexApp) activity.getApplication()).getLocationName()); } return root; } @Override protected int getLayoutId() { return R.layout.fragment_edit_info; } /** * Create the EntryFormHelper. * * @param root The root of the layout * @return The EntryFormHelper */ @NonNull protected EntryFormHelper createHelper(@NonNull View root) { return new EntryFormHelper(this, root); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable(STATE_EXTRAS, mFormHelper.getExtras()); } /** * Load the values for the main entry fields. * * @param entry The entry */ private void populateFields(@NonNull EntryHolder entry) { mFormHelper.mTxtTitle.setText(entry.title); mFormHelper.mTxtMaker.setText(entry.maker); mFormHelper.mTxtOrigin.setText(entry.origin); mFormHelper.mTxtPrice.setText(entry.price); mFormHelper.mTxtLocation.setText(entry.location); mDateInputWidget.setDate(new Date(entry.date)); mRatingBar.setRating(entry.rating); mFormHelper.mTxtNotes.setText(entry.notes); } /** * Test if the required fields are properly filled out. * * @return Whether the form is valid */ public boolean isValid() { if (TextUtils.isEmpty(mFormHelper.mTxtTitle.getText().toString())) { mFormHelper.mTxtTitle.setError(getString(R.string.error_required)); mFormHelper.mTxtTitle.requestFocus(); return false; } return !mIsLoading; } /** * Is this fragment currently loading data? * * @return True while the fragment is loading data. */ public boolean isLoading() { return mIsLoading; } /** * Get the data for this entry, including the main info fields and extra fields. * * @param entry An EntryHolder */ public final void getData(@NonNull EntryHolder entry) { entry.id = mEntryId; if (entry.id == 0) { entry.catId = mCatId; } final Date date = mDateInputWidget.getDate(); entry.title = mFormHelper.mTxtTitle.getText().toString(); entry.maker = mFormHelper.mTxtMaker.getText().toString(); entry.origin = mFormHelper.mTxtOrigin.getText().toString(); entry.price = mFormHelper.mTxtPrice.getText().toString(); entry.location = mFormHelper.mTxtLocation.getText().toString(); entry.date = date == null ? new Date().getTime() : date.getTime(); entry.rating = mRatingBar.getRating(); entry.notes = mFormHelper.mTxtNotes.getText().toString(); entry.getExtras().addAll(mFormHelper.getExtras().values()); } @SuppressWarnings("ConstantConditions") @NonNull @Override public Loader onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_MAIN: mIsLoading = true; final Activity activity = getActivity(); if (activity != null) { activity.invalidateOptionsMenu(); return new DataLoader(activity, mCatId, mEntryId); } } return null; } @Override public void onLoadFinished(@NonNull Loader loader, Object data) { switch (loader.getId()) { case LOADER_MAIN: final DataLoader.Holder holder = (DataLoader.Holder) data; if (holder != null) { if (holder.entry != null) { populateFields(holder.entry); } mFormHelper.setExtras(holder.extras); } hideLoadingIndicator(true); mFormHelper.mTxtTitle.setSelection(mFormHelper.mTxtTitle.getText().length()); mIsLoading = false; final Activity activity = getActivity(); if (activity != null) { activity.invalidateOptionsMenu(); } break; } } @Override public void onLoaderReset(@NonNull Loader loader) { } /** * Custom Loader to load everything in one task */ public static class DataLoader extends AsyncTaskLoader<DataLoader.Holder> { /** * The ContentResolver to use */ @NonNull private final ContentResolver mResolver; /** * The category ID */ private long mCatId; /** * The entry ID, if editing */ private final long mEntryId; /** * @param context The Context * @param catId The category ID * @param entryId The entry ID, if editing */ DataLoader(@NonNull Context context, long catId, long entryId) { super(context); mResolver = context.getContentResolver(); mCatId = catId; mEntryId = entryId; } @Override public Holder loadInBackground() { final Holder holder = new Holder(); if (mEntryId > 0) { final Uri uri = ContentUris.withAppendedId(Tables.Entries.CONTENT_ID_URI_BASE, mEntryId); holder.entry = loadEntry(uri); loadExtras(holder); loadExtrasValues(holder, uri); } else { loadExtras(holder); } return holder; } /** * Load the entry from the database. * * @param entryUri The Uri for the entry * @return The entry */ @NonNull private EntryHolder loadEntry(@NonNull Uri entryUri) { final EntryHolder entry = new EntryHolder(); final Cursor cursor = getContext().getContentResolver().query(entryUri, null, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { entry.title = cursor.getString(cursor.getColumnIndex(Tables.Entries.TITLE)); entry.maker = cursor.getString(cursor.getColumnIndex(Tables.Entries.MAKER)); entry.origin = cursor.getString(cursor.getColumnIndex(Tables.Entries.ORIGIN)); entry.price = cursor.getString(cursor.getColumnIndex(Tables.Entries.PRICE)); entry.location = cursor.getString(cursor.getColumnIndex(Tables.Entries.LOCATION)); entry.date = cursor.getLong(cursor.getColumnIndex(Tables.Entries.DATE)); entry.rating = cursor.getFloat(cursor.getColumnIndex(Tables.Entries.RATING)); entry.notes = cursor.getString(cursor.getColumnIndex(Tables.Entries.NOTES)); mCatId = cursor.getLong(cursor.getColumnIndex(Tables.Entries.CAT_ID)); } } finally { cursor.close(); } } return entry; } /** * Load the extra fields from the database. * * @param holder The Holder */ private void loadExtras(@NonNull Holder holder) { final Uri uri = ContentUris.withAppendedId(Tables.Cats.CONTENT_ID_URI_BASE, mCatId); final Cursor cursor = mResolver.query(Uri.withAppendedPath(uri, "extras"), null, null, null, Tables.Extras.POS + " ASC"); if (cursor != null) { long id; String name; boolean preset; boolean deleted; try { while (cursor.moveToNext()) { id = cursor.getLong(cursor.getColumnIndex(Tables.Extras._ID)); name = cursor.getString(cursor.getColumnIndex(Tables.Extras.NAME)); preset = cursor.getInt(cursor.getColumnIndex(Tables.Extras.PRESET)) == 1; deleted = cursor.getInt(cursor.getColumnIndex(Tables.Extras.DELETED)) == 1; holder.extras.put(name, new ExtraFieldHolder(id, name, preset, deleted)); } } finally { cursor.close(); } } } /** * Load the extra field values from the database. * * @param holder The Holder * @param entryUri The Uri for the entry */ private void loadExtrasValues(@NonNull Holder holder, @NonNull Uri entryUri) { final Cursor cursor = mResolver.query(Uri.withAppendedPath(entryUri, "extras"), null, null, null, null); if (cursor != null) { String name; String value; ExtraFieldHolder extra; try { while (cursor.moveToNext()) { name = cursor.getString(cursor.getColumnIndex(Tables.Extras.NAME)); value = cursor.getString(cursor.getColumnIndex(Tables.EntriesExtras.VALUE)); extra = holder.extras.get(name); extra.value = value; extra.deleted = false; } } finally { cursor.close(); } } } /** * The holder for return data */ public static class Holder { /** * The entry */ EntryHolder entry; /** * Map of extra field names to their data */ final LinkedHashMap<String, ExtraFieldHolder> extras = new LinkedHashMap<>(); } } }