/* * @copyright 2012 Philip Warner * @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.booklist; import static com.eleybourn.bookcatalogue.booklist.BooklistGroup.RowKinds.*; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences.Editor; import android.database.Cursor; import com.eleybourn.bookcatalogue.BookCatalogueApp; import com.eleybourn.bookcatalogue.BookCataloguePreferences; import com.eleybourn.bookcatalogue.CatalogueDBAdapter; import com.eleybourn.bookcatalogue.R; import com.eleybourn.bookcatalogue.UniqueId; import com.eleybourn.bookcatalogue.booklist.BooklistGroup.RowKinds; import com.eleybourn.bookcatalogue.database.SerializationUtils; import com.eleybourn.bookcatalogue.database.SerializationUtils.DeserializationException; import com.eleybourn.bookcatalogue.utils.Utils; /** * Collection of system-defined and user-defined Book List styles. * * @author Philip Warner */ public class BooklistStyles implements Iterable<BooklistStyle> { /** Internal storage for defined styles represented by this object */ private ArrayList<BooklistStyle> mList = new ArrayList<BooklistStyle>(); private HashSet<String> mPreferredStyleNames; private ArrayList<String> mPreferredStyleList; public static final String TAG = "BooklistStyles"; public static final String PREF_MENU_PREFIX = TAG + ".Menu"; public static final String PREF_MENU_ITEMS = TAG + ".Menu.Items"; public static final String PREF_USER_STYLE_PREFIX = TAG + ".Styles."; public static final String PREF_USER_STYLE_COUNT = PREF_USER_STYLE_PREFIX + "Count"; public static final String PREF_USER_STYLE_LAST_ID = PREF_USER_STYLE_PREFIX + "LastId"; /** * Constructor */ public BooklistStyles() { mPreferredStyleNames = new HashSet<String>(); mPreferredStyleList = new ArrayList<String>(); getPreferredStyleNames(mPreferredStyleNames, mPreferredStyleList); } /** * Fill in the passed objects with the canonical names of the preferred styles * from user preferences. * * @param hash Hashtable of names * @param list */ private static void getPreferredStyleNames(HashSet<String> hash, ArrayList<String> list) { BookCataloguePreferences prefs = BookCatalogueApp.getAppPreferences(); String itemStr = prefs.getString(PREF_MENU_ITEMS, null); if (itemStr != null && !itemStr.equals("")) { list = Utils.decodeList(itemStr, '|'); for(int i = 0; i < list.size(); i++) { String name = list.get(i); if (name != null && !name.equals("") && !hash.contains(name)) hash.add(name); } } } /** * Static method to get all defined styles, including user-defined styles (the latter is * not supported yet). * * NOTE: Do NOT call this in static initialization of application. This method requires the * application context to be present. * * @return BooklistStyles object */ //public static final int BUILTIN_AUTHOR_SERIES = 1; private static void getBuiltinStyles(BooklistStyles styles) { // First build the stock ones BooklistStyle style; // Author/Series style = new BooklistStyle(R.string.sort_author_series); styles.add(style); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); //// Author(first)/Series //style = new BooklistStyle(R.string.sort_first_author_series); //styles.add(style); //a = new BooklistAuthorGroup(); //a.setAllAuthors(false); //style.addGroup(a); //style.addGroup(ROW_KIND_SERIES); // Unread style = new BooklistStyle(R.string.sort_unread); styles.add(style); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); style.setReadUnreadAll(BooklistStyle.FILTER_UNREAD); // Compact style = new BooklistStyle(R.string.compact); styles.add(style); style.addGroup(ROW_KIND_AUTHOR); style.setCondensed(true); style.setShowThumbnails(false); // Title style = new BooklistStyle(R.string.sort_title_first_letter); styles.add(style); style.addGroup(RowKinds.ROW_KIND_TITLE_LETTER); // Series style = new BooklistStyle(R.string.sort_series); styles.add(style); style.addGroup(RowKinds.ROW_KIND_SERIES); // Genre style = new BooklistStyle(R.string.sort_genre); styles.add(style); style.addGroup(ROW_KIND_GENRE); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Loaned style = new BooklistStyle(R.string.sort_loaned); styles.add(style); style.addGroup(ROW_KIND_LOANED); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Read & Unread style = new BooklistStyle(R.string.sort_read_and_unread); styles.add(style); style.addGroup(ROW_KIND_READ_AND_UNREAD); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Publication date style = new BooklistStyle(R.string.sort_publication_date); styles.add(style); style.addGroup(ROW_KIND_YEAR_PUBLISHED); style.addGroup(ROW_KIND_MONTH_PUBLISHED); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Added date style = new BooklistStyle(R.string.sort_added_date); styles.add(style); style.addGroup(ROW_KIND_YEAR_ADDED); style.addGroup(ROW_KIND_MONTH_ADDED); style.addGroup(ROW_KIND_DAY_ADDED); style.addGroup(ROW_KIND_AUTHOR); // Author/Publication date style = new BooklistStyle(R.string.sort_author_year); styles.add(style); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_YEAR_PUBLISHED); style.addGroup(ROW_KIND_SERIES); // Format style = new BooklistStyle(R.string.format); styles.add(style); style.addGroup(ROW_KIND_FORMAT); // Read date style = new BooklistStyle(R.string.sort_read_date); styles.add(style); style.addGroup(ROW_KIND_YEAR_READ); style.addGroup(ROW_KIND_MONTH_READ); style.addGroup(ROW_KIND_AUTHOR); // Location style = new BooklistStyle(R.string.location); styles.add(style); style.addGroup(ROW_KIND_LOCATION); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Location style = new BooklistStyle(R.string.language); styles.add(style); style.addGroup(ROW_KIND_LANGUAGE); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Rating style = new BooklistStyle(R.string.rating); styles.add(style); style.addGroup(ROW_KIND_RATING); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Bookshelf style = new BooklistStyle(R.string.bookshelf); styles.add(style); style.addGroup(ROW_KIND_BOOKSHELF); style.addGroup(ROW_KIND_AUTHOR); style.addGroup(ROW_KIND_SERIES); // Update date style = new BooklistStyle(R.string.update_date); styles.add(style); style.addGroup(ROW_KIND_UPDATE_YEAR); style.addGroup(ROW_KIND_UPDATE_MONTH); style.addGroup(ROW_KIND_UPDATE_DAY); style.setShowAuthor(true); // NEWKIND: Add new kinds to this list so the user sees them (Optional) } /** * Static method to get all user-defined styles from the passed database. * * @return BooklistStyles object */ private static void getUserStyles(CatalogueDBAdapter db, BooklistStyles styles) { BooklistStyle style; Cursor c = db.getBooklistStyles(); try { // Get the columns we want int idCol = c.getColumnIndex(DatabaseDefinitions.DOM_ID.name); int blobCol = c.getColumnIndex(DatabaseDefinitions.DOM_STYLE.name); // Loop over all rows while (c.moveToNext()) { long id = c.getLong(idCol); byte[] blob = c.getBlob(blobCol); try { style = SerializationUtils.deserializeObject(blob); } catch (DeserializationException e) { // Not much we can do; just delete it. Really should only happen in development. db.deleteBooklistStyle(id); style = null; } if (style != null) { style.setRowId(id); styles.add(style); } } } finally { if (c != null) c.close(); } } /** * Internal implementation which is passed a collection of styles, and returns * the ordered set of preferred styles. * * @param allStyles * * @return */ private static BooklistStyles filterPreferredStyles(BooklistStyles allStyles) { BooklistStyles styles = new BooklistStyles(); // Get the user preference String itemStr = BookCatalogueApp.getAppPreferences().getString(PREF_MENU_ITEMS, null); if (itemStr != null && !itemStr.equals("")) { // Break it up and process in order ArrayList<String> list = Utils.decodeList(itemStr, '|'); if (list != null) { for(String n: list) { // Add any exiting style that is preferred BooklistStyle s = allStyles.findCanonical(n); if (s != null) styles.add(s); } } } // If none found, return all. Otherwise return the ones we found. if (styles.size() > 0) return styles; else { return allStyles; } } /** * Get the preferred styles using system and user-defined styles. * * @param db * @return */ public static BooklistStyles getPreferredStyles(CatalogueDBAdapter db) { BooklistStyles allStyles = new BooklistStyles(); // Get all styles: user & builtin getUserStyles(db, allStyles); getBuiltinStyles(allStyles); // Return filtered list return filterPreferredStyles(allStyles); } /** * Return all styles, with the preferred styles move to front of list. * * @param db * @return */ public static BooklistStyles getAllStyles(CatalogueDBAdapter db) { BooklistStyles allStyles = new BooklistStyles(); // Get all styles and preferred styles. getUserStyles(db, allStyles); getBuiltinStyles(allStyles); BooklistStyles styles = filterPreferredStyles(allStyles); // Add missing styles to the end of the list if (styles != allStyles) { for(BooklistStyle s: allStyles) if (!styles.contains(s)) styles.add(s); } return styles; } /** * Return the number of styles in this collection * * @return */ public int size() { return mList.size(); } /** * Utility to check if this collection contains a specific style INSTANCE. * * @param style * @return */ private boolean contains(BooklistStyle style) { return mList.contains(style); } /** * Add a style to this list * * @param style */ public void add(BooklistStyle style) { style.setPreferred(mPreferredStyleNames.contains(style.getCanonicalName())); mList.add(style); } /** * Find a style based on the passed name. * * @param name * * @return Named style, or null */ public BooklistStyle findCanonical(String name) { for(BooklistStyle style: mList) { if (style.getCanonicalName().equalsIgnoreCase(name)) return style; } return null; } /** * Return the i'th style in the list * * @param i * * @return */ public BooklistStyle get(int i) { return mList.get(i); } /** * Return an iterator for the list of styles. * * @return */ public Iterator<BooklistStyle> iterator() { return mList.iterator(); } /** * Save the preferred style menu list. * * @return */ public static boolean SaveMenuOrder(ArrayList<BooklistStyle> list) { String items = ""; for(int i = 0; i < list.size(); i++) { BooklistStyle s = list.get(i); if (s.isPreferred()) { if (!items.equals("")) items += "|"; items += Utils.encodeListItem(s.getCanonicalName(), '|'); } } Editor e = BookCatalogueApp.getAppPreferences().edit(); e.putString(PREF_MENU_ITEMS, items); e.commit(); return true; }; /** * Start the activity to edit this style. * * @param a */ public static void startEditActivity(Activity a) { Intent i = new Intent(a, BooklistStylesActivity.class); a.startActivityForResult(i, UniqueId.ACTIVITY_BOOKLIST_STYLES); } }