1 /* 2 * Copyright (C) 2013 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 17 package com.android.photos.shims; 18 19 import android.content.AsyncTaskLoader; 20 import android.content.Context; 21 import android.database.Cursor; 22 import android.database.MatrixCursor; 23 import android.graphics.drawable.Drawable; 24 import android.net.Uri; 25 import android.provider.MediaStore.Files.FileColumns; 26 import android.util.SparseArray; 27 28 import com.android.gallery3d.data.ContentListener; 29 import com.android.gallery3d.data.DataManager; 30 import com.android.gallery3d.data.MediaItem; 31 import com.android.gallery3d.data.MediaObject; 32 import com.android.gallery3d.data.MediaSet; 33 import com.android.gallery3d.data.MediaSet.ItemConsumer; 34 import com.android.gallery3d.data.MediaSet.SyncListener; 35 import com.android.gallery3d.data.Path; 36 import com.android.gallery3d.util.Future; 37 import com.android.photos.data.PhotoSetLoader; 38 39 import java.util.ArrayList; 40 41 /** 42 * Returns all MediaItems in a MediaSet, wrapping them in a cursor to appear 43 * like a PhotoSetLoader 44 */ 45 public class MediaItemsLoader extends AsyncTaskLoader<Cursor> implements LoaderCompatShim<Cursor> { 46 47 private static final SyncListener sNullListener = new SyncListener() { 48 @Override 49 public void onSyncDone(MediaSet mediaSet, int resultCode) { 50 } 51 }; 52 53 private final MediaSet mMediaSet; 54 private final DataManager mDataManager; 55 private Future<Integer> mSyncTask = null; 56 private ContentListener mObserver = new ContentListener() { 57 @Override 58 public void onContentDirty() { 59 onContentChanged(); 60 } 61 }; 62 private SparseArray<MediaItem> mMediaItems; 63 64 public MediaItemsLoader(Context context) { 65 super(context); 66 mDataManager = DataManager.from(context); 67 String path = mDataManager.getTopSetPath(DataManager.INCLUDE_ALL); 68 mMediaSet = mDataManager.getMediaSet(path); 69 } 70 71 public MediaItemsLoader(Context context, String parentPath) { 72 super(context); 73 mDataManager = DataManager.from(getContext()); 74 mMediaSet = mDataManager.getMediaSet(parentPath); 75 } 76 77 @Override 78 protected void onStartLoading() { 79 super.onStartLoading(); 80 mMediaSet.addContentListener(mObserver); 81 mSyncTask = mMediaSet.requestSync(sNullListener); 82 forceLoad(); 83 } 84 85 @Override 86 protected boolean onCancelLoad() { 87 if (mSyncTask != null) { 88 mSyncTask.cancel(); 89 mSyncTask = null; 90 } 91 return super.onCancelLoad(); 92 } 93 94 @Override 95 protected void onStopLoading() { 96 super.onStopLoading(); 97 cancelLoad(); 98 mMediaSet.removeContentListener(mObserver); 99 } 100 101 @Override 102 protected void onReset() { 103 super.onReset(); 104 onStopLoading(); 105 } 106 107 @Override 108 public Cursor loadInBackground() { 109 // TODO: This probably doesn't work 110 mMediaSet.reload(); 111 final MatrixCursor cursor = new MatrixCursor(PhotoSetLoader.PROJECTION); 112 final Object[] row = new Object[PhotoSetLoader.PROJECTION.length]; 113 final SparseArray<MediaItem> mediaItems = new SparseArray<MediaItem>(); 114 mMediaSet.enumerateTotalMediaItems(new ItemConsumer() { 115 @Override 116 public void consume(int index, MediaItem item) { 117 row[PhotoSetLoader.INDEX_ID] = index; 118 row[PhotoSetLoader.INDEX_DATA] = item.getContentUri().toString(); 119 row[PhotoSetLoader.INDEX_DATE_ADDED] = item.getDateInMs(); 120 row[PhotoSetLoader.INDEX_HEIGHT] = item.getHeight(); 121 row[PhotoSetLoader.INDEX_WIDTH] = item.getWidth(); 122 row[PhotoSetLoader.INDEX_WIDTH] = item.getWidth(); 123 int rawMediaType = item.getMediaType(); 124 int mappedMediaType = FileColumns.MEDIA_TYPE_NONE; 125 if (rawMediaType == MediaItem.MEDIA_TYPE_IMAGE) { 126 mappedMediaType = FileColumns.MEDIA_TYPE_IMAGE; 127 } else if (rawMediaType == MediaItem.MEDIA_TYPE_VIDEO) { 128 mappedMediaType = FileColumns.MEDIA_TYPE_VIDEO; 129 } 130 row[PhotoSetLoader.INDEX_MEDIA_TYPE] = mappedMediaType; 131 row[PhotoSetLoader.INDEX_SUPPORTED_OPERATIONS] = 132 item.getSupportedOperations(); 133 cursor.addRow(row); 134 mediaItems.append(index, item); 135 } 136 }); 137 synchronized (mMediaSet) { 138 mMediaItems = mediaItems; 139 } 140 return cursor; 141 } 142 143 @Override 144 public Drawable drawableForItem(Cursor item, Drawable recycle) { 145 BitmapJobDrawable drawable = null; 146 if (recycle == null || !(recycle instanceof BitmapJobDrawable)) { 147 drawable = new BitmapJobDrawable(); 148 } else { 149 drawable = (BitmapJobDrawable) recycle; 150 } 151 int index = item.getInt(PhotoSetLoader.INDEX_ID); 152 drawable.setMediaItem(mMediaItems.get(index)); 153 return drawable; 154 } 155 156 public static int getThumbnailSize() { 157 return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL); 158 } 159 160 @Override 161 public Uri uriForItem(Cursor item) { 162 int index = item.getInt(PhotoSetLoader.INDEX_ID); 163 MediaItem mi = mMediaItems.get(index); 164 return mi == null ? null : mi.getContentUri(); 165 } 166 167 @Override 168 public ArrayList<Uri> urisForSubItems(Cursor item) { 169 return null; 170 } 171 172 @Override 173 public void deleteItemWithPath(Object path) { 174 MediaObject o = mDataManager.getMediaObject((Path) path); 175 if (o != null) { 176 o.delete(); 177 } 178 } 179 180 @Override 181 public Object getPathForItem(Cursor item) { 182 int index = item.getInt(PhotoSetLoader.INDEX_ID); 183 MediaItem mi = mMediaItems.get(index); 184 if (mi != null) { 185 return mi.getPath(); 186 } 187 return null; 188 } 189 190 } 191