From 279119a4521e97692bf4089ad20284e099b473be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Apr 2015 18:10:45 +0000 Subject: [PATCH] [SVN] r1542 Inventory in /Libraries/twister-lib-android/ [ADD] Finalize BLOB storage for images [FIX] Import to read to BLOBs [FIX] Don't query InventoryProvider twice (bumptech/glide#420) --- .../android/db/DatabaseOpenHelper.java | 4 +- .../android/utils/concurrent/Executable.java | 2 + .../android/utils/tools/DatabaseTools.java | 82 +++++++++++++------ src/main/assets/MagicHomeInventory.schema.sql | 6 +- .../inventory/android/Constants.java | 36 +++++--- .../android/activity/ManageSpaceActivity.java | 2 +- .../inventory/android/content/Database.java | 52 ++++++++---- .../android/content/InventoryProvider.java | 32 ++------ .../android/content/io/ExporterTask.java | 4 +- .../android/content/io/ImporterTask.java | 22 +++-- .../android/content/io/csv/CSVImporter.java | 20 ++--- .../android/content/io/xml/XMLImporter.java | 6 +- .../android/content/model/ImagedDTO.java | 12 +-- .../fragment/data/BaseGalleryFragment.java | 5 +- .../fragment/data/ItemEditFragment.java | 4 +- .../fragment/data/ItemViewFragment.java | 2 +- .../fragment/data/PropertyEditFragment.java | 4 +- .../fragment/data/PropertyViewFragment.java | 2 +- .../fragment/data/RoomEditFragment.java | 4 +- .../fragment/data/RoomViewFragment.java | 2 +- .../view/adapters/BaseImagedAdapter.java | 4 +- .../view/adapters/GalleryGroupViewHolder.java | 3 +- .../view/adapters/GalleryViewHolder.java | 7 +- src/main/res/values/queries.xml | 66 +++++++++------ 24 files changed, 231 insertions(+), 152 deletions(-) diff --git a/libs/twister-lib-android/src/main/java/net/twisterrob/android/db/DatabaseOpenHelper.java b/libs/twister-lib-android/src/main/java/net/twisterrob/android/db/DatabaseOpenHelper.java index 57bcbd83b..8ae512780 100644 --- a/libs/twister-lib-android/src/main/java/net/twisterrob/android/db/DatabaseOpenHelper.java +++ b/libs/twister-lib-android/src/main/java/net/twisterrob/android/db/DatabaseOpenHelper.java @@ -113,7 +113,9 @@ public void onDestroy(SQLiteDatabase db) { execFile(db, String.format(DB_TEST_FILE, dbName)); } if (devMode) { - backupDB(db, "onOpen_backup"); + if (dumpOnOpen) { + backupDB(db, "onOpen_backup"); + } execFile(db, String.format(DB_DEVELOPMENT_FILE, dbName)); } LOG.info("Opened database: {}", dbToString(db)); diff --git a/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/concurrent/Executable.java b/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/concurrent/Executable.java index 9339a742d..1d3c52bf3 100644 --- a/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/concurrent/Executable.java +++ b/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/concurrent/Executable.java @@ -1,6 +1,8 @@ package net.twisterrob.android.utils.concurrent; public interface Executable { + @SuppressWarnings("unchecked") void executeParallel(Param... params); + @SuppressWarnings("unchecked") void executeSerial(Param... params); } diff --git a/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/tools/DatabaseTools.java b/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/tools/DatabaseTools.java index 649d86702..44cfb4193 100644 --- a/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/tools/DatabaseTools.java +++ b/libs/twister-lib-android/src/main/java/net/twisterrob/android/utils/tools/DatabaseTools.java @@ -2,6 +2,8 @@ import java.util.Locale; +import javax.annotation.Nullable; + import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.support.annotation.NonNull; @@ -17,6 +19,17 @@ public static String dbToString(final SQLiteDatabase database) { String path = database != null? database.getPath() : null; return String.format(Locale.ROOT, "v%d@%s", version, path); } + public static boolean getBoolean(Cursor cursor, String columnName) { + int col = cursor.getColumnIndex(columnName); + return cursor.getInt(col) != 0; + } + public static boolean getOptionalBoolean(Cursor cursor, String columnName, boolean defaultValue) { + int col = cursor.getColumnIndex(columnName); + if (col != DatabaseOpenHelper.CURSOR_NO_COLUMN) { + return cursor.getInt(col) != 0; + } + return defaultValue; + } public static int getOptionalInt(Cursor cursor, String columnName, int defaultValue) { int col = cursor.getColumnIndex(columnName); @@ -57,41 +70,64 @@ public static String getOptionalString(Cursor cursor, String columnName, String return defaultValue; } - public static Long singleResult(@NonNull Cursor cursor) { + public static Long singleLong(@NonNull Cursor cursor, @Nullable String columnName) { try { - if (cursor.getCount() == 0 || cursor.getColumnCount() == 0) { - throw new IllegalArgumentException("Empty cursor"); - } - if (1 < cursor.getCount()) { - throw new IllegalArgumentException("Multiple rows returned"); - } - if (1 < cursor.getColumnCount()) { - throw new IllegalArgumentException("Multiple columns returned"); + checkSingleRow(cursor); + int columnIndex; + if (columnName == null) { + checkSingleColumn(cursor); + columnIndex = 0; + } else { + columnIndex = cursor.getColumnIndexOrThrow(columnName); } - if (!cursor.moveToFirst()) { - throw new IllegalArgumentException("Cannot move to first item"); - } - return cursor.isNull(0)? null : cursor.getLong(0); + return cursor.isNull(columnIndex)? null : cursor.getLong(columnIndex); } finally { cursor.close(); } } - public static String singleResultFromColumn(@NonNull Cursor cursor, @NonNull String columnName) { + public static String singleString(@NonNull Cursor cursor, @Nullable String columnName) { try { - if (cursor.getCount() == 0 || cursor.getColumnCount() == 0) { - throw new IllegalArgumentException("Empty cursor"); - } - if (1 < cursor.getCount()) { - throw new IllegalArgumentException("Multiple rows returned"); + checkSingleRow(cursor); + if (columnName == null) { + checkSingleColumn(cursor); + return cursor.getString(0); + } else { + return cursor.getString(cursor.getColumnIndexOrThrow(columnName)); } - if (!cursor.moveToFirst()) { - throw new IllegalArgumentException("Cannot move to first item"); + } finally { + cursor.close(); + } + } + + public static byte[] singleBlob(@NonNull Cursor cursor, @Nullable String columnName) { + try { + checkSingleRow(cursor); + if (columnName == null) { + checkSingleColumn(cursor); + return cursor.getBlob(0); + } else { + return cursor.getBlob(cursor.getColumnIndexOrThrow(columnName)); } - int columnIndex = cursor.getColumnIndexOrThrow(columnName); - return cursor.isNull(columnIndex)? null : cursor.getString(columnIndex); } finally { cursor.close(); } } + + private static void checkSingleRow(@NonNull Cursor cursor) { + if (cursor.getCount() == 0 || cursor.getColumnCount() == 0) { + throw new IllegalArgumentException("Empty cursor"); + } + if (1 < cursor.getCount()) { + throw new IllegalArgumentException("Multiple rows returned"); + } + if (!cursor.moveToFirst()) { + throw new IllegalArgumentException("Cannot move to first item"); + } + } + private static void checkSingleColumn(@NonNull Cursor cursor) { + if (1 < cursor.getColumnCount()) { + throw new IllegalArgumentException("Multiple columns returned"); + } + } } diff --git a/src/main/assets/MagicHomeInventory.schema.sql b/src/main/assets/MagicHomeInventory.schema.sql index 3151eb588..dc45dd723 100644 --- a/src/main/assets/MagicHomeInventory.schema.sql +++ b/src/main/assets/MagicHomeInventory.schema.sql @@ -107,7 +107,7 @@ CREATE TABLE Item ( _id INTEGER NOT NULL, name NVARCHAR NOT NULL, -- user entered description TEXT NULL, -- user entered - image VARCHAR NULL, -- relative path + image BLOB NULL, -- JPEG image category INTEGER DEFAULT 0 -- uncategorized CONSTRAINT fk_Item_category REFERENCES Category(_id) @@ -193,7 +193,7 @@ CREATE TABLE Property ( _id INTEGER NOT NULL, name NVARCHAR NOT NULL, -- user entered description TEXT NULL, -- user entered - image VARCHAR NULL, -- relative path + image BLOB NULL, -- JPEG image type INTEGER DEFAULT 0 -- other CONSTRAINT fk_Property_type REFERENCES PropertyType(_id) @@ -228,7 +228,7 @@ CREATE TABLE Room ( _id INTEGER NOT NULL, name NVARCHAR NOT NULL, -- user entered description TEXT NULL, -- user entered - image VARCHAR NULL, -- relative path + image BLOB NULL, -- JPEG image type INTEGER DEFAULT 0 -- other CONSTRAINT fk_Room_type REFERENCES RoomType(_id) diff --git a/src/main/java/net/twisterrob/inventory/android/Constants.java b/src/main/java/net/twisterrob/inventory/android/Constants.java index 79214c1c8..02c2e41bf 100644 --- a/src/main/java/net/twisterrob/inventory/android/Constants.java +++ b/src/main/java/net/twisterrob/inventory/android/Constants.java @@ -8,7 +8,10 @@ import android.support.annotation.*; import com.bumptech.glide.*; +import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.stream.StreamModelLoader; import com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.load.resource.gif.GifResourceDecoder; @@ -73,20 +76,29 @@ class Pic { private static final LoggingListener IMAGE_LOGGING_LISTENER = new LoggingListener<>("image"); - public static final DrawableRequestBuilder SVG_REQUEST = Glide - .with(App.getAppContext()) - .fromResource() + private static DrawableRequestBuilder baseRequest(Class clazz) { + ModelLoader loader = Glide.buildModelLoader(clazz, InputStream.class, App.getAppContext()); + DrawableRequestBuilder builder = Glide + .with(App.getAppContext()) + .using((StreamModelLoader)loader) + .from(clazz) + .animate(android.R.anim.fade_in) + .error(R.drawable.image_error); + if (DISABLE && BuildConfig.DEBUG) { + builder = builder + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + ; + } + return builder; + } + + public static final DrawableRequestBuilder SVG_REQUEST = baseRequest(Integer.class) .listener(SVG_LOGGING_LISTENER) - .decoder(getSvgDecoder()) - .animate(android.R.anim.fade_in) - .error(R.drawable.image_error); + .decoder(getSvgDecoder()); - public static final DrawableRequestBuilder IMAGE_REQUEST = Glide - .with(App.getAppContext()) - .fromString() - .listener(IMAGE_LOGGING_LISTENER) - .animate(android.R.anim.fade_in) - .error(R.drawable.image_error); + public static final DrawableRequestBuilder IMAGE_REQUEST = baseRequest(String.class) + .listener(IMAGE_LOGGING_LISTENER); private static GifBitmapWrapperResourceDecoder getSvgDecoder() { Context context = App.getAppContext(); diff --git a/src/main/java/net/twisterrob/inventory/android/activity/ManageSpaceActivity.java b/src/main/java/net/twisterrob/inventory/android/activity/ManageSpaceActivity.java index 72fc460de..8b79247f9 100644 --- a/src/main/java/net/twisterrob/inventory/android/activity/ManageSpaceActivity.java +++ b/src/main/java/net/twisterrob/inventory/android/activity/ManageSpaceActivity.java @@ -170,7 +170,7 @@ void recalculate() { // sum 0 rows is NULL, count 0 rows is 0... String sql = "select coalesce(sum(length(name) + length(location)), 0) + count() * 4 * 3 from Search"; Cursor cursor = App.db().getReadableDatabase().rawQuery(sql, null); - return (long)DatabaseTools.singleResult(cursor); + return (long)DatabaseTools.singleLong(cursor, null); } }); } diff --git a/src/main/java/net/twisterrob/inventory/android/content/Database.java b/src/main/java/net/twisterrob/inventory/android/content/Database.java index 765dfe9bf..bc8ec89d1 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/Database.java +++ b/src/main/java/net/twisterrob/inventory/android/content/Database.java @@ -33,7 +33,6 @@ public void onConfigure(SQLiteDatabase db) { } }; // TODO App.getPrefEditor().remove(Prefs.CURRENT_LANGUAGE).apply(); - m_helper.setDumpOnOpen(BuildConfig.DEBUG); m_helper.setDevMode(BuildConfig.DEBUG); } @@ -73,6 +72,7 @@ private Cursor rawQuery(SQLiteDatabase db, int queryResource, Object... params) try { long start = System.nanoTime(); Cursor cursor = db.rawQuery(m_resources.getString(queryResource), StringTools.toStringArray(params)); + cursor.moveToFirst(); // make sure the query runs now long end = System.nanoTime(); LOG.debug("rawQuery({}, {}): {}ms", name, paramString, (end - start) / 10000000); return cursor; @@ -179,28 +179,40 @@ public Cursor getCategory(long itemID) { return rawQuery(R.string.query_category, itemID); } - public long createProperty(long type, String name, String description, String image) { - return rawInsert(R.string.query_property_create, type, name, description, image); + public long createProperty(long type, String name, String description) { + return rawInsert(R.string.query_property_create, type, name, description); } public Long findProperty(String name) { return getID(R.string.query_property_find, name); } - public void updateProperty(long id, long type, String name, String description, String image) { - execSQL(R.string.query_property_update, type, name, description, image, id); + public void updateProperty(long id, long type, String name, String description) { + execSQL(R.string.query_property_update, type, name, description, id); + } + public void addPropertyImage(long id, byte[] imageContents) { + execSQL(R.string.query_property_image_set, imageContents, id); + } + public Cursor getPropertyImage(long id) { + return rawQuery(R.string.query_property_image_get, id); } public void deleteProperty(long id) { execSQL(R.string.query_property_delete, id); } - public long createRoom(long propertyID, long type, String name, String description, String image) { - rawInsert(R.string.query_room_create, propertyID, type, name, description, image); + public long createRoom(long propertyID, long type, String name, String description) { + rawInsert(R.string.query_room_create, propertyID, type, name, description); return findRoom(propertyID, name); // last_insert_rowid() doesn't work with INSTEAD OF INSERT triggers on VIEWs } public Long findRoom(long propertyID, String name) { return getID(R.string.query_room_find, propertyID, name); } - public void updateRoom(long id, long type, String name, String description, String image) { - execSQL(R.string.query_room_update, type, name, description, image, id); + public void updateRoom(long id, long type, String name, String description) { + execSQL(R.string.query_room_update, type, name, description, id); + } + public void addRoomImage(long id, byte[] imageContents) { + execSQL(R.string.query_room_image_set, imageContents, id); + } + public Cursor getRoomImage(long id) { + return rawQuery(R.string.query_room_image_get, id); } public void deleteRoom(long id) { execSQL(R.string.query_room_delete, id); @@ -221,17 +233,23 @@ public void moveRooms(long propertyID, long[] roomIDs) { } } - private long createItem(Long parentID, long category, String name, String description, String image) { - return rawInsert(R.string.query_item_create, parentID, category, name, description, image); + private long createItem(Long parentID, long category, String name, String description) { + return rawInsert(R.string.query_item_create, parentID, category, name, description); } - public long createItem(long parentID, long category, String name, String description, String image) { - return createItem((Long)parentID, category, name, description, image); + public long createItem(long parentID, long category, String name, String description) { + return createItem((Long)parentID, category, name, description); } public Long findItem(long parentID, String name) { return getID(R.string.query_item_find, parentID, name); } - public void updateItem(long id, long category, String name, String description, String image) { - execSQL(R.string.query_item_update, category, name, description, image, id); + public void updateItem(long id, long category, String name, String description) { + execSQL(R.string.query_item_update, category, name, description, id); + } + public void addItemImage(long id, byte[] imageContents) { + execSQL(R.string.query_item_image_set, imageContents, id); + } + public Cursor getItemImage(long id) { + return rawQuery(R.string.query_item_image_get, id); } public void deleteItem(long id) { execSQL(R.string.query_item_delete, id); @@ -338,7 +356,7 @@ public void clearImages() { } finally { db.endTransaction(); } - db.execSQL("VACUUM"); // must be outside a transaction + //db.execSQL("VACUUM"); // must be outside a transaction } public void rebuildSearch() { @@ -351,6 +369,6 @@ public void rebuildSearch() { } finally { db.endTransaction(); } - db.execSQL("VACUUM"); // must be outside a transaction + //db.execSQL("VACUUM"); // must be outside a transaction } } diff --git a/src/main/java/net/twisterrob/inventory/android/content/InventoryProvider.java b/src/main/java/net/twisterrob/inventory/android/content/InventoryProvider.java index 239971b91..c44ceddbe 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/InventoryProvider.java +++ b/src/main/java/net/twisterrob/inventory/android/content/InventoryProvider.java @@ -1,6 +1,6 @@ package net.twisterrob.inventory.android.content; -import java.io.*; +import java.io.FileNotFoundException; import java.lang.reflect.Field; import java.util.*; @@ -16,7 +16,6 @@ import net.twisterrob.android.utils.tools.*; import net.twisterrob.inventory.android.App; -import net.twisterrob.inventory.android.Constants.Paths; import net.twisterrob.inventory.android.content.InventoryContract.*; import net.twisterrob.java.annotations.DebugHelper; import net.twisterrob.java.utils.StringTools; @@ -124,46 +123,25 @@ private Cursor queryInternal(Uri uri, String[] projection, String[] selectionArg if (!Arrays.asList(projection).contains(FILE_COLUMN)) { throw new IllegalArgumentException("Property can only be queried as a file"); } - long propertyID = Property.getID(uri); - return buildImageCursor(propertyID, getImageFullPath(App.db().getProperty(propertyID))); + return App.db().getPropertyImage(Property.getID(uri)); } case ROOM: { if (!Arrays.asList(projection).contains(FILE_COLUMN)) { throw new IllegalArgumentException("Room can only be queried as a file"); } - long roomID = Room.getID(uri); - return buildImageCursor(roomID, getImageFullPath(App.db().getRoom(roomID))); + return App.db().getRoomImage(Room.getID(uri)); } case ITEM: { if (!Arrays.asList(projection).contains(FILE_COLUMN)) { throw new IllegalArgumentException("Item can only be queried as a file"); } - long itemID = Item.getID(uri); - return buildImageCursor(itemID, getImageFullPath(App.db().getItem(itemID, false))); + return App.db().getItemImage(Item.getID(uri)); } default: throw new UnsupportedOperationException("Unknown URI: " + uri); } } - private Cursor buildImageCursor(long belongingID, String imagePath) throws IOException { - byte[] imageContents = null; - if (imagePath != null) { - File file = new File(imagePath); - ByteArrayOutputStream bytes = new ByteArrayOutputStream((int)file.length()); - IOTools.copyStream(new FileInputStream(file), bytes); - imageContents = bytes.toByteArray(); - } - MatrixCursor cursor = new MatrixCursor(new String[] {BaseColumns._ID, FILE_COLUMN}, 1); - cursor.addRow(new Object[] {belongingID, imageContents}); - return cursor; - } - - private String getImageFullPath(Cursor belonging) { - String imageFileName = DatabaseTools.singleResultFromColumn(belonging, CommonColumns.IMAGE); - return Paths.getImagePath(getContext(), imageFileName); - } - /** * Return a singular suggestion to display more text about what can be searched * Tapping it would open the search activity. @@ -206,7 +184,7 @@ private Cursor createItemSearchHelp() { return openBlobHelper(uri, mode); case CATEGORY: Cursor category = App.db().getCategory(Category.getID(uri)); - String name = DatabaseTools.singleResultFromColumn(category, + String name = DatabaseTools.singleString(category, net.twisterrob.inventory.android.content.contract.Category.TYPE_IMAGE); int svgID = AndroidTools.getRawResourceID(getContext(), name); // The following only works if the resource is uncompressed: android.aaptOptions.noCompress 'svg' diff --git a/src/main/java/net/twisterrob/inventory/android/content/io/ExporterTask.java b/src/main/java/net/twisterrob/inventory/android/content/io/ExporterTask.java index 3c072af35..387fafee1 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/io/ExporterTask.java +++ b/src/main/java/net/twisterrob/inventory/android/content/io/ExporterTask.java @@ -10,7 +10,7 @@ import android.database.Cursor; import net.twisterrob.android.utils.concurrent.SimpleAsyncTask; -import net.twisterrob.android.utils.tools.IOTools; +import net.twisterrob.android.utils.tools.*; import net.twisterrob.inventory.android.*; import net.twisterrob.inventory.android.Constants.Paths; import net.twisterrob.inventory.android.content.contract.*; @@ -142,7 +142,7 @@ protected void saveData() throws Throwable { cursor.moveToPosition(-1); publishStart(); while (cursor.moveToNext()) { - if (!cursor.isNull(cursor.getColumnIndex(CommonColumns.IMAGE))) { + if (DatabaseTools.getBoolean(cursor, CommonColumns.IMAGE)) { progress.imagesCount++; } exporter.writeData(cursor); diff --git a/src/main/java/net/twisterrob/inventory/android/content/io/ImporterTask.java b/src/main/java/net/twisterrob/inventory/android/content/io/ImporterTask.java index 4f512effb..d4fa98d32 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/io/ImporterTask.java +++ b/src/main/java/net/twisterrob/inventory/android/content/io/ImporterTask.java @@ -155,11 +155,23 @@ public void error(String message) { public void importImage(Type type, long id, String name, String image) throws IOException { ZipEntry imageEntry = zip.getEntry(image); if (imageEntry != null) { - File imageFile = Constants.Paths.getImageFile(context, image); - //noinspection ResultOfMethodCallIgnored FileOutputStream will fail nicely - imageFile.getParentFile().mkdirs(); - InputStream imageStream = zip.getInputStream(imageEntry); - IOTools.copyStream(imageStream, new FileOutputStream(imageFile)); + InputStream zipImage = zip.getInputStream(imageEntry); + ByteArrayOutputStream byteOut = new ByteArrayOutputStream((int)imageEntry.getSize()); + IOTools.copyStream(zipImage, byteOut); + byte[] imageContents = byteOut.toByteArray(); + switch (type) { + case Property: + App.db().addPropertyImage(id, imageContents); + break; + case Room: + App.db().addRoomImage(id, imageContents); + break; + case Item: + App.db().addItemImage(id, imageContents); + break; + default: + throw new IllegalArgumentException(type + " cannot have images."); + } } else { warning(R.string.backup_import_invalid_image, name, image); } diff --git a/src/main/java/net/twisterrob/inventory/android/content/io/csv/CSVImporter.java b/src/main/java/net/twisterrob/inventory/android/content/io/csv/CSVImporter.java index 549a970ae..5d6d2ee80 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/io/csv/CSVImporter.java +++ b/src/main/java/net/twisterrob/inventory/android/content/io/csv/CSVImporter.java @@ -43,12 +43,7 @@ public void doImport(InputStream stream) throws IOException { processor.id = Long.parseLong(record.get("id")); } - long dbID = processor.process(); - if (processor.imageFileName != null) { - progress.importImage(null, dbID, - coalesce(processor.itemName, processor.roomName, processor.propertyName), - processor.imageFileName); - } + processor.process(); progress.publishIncrement(); } catch (CancellationException ex) { throw ex; @@ -85,17 +80,19 @@ private class ImportProcessor { String imageFileName; String parentName; long id; + Type type; protected long process() throws IOException { long dbID = processInternal(); if (imageFileName != null) { - progress.importImage(null, dbID, coalesce(itemName, roomName, propertyName), imageFileName); + progress.importImage(type, dbID, coalesce(itemName, roomName, propertyName), imageFileName); } return dbID; } protected long processInternal() { + type = null; if (itemName != null) { // item: property ?= null && room ?= null && item != null if (propertyName == null) { throw new IllegalArgumentException("Cannot process item which is not in a property"); @@ -103,13 +100,16 @@ protected long processInternal() { if (roomName == null) { throw new IllegalArgumentException("Cannot process item which is not in a room"); } + type = Type.Item; return processItem(); } else if (roomName != null) { // room: property ?= null && room != null && item == null if (propertyName == null) { throw new IllegalArgumentException("Cannot process room which is not in a property"); } + type = Type.Room; return processRoom(); } else if (propertyName != null) { // property: property != null && room == null && item == null + type = Type.Property; return processProperty(); } else { // invalid: property: property == null && room == null && item == null progress.warning(R.string.backup_import_invalid_belonging, belongingTypeName, imageFileName, @@ -132,7 +132,7 @@ private Long getOrCreateProperty() { progress.warning(R.string.backup_import_invalid_type, belongingTypeName, propertyName); typeID = PropertyType.DEFAULT; } - propertyID = App.db().createProperty(typeID, propertyName, description, imageFileName); + propertyID = App.db().createProperty(typeID, propertyName, description); } else { progress.warning(R.string.backup_import_conflict_property, propertyName); } @@ -154,7 +154,7 @@ private Long getOrCreateRoom(long propertyID) { progress.warning(R.string.backup_import_invalid_type, belongingTypeName, roomName); typeID = RoomType.DEFAULT; } - roomID = App.db().createRoom(propertyID, typeID, roomName, description, imageFileName); + roomID = App.db().createRoom(propertyID, typeID, roomName, description); } else { progress.warning(R.string.backup_import_conflict_room, propertyName, roomName); } @@ -177,7 +177,7 @@ private Long getOrCreateItem(long parentID) { progress.warning(R.string.backup_import_invalid_type, belongingTypeName, itemName); typeID = Category.DEFAULT; } - itemID = App.db().createItem(parentID, typeID, itemName, description, imageFileName); + itemID = App.db().createItem(parentID, typeID, itemName, description); } else { progress.warning(R.string.backup_import_conflict_item, propertyName, roomName, itemName); } diff --git a/src/main/java/net/twisterrob/inventory/android/content/io/xml/XMLImporter.java b/src/main/java/net/twisterrob/inventory/android/content/io/xml/XMLImporter.java index ee2f0a19f..dfa4e157b 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/io/xml/XMLImporter.java +++ b/src/main/java/net/twisterrob/inventory/android/content/io/xml/XMLImporter.java @@ -199,7 +199,7 @@ public PropertyElementListener() { progress.warning(R.string.backup_import_invalid_type, type, name); typeID = PropertyType.DEFAULT; } - id = App.db().createProperty(typeID, name, description, image); + id = App.db().createProperty(typeID, name, description); } else { progress.warning(R.string.backup_import_conflict_property, name); } @@ -220,7 +220,7 @@ public RoomElementListener(Parent parent, TraverseFactory factory) { progress.warning(R.string.backup_import_invalid_type, type, name); typeID = RoomType.DEFAULT; } - id = App.db().createRoom(propertyID, typeID, name, description, image); + id = App.db().createRoom(propertyID, typeID, name, description); } else { progress.warning(R.string.backup_import_conflict_room, null, name); } @@ -273,7 +273,7 @@ public ItemElementListener(Parent parent, TraverseFactory factory) { progress.warning(R.string.backup_import_invalid_type, type, name); typeID = Category.DEFAULT; } - id = App.db().createItem(parentID, typeID, name, description, image); + id = App.db().createItem(parentID, typeID, name, description); } else { progress.warning(R.string.backup_import_conflict_item, null, null, name); } diff --git a/src/main/java/net/twisterrob/inventory/android/content/model/ImagedDTO.java b/src/main/java/net/twisterrob/inventory/android/content/model/ImagedDTO.java index dbf9e13ee..e902f14ba 100644 --- a/src/main/java/net/twisterrob/inventory/android/content/model/ImagedDTO.java +++ b/src/main/java/net/twisterrob/inventory/android/content/model/ImagedDTO.java @@ -20,7 +20,7 @@ public abstract class ImagedDTO extends DTO { private static final Logger LOG = LoggerFactory.getLogger(ImagedDTO.class); - public String image; + public boolean image; public String typeImage; public long type; @@ -29,7 +29,7 @@ protected ImagedDTO fromCursorInternal(Cursor cursor) { super.fromCursorInternal(cursor); typeImage = DatabaseTools.getOptionalString(cursor, CommonColumns.TYPE_IMAGE); - image = DatabaseTools.getOptionalString(cursor, CommonColumns.IMAGE); + image = DatabaseTools.getOptionalBoolean(cursor, CommonColumns.IMAGE, false); type = DatabaseTools.getOptionalLong(cursor, CommonColumns.TYPE_ID, type); return this; @@ -38,12 +38,12 @@ protected ImagedDTO fromCursorInternal(Cursor cursor) { protected abstract Uri getImageUri(); public String getImage(Context context) { - return Constants.Paths.getImagePath(context, image); + return Constants.Paths.getImagePath(context, null); } public void setImage(Context context, String fullImage) { if (fullImage == null) { - this.image = null; + this.image = false; // null } else { try { String root = Paths.getImageDirectory(context).getCanonicalPath() + File.separator; @@ -52,7 +52,7 @@ public void setImage(Context context, String fullImage) { throw new IllegalArgumentException(String.format(Locale.ROOT, "Image is not in internal storage (%3$s): %2$s (<- %1$s)", fullImage, image, root)); } - this.image = image.substring(root.length()); + this.image = true; //image.substring(root.length()); } catch (IOException e) { LOG.error("Cannot find out the location of {}", fullImage, e); } @@ -68,7 +68,7 @@ public void setImage(Context context, String fullImage) { } public void loadInto(ImageView image, ImageView type, boolean alwaysShowType) { - String fullImagePath = this.image != null? StringTools.toString(getImageUri(), null) : null; + String fullImagePath = this.image? StringTools.toString(getImageUri(), null) : null; int typeID = AndroidTools.getRawResourceID(image.getContext(), this.typeImage); loadInto(image, type, fullImagePath, typeID, alwaysShowType); } diff --git a/src/main/java/net/twisterrob/inventory/android/fragment/data/BaseGalleryFragment.java b/src/main/java/net/twisterrob/inventory/android/fragment/data/BaseGalleryFragment.java index b68a8d210..ad0ed7d2d 100644 --- a/src/main/java/net/twisterrob/inventory/android/fragment/data/BaseGalleryFragment.java +++ b/src/main/java/net/twisterrob/inventory/android/fragment/data/BaseGalleryFragment.java @@ -11,7 +11,7 @@ import android.view.*; import net.twisterrob.android.adapter.CursorRecyclerAdapter; -import net.twisterrob.android.db.DatabaseOpenHelper; +import net.twisterrob.android.utils.tools.DatabaseTools; import net.twisterrob.android.view.*; import net.twisterrob.android.view.ViewProvider.StaticViewProvider; import net.twisterrob.inventory.android.R; @@ -209,8 +209,7 @@ public GalleryAdapter(Cursor cursor, RecyclerViewItemEvents listener) { private boolean isGroup(int position) { Cursor c = getCursor(); c.moveToPosition(position); - int groupIndex = c.getColumnIndex("group"); - return groupIndex != DatabaseOpenHelper.CURSOR_NO_COLUMN && c.getInt(groupIndex) == 1; // boolean + return DatabaseTools.getOptionalBoolean(c, "group", false); // boolean } @Override protected ViewHolder onCreateNonHeaderViewHolder(ViewGroup parent, int viewType) { diff --git a/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemEditFragment.java b/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemEditFragment.java index 29bd9f5c8..6d9106e31 100644 --- a/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemEditFragment.java +++ b/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemEditFragment.java @@ -95,9 +95,9 @@ private final class SaveTask extends SimpleSafeAsyncTask { @Override protected Long doInBackground(ItemDTO param) throws SQLiteConstraintException { Database db = App.db(); if (param.id == Item.ID_ADD) { - return db.createItem(param.parentID, param.type, param.name, param.description, param.image); + return db.createItem(param.parentID, param.type, param.name, param.description); } else { - db.updateItem(param.id, param.type, param.name, param.description, param.image); + db.updateItem(param.id, param.type, param.name, param.description); return param.id; } } diff --git a/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemViewFragment.java b/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemViewFragment.java index 6d0b8d9cc..6adaaa1c8 100644 --- a/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemViewFragment.java +++ b/src/main/java/net/twisterrob/inventory/android/fragment/data/ItemViewFragment.java @@ -173,7 +173,7 @@ private void delete(final long itemID) { } @Override protected CharSequence update(ItemDTO entity, long newType, String newTypeName) { - App.db().updateItem(entity.id, newType, entity.name, entity.description, entity.image); + App.db().updateItem(entity.id, newType, entity.name, entity.description); return newTypeName; } diff --git a/src/main/java/net/twisterrob/inventory/android/fragment/data/PropertyEditFragment.java b/src/main/java/net/twisterrob/inventory/android/fragment/data/PropertyEditFragment.java index 726012df5..7d8bb5bc5 100644 --- a/src/main/java/net/twisterrob/inventory/android/fragment/data/PropertyEditFragment.java +++ b/src/main/java/net/twisterrob/inventory/android/fragment/data/PropertyEditFragment.java @@ -96,9 +96,9 @@ private final class SaveTask extends SimpleSafeAsyncTask { @Override protected Long doInBackground(RoomDTO param) throws SQLiteConstraintException { Database db = App.db(); if (param.id == Room.ID_ADD) { - return db.createRoom(param.propertyID, param.type, param.name, param.description, param.image); + return db.createRoom(param.propertyID, param.type, param.name, param.description); } else { - db.updateRoom(param.id, param.type, param.name, param.description, param.image); + db.updateRoom(param.id, param.type, param.name, param.description); return param.id; } } diff --git a/src/main/java/net/twisterrob/inventory/android/fragment/data/RoomViewFragment.java b/src/main/java/net/twisterrob/inventory/android/fragment/data/RoomViewFragment.java index a448b12b2..b390ee54a 100644 --- a/src/main/java/net/twisterrob/inventory/android/fragment/data/RoomViewFragment.java +++ b/src/main/java/net/twisterrob/inventory/android/fragment/data/RoomViewFragment.java @@ -137,7 +137,7 @@ private void delete(final long roomID) { } @Override protected CharSequence update(RoomDTO entity, long newType, String newTypeName) { - App.db().updateRoom(entity.id, newType, entity.name, entity.description, entity.image); + App.db().updateRoom(entity.id, newType, entity.name, entity.description); return newTypeName; } diff --git a/src/main/java/net/twisterrob/inventory/android/view/adapters/BaseImagedAdapter.java b/src/main/java/net/twisterrob/inventory/android/view/adapters/BaseImagedAdapter.java index 84b44f36f..6f59a7127 100644 --- a/src/main/java/net/twisterrob/inventory/android/view/adapters/BaseImagedAdapter.java +++ b/src/main/java/net/twisterrob/inventory/android/view/adapters/BaseImagedAdapter.java @@ -7,7 +7,7 @@ import android.widget.*; import net.twisterrob.android.adapter.CursorRecyclerAdapter; -import net.twisterrob.android.utils.tools.AndroidTools; +import net.twisterrob.android.utils.tools.*; import net.twisterrob.inventory.android.R; import net.twisterrob.inventory.android.content.contract.*; import net.twisterrob.inventory.android.content.model.ImagedDTO; @@ -61,7 +61,7 @@ protected View inflateView(ViewGroup parent, int layoutResource) { long id = cursor.getLong(cursor.getColumnIndexOrThrow(CommonColumns.ID)); Type type = Type.from(cursor, CommonColumns.TYPE); String name = cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.NAME)); - boolean hasImage = cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.IMAGE)) != null; + boolean hasImage = DatabaseTools.getBoolean(cursor, CommonColumns.IMAGE); String typeImage = cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.TYPE_IMAGE)); holder.title.setText(name); diff --git a/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryGroupViewHolder.java b/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryGroupViewHolder.java index cc8cb9a47..a2615bc91 100644 --- a/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryGroupViewHolder.java +++ b/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryGroupViewHolder.java @@ -6,6 +6,7 @@ import android.view.View.*; import android.widget.*; +import net.twisterrob.android.utils.tools.DatabaseTools; import net.twisterrob.inventory.android.R; import net.twisterrob.inventory.android.content.contract.*; import net.twisterrob.inventory.android.content.model.ImagedDTO; @@ -38,7 +39,7 @@ public void bind(Cursor cursor) { count.setText(getCountText(cursor)); String typeImage = cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.TYPE_IMAGE)); - boolean hasImage = cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.IMAGE)) != null; + boolean hasImage = DatabaseTools.getBoolean(cursor, CommonColumns.IMAGE); Type type = Type.from(cursor, CommonColumns.TYPE); long id = cursor.getLong(cursor.getColumnIndexOrThrow(CommonColumns.ID)); ImagedDTO.loadInto(image, hasImage? type : null, id, typeImage); diff --git a/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryViewHolder.java b/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryViewHolder.java index 989cb9040..122f0a181 100644 --- a/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryViewHolder.java +++ b/src/main/java/net/twisterrob/inventory/android/view/adapters/GalleryViewHolder.java @@ -7,6 +7,7 @@ import android.widget.*; import net.twisterrob.android.db.DatabaseOpenHelper; +import net.twisterrob.android.utils.tools.DatabaseTools; import net.twisterrob.inventory.android.R; import net.twisterrob.inventory.android.content.contract.*; import net.twisterrob.inventory.android.content.model.ImagedDTO; @@ -43,7 +44,7 @@ public void bind(Cursor cursor) { count.setVisibility(countText != null? View.VISIBLE : View.GONE); String typeImage = cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.TYPE_IMAGE)); - ImagedDTO.loadInto(image, type, getImage(cursor) != null? getType(cursor) : null, getID(cursor), typeImage, + ImagedDTO.loadInto(image, type, hasImage(cursor)? getType(cursor) : null, getID(cursor), typeImage, false); } private static Type getType(Cursor cursor) { @@ -55,8 +56,8 @@ private static long getID(Cursor cursor) { private static String getName(Cursor cursor) { return cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.NAME)); } - private static String getImage(Cursor cursor) { - return cursor.getString(cursor.getColumnIndexOrThrow(CommonColumns.IMAGE)); + private static boolean hasImage(Cursor cursor) { + return DatabaseTools.getBoolean(cursor, CommonColumns.IMAGE); } private static String getCountText(Cursor cursor) { diff --git a/src/main/res/values/queries.xml b/src/main/res/values/queries.xml index 431d58039..6c43b4191 100644 --- a/src/main/res/values/queries.xml +++ b/src/main/res/values/queries.xml @@ -6,7 +6,7 @@ 'property' as type, p._id as _id, p.name as name, - p.image as image, + p.image IS NOT NULL as image, pt.image as typeImage, (select count() from Room where property = p._id) as countChildren from Property p @@ -20,7 +20,7 @@ p.name as name, p.description as description, p.type as typeID, - p.image as image, + p.image IS NOT NULL as image, pt.image as typeImage, (select count() from Room where property = p._id) as countChildren, (select count() from Item where parent in (select root from Room where property = p._id)) as countDirectItems, @@ -54,7 +54,7 @@ r._id as _id, r.root as root, r.name as name, - r.image as image, + r.image IS NOT NULL as image, COALESCE(rt.image, rtk.image) as typeImage, p._id as property, p.name as propertyName, @@ -76,7 +76,7 @@ r.root as root, p._id as property, p.name as propertyName, - r.image as image, + r.image IS NOT NULL as image, COALESCE(rt.image, rtk.image) as typeImage, (select count() from Item where parent = r.root) as countDirectItems, (select count(distinct item) from Item_Path_Node where root = r.root and root <> node) as countAllItems @@ -107,7 +107,7 @@ --g._id as gid, i._id = g._id as [group], i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -139,7 +139,7 @@ i._id as _id, i.name as name, i.description as description, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, ip.categoryID as typeID, ip.categoryName as categoryName, @@ -169,7 +169,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -182,7 +182,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -197,7 +197,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -211,7 +211,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -233,7 +233,7 @@ c._id as typeID, c.name as typeName, c.image as typeImage, - i.image as image + i.image IS NOT NULL as image from Item_Path_Node n join Item i ON i._id = n.node join Category c ON i.category = c._id @@ -249,7 +249,7 @@ c._id as typeID, c.name as typeName, c.image as typeImage, - i.image as image + i.image IS NOT NULL as image from Item_Path_Node n join Item i ON i._id = n.node join Category c ON i.category = c._id @@ -265,7 +265,7 @@ rt._id as typeID, rt.name as typeName, COALESCE(rt.image, rtk.image) as typeImage, - r.image as image + r.image IS NOT NULL as image from Item_Path_Node n join Room r ON n.root = r.root join RoomType rt ON r.type = rt._id @@ -282,7 +282,7 @@ pt._id as typeID, pt.name as typeName, pt.image as typeImage, - p.image as image + p.image IS NOT NULL as image from Item_Path_Node n join Room r ON n.root = r.root join Property p ON r.property = p._id @@ -299,7 +299,7 @@ 'category' as type, c._id as _id, c.name as name, - NULL as image, + 1 <> 2 as image, c.image as typeImage, (select count() from Category where parent = c._id) as countChildren, (select count() from Item where category in (select descendant from Category_Descendant where category = c._id)) as countAllItems @@ -376,7 +376,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -393,7 +393,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, datetime(r.visit, 'localtime') as visit, r.recency as recency, @@ -430,7 +430,7 @@ 'item' as type, i._id as _id, i.name as name, - i.image as image, + i.image IS NOT NULL as image, c.image as typeImage, (select count() from Item where parent = i._id) as countChildren, c.name as category @@ -503,13 +503,19 @@ + + + + + +