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
17package com.android.photos.shims;
18
19import android.content.AsyncTaskLoader;
20import android.content.Context;
21import android.database.Cursor;
22import android.database.MatrixCursor;
23import android.graphics.drawable.Drawable;
24import android.net.Uri;
25import android.provider.MediaStore.Files.FileColumns;
26import android.util.SparseArray;
27
28import com.android.gallery3d.data.ContentListener;
29import com.android.gallery3d.data.DataManager;
30import com.android.gallery3d.data.MediaItem;
31import com.android.gallery3d.data.MediaObject;
32import com.android.gallery3d.data.MediaSet;
33import com.android.gallery3d.data.MediaSet.ItemConsumer;
34import com.android.gallery3d.data.MediaSet.SyncListener;
35import com.android.gallery3d.data.Path;
36import com.android.gallery3d.util.Future;
37import com.android.photos.data.PhotoSetLoader;
38
39import java.util.ArrayList;
40
41/**
42 * Returns all MediaItems in a MediaSet, wrapping them in a cursor to appear
43 * like a PhotoSetLoader
44 */
45public 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