1c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren/*
2c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * Copyright (C) 2012 The Android Open Source Project
3c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren *
4c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * Licensed under the Apache License, Version 2.0 (the "License");
5c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * you may not use this file except in compliance with the License.
6c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * You may obtain a copy of the License at
7c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren *
8c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren *      http://www.apache.org/licenses/LICENSE-2.0
9c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren *
10c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * Unless required by applicable law or agreed to in writing, software
11c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * distributed under the License is distributed on an "AS IS" BASIS,
12c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * See the License for the specific language governing permissions and
14c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren * limitations under the License.
15c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren */
16c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenpackage com.android.dreams.phototable;
17c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
18c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenimport android.content.Context;
19d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.content.SharedPreferences;
20c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenimport android.database.Cursor;
21c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenimport android.provider.MediaStore;
22c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
23c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenimport java.io.FileInputStream;
2483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.InputStream;
2583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Collection;
26d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport java.util.HashMap;
27c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wrenimport java.util.LinkedList;
28d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wrenimport java.util.Set;
29c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
30c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren/**
3183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * Loads images from the local store.
32c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren */
3383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenpublic class LocalSource extends PhotoSource {
34c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren    private static final String TAG = "PhotoTable.LocalSource";
35c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
36e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren    private final String mUnknownAlbumName;
37e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren    private final String mLocalSourceName;
38d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren    private Set<String> mFoundAlbumIds;
39c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren    private int mNextPosition;
40c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
41d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public LocalSource(Context context, SharedPreferences settings) {
42d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        super(context, settings);
43e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren        mLocalSourceName = mResources.getString(R.string.local_source_name, "Photos on Device");
44e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren        mUnknownAlbumName = mResources.getString(R.string.unknown_album_name, "Unknown");
4583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mSourceName = TAG;
46c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren        mNextPosition = -1;
47c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren        fillQueue();
48c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren    }
49c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
50d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren    private Set<String> getFoundAlbums() {
51d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        if (mFoundAlbumIds == null) {
52d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren            findAlbums();
53d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        }
54d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        return mFoundAlbumIds;
55d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren    }
56d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren
5783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    @Override
58d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public Collection<AlbumData> findAlbums() {
59d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        log(TAG, "finding albums");
60d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        HashMap<String, AlbumData> foundAlbums = new HashMap<String, AlbumData>();
61d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
62d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.BUCKET_ID,
63d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.DATE_TAKEN};
64d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        // This is a horrible hack that closes the where clause and injects a grouping clause.
65d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
66d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                projection, null, null, null);
67d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        if (cursor != null) {
68d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            cursor.moveToFirst();
69d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
70d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
71d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            int bucketIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID);
72d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            int nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
73d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            int updatedIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN);
74d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
75d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            if (bucketIndex < 0) {
76d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                log(TAG, "can't find the ID column!");
77d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            } else {
78d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                while (!cursor.isAfterLast()) {
79d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    String id = TAG + ":" + cursor.getString(bucketIndex);
80d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    AlbumData data = foundAlbums.get(id);
81d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    if (foundAlbums.get(id) == null) {
82d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        data = new AlbumData();
83d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        data.id = id;
84e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren                        data.account = mLocalSourceName;
85d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
86d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        if (dataIndex >= 0) {
87d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                            data.thumbnailUrl = cursor.getString(dataIndex);
88d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        }
89d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
90d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        if (nameIndex >= 0) {
91d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                            data.title = cursor.getString(nameIndex);
92d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        } else {
93e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren                            data.title = mUnknownAlbumName;
94d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        }
95d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
96d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        log(TAG, data.title + " found");
97d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        foundAlbums.put(id, data);
98d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    }
99d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    if (updatedIndex >= 0) {
100d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        long updated = cursor.getLong(updatedIndex);
101d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        data.updated = (data.updated == 0 ?
102d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                                        updated :
103d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                                        Math.min(data.updated, updated));
104d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    }
105d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    cursor.moveToNext();
106d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                }
107d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            }
108d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            cursor.close();
109d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
110d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        }
111d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        log(TAG, "found " + foundAlbums.size() + " items.");
112d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        mFoundAlbumIds = foundAlbums.keySet();
113d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        return foundAlbums.values();
114d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    }
115d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
116d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    @Override
11783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected Collection<ImageData> findImages(int howMany) {
11883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "finding images");
11983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        LinkedList<ImageData> foundImages = new LinkedList<ImageData>();
12083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
12183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION,
12283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME};
12383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        String selection = "";
124d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        for (String id : getFoundAlbums()) {
125d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren            if (mSettings.isAlbumEnabled(id)) {
126d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                String[] parts = id.split(":");
127d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                if (parts.length > 1) {
128d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    if (selection.length() > 0) {
129d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                        selection += " OR ";
130d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    }
131d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                    selection += MediaStore.Images.Media.BUCKET_ID + " = '" + parts[1] + "'";
132d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren                }
13383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            }
13483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        }
135d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        if (selection.isEmpty()) {
136d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren            return foundImages;
137d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        }
138d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
139c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren        Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
14083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                projection, selection, null, null);
141c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren        if (cursor != null) {
14283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            if (cursor.getCount() > howMany && mNextPosition == -1) {
14383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                mNextPosition = mRNG.nextInt() % (cursor.getCount() - howMany);
144c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            }
145c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            if (mNextPosition == -1) {
146c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                mNextPosition = 0;
147c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            }
148c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            cursor.moveToPosition(mNextPosition);
149c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
150c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
151c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            int orientationIndex = cursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION);
15283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            int bucketIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID);
15383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            int nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
15483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
155c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            if (dataIndex < 0) {
15683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                log(TAG, "can't find the DATA column!");
157c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            } else {
15883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                while (foundImages.size() < howMany && !cursor.isAfterLast()) {
159c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                    ImageData data = new ImageData();
16083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                    data.url = cursor.getString(dataIndex);
161c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                    data.orientation = cursor.getInt(orientationIndex);
16283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                    foundImages.offer(data);
163c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                    if (cursor.moveToNext()) {
164c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                        mNextPosition++;
165c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                    }
166c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                }
167c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                if (cursor.isAfterLast()) {
168c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                    mNextPosition = 0;
169c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren                }
170c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            }
17183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
172c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren            cursor.close();
173c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren        }
17483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "found " + foundImages.size() + " items.");
17583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        return foundImages;
176c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren    }
177c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
17883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    @Override
179c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren    protected InputStream getStream(ImageData data, int longSide) {
18083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        FileInputStream fis = null;
18183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        try {
18283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            log(TAG, "opening:" + data.url);
18383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            fis = new FileInputStream(data.url);
18483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        } catch (Exception ex) {
18583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            log(TAG, ex.toString());
18683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            fis = null;
187c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren        }
188c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren
18983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        return (InputStream) fis;
190c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren    }
191c8ca07eb52a54efe327d024dc542e0a7f8849bfbChris Wren}
192