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 
MediaItemsLoader(Context context)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 
MediaItemsLoader(Context context, String parentPath)71     public MediaItemsLoader(Context context, String parentPath) {
72         super(context);
73         mDataManager = DataManager.from(getContext());
74         mMediaSet = mDataManager.getMediaSet(parentPath);
75     }
76 
77     @Override
onStartLoading()78     protected void onStartLoading() {
79         super.onStartLoading();
80         mMediaSet.addContentListener(mObserver);
81         mSyncTask = mMediaSet.requestSync(sNullListener);
82         forceLoad();
83     }
84 
85     @Override
onCancelLoad()86     protected boolean onCancelLoad() {
87         if (mSyncTask != null) {
88             mSyncTask.cancel();
89             mSyncTask = null;
90         }
91         return super.onCancelLoad();
92     }
93 
94     @Override
onStopLoading()95     protected void onStopLoading() {
96         super.onStopLoading();
97         cancelLoad();
98         mMediaSet.removeContentListener(mObserver);
99     }
100 
101     @Override
onReset()102     protected void onReset() {
103         super.onReset();
104         onStopLoading();
105     }
106 
107     @Override
loadInBackground()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
drawableForItem(Cursor item, Drawable recycle)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 
getThumbnailSize()156     public static int getThumbnailSize() {
157         return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
158     }
159 
160     @Override
uriForItem(Cursor item)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
urisForSubItems(Cursor item)168     public ArrayList<Uri> urisForSubItems(Cursor item) {
169         return null;
170     }
171 
172     @Override
deleteItemWithPath(Object path)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
getPathForItem(Cursor item)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