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