1f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro/*
2f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * Copyright (C) 2011 The Android Open Source Project
3f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro *
4f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * use this file except in compliance with the License. You may obtain a copy of
6f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * the License at
7f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro *
8f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * http://www.apache.org/licenses/LICENSE-2.0
9f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro *
10f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * Unless required by applicable law or agreed to in writing, software
11f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * License for the specific language governing permissions and limitations under
14f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * the License
15f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro */
16f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoropackage com.android.providers.contacts;
17f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
18f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.content.ContentValues;
19f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.database.sqlite.SQLiteDatabase;
20f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.graphics.Bitmap;
21f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.provider.ContactsContract.PhotoFiles;
22f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport android.util.Log;
23f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
2438210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.PhotoFilesColumns;
2538210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
2638210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.google.common.annotations.VisibleForTesting;
2738210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki
28f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.File;
29f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.FileOutputStream;
30f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.io.IOException;
31f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.util.HashMap;
32f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.util.HashSet;
33f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.util.Map;
34f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoroimport java.util.Set;
35f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
36f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro/**
37f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * Photo storage system that stores the files directly onto the hard disk
38f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro * in the specified directory.
39f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro */
40f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoropublic class PhotoStore {
41f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
42f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private final String TAG = PhotoStore.class.getSimpleName();
43f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
44f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    // Directory name under the root directory for photo storage.
45f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private final String DIRECTORY = "photos";
46f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
47f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Map of keys to entries in the directory. */
48f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private final Map<Long, Entry> mEntries;
49f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
50f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** Total amount of space currently used by the photo store in bytes. */
51f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private long mTotalSize = 0;
52f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
53f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** The file path for photo storage. */
54f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private final File mStorePath;
55f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
56f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** The database helper. */
57f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private final ContactsDatabaseHelper mDatabaseHelper;
58f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
59f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /** The database to use for storing metadata for the photo files. */
60f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private SQLiteDatabase mDb;
61f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
62f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
63f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Constructs an instance of the PhotoStore under the specified directory.
64f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param rootDirectory The root directory of the storage.
65f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param databaseHelper Helper class for obtaining a database instance.
66f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
67f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    public PhotoStore(File rootDirectory, ContactsDatabaseHelper databaseHelper) {
68f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mStorePath = new File(rootDirectory, DIRECTORY);
69f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (!mStorePath.exists()) {
70f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            if(!mStorePath.mkdirs()) {
71f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                throw new RuntimeException("Unable to create photo storage directory "
72f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        + mStorePath.getPath());
73f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
74f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
75f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mDatabaseHelper = databaseHelper;
76f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mEntries = new HashMap<Long, Entry>();
77f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        initialize();
78f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
79f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
80f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
81f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Clears the photo storage. Deletes all files from disk.
82f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
83fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public void clear() {
84f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        File[] files = mStorePath.listFiles();
85f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (files != null) {
86f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            for (File file : files) {
87f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cleanupFile(file);
88f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
89f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
90bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        if (mDb == null) {
91bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro            mDb = mDatabaseHelper.getWritableDatabase();
92bd20dbedba706fdf2db7acb1c7d4391e57129d44Dave Santoro        }
93f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mDb.delete(Tables.PHOTO_FILES, null, null);
94f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mEntries.clear();
95f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mTotalSize = 0;
96f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
97f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
98c6eab5080340824edd2c6676c4e6b96e142f87e4Dave Santoro    @VisibleForTesting
99fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public long getTotalSize() {
100f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return mTotalSize;
101f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
102f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
103f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
104f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Returns the entry with the specified key if it exists, null otherwise.
105f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
106fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public Entry get(long key) {
107f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return mEntries.get(key);
108f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
109f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
110f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
111f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Initializes the PhotoStore by scanning for all files currently in the
112f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * specified root directory.
113f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
114fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public final void initialize() {
115f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        File[] files = mStorePath.listFiles();
116f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (files == null) {
117f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            return;
118f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
119f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        for (File file : files) {
120f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
121f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                Entry entry = new Entry(file);
122f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                putEntry(entry.id, entry);
123f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (NumberFormatException nfe) {
124f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Not a valid photo store entry - delete the file.
125f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cleanupFile(file);
126f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
127f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
128f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
129f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        // Get a reference to the database.
130f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mDb = mDatabaseHelper.getWritableDatabase();
131f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
132f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
133f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
134f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Cleans up the photo store such that only the keys in use still remain as
135f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * entries in the store (all other entries are deleted).
136f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *
137f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * If an entry in the keys in use does not exist in the photo store, that key
138f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * will be returned in the result set - the caller should take steps to clean
139f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * up those references, as the underlying photo entries do not exist.
140f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *
141f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param keysInUse The set of all keys that are in use in the photo store.
142f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return The set of the keys in use that refer to non-existent entries.
143f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
144fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public Set<Long> cleanup(Set<Long> keysInUse) {
145f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        Set<Long> keysToRemove = new HashSet<Long>();
146f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        keysToRemove.addAll(mEntries.keySet());
147f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        keysToRemove.removeAll(keysInUse);
148f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (!keysToRemove.isEmpty()) {
149f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.d(TAG, "cleanup removing " + keysToRemove.size() + " entries");
150f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            for (long key : keysToRemove) {
151f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                remove(key);
152f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
153f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
154f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
155f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        Set<Long> missingKeys = new HashSet<Long>();
156f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        missingKeys.addAll(keysInUse);
157f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        missingKeys.removeAll(mEntries.keySet());
158f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return missingKeys;
159f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
160f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
161f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
162f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Inserts the photo in the given photo processor into the photo store.  If the display photo
163f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * is already thumbnail-sized or smaller, this will do nothing (and will return 0).
164f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param photoProcessor A photo processor containing the photo data to insert.
165f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @return The photo file ID associated with the file, or 0 if the file could not be created or
166f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     *     is thumbnail-sized or smaller.
167f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
168fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public long insert(PhotoProcessor photoProcessor) {
1696802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        return insert(photoProcessor, false);
1706802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    }
1716802030a777c0c3ba1dc029c534cca4784260632Dave Santoro
1726802030a777c0c3ba1dc029c534cca4784260632Dave Santoro    /**
1736802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * Inserts the photo in the given photo processor into the photo store.  If the display photo
1746802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * is already thumbnail-sized or smaller, this will do nothing (and will return 0) unless
1756802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * allowSmallImageStorage is specified.
1766802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param photoProcessor A photo processor containing the photo data to insert.
1776802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @param allowSmallImageStorage Whether thumbnail-sized or smaller photos should still be
1786802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     stored in the file store.
1796802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     * @return The photo file ID associated with the file, or 0 if the file could not be created or
1806802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     *     is thumbnail-sized or smaller and allowSmallImageStorage is false.
1816802030a777c0c3ba1dc029c534cca4784260632Dave Santoro     */
182fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public long insert(PhotoProcessor photoProcessor, boolean allowSmallImageStorage) {
183f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        Bitmap displayPhoto = photoProcessor.getDisplayPhoto();
184f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        int width = displayPhoto.getWidth();
185f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        int height = displayPhoto.getHeight();
186f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        int thumbnailDim = photoProcessor.getMaxThumbnailPhotoDim();
1876802030a777c0c3ba1dc029c534cca4784260632Dave Santoro        if (allowSmallImageStorage || width > thumbnailDim || height > thumbnailDim) {
1886802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // Write the photo to a temp file, create the DB record for tracking it, and rename the
1896802030a777c0c3ba1dc029c534cca4784260632Dave Santoro            // temp file to match.
190f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            File file = null;
191f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            try {
192f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Write the display photo to a temp file.
193f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                byte[] photoBytes = photoProcessor.getDisplayPhotoBytes();
194f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                file = File.createTempFile("img", null, mStorePath);
195f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                FileOutputStream fos = new FileOutputStream(file);
196eae25ef81bfe12946f50c72be9647447bb2a16b5Daniel Lehmann                fos.write(photoBytes);
197f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                fos.close();
198f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
199f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Create the DB entry.
200f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                ContentValues values = new ContentValues();
201f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                values.put(PhotoFiles.HEIGHT, height);
202f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                values.put(PhotoFiles.WIDTH, width);
203f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                values.put(PhotoFiles.FILESIZE, photoBytes.length);
204f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                long id = mDb.insert(Tables.PHOTO_FILES, null, values);
205f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                if (id != 0) {
206f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    // Rename the temp file.
207f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    File target = getFileForPhotoFileId(id);
208f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    if (file.renameTo(target)) {
209f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        Entry entry = new Entry(target);
210f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        putEntry(entry.id, entry);
211f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                        return id;
212f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                    }
213f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                }
214f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            } catch (IOException e) {
215f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                // Write failed - will delete the file below.
216f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
217f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
218f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            // If anything went wrong, clean up the file before returning.
219f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            if (file != null) {
220f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                cleanupFile(file);
221f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            }
222f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
223f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return 0;
224f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
225f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
226f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private void cleanupFile(File file) {
227f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        boolean deleted = file.delete();
228f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (!deleted) {
229f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Log.d("Could not clean up file %s", file.getAbsolutePath());
230f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
231f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
232f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
233f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
234f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Removes the specified photo file from the store if it exists.
235f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
236fa4db3db4146a26f154ef2e89352ad70a5415b8eDaniel Lehmann    public void remove(long id) {
237f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        cleanupFile(getFileForPhotoFileId(id));
238f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        removeEntry(id);
239f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
240f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
241f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
242f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Returns a file object for the given photo file ID.
243f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
244f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private File getFileForPhotoFileId(long id) {
245f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        return new File(mStorePath, String.valueOf(id));
246f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
247f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
248f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
249f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Puts the entry with the specified photo file ID into the store.
250f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param id The photo file ID to identify the entry by.
251f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * @param entry The entry to store.
252f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
253f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private void putEntry(long id, Entry entry) {
254f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (!mEntries.containsKey(id)) {
255f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mTotalSize += entry.size;
256f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        } else {
257f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            Entry oldEntry = mEntries.get(id);
258f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mTotalSize += (entry.size - oldEntry.size);
259f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
260f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mEntries.put(id, entry);
261f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
262f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
263f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    /**
264f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * Removes the entry identified by the given photo file ID from the store, removing
265f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     * the associated photo file entry from the database.
266f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro     */
267f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    private void removeEntry(long id) {
268f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        Entry entry = mEntries.get(id);
269f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        if (entry != null) {
270f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mTotalSize -= entry.size;
271f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            mEntries.remove(id);
272f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
273f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        mDb.delete(ContactsDatabaseHelper.Tables.PHOTO_FILES, PhotoFilesColumns.CONCRETE_ID + "=?",
274f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro                new String[]{String.valueOf(id)});
275f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
276f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
277f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    public static class Entry {
278f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        /** The photo file ID that identifies the entry. */
279f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public final long id;
280f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
281f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        /** The size of the data, in bytes. */
282f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public final long size;
283f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
284f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        /** The path to the file. */
285f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public final String path;
286f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro
287f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        public Entry(File file) {
288f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            id = Long.parseLong(file.getName());
289f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            size = file.length();
290f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro            path = file.getAbsolutePath();
291f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro        }
292f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro    }
293f547fd54d7933e1c03af4a8dc10560c71c38f6b8Dave Santoro}
294