Java tutorial
/* * @copyright 2010 Evan Leybourn * @license GNU General Public License * * This file is part of Book Catalogue. * * Book Catalogue 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 3 of the License, or * (at your option) any later version. * * Book Catalogue 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 Book Catalogue. If not, see <http://www.gnu.org/licenses/>. */ package com.eleybourn.bookcatalogue; import java.io.File; import java.io.IOException; import java.util.ArrayList; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.Tab; import com.actionbarsherlock.view.MenuItem; import com.eleybourn.bookcatalogue.booklist.BooklistBuilder; import com.eleybourn.bookcatalogue.booklist.FlattenedBooklist; import com.eleybourn.bookcatalogue.compat.BookCatalogueActivity; import com.eleybourn.bookcatalogue.compat.BookCatalogueFragment; import com.eleybourn.bookcatalogue.datamanager.DataEditor; import com.eleybourn.bookcatalogue.debug.Tracker; import com.eleybourn.bookcatalogue.dialogs.BookshelfDialogFragment; import com.eleybourn.bookcatalogue.dialogs.BookshelfDialogFragment.OnBookshelfCheckChangeListener; import com.eleybourn.bookcatalogue.dialogs.PartialDatePickerFragment; import com.eleybourn.bookcatalogue.dialogs.PartialDatePickerFragment.OnPartialDatePickerListener; import com.eleybourn.bookcatalogue.dialogs.StandardDialogs; import com.eleybourn.bookcatalogue.dialogs.TextFieldEditorFragment; import com.eleybourn.bookcatalogue.dialogs.TextFieldEditorFragment.OnTextFieldEditorListener; import com.eleybourn.bookcatalogue.utils.Logger; import com.eleybourn.bookcatalogue.utils.Utils; //import android.app.LocalActivityManager; /** * A tab host activity which holds the three edit book tabs 1. Edit Details / * Book Details 2. Edit Comments 3. Loan Book * * @author Evan Leybourn */ public class BookEdit extends BookCatalogueActivity implements BookEditFragmentAbstract.BookEditManager, OnPartialDatePickerListener, OnTextFieldEditorListener, OnBookshelfCheckChangeListener { private FlattenedBooklist mList = null; private GestureDetector mGestureDetector; private boolean mIsDirtyFlg = false; public static final String TAB = "tab"; public static final int TAB_EDIT = 0; public static final int TAB_EDIT_NOTES = 1; public static final int TAB_EDIT_FRIENDS = 2; private String added_genre = ""; private String added_series = ""; private String added_title = ""; private String added_author = ""; public static final String ADDED_HAS_INFO = "ADDED_HAS_INFO"; public static final String ADDED_GENRE = "ADDED_GENRE"; public static final String ADDED_SERIES = "ADDED_SERIES"; public static final String ADDED_TITLE = "ADDED_TITLE"; public static final String ADDED_AUTHOR = "ADDED_AUTHOR"; /** * Standardize the names of tabs ANY NEW NAMES NEED TO BE ADDED TO * mTabNames, below */ private static String TAB_NAME_EDIT_BOOK = "edit_book"; private static String TAB_NAME_EDIT_NOTES = "edit_book_notes"; private static String TAB_NAME_EDIT_FRIENDS = "edit_book_friends"; private static String TAB_NAME_EDIT_ANTHOLOGY = "edit_book_anthology"; /** Key using in intent to start this class in read-only mode */ public static final String KEY_READ_ONLY = "key_read_only"; //public int mCurrentTab = 0; private long mRowId; private CatalogueDBAdapter mDbHelper = new CatalogueDBAdapter(this); private Tab mAnthologyTab = null; private BookData mBookData; private boolean mIsReadOnly; private Button mConfirmButton; private Button mCancelButton; public void onCreate(Bundle savedInstanceState) { Tracker.enterOnCreate(this); super.onCreate(savedInstanceState); setContentView(R.layout.book_edit_base); final ActionBar actionBar = getSupportActionBar(); // Get the extras; we use them a lot Bundle extras = getIntent().getExtras(); // We need the row ID { Long rowId = savedInstanceState != null ? savedInstanceState.getLong(CatalogueDBAdapter.KEY_ROWID) : null; if (rowId == null) { rowId = extras != null ? extras.getLong(CatalogueDBAdapter.KEY_ROWID) : null; } if (rowId == null) { mRowId = 0; } else { mRowId = rowId; } } // Various functions depend on read-only state if (extras != null && extras.containsKey(KEY_READ_ONLY) && extras.getBoolean(KEY_READ_ONLY)) { mIsReadOnly = (mRowId != 0); } else { mIsReadOnly = false; } mDbHelper.open(); // Get the book data from the bundle or the database loadBookData(mRowId, savedInstanceState == null ? extras : savedInstanceState); // get the passed parameters int tabIndex; if (savedInstanceState != null && savedInstanceState.containsKey(BookEdit.TAB)) { tabIndex = savedInstanceState.getInt(BookEdit.TAB); } else if (extras != null && extras.containsKey(BookEdit.TAB)) { tabIndex = extras.getInt(BookEdit.TAB); } else { tabIndex = 0; } int anthology_num = 0; if (mBookData.getRowId() > 0) { anthology_num = mBookData.getInt(BookData.KEY_ANTHOLOGY); } // Class needed for the first tab: BookEditFields except when book is // exist and read-only mode enabled if (mIsReadOnly) { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); BookDetailsReadOnly details = new BookDetailsReadOnly(); details.setArguments(getIntent().getExtras()); getSupportFragmentManager().beginTransaction().replace(R.id.fragment, details).commit(); } else { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); initTab(actionBar, new TabListener<BookEditFields>(this, TAB_NAME_EDIT_BOOK, BookEditFields.class), R.string.details, R.drawable.ic_tab_edit, extras); initTab(actionBar, new TabListener<BookEditNotes>(this, TAB_NAME_EDIT_NOTES, BookEditNotes.class), R.string.notes, R.drawable.ic_tab_notes, extras); // Only show the other tabs if it is not new book, otherwise only // show the first tab if (mRowId > 0) { initTab(actionBar, new TabListener<BookEditLoaned>(this, TAB_NAME_EDIT_FRIENDS, BookEditLoaned.class), R.string.loan, R.drawable.ic_tab_friends, extras); // Only show the anthology tab if the book is marked as an // anthology if (anthology_num != 0) { mAnthologyTab = initTab(actionBar, new TabListener<BookEditAnthology>(this, TAB_NAME_EDIT_ANTHOLOGY, BookEditAnthology.class), R.string.edit_book_anthology, R.drawable.ic_tab_anthology, extras); } } actionBar.setSelectedNavigationItem(tabIndex); } if (mIsReadOnly) { findViewById(R.id.buttons).setVisibility(View.GONE); } else { findViewById(R.id.buttons).setVisibility(View.VISIBLE); } mConfirmButton = (Button) findViewById(R.id.confirm); mCancelButton = (Button) findViewById(R.id.cancel); // mConfirmButton.setOnClickListener - This is set in populate fields. // The behaviour changes depending on if it is adding or saving mCancelButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { // Cleanup because we may have made global changes mDbHelper.purgeAuthors(); mDbHelper.purgeSeries(); // We're done. setResult(Activity.RESULT_OK); if (isDirty()) { StandardDialogs.showConfirmUnsavedEditsDialog(BookEdit.this, null); } else { finish(); } } }); mConfirmButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { saveState(new DoConfirmAction()); } }); if (mRowId > 0) { mConfirmButton.setText(R.string.confirm_save); } else { mConfirmButton.setText(R.string.confirm_add); } initBooklist(extras, savedInstanceState); // Must come after all book data and list retrieved. setActivityTitle(); // Setup the background Utils.initBackground(R.drawable.bc_background_gradient_dim, this, false); Tracker.exitOnCreate(this); } /** * If we are passed a flatened book list, get it and validate it * * @param extras * @param savedInstanceState */ private void initBooklist(Bundle extras, Bundle savedInstanceState) { if (extras != null) { String list = extras.getString("FlattenedBooklist"); if (list != null && !list.equals("")) { mList = new FlattenedBooklist(mDbHelper.getDb(), list); // Check to see it really exists. The underlying table // disappeared once in testing // which is hard to explain; it theoretically should only happen // if the app closes // the database or if the activity pauses with 'isFinishing()' // returning true. if (mList.exists()) { int pos; if (savedInstanceState != null && savedInstanceState.containsKey("FlattenedBooklistPosition")) { pos = savedInstanceState.getInt("FlattenedBooklistPosition"); } else if (extras.containsKey("FlattenedBooklistPosition")) { pos = extras.getInt("FlattenedBooklistPosition"); } else { pos = 0; } mList.moveTo(pos); while (!mList.getBookId().equals(mRowId)) { if (!mList.moveNext()) break; } if (!mList.getBookId().equals(mRowId)) { mList.close(); mList = null; } else { // Add a gesture lister for 'swipe' gestures mGestureDetector = new GestureDetector(this, mGestureListener); } } else { mList.close(); mList = null; } } } } @Override /** * We override the dispatcher because the ScrollView will consume * all events otherwise. */ public boolean dispatchTouchEvent(MotionEvent event) { if (mGestureDetector != null && mGestureDetector.onTouchEvent(event)) return true; super.dispatchTouchEvent(event); // Always return true; we want the events. return true; } /** * Listener to handle 'fling' events; we could handle others but need to be * careful about possible clicks and scrolling. */ GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (mList == null) return false; // Make sure we have considerably more X-velocity than Y-velocity; // otherwise it might be a scroll. if (Math.abs(velocityX / velocityY) > 2) { boolean moved; // Work out which way to move, and do it. if (velocityX > 0) { moved = mList.movePrev(); } else { moved = mList.moveNext(); } if (moved) { setRowId(mList.getBookId()); } return true; } else { return false; } } }; /** * This function will populate the forms elements in three different ways 1. * If a valid rowId exists it will populate the fields from the database 2. * If fields have been passed from another activity (e.g. ISBNSearch) it * will populate the fields from the bundle 3. It will leave the fields * blank for new books. */ private void loadBookData(Long rowId, Bundle bestBundle) { if (bestBundle != null && bestBundle.containsKey("bookData")) { // If we have saved book data, use it mBookData = new BookData(rowId, bestBundle.getBundle("bookData")); } else { // Just load based on rowId mBookData = new BookData(rowId); } } /** * This is a straight passthrough */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); // 1. the call to duplicateBook() no longer uses this ID // 2. We can't just finish(); there might be unsaved edits. // 3. if we want to finish on creating a new book, we should do it when // we start the activity // switch (requestCode) { // setResult(resultCode, intent); // finish(); // break; // } } @Override protected void onDestroy() { Tracker.enterOnDestroy(this); super.onDestroy(); mDbHelper.close(); Tracker.exitOnDestroy(this); } @Override /** * Close the list object (frees statements) and if we are finishing, delete the temp table. * * This is an ESSENTIAL step; for some reason, in Android 2.1 if these statements are not * cleaned up, then the underlying SQLiteDatabase gets double-dereferenced, resulting in * the database being closed by the deeply dodgy auto-close code in Android. */ public void onPause() { if (mList != null) { mList.close(); if (this.isFinishing()) { mList.deleteData(); } } super.onPause(); } @Override protected void onSaveInstanceState(Bundle outState) { Tracker.enterOnSaveInstanceState(this); super.onSaveInstanceState(outState); ActionBar actionBar = this.getSupportActionBar(); outState.putLong(CatalogueDBAdapter.KEY_ROWID, mRowId); outState.putBundle("bookData", mBookData.getRawData()); if (mList != null) { outState.putInt("FlattenedBooklistPosition", (int) mList.getPosition()); } outState.putInt(BookEdit.TAB, actionBar.getSelectedNavigationIndex()); Tracker.exitOnSaveInstanceState(this); } /******************************************************** * Standard STATIC Methods * ****************************************************** */ /** * Open book for viewing in edit or read-only mode. The mode depends on * {@link BookCataloguePreferences#PREF_OPEN_BOOK_READ_ONLY} preference * option. If it set book opened in read-only mode otherwise in edit mode * (default). * * @param a * current activity from which we start * @param id * The id of the book to view * @param builder * (Optional) builder for underlying book list. Only used in * read-only view. * @param position * (Optional) position in underlying book list. Only used in * read-only view. */ public static void openBook(Activity a, long id, BooklistBuilder builder, Integer position) { boolean isReadOnly = BookCatalogueApp.getAppPreferences() .getBoolean(BookCataloguePreferences.PREF_OPEN_BOOK_READ_ONLY, true); if (isReadOnly) { // Make a flattened copy of the list of books, if available String listTable = null; if (builder != null) { listTable = builder.createFlattenedBooklist().getTable().getName(); } BookEdit.viewBook(a, id, listTable, position); } else { BookEdit.editBook(a, id, BookEdit.TAB_EDIT); } } /** * Load the EditBook activity based on the provided id in edit mode. Also * open to the provided tab. * * @param id * The id of the book to edit * @param tab * Which tab to open first */ public static void editBook(Activity a, long id, int tab) { Intent i = new Intent(a, BookEdit.class); i.putExtra(CatalogueDBAdapter.KEY_ROWID, id); i.putExtra(BookEdit.TAB, tab); a.startActivityForResult(i, UniqueId.ACTIVITY_EDIT_BOOK); return; } /** * Load the EditBook tab activity in read-only mode. The first tab is book * details. * * @param a * current activity from which we start * @param id * The id of the book to view * @param listTable * (Optional) name of the temp table comtaining a list of book * IDs. * @param position * (Optional) position in underlying book list. Only used in * read-only view. */ public static void viewBook(Activity a, long id, String listTable, Integer position) { Intent i = new Intent(a, BookEdit.class); i.putExtra("FlattenedBooklist", listTable); if (position != null) { i.putExtra("FlattenedBooklistPosition", position); } i.putExtra(CatalogueDBAdapter.KEY_ROWID, id); i.putExtra(BookEdit.TAB, BookEdit.TAB_EDIT); // needed extra for // creating BookEdit i.putExtra(BookEdit.KEY_READ_ONLY, true); a.startActivityForResult(i, UniqueId.ACTIVITY_VIEW_BOOK); return; } /** * Initialize a TabSpec according to defined parameters and add it to the * TabHost. * * @param tabHost * parent TabHost * @param intentClass * class for specifying intent. It`s the Activity class contained * in this tab. * @param tabTag * required tag of tab * @param titleResId * resource id of a title of the tab * @param iconResId * resource id of an icon (drawable) of the tab * @param extras * extras for putting in the intent. If extras is null they will * not be added. */ private <T extends BookCatalogueFragment> Tab initTab(ActionBar actionBar, TabListener<T> listener, int titleResId, int iconResId, Bundle extras) { Resources resources = getResources(); String tabTitle = resources.getString(titleResId); Tab tab = actionBar.newTab().setText(tabTitle).setTabListener(listener).setTag(titleResId); // tab.setIcon(iconResId); actionBar.addTab(tab); return tab; } /** * Mark the data as dirty (or not) */ public void setDirty(boolean dirty) { mIsDirtyFlg = dirty; } /** * Get the current status of the data in this activity */ public boolean isDirty() { return mIsDirtyFlg; } /** * If 'back' is pressed, and the user has made changes, ask them if they * really want to lose the changes. * * We don't use onBackPressed because it does not work with API level 4. */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (isDirty()) { StandardDialogs.showConfirmUnsavedEditsDialog(this, null); } else { doFinish(); } return true; } else { return super.onKeyDown(keyCode, event); } } /** * Check if edits need saving, and finish the activity if not */ private void doFinish() { if (isDirty()) { StandardDialogs.showConfirmUnsavedEditsDialog(this, new Runnable() { @Override public void run() { finishAndSendIntent(); } }); } else { finishAndSendIntent(); } } /** * Actually finish this activity making sure an intent is returned. */ private void finishAndSendIntent() { Intent i = new Intent(); i.putExtra(CatalogueDBAdapter.KEY_ROWID, mBookData.getRowId()); setResult(Activity.RESULT_OK, i); finish(); } /** * Show or hide the anthology tab */ @Override public void setShowAnthology(boolean showAnthology) { ActionBar actionBar = this.getSupportActionBar(); if (showAnthology) { if (mAnthologyTab == null) { mAnthologyTab = initTab(actionBar, new TabListener<BookEditAnthology>(this, TAB_NAME_EDIT_ANTHOLOGY, BookEditAnthology.class), R.string.edit_book_anthology, R.drawable.ic_tab_anthology, getIntent().getExtras()); } } else { if (mAnthologyTab != null) { actionBar.removeTab(mAnthologyTab); mAnthologyTab = null; } } } /** * Listener to create/show the relevant tabs * * @author pjw * * @param <T> Fragment type */ public static class TabListener<T extends BookCatalogueFragment> implements ActionBar.TabListener { private Fragment mFragment; private final Activity mActivity; private final String mTag; private final Class<T> mClass; /** * Constructor used each time a new tab is created. * * @param activity * The host Activity, used to instantiate the fragment * @param tag * The identifier tag for the fragment * @param clz * The fragment's Class, used to instantiate the fragment */ public TabListener(Activity activity, String tag, Class<T> clz) { mActivity = activity; mTag = tag; mClass = clz; } /* The following are each of the ActionBar.TabListener callbacks */ public void onTabSelected(Tab tab, FragmentTransaction ft) { // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity mFragment = T.instantiate(mActivity, mClass.getName()); // ft.add(android.R.id.content, mFragment, mTag); ft.replace(R.id.fragment, mFragment, mTag); } else { // If it exists, simply attach it in order to show it ft.attach(mFragment); } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { // Detach the fragment, because another one is being attached ft.detach(mFragment); } } public void onTabReselected(Tab tab, FragmentTransaction ft) { // User selected the already selected tab. Usually do nothing. } } @Override public BookData getBookData() { return mBookData; } @Override public void setRowId(Long id) { if (mRowId != id) { mRowId = id; loadBookData(id, null); Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof DataEditor) { ((DataEditor) frag).reloadData(mBookData); } setActivityTitle(); } } /** * Validate the current data in all fields that have validators. Display any * errors. * * @param values * The values to use * * @return Boolean success or failure. */ private boolean validate() { Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof DataEditor) { ((DataEditor) frag).saveAllEdits(mBookData); } if (!mBookData.validate()) { Toast.makeText(this, mBookData.getValidationExceptionMessage(getResources()), Toast.LENGTH_LONG).show(); return false; } return true; } /** * This will save a book into the database, by either updating or created a * book. Minor modifications will be made to the strings: - Titles will be * rewords so 'a', 'the', 'an' will be moved to the end of the string (this * is only done for NEW books) * * - Date published will be converted from a date to a string * * Thumbnails will also be saved to the correct location * * It will check if the book already exists (isbn search) if you are * creating a book; if so the user will be prompted to confirm. * * In all cases, once the book is added/created, or not, the appropriate * method of the passed nextStep parameter will be executed. Passing * nextStep is necessary because this method may return after displaying a * dialogue. * * @param nextStep * The next step to be executed on success/failure. * * @throws IOException */ private void saveState(final PostSaveAction nextStep) { // Ignore validation failures; we still validate to get the current // values. if (!validate()) { // nextStep.failure(); // return; } // However, there is some data that we really do require... if (mBookData.getAuthorList().size() == 0) { Toast.makeText(this, getResources().getText(R.string.author_required), Toast.LENGTH_LONG).show(); return; } if (!mBookData.containsKey(CatalogueDBAdapter.KEY_TITLE) || mBookData.getString(CatalogueDBAdapter.KEY_TITLE).trim().length() == 0) { Toast.makeText(this, getResources().getText(R.string.title_required), Toast.LENGTH_LONG).show(); return; } if (mRowId == 0) { String isbn = mBookData.getString(CatalogueDBAdapter.KEY_ISBN); /* Check if the book currently exists */ if (!isbn.equals("")) { if (mDbHelper.checkIsbnExists(isbn, true)) { /* * If it exists, show a dialog and use it to perform the * next action, according to the users choice. */ SaveAlert alert = new SaveAlert(); alert.setMessage(getResources().getString(R.string.duplicate_book_message)); alert.setTitle(R.string.duplicate_book_title); alert.setIcon(android.R.drawable.ic_menu_info_details); alert.setButton2(this.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { updateOrCreate(); nextStep.success(); return; } }); alert.setButton(this.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { nextStep.failure(); return; } }); alert.show(); return; } } } // No special actions required...just do it. updateOrCreate(); nextStep.success(); return; } private class SaveAlert extends AlertDialog { protected SaveAlert() { super(BookEdit.this); } } /** * Save the collected book details */ private void updateOrCreate() { if (mRowId == 0) { long id = mDbHelper.createBook(mBookData, 0); if (id > 0) { setRowId(id); File thumb = CatalogueDBAdapter.getTempThumbnail(); File real = CatalogueDBAdapter.fetchThumbnailByUuid(mDbHelper.getBookUuid(mRowId)); thumb.renameTo(real); } } else { mDbHelper.updateBook(mRowId, mBookData, 0); } /* * These are global variables that will be sent via intent back to the * list view, if added/created */ try { ArrayList<Author> authors = mBookData.getAuthorList(); if (authors.size() > 0) { added_author = authors.get(0).getSortName(); } else { added_author = ""; } } catch (Exception e) { Logger.logError(e); } ; try { ArrayList<Series> series = mBookData.getSeriesList(); if (series.size() > 0) added_series = series.get(0).name; else added_series = ""; } catch (Exception e) { Logger.logError(e); } ; added_title = mBookData.getString(CatalogueDBAdapter.KEY_TITLE); added_genre = mBookData.getString(CatalogueDBAdapter.KEY_GENRE); } public interface PostSaveAction { public void success(); public void failure(); } private class DoConfirmAction implements PostSaveAction { DoConfirmAction() { } public void success() { Intent i = new Intent(); i.putExtra(CatalogueDBAdapter.KEY_ROWID, mBookData.getRowId()); i.putExtra(ADDED_HAS_INFO, true); i.putExtra(ADDED_GENRE, added_genre); i.putExtra(ADDED_SERIES, added_series); i.putExtra(ADDED_TITLE, added_title); i.putExtra(ADDED_AUTHOR, added_author); setResult(Activity.RESULT_OK, i); finish(); } public void failure() { // Do nothing } } /** * Sets title of the parent activity in the next format:<br> * <i>"title"</i> * * @param title */ private void setActivityTitle() { ActionBar bar = this.getSupportActionBar(); if (mIsReadOnly && mList != null) { bar.setTitle(mBookData.getString(CatalogueDBAdapter.KEY_TITLE)); bar.setSubtitle( mBookData.getAuthorTextShort() + " (" + String.format(getResources().getString(R.string.x_of_y), mList.getAbsolutePosition(), mList.getCount()) + ")"); } else if (mBookData.getRowId() > 0) { bar.setTitle(mBookData.getString(CatalogueDBAdapter.KEY_TITLE)); bar.setSubtitle(mBookData.getAuthorTextShort()); } else { bar.setTitle(this.getResources().getString(R.string.menu_insert)); bar.setSubtitle(null); } } private ArrayList<String> mPublishers; /** * Load a publisher list; reloading this list every time a tab changes is * slow. So we cache it. * * @return List of publishers */ public ArrayList<String> getPublishers() { if (mPublishers == null) { mPublishers = new ArrayList<String>(); Cursor publisher_cur = mDbHelper.fetchAllPublishers(); final int col = publisher_cur.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_PUBLISHER); try { while (publisher_cur.moveToNext()) { mPublishers.add(publisher_cur.getString(col)); } } finally { publisher_cur.close(); } } return mPublishers; } private ArrayList<String> mGenres; /** * Load a genre list; reloading this list every time a tab changes is slow. * So we cache it. * * @return List of publishers */ public ArrayList<String> getGenres() { if (mGenres == null) { mGenres = new ArrayList<String>(); Cursor genre_cur = mDbHelper.fetchAllGenres(""); final int col = genre_cur.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_ROWID); try { while (genre_cur.moveToNext()) { mGenres.add(genre_cur.getString(col)); } } finally { genre_cur.close(); } } return mGenres; } /** List of languages in database so far */ private ArrayList<String> mLanguages; /** * Load a language list; reloading this list every time a tab changes is slow. * So we cache it. * * @return List of languages */ @Override public ArrayList<String> getLanguages() { if (mLanguages == null) { mLanguages = new ArrayList<String>(); Cursor cur = mDbHelper.fetchAllLanguages(""); final int col = cur.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_ROWID); try { while (cur.moveToNext()) { String s = cur.getString(col); if (s != null && !s.equals("")) { mLanguages.add(cur.getString(col)); } } } finally { cur.close(); } } return mLanguages; } private ArrayList<String> mFormats; /** * Load a format list; reloading this list every time a tab changes is slow. * So we cache it. * * @return List of publishers */ public ArrayList<String> getFormats() { if (mFormats == null) { mFormats = mDbHelper.getFormats(); } return mFormats; } /** * Dialog handler; pass results to relevant destination */ @Override public void onPartialDatePickerSet(int dialogId, PartialDatePickerFragment dialog, Integer year, Integer month, Integer day) { Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof OnPartialDatePickerListener) { ((OnPartialDatePickerListener) frag).onPartialDatePickerSet(dialogId, dialog, year, month, day); } else { Toast.makeText(this, R.string.unexpected_error, Toast.LENGTH_LONG).show(); Logger.logError(new RuntimeException("Received date dialog result with no fragment to handle it")); } // Make sure it's dismissed if (dialog.isVisible()) dialog.dismiss(); } /** * Dialog handler; pass results to relevant destination */ @Override public void onPartialDatePickerCancel(int dialogId, PartialDatePickerFragment dialog) { Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof OnPartialDatePickerListener) { ((OnPartialDatePickerListener) frag).onPartialDatePickerCancel(dialogId, dialog); } else { Toast.makeText(this, R.string.unexpected_error, Toast.LENGTH_LONG).show(); Logger.logError( new RuntimeException("Received date dialog cancellation with no fragment to handle it")); } // Make sure it's dismissed if (dialog.isVisible()) dialog.dismiss(); } /** * Dialog handler; pass results to relevant destination */ @Override public void onTextFieldEditorSave(int dialogId, TextFieldEditorFragment dialog, String newText) { Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof OnTextFieldEditorListener) { ((OnTextFieldEditorListener) frag).onTextFieldEditorSave(dialogId, dialog, newText); } else { Toast.makeText(this, R.string.unexpected_error, Toast.LENGTH_LONG).show(); Logger.logError( new RuntimeException("Received onTextFieldEditorSave result with no fragment to handle it")); } // Make sure it's dismissed if (dialog.isVisible()) dialog.dismiss(); } /** * Dialog handler; pass results to relevant destination */ @Override public void onTextFieldEditorCancel(int dialogId, TextFieldEditorFragment dialog) { Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof OnTextFieldEditorListener) { ((OnTextFieldEditorListener) frag).onTextFieldEditorCancel(dialogId, dialog); } else { Toast.makeText(this, R.string.unexpected_error, Toast.LENGTH_LONG).show(); Logger.logError( new RuntimeException("Received onTextFieldEditorCancel result with no fragment to handle it")); } // Make sure it's dismissed if (dialog.isVisible()) dialog.dismiss(); } /** * Dialog handler; pass results to relevant destination */ @Override public void onBookshelfCheckChanged(int dialogId, BookshelfDialogFragment dialog, boolean checked, String shelf, String textList, String encodedList) { Fragment frag = getSupportFragmentManager().findFragmentById(R.id.fragment); if (frag instanceof OnBookshelfCheckChangeListener) { ((OnBookshelfCheckChangeListener) frag).onBookshelfCheckChanged(dialogId, dialog, checked, shelf, textList, encodedList); } else { Toast.makeText(this, R.string.unexpected_error, Toast.LENGTH_LONG).show(); Logger.logError( new RuntimeException("Received onBookshelfCheckChanged result with no fragment to handle it")); } } /** * menu handler; handle the 'home' key, otherwise, pass on the event */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: doFinish(); return true; default: return super.onOptionsItemSelected(item); } } }