Fixes to issue 2 and 13

master
Evan Leybourn 2010-05-01 17:31:28 +10:00
parent f6ae1d0c6a
commit bdfce0ed2b
23 changed files with 311 additions and 21 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.eleybourn.bookcatalogue"
android:versionName="1.03" android:versionCode="3">
android:versionName="1.04" android:versionCode="4">
<application android:label="@string/app_name" android:icon="@drawable/logo_bc">
<activity android:name=".BookCatalogue"
android:label="@string/app_name">

29
README
View File

@ -30,19 +30,34 @@ Features include;
* The obvious; you can add, edit, and display a list of books
* Books can be sorted by Author or Title.
* Books can be automatically added by typing in the ISBN or scanning the
barcode. It will search Google Books for details (and download a thumbnail).
It will search more sites as I add them.
barcode. It will search Google Books for details (and download a thumbnail).
It will search more sites as I add them.
* Multiple Books lists (or bookshelfs). For example I like to keep my Doctor Who
books seperate from my normal fiction books.
books seperate from my normal fiction books.
* You can search for a book by title, author or series.
* When adding or editing books author, series and publisher should display a
suggestion list
suggestion list
* When downloading book information, authors and titles are converted to proper
case. e.g. "The murder on the links" becomes "The Murder on the Links"
case. e.g. "The murder on the links" becomes "The Murder on the Links"
* Using ISBN or Barcode scanning will also download a thumbnail (if available)
Fixes in v0.2
New in v0.4
* There is now an option in the main menu to export all data to the SDCard.
This will be saved as a tab separated file in the bookCatalogue directory.
* There is now an option in the main menu to import data from the SDCard.
The import function, expects a file in the same format, location and name
as the export function created.
* Improvements have been made to the search results screen to make it more
obvious
New in v0.3
* Authors like "Marianne De Pierres" and "Ursula Le Guin" are now listed
correctly. They will list as;
De Pierres, Marianne
Le Guin, Ursula
New in v0.2
* The, An, and A should be listed after the title. e.g. "The Trigger" becomes
"Trigger, The"
"Trigger, The"
* Books lists should sort by Series, Series #, then Author (or Title)

Binary file not shown.

Binary file not shown.

View File

@ -79,7 +79,13 @@ public final class R {
public static final int date_published=0x7f050008;
public static final int delete_1st_bs=0x7f050021;
public static final int edit_title=0x7f050015;
public static final int export_complete=0x7f05002f;
public static final int export_data=0x7f05002e;
public static final int export_failed=0x7f050032;
public static final int hello=0x7f050000;
public static final int import_alert=0x7f050031;
public static final int import_data=0x7f050030;
public static final int import_failed=0x7f050033;
public static final int isbn=0x7f050006;
public static final int isbn_found=0x7f050024;
public static final int menu_bookshelf=0x7f05001e;
@ -99,9 +105,11 @@ public final class R {
public static final int publisher=0x7f050007;
public static final int rating=0x7f050009;
public static final int read=0x7f05000b;
public static final int results_found=0x7f050035;
public static final int search=0x7f050018;
public static final int search_hint=0x7f05002a;
public static final int search_label=0x7f050029;
public static final int search_title=0x7f050034;
public static final int series=0x7f05000c;
public static final int series_num=0x7f05002c;
public static final int sort_title=0x7f050014;

View File

@ -42,8 +42,16 @@
<string name="title_edit_bs">Book Catalogue (Edit Bookshelf)</string>
<string name="book_exists">The book you are trying to add already exists. Skipping.</string>
<string name="search_label">Search for Books</string>
<string name="search_hint">Start typing in the author or title</string>
<string name="search_hint">Search Author or Title</string>
<string name="book_not_found">The scanned book was not found. Please enter the details manually.</string>
<string name="series_num">#</string>
<string name="unable_to_connect">Unable to Connect to Google Books</string>
<string name="export_data">Export Books</string>
<string name="export_complete">Export (to SDCard) of data complete. </string>
<string name="import_data">Import Books</string>
<string name="import_alert">Warning. Importing data may update existing book records with new information if the id in the import file is the same as an existing id. This is usually fine if you changed a field in an exported file. If you created a new book record, ensure the id field is blank.</string>
<string name="export_failed">ERROR: Export (to SDCard) of data failed. </string>
<string name="import_failed">ERROR: Import of data (from SDCard) failed. Is the file in the correct place?</string>
<string name="search_title">Book Catalogue: Search Results</string>
<string name="results_found">Results Found</string>
</resources>

View File

@ -21,17 +21,28 @@
package com.eleybourn.bookcatalogue;
//import android.R;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import android.app.AlertDialog;
import android.app.ExpandableListActivity;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Environment;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
@ -72,6 +83,8 @@ public class BookCatalogue extends ExpandableListActivity {
private static final int DELETE_ID = Menu.FIRST + 7;
private static final int BOOKSHELVES = Menu.FIRST + 8;
private static final int SORT_BY_AUTHOR = Menu.FIRST + 9;
private static final int EXPORT = Menu.FIRST + 10;
private static final int IMPORT = Menu.FIRST + 11;
public String bookshelf = "";
private ArrayAdapter<String> spinnerAdapter;
@ -80,6 +93,8 @@ public class BookCatalogue extends ExpandableListActivity {
public int sort = 0;
public int numAuthors = 0;
private ArrayList<Integer> currentGroup = new ArrayList<Integer>();
private int importUpdated = 0;
private int importCreated = 0;
/** Called when the activity is first created. */
@Override
@ -152,10 +167,14 @@ public class BookCatalogue extends ExpandableListActivity {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
BooksCursor = mDbHelper.searchAuthors(query, bookshelf);
numAuthors = BooksCursor.getCount();
Toast.makeText(this, numAuthors + " " + this.getResources().getString(R.string.results_found), Toast.LENGTH_LONG).show();
this.setTitle(R.string.search_title);
} else {
BooksCursor = mDbHelper.fetchAllAuthors(bookshelf);
numAuthors = BooksCursor.getCount();
this.setTitle(R.string.app_name);
}
numAuthors = BooksCursor.getCount();
mGroupIdColumnIndex = BooksCursor.getColumnIndexOrThrow("_id");
startManagingCursor(BooksCursor);
@ -204,10 +223,14 @@ public class BookCatalogue extends ExpandableListActivity {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
BooksCursor = mDbHelper.searchBooks(query, order, bookshelf);
numAuthors = BooksCursor.getCount();
Toast.makeText(this, numAuthors + " " + this.getResources().getString(R.string.results_found), Toast.LENGTH_LONG).show();
this.setTitle(R.string.search_title);
} else {
BooksCursor = mDbHelper.fetchAllBooks(order, bookshelf);
numAuthors = BooksCursor.getCount();
this.setTitle(R.string.app_name);
}
numAuthors = BooksCursor.getCount();
mGroupIdColumnIndex = BooksCursor.getColumnIndexOrThrow("_id");
startManagingCursor(BooksCursor);
@ -360,6 +383,12 @@ public class BookCatalogue extends ExpandableListActivity {
MenuItem bookshelf = menu.add(2, BOOKSHELVES, 4, R.string.menu_bookshelf);
bookshelf.setIcon(R.drawable.ic_menu_bookshelves);
MenuItem export = menu.add(2, EXPORT, 4, R.string.export_data);
export.setIcon(android.R.drawable.ic_menu_save);
MenuItem importM = menu.add(2, IMPORT, 4, R.string.import_data);
importM.setIcon(android.R.drawable.ic_menu_upload);
return true;
}
@ -392,6 +421,32 @@ public class BookCatalogue extends ExpandableListActivity {
case BOOKSHELVES:
manageBookselves();
return true;
case EXPORT:
exportData();
return true;
case IMPORT:
// Verify - this can be a dangerous operation
AlertDialog alertDialog = new AlertDialog.Builder(this).setMessage(R.string.import_alert).create();
alertDialog.setTitle(R.string.import_data);
alertDialog.setIcon(android.R.drawable.ic_menu_info_details);
/* Hack to pass this into the class */
final BookCatalogue pthis = this;
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
importData();
fillData();
Toast.makeText(pthis, importUpdated + " Updated, " + importCreated + " Created", Toast.LENGTH_LONG).show();
return;
}
});
alertDialog.setButton2("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//do nothing
return;
}
});
alertDialog.show();
return true;
}
return super.onMenuItemSelected(featureId, item);
@ -567,7 +622,183 @@ public class BookCatalogue extends ExpandableListActivity {
Intent i = new Intent(this, Bookshelf.class);
startActivityForResult(i, ACTIVITY_BOOKSHELF);
}
/*
* Export all data to a CSV file
*
* return void
*/
private void exportData() {
Cursor books = mDbHelper.exportBooks();
String export =
CatalogueDBAdapter.KEY_ROWID + "\t" +
CatalogueDBAdapter.KEY_FAMILY_NAME + "\t" +
CatalogueDBAdapter.KEY_GIVEN_NAMES + "\t" +
CatalogueDBAdapter.KEY_AUTHOR + "\t" +
CatalogueDBAdapter.KEY_TITLE + "\t" +
CatalogueDBAdapter.KEY_ISBN + "\t" +
CatalogueDBAdapter.KEY_PUBLISHER + "\t" +
CatalogueDBAdapter.KEY_DATE_PUBLISHED + "\t" +
CatalogueDBAdapter.KEY_RATING + "\t" +
"bookshelf_id\t" +
CatalogueDBAdapter.KEY_BOOKSHELF + "\t" +
CatalogueDBAdapter.KEY_READ + "\t" +
CatalogueDBAdapter.KEY_SERIES + "\t" +
CatalogueDBAdapter.KEY_SERIES_NUM + "\t" +
CatalogueDBAdapter.KEY_PAGES + "\t" +
"\n";
if (books.moveToFirst()) {
do {
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_ROWID)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_FAMILY_NAME)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_GIVEN_NAMES)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_AUTHOR)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_TITLE)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_ISBN)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_PUBLISHER)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_DATE_PUBLISHED)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_RATING)) + "\t";
export += books.getString(books.getColumnIndexOrThrow("bookshelf_id")) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_BOOKSHELF)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_READ)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_SERIES)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_SERIES_NUM)) + "\t";
export += books.getString(books.getColumnIndexOrThrow(CatalogueDBAdapter.KEY_PAGES)) + "\t";
export += "\n";
}
while (books.moveToNext());
}
/* write to the SDCard */
try {
BufferedWriter out = new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory() + "/" + CatalogueDBAdapter.LOCATION + "/export.tab"));
out.write(export);
out.close();
Toast.makeText(this, R.string.export_complete, Toast.LENGTH_LONG).show();
} catch (IOException e) {
//Log.e("Book Catalogue", "Could not write to the SDCard");
Toast.makeText(this, R.string.export_failed, Toast.LENGTH_LONG).show();
}
}
/**
* This program reads a text file line by line and print to the console. It uses
* FileOutputStream to read the file.
*
*/
public ArrayList<String> readFile(String filename) {
ArrayList<String> importedString = new ArrayList<String>();
File file = new File(filename);
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;
try {
fis = new FileInputStream(file);
// Here BufferedInputStream is added for fast reading.
bis = new BufferedInputStream(fis);
dis = new DataInputStream(bis);
// dis.available() returns 0 if the file does not have more lines.
while (dis.available() != 0) {
// this statement reads the line from the file and print it to the console.
importedString.add(dis.readLine());
}
// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();
} catch (FileNotFoundException e) {
Toast.makeText(this, R.string.import_failed, Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(this, R.string.import_failed, Toast.LENGTH_LONG).show();
}
return importedString;
}
/*
* Export all data to a CSV file
*
* return void
*/
private void importData() {
importUpdated = 0;
importCreated = 0;
ArrayList<String> export = readFile(Environment.getExternalStorageDirectory() + "/" + CatalogueDBAdapter.LOCATION + "/export.tab");
int row = 1;
/* Iterate through each imported row */
while (row < export.size()) {
String[] imported = export.get(row).split("\t");
row++;
/* Setup aliases for each cell*/
Long id = null;
try {
id = Long.parseLong(imported[0]);
} catch(Exception e) {
id = Long.parseLong("0");
}
String family = imported[1];
String given = imported[2];
//String author_id = imported[3];
String title = imported[4];
String isbn = imported[5];
String publisher = imported[6];
String date_published = imported[7];
float rating = 0;
try {
rating = Float.valueOf(imported[8]);
} catch (Exception e) {
rating = 0;
}
//String bookshelf_id = imported[9];
String bookshelf = imported[10];
Boolean read;
if (imported[11].equals("0")) {
read = false;
} else {
read = true;
}
String series = imported[12];
String series_num = imported[13];
int pages = 0;
try {
pages = Integer.parseInt(imported[14]);
} catch (Exception e) {
pages = 0;
}
String author = family + ", " + given;
if (id == 0) {
// Book is new. It does not exist in the current database
if (!isbn.equals("")) {
Cursor book = mDbHelper.fetchBookByISBN(isbn);
int rows = book.getCount();
if (rows != 0) {
// Its a new entry, but the ISBN exists
book.moveToFirst();
mDbHelper.updateBook(book.getLong(0), author, title, isbn, publisher, date_published, rating, bookshelf, read, series, pages, series_num);
importUpdated++;
continue;
}
}
mDbHelper.createBook(author, title, isbn, publisher, date_published, rating, bookshelf, read, series, pages, series_num);
importCreated++;
continue;
} else {
// Book exists and should be updated if it has changed
mDbHelper.updateBook(id, author, title, isbn, publisher, date_published, rating, bookshelf, read, series, pages, series_num);
importUpdated++;
continue;
}
}
}
@Override
public boolean onChildClick(ExpandableListView l, View v, int position, int childPosition, long id) {
boolean result = super.onChildClick(l, v, position, childPosition, id);

View File

@ -40,7 +40,6 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

View File

@ -67,11 +67,11 @@ public class CatalogueDBAdapter {
* Database creation sql statement
*/
private static final String DATABASE_CREATE_AUTHORS =
"create table " + DATABASE_TABLE_AUTHORS +
" (_id integer primary key autoincrement, " +
KEY_FAMILY_NAME + " text not null, " +
KEY_GIVEN_NAMES + " text not null" +
")";
"create table " + DATABASE_TABLE_AUTHORS +
" (_id integer primary key autoincrement, " +
KEY_FAMILY_NAME + " text not null, " +
KEY_GIVEN_NAMES + " text not null" +
")";
private static final String DATABASE_CREATE_BOOKSHELF =
"create table " + DATABASE_TABLE_BOOKSHELF +
@ -95,8 +95,8 @@ public class CatalogueDBAdapter {
KEY_BOOKSHELF + " integer REFERENCES " + DATABASE_TABLE_BOOKSHELF + " ON DELETE SET NULL ON UPDATE SET NULL, " +
KEY_READ + " boolean not null default 'f', " +
KEY_SERIES + " text, " +
KEY_PAGES + " int" +
KEY_SERIES_NUM + " text, " +
KEY_PAGES + " int, " +
KEY_SERIES_NUM + " text " +
")";
private final Context mCtx;
@ -157,6 +157,9 @@ public class CatalogueDBAdapter {
* @throws SQLException if the database could be neither opened or created
*/
public CatalogueDBAdapter open() throws SQLException {
/* Create the bookCatalogue directory if it does not exist */
new File(Environment.getExternalStorageDirectory() + "/" + LOCATION + "/").mkdirs();
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
@ -363,7 +366,7 @@ public class CatalogueDBAdapter {
}
String sql = "SELECT b." + KEY_ROWID + ", a." + KEY_FAMILY_NAME + " || ', ' || a." + KEY_GIVEN_NAMES + " as " + KEY_AUTHOR + ", b." + KEY_TITLE +
", b." + KEY_ISBN + ", b." + KEY_PUBLISHER + ", b." + KEY_DATE_PUBLISHED + ", b." + KEY_RATING + ", bs." + KEY_BOOKSHELF +
", b." + KEY_READ + ", b." + KEY_SERIES + ", b." + KEY_PAGES + ", b." + KEY_SERIES_NUM +
", b." + KEY_READ + ", b." + KEY_SERIES + ", b." + KEY_PAGES + ", b." + KEY_SERIES_NUM + ", b." + KEY_BOOKSHELF + " as bookshelf_id" +
" FROM " + DATABASE_TABLE_BOOKS + " b, " + DATABASE_TABLE_BOOKSHELF + " bs, " + DATABASE_TABLE_AUTHORS + " a" +
" WHERE bs._id=b." + KEY_BOOKSHELF + " AND a._id=b." + KEY_AUTHOR + where +
" ORDER BY " + order + "";
@ -372,6 +375,32 @@ public class CatalogueDBAdapter {
// KEY_DATE_PUBLISHED, KEY_RATING, KEY_BOOKSHELF, KEY_READ, KEY_SERIES, KEY_PAGES}, null, null, null, null, null);
}
/**
* Return a Cursor over the list of all books in the database
*
* @return Cursor over all notes
*/
public Cursor fetchAllBooks(String order) {
return fetchAllBooks(order, "All Books");
}
/**
* Return a Cursor over the list of all books in the database
*
* @return Cursor over all notes
*/
public Cursor exportBooks() {
String sql = "SELECT b." + KEY_ROWID + ", b." + KEY_AUTHOR + ", a." + KEY_FAMILY_NAME + ", a." + KEY_GIVEN_NAMES + ", b." + KEY_TITLE +
", b." + KEY_ISBN + ", b." + KEY_PUBLISHER + ", b." + KEY_DATE_PUBLISHED + ", b." + KEY_RATING +
", b." + KEY_BOOKSHELF + " as bookshelf_id" + ", bs." + KEY_BOOKSHELF + ", b." + KEY_READ + ", b." + KEY_SERIES +
", b." + KEY_PAGES + ", b." + KEY_SERIES_NUM +
" FROM " + DATABASE_TABLE_BOOKS + " b, " + DATABASE_TABLE_BOOKSHELF + " bs, " + DATABASE_TABLE_AUTHORS + " a" +
" WHERE bs._id=b." + KEY_BOOKSHELF + " AND a._id=b." + KEY_AUTHOR;
return mDb.rawQuery(sql, new String[]{});
//return mDb.query(DATABASE_TABLE_BOOKS, new String[] {KEY_ROWID, KEY_AUTHOR, KEY_TITLE, KEY_ISBN, KEY_PUBLISHER,
// KEY_DATE_PUBLISHED, KEY_RATING, KEY_BOOKSHELF, KEY_READ, KEY_SERIES, KEY_PAGES}, null, null, null, null, null);
}
/**
* Return a Cursor over the list of all books in the database by author
*