/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.gallery3d.data; import android.content.ContentResolver; import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.provider.MediaStore.Images; import android.provider.MediaStore.Images.ImageColumns; import android.provider.MediaStore.Video; import android.provider.MediaStore.Video.VideoColumns; import com.android.gallery3d.R; import com.android.gallery3d.app.GalleryApp; import com.android.gallery3d.common.Utils; import com.android.gallery3d.util.BucketNames; import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.MediaSetUtils; import java.io.File; import java.util.ArrayList; // LocalAlbumSet lists all media items in one bucket on local storage. // The media items need to be all images or all videos, but not both. public class LocalAlbum extends MediaSet { private static final String TAG = "LocalAlbum"; private static final String[] COUNT_PROJECTION = { "COUNT(_id)" }; private static final int INVALID_COUNT = -1; private final String mWhereClause; private final String mOrderClause; private final Uri mBaseUri; private final String[] mProjection; private final GalleryApp mApplication; private final ContentResolver mResolver; private final int mBucketId; private final String mName; private final boolean mIsImage; private final ChangeNotifier mNotifier; private final Path mItemPath; private int mCachedCount = INVALID_COUNT; public LocalAlbum(Path path, GalleryApp application, int bucketId, boolean isImage, String name) { super(path, nextVersionNumber()); mApplication = application; mResolver = application.getContentResolver(); mBucketId = bucketId; mName = name; mIsImage = isImage; if (isImage) { mWhereClause = ImageColumns.BUCKET_ID + " = ?"; mOrderClause = ImageColumns.DATE_TAKEN + " DESC, " + ImageColumns._ID + " DESC"; mBaseUri = Images.Media.EXTERNAL_CONTENT_URI; mProjection = LocalImage.PROJECTION; mItemPath = LocalImage.ITEM_PATH; } else { mWhereClause = VideoColumns.BUCKET_ID + " = ?"; mOrderClause = VideoColumns.DATE_TAKEN + " DESC, " + VideoColumns._ID + " DESC"; mBaseUri = Video.Media.EXTERNAL_CONTENT_URI; mProjection = LocalVideo.PROJECTION; mItemPath = LocalVideo.ITEM_PATH; } mNotifier = new ChangeNotifier(this, mBaseUri, application); } public LocalAlbum(Path path, GalleryApp application, int bucketId, boolean isImage) { this(path, application, bucketId, isImage, BucketHelper.getBucketName( application.getContentResolver(), bucketId)); } @Override public boolean isCameraRoll() { return mBucketId == MediaSetUtils.CAMERA_BUCKET_ID; } @Override public Uri getContentUri() { if (mIsImage) { return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon() .appendQueryParameter(LocalSource.KEY_BUCKET_ID, String.valueOf(mBucketId)).build(); } else { return MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon() .appendQueryParameter(LocalSource.KEY_BUCKET_ID, String.valueOf(mBucketId)).build(); } } @Override public ArrayList getMediaItem(int start, int count) { DataManager dataManager = mApplication.getDataManager(); Uri uri = mBaseUri.buildUpon() .appendQueryParameter("limit", start + "," + count).build(); ArrayList list = new ArrayList(); GalleryUtils.assertNotInRenderThread(); Cursor cursor = mResolver.query( uri, mProjection, mWhereClause, new String[]{String.valueOf(mBucketId)}, mOrderClause); if (cursor == null) { Log.w(TAG, "query fail: " + uri); return list; } try { while (cursor.moveToNext()) { int id = cursor.getInt(0); // _id must be in the first column Path childPath = mItemPath.getChild(id); MediaItem item = loadOrUpdateItem(childPath, cursor, dataManager, mApplication, mIsImage); list.add(item); } } finally { cursor.close(); } return list; } private static MediaItem loadOrUpdateItem(Path path, Cursor cursor, DataManager dataManager, GalleryApp app, boolean isImage) { synchronized (DataManager.LOCK) { LocalMediaItem item = (LocalMediaItem) dataManager.peekMediaObject(path); if (item == null) { if (isImage) { item = new LocalImage(path, app, cursor); } else { item = new LocalVideo(path, app, cursor); } } else { item.updateContent(cursor); } return item; } } // The pids array are sorted by the (path) id. public static MediaItem[] getMediaItemById( GalleryApp application, boolean isImage, ArrayList ids) { // get the lower and upper bound of (path) id MediaItem[] result = new MediaItem[ids.size()]; if (ids.isEmpty()) return result; int idLow = ids.get(0); int idHigh = ids.get(ids.size() - 1); // prepare the query parameters Uri baseUri; String[] projection; Path itemPath; if (isImage) { baseUri = Images.Media.EXTERNAL_CONTENT_URI; projection = LocalImage.PROJECTION; itemPath = LocalImage.ITEM_PATH; } else { baseUri = Video.Media.EXTERNAL_CONTENT_URI; projection = LocalVideo.PROJECTION; itemPath = LocalVideo.ITEM_PATH; } ContentResolver resolver = application.getContentResolver(); DataManager dataManager = application.getDataManager(); Cursor cursor = resolver.query(baseUri, projection, "_id BETWEEN ? AND ?", new String[]{String.valueOf(idLow), String.valueOf(idHigh)}, "_id"); if (cursor == null) { Log.w(TAG, "query fail" + baseUri); return result; } try { int n = ids.size(); int i = 0; while (i < n && cursor.moveToNext()) { int id = cursor.getInt(0); // _id must be in the first column // Match id with the one on the ids list. if (ids.get(i) > id) { continue; } while (ids.get(i) < id) { if (++i >= n) { return result; } } Path childPath = itemPath.getChild(id); MediaItem item = loadOrUpdateItem(childPath, cursor, dataManager, application, isImage); result[i] = item; ++i; } return result; } finally { cursor.close(); } } public static Cursor getItemCursor(ContentResolver resolver, Uri uri, String[] projection, int id) { return resolver.query(uri, projection, "_id=?", new String[]{String.valueOf(id)}, null); } @Override public int getMediaItemCount() { if (mCachedCount == INVALID_COUNT) { Cursor cursor = mResolver.query( mBaseUri, COUNT_PROJECTION, mWhereClause, new String[]{String.valueOf(mBucketId)}, null); if (cursor == null) { Log.w(TAG, "query fail"); return 0; } try { Utils.assertTrue(cursor.moveToNext()); mCachedCount = cursor.getInt(0); } finally { cursor.close(); } } return mCachedCount; } @Override public String getName() { return getLocalizedName(mApplication.getResources(), mBucketId, mName); } @Override public long reload() { if (mNotifier.isDirty()) { mDataVersion = nextVersionNumber(); mCachedCount = INVALID_COUNT; } return mDataVersion; } @Override public int getSupportedOperations() { return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_INFO; } @Override public void delete() { GalleryUtils.assertNotInRenderThread(); mResolver.delete(mBaseUri, mWhereClause, new String[]{String.valueOf(mBucketId)}); } @Override public boolean isLeafAlbum() { return true; } public static String getLocalizedName(Resources res, int bucketId, String name) { if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) { return res.getString(R.string.folder_camera); } else if (bucketId == MediaSetUtils.DOWNLOAD_BUCKET_ID) { return res.getString(R.string.folder_download); } else if (bucketId == MediaSetUtils.IMPORTED_BUCKET_ID) { return res.getString(R.string.folder_imported); } else if (bucketId == MediaSetUtils.SNAPSHOT_BUCKET_ID) { return res.getString(R.string.folder_screenshot); } else if (bucketId == MediaSetUtils.EDITED_ONLINE_PHOTOS_BUCKET_ID) { return res.getString(R.string.folder_edited_online_photos); } else { return name; } } // Relative path is the absolute path minus external storage path public static String getRelativePath(int bucketId) { String relativePath = "/"; if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) { relativePath += BucketNames.CAMERA; } else if (bucketId == MediaSetUtils.DOWNLOAD_BUCKET_ID) { relativePath += BucketNames.DOWNLOAD; } else if (bucketId == MediaSetUtils.IMPORTED_BUCKET_ID) { relativePath += BucketNames.IMPORTED; } else if (bucketId == MediaSetUtils.SNAPSHOT_BUCKET_ID) { relativePath += BucketNames.SCREENSHOTS; } else if (bucketId == MediaSetUtils.EDITED_ONLINE_PHOTOS_BUCKET_ID) { relativePath += BucketNames.EDITED_ONLINE_PHOTOS; } else { // If the first few cases didn't hit the matching path, do a // thorough search in the local directories. File extStorage = Environment.getExternalStorageDirectory(); String path = GalleryUtils.searchDirForPath(extStorage, bucketId); if (path == null) { Log.w(TAG, "Relative path for bucket id: " + bucketId + " is not found."); relativePath = null; } else { relativePath = path.substring(extStorage.getAbsolutePath().length()); } } return relativePath; } }