1f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov/* 2f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 3f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * 4f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * use this file except in compliance with the License. You may obtain a copy of 6f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * the License at 7f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * 8f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 9f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * 10f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 11f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * License for the specific language governing permissions and limitations under 14f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * the License 15f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov */ 16f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikovpackage com.android.providers.contacts; 17f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 18f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikovimport android.content.ContentValues; 196d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikovimport android.content.Context; 20f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikovimport android.database.Cursor; 21f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikovimport android.database.sqlite.SQLiteDatabase; 22f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo; 23f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.util.Log; 24f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 2538210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.aggregation.ContactAggregator; 2638210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki 27f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.IOException; 28f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 29f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov/** 30f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov * Handler for photo data rows. 31f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov */ 32f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikovpublic class DataRowHandlerForPhoto extends DataRowHandler { 33f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 34f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro private static final String TAG = "DataRowHandlerForPhoto"; 35f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 36f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro private final PhotoStore mPhotoStore; 3787426833d4c2c626e032f5d0b84a08b58024daf6Makoto Onuki private final int mMaxDisplayPhotoDim; 3887426833d4c2c626e032f5d0b84a08b58024daf6Makoto Onuki private final int mMaxThumbnailPhotoDim; 39f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 40f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro /** 41f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * If this is set in the ContentValues passed in, it indicates that the caller has 42f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * already taken care of photo processing, and that the row should be ready for 43f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * insert/update. This is used when the photo has been written directly to an 44f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * asset file. 45f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro */ 46f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro /* package */ static final String SKIP_PROCESSING_KEY = "skip_processing"; 47f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 486d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov public DataRowHandlerForPhoto( 49f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro Context context, ContactsDatabaseHelper dbHelper, ContactAggregator aggregator, 5087426833d4c2c626e032f5d0b84a08b58024daf6Makoto Onuki PhotoStore photoStore, int maxDisplayPhotoDim, int maxThumbnailPhotoDim) { 516d9702cec82fd27a1c3093c64df9dcc22744899aDmitri Plotnikov super(context, dbHelper, aggregator, Photo.CONTENT_ITEM_TYPE); 52f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro mPhotoStore = photoStore; 5387426833d4c2c626e032f5d0b84a08b58024daf6Makoto Onuki mMaxDisplayPhotoDim = maxDisplayPhotoDim; 5487426833d4c2c626e032f5d0b84a08b58024daf6Makoto Onuki mMaxThumbnailPhotoDim = maxThumbnailPhotoDim; 55f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov } 56f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 57f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov @Override 58f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId, 59f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov ContentValues values) { 60f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 61f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro if (values.containsKey(SKIP_PROCESSING_KEY)) { 62f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro values.remove(SKIP_PROCESSING_KEY); 63f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } else { 64d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro // Pre-process the photo if one exists. 65d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro if (!preProcessPhoto(values)) { 66f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro return 0; 67f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 68f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 69f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 70f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov long dataId = super.insert(db, txContext, rawContactId, values); 71f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov if (!txContext.isNewRawContact(rawContactId)) { 72f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov mContactAggregator.updatePhotoId(db, rawContactId); 73f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov } 74f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov return dataId; 75f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov } 76f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 77f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov @Override 78f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values, 79f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov Cursor c, boolean callerIsSyncAdapter) { 80f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID); 81f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 82f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro if (values.containsKey(SKIP_PROCESSING_KEY)) { 83f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro values.remove(SKIP_PROCESSING_KEY); 84f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } else { 85f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro // Pre-process the photo if one exists. 86d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro if (!preProcessPhoto(values)) { 87d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro return false; 88f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 89f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 90f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 91f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro // Do the actual update. 92f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) { 93f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov return false; 94f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov } 95f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 96f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov mContactAggregator.updatePhotoId(db, rawContactId); 97f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov return true; 98d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro } 99d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro 100d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro /** 101d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro * Pre-processes the given content values for update or insert. If the photo column contains 102d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro * null or an empty byte array, both that column and the photo file ID will be nulled out. 103d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro * If a photo was specified but could not be processed, this will return false. 104d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro * @param values The content values passed in. 105d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro * @return Whether processing was successful - on failure, the operation should abort. 106d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro */ 107d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro private boolean preProcessPhoto(ContentValues values) { 108d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro if (values.containsKey(Photo.PHOTO)) { 109d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro boolean photoExists = hasNonNullPhoto(values); 110d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro if (photoExists) { 111d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro if (!processPhoto(values)) { 112d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro // A photo was passed in, but we couldn't process it. Update failed. 113d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro return false; 114d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro } 115d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro } else { 116d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro // The photo key was passed in, but it was either null or an empty byte[]. 117d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro // We should set the photo and photo file ID fields to null for the update. 118d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro values.putNull(Photo.PHOTO); 119d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro values.putNull(Photo.PHOTO_FILE_ID); 120d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro } 121d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro } 122d0dba67000bfb73b6281a9ca6d476470f1ac41f2Dave Santoro return true; 123f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov } 124f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov 125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro private boolean hasNonNullPhoto(ContentValues values) { 126cfc422b077c30f3e70c8506952de02d526d71a04Dave Santoro byte[] photoBytes = values.getAsByteArray(Photo.PHOTO); 127cfc422b077c30f3e70c8506952de02d526d71a04Dave Santoro return photoBytes != null && photoBytes.length > 0; 128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 129f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 130f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov @Override 131f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) { 132f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID); 133f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov int count = super.delete(db, txContext, c); 134f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov mContactAggregator.updatePhotoId(db, rawContactId); 135f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov return count; 136f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov } 137f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro 138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro /** 139f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * Reads the photo out of the given values object and processes it, placing the processed 140f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * photos (a photo store file ID and a compressed thumbnail) back into the ContentValues 141f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * object. 142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * @param values The values being inserted or updated - assumed to contain a photo BLOB. 143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * @return Whether an image was successfully decoded and processed. 144f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro */ 145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro private boolean processPhoto(ContentValues values) { 146f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro byte[] originalPhoto = values.getAsByteArray(Photo.PHOTO); 147f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro if (originalPhoto != null) { 148f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro try { 149f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro PhotoProcessor processor = new PhotoProcessor( 15087426833d4c2c626e032f5d0b84a08b58024daf6Makoto Onuki originalPhoto, mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim); 151f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro long photoFileId = mPhotoStore.insert(processor); 152f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro if (photoFileId != 0) { 153f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro values.put(Photo.PHOTO_FILE_ID, photoFileId); 154f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } else { 155f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro values.putNull(Photo.PHOTO_FILE_ID); 156f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 157f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro values.put(Photo.PHOTO, processor.getThumbnailPhotoBytes()); 158f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro return true; 159f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } catch (IOException ioe) { 160f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro Log.e(TAG, "Could not process photo for insert or update", ioe); 161f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 162f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 163f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro return false; 164f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro } 165f6d4922f664127d0455b45b1f7444c4553581282Dmitri Plotnikov} 166