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