1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.dreams.phototable; 17 18 import android.content.Context; 19 import android.content.SharedPreferences; 20 import android.database.Cursor; 21 import android.provider.MediaStore; 22 23 import java.io.FileInputStream; 24 import java.io.InputStream; 25 import java.util.Collection; 26 import java.util.HashMap; 27 import java.util.LinkedList; 28 import java.util.Set; 29 30 /** 31 * Loads images from the local store. 32 */ 33 public class LocalSource extends CursorPhotoSource { 34 private static final String TAG = "PhotoTable.LocalSource"; 35 36 private final String mUnknownAlbumName; 37 private final String mLocalSourceName; 38 private Set<String> mFoundAlbumIds; 39 private int mLastPosition; 40 LocalSource(Context context, SharedPreferences settings)41 public LocalSource(Context context, SharedPreferences settings) { 42 super(context, settings); 43 mLocalSourceName = mResources.getString(R.string.local_source_name, "Photos on Device"); 44 mUnknownAlbumName = mResources.getString(R.string.unknown_album_name, "Unknown"); 45 mSourceName = TAG; 46 mLastPosition = INVALID; 47 fillQueue(); 48 } 49 getFoundAlbums()50 private Set<String> getFoundAlbums() { 51 if (mFoundAlbumIds == null) { 52 findAlbums(); 53 } 54 return mFoundAlbumIds; 55 } 56 57 @Override findAlbums()58 public Collection<AlbumData> findAlbums() { 59 log(TAG, "finding albums"); 60 HashMap<String, AlbumData> foundAlbums = new HashMap<String, AlbumData>(); 61 62 String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.BUCKET_ID, 63 MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.DATE_TAKEN}; 64 // This is a horrible hack that closes the where clause and injects a grouping clause. 65 Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 66 projection, null, null, null); 67 if (cursor != null) { 68 cursor.moveToPosition(-1); 69 70 int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA); 71 int bucketIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID); 72 int nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME); 73 int updatedIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN); 74 75 if (bucketIndex < 0) { 76 log(TAG, "can't find the ID column!"); 77 } else { 78 while (cursor.moveToNext()) { 79 String id = TAG + ":" + cursor.getString(bucketIndex); 80 AlbumData data = foundAlbums.get(id); 81 if (foundAlbums.get(id) == null) { 82 data = new AlbumData(); 83 data.id = id; 84 data.account = mLocalSourceName; 85 86 if (dataIndex >= 0) { 87 data.thumbnailUrl = cursor.getString(dataIndex); 88 } 89 90 if (nameIndex >= 0) { 91 data.title = cursor.getString(nameIndex); 92 } else { 93 data.title = mUnknownAlbumName; 94 } 95 96 log(TAG, data.title + " found"); 97 foundAlbums.put(id, data); 98 } 99 if (updatedIndex >= 0) { 100 long updated = cursor.getLong(updatedIndex); 101 data.updated = (data.updated == 0 ? 102 updated : 103 Math.min(data.updated, updated)); 104 } 105 } 106 } 107 cursor.close(); 108 109 } 110 log(TAG, "found " + foundAlbums.size() + " items."); 111 mFoundAlbumIds = foundAlbums.keySet(); 112 return foundAlbums.values(); 113 } 114 115 @Override openCursor(ImageData data)116 protected void openCursor(ImageData data) { 117 log(TAG, "opening single album"); 118 119 String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION, 120 MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; 121 String selection = MediaStore.Images.Media.BUCKET_ID + " = '" + data.albumId + "'"; 122 123 data.cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 124 projection, selection, null, null); 125 } 126 127 @Override findPosition(ImageData data)128 protected void findPosition(ImageData data) { 129 if (data.position == -1) { 130 if (data.cursor == null) { 131 openCursor(data); 132 } 133 if (data.cursor != null) { 134 int dataIndex = data.cursor.getColumnIndex(MediaStore.Images.Media.DATA); 135 data.cursor.moveToPosition(-1); 136 while (data.position == -1 && data.cursor.moveToNext()) { 137 String url = data.cursor.getString(dataIndex); 138 if (url != null && url.equals(data.url)) { 139 data.position = data.cursor.getPosition(); 140 } 141 } 142 if (data.position == -1) { 143 // oops! The image isn't in this album. How did we get here? 144 data.position = INVALID; 145 } 146 } 147 } 148 } 149 150 @Override unpackImageData(Cursor cursor, ImageData data)151 protected ImageData unpackImageData(Cursor cursor, ImageData data) { 152 if (data == null) { 153 data = new ImageData(); 154 } 155 int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA); 156 int orientationIndex = cursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION); 157 int bucketIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID); 158 159 data.url = cursor.getString(dataIndex); 160 data.albumId = cursor.getString(bucketIndex); 161 data.position = UNINITIALIZED; 162 data.cursor = null; 163 data.orientation = cursor.getInt(orientationIndex); 164 165 return data; 166 } 167 168 @Override findImages(int howMany)169 protected Collection<ImageData> findImages(int howMany) { 170 log(TAG, "finding images"); 171 LinkedList<ImageData> foundImages = new LinkedList<ImageData>(); 172 173 String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION, 174 MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; 175 String selection = ""; 176 for (String id : getFoundAlbums()) { 177 if (mSettings.isAlbumEnabled(id)) { 178 String[] parts = id.split(":"); 179 if (parts.length > 1) { 180 if (selection.length() > 0) { 181 selection += " OR "; 182 } 183 selection += MediaStore.Images.Media.BUCKET_ID + " = '" + parts[1] + "'"; 184 } 185 } 186 } 187 if (selection.isEmpty()) { 188 return foundImages; 189 } 190 191 Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 192 projection, selection, null, null); 193 if (cursor != null) { 194 int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA); 195 196 if (cursor.getCount() > howMany && mLastPosition == INVALID) { 197 mLastPosition = pickRandomStart(cursor.getCount(), howMany); 198 } 199 cursor.moveToPosition(mLastPosition); 200 201 if (dataIndex < 0) { 202 log(TAG, "can't find the DATA column!"); 203 } else { 204 while (foundImages.size() < howMany && cursor.moveToNext()) { 205 ImageData data = unpackImageData(cursor, null); 206 foundImages.offer(data); 207 mLastPosition = cursor.getPosition(); 208 } 209 if (cursor.isAfterLast()) { 210 mLastPosition = -1; 211 } 212 if (cursor.isBeforeFirst()) { 213 mLastPosition = INVALID; 214 } 215 } 216 217 cursor.close(); 218 } 219 log(TAG, "found " + foundImages.size() + " items."); 220 return foundImages; 221 } 222 223 @Override getStream(ImageData data, int longSide)224 protected InputStream getStream(ImageData data, int longSide) { 225 FileInputStream fis = null; 226 try { 227 log(TAG, "opening:" + data.url); 228 fis = new FileInputStream(data.url); 229 } catch (Exception ex) { 230 log(TAG, ex.toString()); 231 fis = null; 232 } 233 234 return (InputStream) fis; 235 } 236 } 237 238