diff options
Diffstat (limited to 'src/com/android/gallery3d/data')
59 files changed, 0 insertions, 9074 deletions
diff --git a/src/com/android/gallery3d/data/ActionImage.java b/src/com/android/gallery3d/data/ActionImage.java deleted file mode 100644 index 58e30b146..000000000 --- a/src/com/android/gallery3d/data/ActionImage.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2012 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.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; -import android.net.Uri; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -public class ActionImage extends MediaItem { - @SuppressWarnings("unused") - private static final String TAG = "ActionImage"; - private GalleryApp mApplication; - private int mResourceId; - - public ActionImage(Path path, GalleryApp application, int resourceId) { - super(path, nextVersionNumber()); - mApplication = Utils.checkNotNull(application); - mResourceId = resourceId; - } - - @Override - public Job<Bitmap> requestImage(int type) { - return new BitmapJob(type); - } - - @Override - public Job<BitmapRegionDecoder> requestLargeImage() { - return null; - } - - private class BitmapJob implements Job<Bitmap> { - private int mType; - - protected BitmapJob(int type) { - mType = type; - } - - @Override - public Bitmap run(JobContext jc) { - int targetSize = MediaItem.getTargetSize(mType); - Bitmap bitmap = BitmapFactory.decodeResource(mApplication.getResources(), - mResourceId); - - if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = BitmapUtils.resizeAndCropCenter(bitmap, targetSize, true); - } else { - bitmap = BitmapUtils.resizeDownBySideLength(bitmap, targetSize, true); - } - return bitmap; - } - } - - @Override - public int getSupportedOperations() { - return SUPPORT_ACTION; - } - - @Override - public int getMediaType() { - return MEDIA_TYPE_UNKNOWN; - } - - @Override - public Uri getContentUri() { - return null; - } - - @Override - public String getMimeType() { - return ""; - } - - @Override - public int getWidth() { - return 0; - } - - @Override - public int getHeight() { - return 0; - } -} diff --git a/src/com/android/gallery3d/data/BucketHelper.java b/src/com/android/gallery3d/data/BucketHelper.java deleted file mode 100644 index 3418dafb7..000000000 --- a/src/com/android/gallery3d/data/BucketHelper.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.android.gallery3d.data; - -import android.annotation.TargetApi; -import android.content.ContentResolver; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore.Files; -import android.provider.MediaStore.Files.FileColumns; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Images.ImageColumns; -import android.provider.MediaStore.Video; -import android.util.Log; - -import com.android.gallery3d.common.ApiHelper; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; - -class BucketHelper { - - private static final String TAG = "BucketHelper"; - private static final String EXTERNAL_MEDIA = "external"; - - // BUCKET_DISPLAY_NAME is a string like "Camera" which is the directory - // name of where an image or video is in. BUCKET_ID is a hash of the path - // name of that directory (see computeBucketValues() in MediaProvider for - // details). MEDIA_TYPE is video, image, audio, etc. - // - // The "albums" are not explicitly recorded in the database, but each image - // or video has the two columns (BUCKET_ID, MEDIA_TYPE). We define an - // "album" to be the collection of images/videos which have the same value - // for the two columns. - // - // The goal of the query (used in loadSubMediaSetsFromFilesTable()) is to - // find all albums, that is, all unique values for (BUCKET_ID, MEDIA_TYPE). - // In the meantime sort them by the timestamp of the latest image/video in - // each of the album. - // - // The order of columns below is important: it must match to the index in - // MediaStore. - private static final String[] PROJECTION_BUCKET = { - ImageColumns.BUCKET_ID, - FileColumns.MEDIA_TYPE, - ImageColumns.BUCKET_DISPLAY_NAME}; - - // The indices should match the above projections. - private static final int INDEX_BUCKET_ID = 0; - private static final int INDEX_MEDIA_TYPE = 1; - private static final int INDEX_BUCKET_NAME = 2; - - // We want to order the albums by reverse chronological order. We abuse the - // "WHERE" parameter to insert a "GROUP BY" clause into the SQL statement. - // The template for "WHERE" parameter is like: - // SELECT ... FROM ... WHERE (%s) - // and we make it look like: - // SELECT ... FROM ... WHERE (1) GROUP BY 1,(2) - // The "(1)" means true. The "1,(2)" means the first two columns specified - // after SELECT. Note that because there is a ")" in the template, we use - // "(2" to match it. - private static final String BUCKET_GROUP_BY = "1) GROUP BY 1,(2"; - - private static final String BUCKET_ORDER_BY = "MAX(datetaken) DESC"; - - // Before HoneyComb there is no Files table. Thus, we need to query the - // bucket info from the Images and Video tables and then merge them - // together. - // - // A bucket can exist in both tables. In this case, we need to find the - // latest timestamp from the two tables and sort ourselves. So we add the - // MAX(date_taken) to the projection and remove the media_type since we - // already know the media type from the table we query from. - private static final String[] PROJECTION_BUCKET_IN_ONE_TABLE = { - ImageColumns.BUCKET_ID, - "MAX(datetaken)", - ImageColumns.BUCKET_DISPLAY_NAME}; - - // We keep the INDEX_BUCKET_ID and INDEX_BUCKET_NAME the same as - // PROJECTION_BUCKET so we can reuse the values defined before. - private static final int INDEX_DATE_TAKEN = 1; - - // When query from the Images or Video tables, we only need to group by BUCKET_ID. - private static final String BUCKET_GROUP_BY_IN_ONE_TABLE = "1) GROUP BY (1"; - - public static BucketEntry[] loadBucketEntries( - JobContext jc, ContentResolver resolver, int type) { - if (ApiHelper.HAS_MEDIA_PROVIDER_FILES_TABLE) { - return loadBucketEntriesFromFilesTable(jc, resolver, type); - } else { - return loadBucketEntriesFromImagesAndVideoTable(jc, resolver, type); - } - } - - private static void updateBucketEntriesFromTable(JobContext jc, - ContentResolver resolver, Uri tableUri, HashMap<Integer, BucketEntry> buckets) { - Cursor cursor = resolver.query(tableUri, PROJECTION_BUCKET_IN_ONE_TABLE, - BUCKET_GROUP_BY_IN_ONE_TABLE, null, null); - if (cursor == null) { - Log.w(TAG, "cannot open media database: " + tableUri); - return; - } - try { - while (cursor.moveToNext()) { - int bucketId = cursor.getInt(INDEX_BUCKET_ID); - int dateTaken = cursor.getInt(INDEX_DATE_TAKEN); - BucketEntry entry = buckets.get(bucketId); - if (entry == null) { - entry = new BucketEntry(bucketId, cursor.getString(INDEX_BUCKET_NAME)); - buckets.put(bucketId, entry); - entry.dateTaken = dateTaken; - } else { - entry.dateTaken = Math.max(entry.dateTaken, dateTaken); - } - } - } finally { - Utils.closeSilently(cursor); - } - } - - private static BucketEntry[] loadBucketEntriesFromImagesAndVideoTable( - JobContext jc, ContentResolver resolver, int type) { - HashMap<Integer, BucketEntry> buckets = new HashMap<Integer, BucketEntry>(64); - if ((type & MediaObject.MEDIA_TYPE_IMAGE) != 0) { - updateBucketEntriesFromTable( - jc, resolver, Images.Media.EXTERNAL_CONTENT_URI, buckets); - } - if ((type & MediaObject.MEDIA_TYPE_VIDEO) != 0) { - updateBucketEntriesFromTable( - jc, resolver, Video.Media.EXTERNAL_CONTENT_URI, buckets); - } - BucketEntry[] entries = buckets.values().toArray(new BucketEntry[buckets.size()]); - Arrays.sort(entries, new Comparator<BucketEntry>() { - @Override - public int compare(BucketEntry a, BucketEntry b) { - // sorted by dateTaken in descending order - return b.dateTaken - a.dateTaken; - } - }); - return entries; - } - - private static BucketEntry[] loadBucketEntriesFromFilesTable( - JobContext jc, ContentResolver resolver, int type) { - Uri uri = getFilesContentUri(); - - Cursor cursor = resolver.query(uri, - PROJECTION_BUCKET, BUCKET_GROUP_BY, - null, BUCKET_ORDER_BY); - if (cursor == null) { - Log.w(TAG, "cannot open local database: " + uri); - return new BucketEntry[0]; - } - ArrayList<BucketEntry> buffer = new ArrayList<BucketEntry>(); - int typeBits = 0; - if ((type & MediaObject.MEDIA_TYPE_IMAGE) != 0) { - typeBits |= (1 << FileColumns.MEDIA_TYPE_IMAGE); - } - if ((type & MediaObject.MEDIA_TYPE_VIDEO) != 0) { - typeBits |= (1 << FileColumns.MEDIA_TYPE_VIDEO); - } - try { - while (cursor.moveToNext()) { - if ((typeBits & (1 << cursor.getInt(INDEX_MEDIA_TYPE))) != 0) { - BucketEntry entry = new BucketEntry( - cursor.getInt(INDEX_BUCKET_ID), - cursor.getString(INDEX_BUCKET_NAME)); - if (!buffer.contains(entry)) { - buffer.add(entry); - } - } - if (jc.isCancelled()) return null; - } - } finally { - Utils.closeSilently(cursor); - } - return buffer.toArray(new BucketEntry[buffer.size()]); - } - - private static String getBucketNameInTable( - ContentResolver resolver, Uri tableUri, int bucketId) { - String selectionArgs[] = new String[] {String.valueOf(bucketId)}; - Uri uri = tableUri.buildUpon() - .appendQueryParameter("limit", "1") - .build(); - Cursor cursor = resolver.query(uri, PROJECTION_BUCKET_IN_ONE_TABLE, - "bucket_id = ?", selectionArgs, null); - try { - if (cursor != null && cursor.moveToNext()) { - return cursor.getString(INDEX_BUCKET_NAME); - } - } finally { - Utils.closeSilently(cursor); - } - return null; - } - - @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) - private static Uri getFilesContentUri() { - return Files.getContentUri(EXTERNAL_MEDIA); - } - - public static String getBucketName(ContentResolver resolver, int bucketId) { - if (ApiHelper.HAS_MEDIA_PROVIDER_FILES_TABLE) { - String result = getBucketNameInTable(resolver, getFilesContentUri(), bucketId); - return result == null ? "" : result; - } else { - String result = getBucketNameInTable( - resolver, Images.Media.EXTERNAL_CONTENT_URI, bucketId); - if (result != null) return result; - result = getBucketNameInTable( - resolver, Video.Media.EXTERNAL_CONTENT_URI, bucketId); - return result == null ? "" : result; - } - } - - public static class BucketEntry { - public String bucketName; - public int bucketId; - public int dateTaken; - - public BucketEntry(int id, String name) { - bucketId = id; - bucketName = Utils.ensureNotNull(name); - } - - @Override - public int hashCode() { - return bucketId; - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof BucketEntry)) return false; - BucketEntry entry = (BucketEntry) object; - return bucketId == entry.bucketId; - } - } -} diff --git a/src/com/android/gallery3d/data/BytesBufferPool.java b/src/com/android/gallery3d/data/BytesBufferPool.java deleted file mode 100644 index d2da323fc..000000000 --- a/src/com/android/gallery3d/data/BytesBufferPool.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.gallery3d.util.ThreadPool.JobContext; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; - -public class BytesBufferPool { - - private static final int READ_STEP = 4096; - - public static class BytesBuffer { - public byte[] data; - public int offset; - public int length; - - private BytesBuffer(int capacity) { - this.data = new byte[capacity]; - } - - // an helper function to read content from FileDescriptor - public void readFrom(JobContext jc, FileDescriptor fd) throws IOException { - FileInputStream fis = new FileInputStream(fd); - length = 0; - try { - int capacity = data.length; - while (true) { - int step = Math.min(READ_STEP, capacity - length); - int rc = fis.read(data, length, step); - if (rc < 0 || jc.isCancelled()) return; - length += rc; - - if (length == capacity) { - byte[] newData = new byte[data.length * 2]; - System.arraycopy(data, 0, newData, 0, data.length); - data = newData; - capacity = data.length; - } - } - } finally { - fis.close(); - } - } - } - - private final int mPoolSize; - private final int mBufferSize; - private final ArrayList<BytesBuffer> mList; - - public BytesBufferPool(int poolSize, int bufferSize) { - mList = new ArrayList<BytesBuffer>(poolSize); - mPoolSize = poolSize; - mBufferSize = bufferSize; - } - - public synchronized BytesBuffer get() { - int n = mList.size(); - return n > 0 ? mList.remove(n - 1) : new BytesBuffer(mBufferSize); - } - - public synchronized void recycle(BytesBuffer buffer) { - if (buffer.data.length != mBufferSize) return; - if (mList.size() < mPoolSize) { - buffer.offset = 0; - buffer.length = 0; - mList.add(buffer); - } - } - - public synchronized void clear() { - mList.clear(); - } -} diff --git a/src/com/android/gallery3d/data/CameraShortcutImage.java b/src/com/android/gallery3d/data/CameraShortcutImage.java deleted file mode 100644 index 865270b4c..000000000 --- a/src/com/android/gallery3d/data/CameraShortcutImage.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.gallery3d.R; -import com.android.gallery3d.app.GalleryApp; - -public class CameraShortcutImage extends ActionImage { - @SuppressWarnings("unused") - private static final String TAG = "CameraShortcutImage"; - - public CameraShortcutImage(Path path, GalleryApp application) { - super(path, application, R.drawable.placeholder_camera); - } - - @Override - public int getSupportedOperations() { - return super.getSupportedOperations() | SUPPORT_CAMERA_SHORTCUT; - } -} diff --git a/src/com/android/gallery3d/data/ChangeNotifier.java b/src/com/android/gallery3d/data/ChangeNotifier.java deleted file mode 100644 index 558a8648e..000000000 --- a/src/com/android/gallery3d/data/ChangeNotifier.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.net.Uri; - -import com.android.gallery3d.app.GalleryApp; - -import java.util.concurrent.atomic.AtomicBoolean; - -// This handles change notification for media sets. -public class ChangeNotifier { - - private MediaSet mMediaSet; - private AtomicBoolean mContentDirty = new AtomicBoolean(true); - - public ChangeNotifier(MediaSet set, Uri uri, GalleryApp application) { - mMediaSet = set; - application.getDataManager().registerChangeNotifier(uri, this); - } - - public ChangeNotifier(MediaSet set, Uri[] uris, GalleryApp application) { - mMediaSet = set; - for (int i = 0; i < uris.length; i++) { - application.getDataManager().registerChangeNotifier(uris[i], this); - } - } - - // Returns the dirty flag and clear it. - public boolean isDirty() { - return mContentDirty.compareAndSet(true, false); - } - - public void fakeChange() { - onChange(false); - } - - protected void onChange(boolean selfChange) { - if (mContentDirty.compareAndSet(false, true)) { - mMediaSet.notifyContentChanged(); - } - } -}
\ No newline at end of file diff --git a/src/com/android/gallery3d/data/ClusterAlbum.java b/src/com/android/gallery3d/data/ClusterAlbum.java deleted file mode 100644 index 8681952bf..000000000 --- a/src/com/android/gallery3d/data/ClusterAlbum.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 java.util.ArrayList; - -public class ClusterAlbum extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "ClusterAlbum"; - private ArrayList<Path> mPaths = new ArrayList<Path>(); - private String mName = ""; - private DataManager mDataManager; - private MediaSet mClusterAlbumSet; - private MediaItem mCover; - - public ClusterAlbum(Path path, DataManager dataManager, - MediaSet clusterAlbumSet) { - super(path, nextVersionNumber()); - mDataManager = dataManager; - mClusterAlbumSet = clusterAlbumSet; - mClusterAlbumSet.addContentListener(this); - } - - public void setCoverMediaItem(MediaItem cover) { - mCover = cover; - } - - @Override - public MediaItem getCoverMediaItem() { - return mCover != null ? mCover : super.getCoverMediaItem(); - } - - void setMediaItems(ArrayList<Path> paths) { - mPaths = paths; - } - - ArrayList<Path> getMediaItems() { - return mPaths; - } - - public void setName(String name) { - mName = name; - } - - @Override - public String getName() { - return mName; - } - - @Override - public int getMediaItemCount() { - return mPaths.size(); - } - - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - return getMediaItemFromPath(mPaths, start, count, mDataManager); - } - - public static ArrayList<MediaItem> getMediaItemFromPath( - ArrayList<Path> paths, int start, int count, - DataManager dataManager) { - if (start >= paths.size()) { - return new ArrayList<MediaItem>(); - } - int end = Math.min(start + count, paths.size()); - ArrayList<Path> subset = new ArrayList<Path>(paths.subList(start, end)); - final MediaItem[] buf = new MediaItem[end - start]; - ItemConsumer consumer = new ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - buf[index] = item; - } - }; - dataManager.mapMediaItems(subset, consumer, 0); - ArrayList<MediaItem> result = new ArrayList<MediaItem>(end - start); - for (int i = 0; i < buf.length; i++) { - result.add(buf[i]); - } - return result; - } - - @Override - protected int enumerateMediaItems(ItemConsumer consumer, int startIndex) { - mDataManager.mapMediaItems(mPaths, consumer, startIndex); - return mPaths.size(); - } - - @Override - public int getTotalMediaItemCount() { - return mPaths.size(); - } - - @Override - public long reload() { - if (mClusterAlbumSet.reload() > mDataVersion) { - mDataVersion = nextVersionNumber(); - } - return mDataVersion; - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - @Override - public int getSupportedOperations() { - return SUPPORT_SHARE | SUPPORT_DELETE | SUPPORT_INFO; - } - - @Override - public void delete() { - ItemConsumer consumer = new ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - if ((item.getSupportedOperations() & SUPPORT_DELETE) != 0) { - item.delete(); - } - } - }; - mDataManager.mapMediaItems(mPaths, consumer, 0); - } - - @Override - public boolean isLeafAlbum() { - return true; - } -} diff --git a/src/com/android/gallery3d/data/ClusterAlbumSet.java b/src/com/android/gallery3d/data/ClusterAlbumSet.java deleted file mode 100644 index cb212ba36..000000000 --- a/src/com/android/gallery3d/data/ClusterAlbumSet.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.Context; -import android.net.Uri; - -import com.android.gallery3d.app.GalleryApp; - -import java.util.ArrayList; -import java.util.HashSet; - -public class ClusterAlbumSet extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "ClusterAlbumSet"; - private GalleryApp mApplication; - private MediaSet mBaseSet; - private int mKind; - private ArrayList<ClusterAlbum> mAlbums = new ArrayList<ClusterAlbum>(); - private boolean mFirstReloadDone; - - public ClusterAlbumSet(Path path, GalleryApp application, - MediaSet baseSet, int kind) { - super(path, INVALID_DATA_VERSION); - mApplication = application; - mBaseSet = baseSet; - mKind = kind; - baseSet.addContentListener(this); - } - - @Override - public MediaSet getSubMediaSet(int index) { - return mAlbums.get(index); - } - - @Override - public int getSubMediaSetCount() { - return mAlbums.size(); - } - - @Override - public String getName() { - return mBaseSet.getName(); - } - - @Override - public long reload() { - if (mBaseSet.reload() > mDataVersion) { - if (mFirstReloadDone) { - updateClustersContents(); - } else { - updateClusters(); - mFirstReloadDone = true; - } - mDataVersion = nextVersionNumber(); - } - return mDataVersion; - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - private void updateClusters() { - mAlbums.clear(); - Clustering clustering; - Context context = mApplication.getAndroidContext(); - switch (mKind) { - case ClusterSource.CLUSTER_ALBUMSET_TIME: - clustering = new TimeClustering(context); - break; - case ClusterSource.CLUSTER_ALBUMSET_LOCATION: - clustering = new LocationClustering(context); - break; - case ClusterSource.CLUSTER_ALBUMSET_TAG: - clustering = new TagClustering(context); - break; - case ClusterSource.CLUSTER_ALBUMSET_FACE: - clustering = new FaceClustering(context); - break; - default: /* CLUSTER_ALBUMSET_SIZE */ - clustering = new SizeClustering(context); - break; - } - - clustering.run(mBaseSet); - int n = clustering.getNumberOfClusters(); - DataManager dataManager = mApplication.getDataManager(); - for (int i = 0; i < n; i++) { - Path childPath; - String childName = clustering.getClusterName(i); - if (mKind == ClusterSource.CLUSTER_ALBUMSET_TAG) { - childPath = mPath.getChild(Uri.encode(childName)); - } else if (mKind == ClusterSource.CLUSTER_ALBUMSET_SIZE) { - long minSize = ((SizeClustering) clustering).getMinSize(i); - childPath = mPath.getChild(minSize); - } else { - childPath = mPath.getChild(i); - } - - ClusterAlbum album; - synchronized (DataManager.LOCK) { - album = (ClusterAlbum) dataManager.peekMediaObject(childPath); - if (album == null) { - album = new ClusterAlbum(childPath, dataManager, this); - } - } - album.setMediaItems(clustering.getCluster(i)); - album.setName(childName); - album.setCoverMediaItem(clustering.getClusterCover(i)); - mAlbums.add(album); - } - } - - private void updateClustersContents() { - final HashSet<Path> existing = new HashSet<Path>(); - mBaseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - existing.add(item.getPath()); - } - }); - - int n = mAlbums.size(); - - // The loop goes backwards because we may remove empty albums from - // mAlbums. - for (int i = n - 1; i >= 0; i--) { - ArrayList<Path> oldPaths = mAlbums.get(i).getMediaItems(); - ArrayList<Path> newPaths = new ArrayList<Path>(); - int m = oldPaths.size(); - for (int j = 0; j < m; j++) { - Path p = oldPaths.get(j); - if (existing.contains(p)) { - newPaths.add(p); - } - } - mAlbums.get(i).setMediaItems(newPaths); - if (newPaths.isEmpty()) { - mAlbums.remove(i); - } - } - } -} diff --git a/src/com/android/gallery3d/data/ClusterSource.java b/src/com/android/gallery3d/data/ClusterSource.java deleted file mode 100644 index a1f22e57a..000000000 --- a/src/com/android/gallery3d/data/ClusterSource.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 com.android.gallery3d.app.GalleryApp; - -class ClusterSource extends MediaSource { - static final int CLUSTER_ALBUMSET_TIME = 0; - static final int CLUSTER_ALBUMSET_LOCATION = 1; - static final int CLUSTER_ALBUMSET_TAG = 2; - static final int CLUSTER_ALBUMSET_SIZE = 3; - static final int CLUSTER_ALBUMSET_FACE = 4; - - static final int CLUSTER_ALBUM_TIME = 0x100; - static final int CLUSTER_ALBUM_LOCATION = 0x101; - static final int CLUSTER_ALBUM_TAG = 0x102; - static final int CLUSTER_ALBUM_SIZE = 0x103; - static final int CLUSTER_ALBUM_FACE = 0x104; - - GalleryApp mApplication; - PathMatcher mMatcher; - - public ClusterSource(GalleryApp application) { - super("cluster"); - mApplication = application; - mMatcher = new PathMatcher(); - mMatcher.add("/cluster/*/time", CLUSTER_ALBUMSET_TIME); - mMatcher.add("/cluster/*/location", CLUSTER_ALBUMSET_LOCATION); - mMatcher.add("/cluster/*/tag", CLUSTER_ALBUMSET_TAG); - mMatcher.add("/cluster/*/size", CLUSTER_ALBUMSET_SIZE); - mMatcher.add("/cluster/*/face", CLUSTER_ALBUMSET_FACE); - - mMatcher.add("/cluster/*/time/*", CLUSTER_ALBUM_TIME); - mMatcher.add("/cluster/*/location/*", CLUSTER_ALBUM_LOCATION); - mMatcher.add("/cluster/*/tag/*", CLUSTER_ALBUM_TAG); - mMatcher.add("/cluster/*/size/*", CLUSTER_ALBUM_SIZE); - mMatcher.add("/cluster/*/face/*", CLUSTER_ALBUM_FACE); - } - - // The names we accept are: - // /cluster/{set}/time /cluster/{set}/time/k - // /cluster/{set}/location /cluster/{set}/location/k - // /cluster/{set}/tag /cluster/{set}/tag/encoded_tag - // /cluster/{set}/size /cluster/{set}/size/min_size - @Override - public MediaObject createMediaObject(Path path) { - int matchType = mMatcher.match(path); - String setsName = mMatcher.getVar(0); - DataManager dataManager = mApplication.getDataManager(); - MediaSet[] sets = dataManager.getMediaSetsFromString(setsName); - switch (matchType) { - case CLUSTER_ALBUMSET_TIME: - case CLUSTER_ALBUMSET_LOCATION: - case CLUSTER_ALBUMSET_TAG: - case CLUSTER_ALBUMSET_SIZE: - case CLUSTER_ALBUMSET_FACE: - return new ClusterAlbumSet(path, mApplication, sets[0], matchType); - case CLUSTER_ALBUM_TIME: - case CLUSTER_ALBUM_LOCATION: - case CLUSTER_ALBUM_TAG: - case CLUSTER_ALBUM_SIZE: - case CLUSTER_ALBUM_FACE: { - MediaSet parent = dataManager.getMediaSet(path.getParent()); - // The actual content in the ClusterAlbum will be filled later - // when the reload() method in the parent is run. - return new ClusterAlbum(path, dataManager, parent); - } - default: - throw new RuntimeException("bad path: " + path); - } - } -} diff --git a/src/com/android/gallery3d/data/Clustering.java b/src/com/android/gallery3d/data/Clustering.java deleted file mode 100644 index 4072bf57b..000000000 --- a/src/com/android/gallery3d/data/Clustering.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 java.util.ArrayList; - -public abstract class Clustering { - public abstract void run(MediaSet baseSet); - public abstract int getNumberOfClusters(); - public abstract ArrayList<Path> getCluster(int index); - public abstract String getClusterName(int index); - public MediaItem getClusterCover(int index) { - return null; - } -} diff --git a/src/com/android/gallery3d/data/ComboAlbum.java b/src/com/android/gallery3d/data/ComboAlbum.java deleted file mode 100644 index cadd9f8af..000000000 --- a/src/com/android/gallery3d/data/ComboAlbum.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2011 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 com.android.gallery3d.util.Future; - -import java.util.ArrayList; - -// ComboAlbum combines multiple media sets into one. It lists all media items -// from the input albums. -// This only handles SubMediaSets, not MediaItems. (That's all we need now) -public class ComboAlbum extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "ComboAlbum"; - private final MediaSet[] mSets; - private String mName; - - public ComboAlbum(Path path, MediaSet[] mediaSets, String name) { - super(path, nextVersionNumber()); - mSets = mediaSets; - for (MediaSet set : mSets) { - set.addContentListener(this); - } - mName = name; - } - - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - ArrayList<MediaItem> items = new ArrayList<MediaItem>(); - for (MediaSet set : mSets) { - int size = set.getMediaItemCount(); - if (count < 1) break; - if (start < size) { - int fetchCount = (start + count <= size) ? count : size - start; - ArrayList<MediaItem> fetchItems = set.getMediaItem(start, fetchCount); - items.addAll(fetchItems); - count -= fetchItems.size(); - start = 0; - } else { - start -= size; - } - } - return items; - } - - @Override - public int getMediaItemCount() { - int count = 0; - for (MediaSet set : mSets) { - count += set.getMediaItemCount(); - } - return count; - } - - @Override - public boolean isLeafAlbum() { - return true; - } - - @Override - public String getName() { - return mName; - } - - public void useNameOfChild(int i) { - if (i < mSets.length) mName = mSets[i].getName(); - } - - @Override - public long reload() { - boolean changed = false; - for (int i = 0, n = mSets.length; i < n; ++i) { - long version = mSets[i].reload(); - if (version > mDataVersion) changed = true; - } - if (changed) mDataVersion = nextVersionNumber(); - return mDataVersion; - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - @Override - public Future<Integer> requestSync(SyncListener listener) { - return requestSyncOnMultipleSets(mSets, listener); - } -} diff --git a/src/com/android/gallery3d/data/ComboAlbumSet.java b/src/com/android/gallery3d/data/ComboAlbumSet.java deleted file mode 100644 index 3f3674500..000000000 --- a/src/com/android/gallery3d/data/ComboAlbumSet.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 com.android.gallery3d.R; -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.util.Future; - -// ComboAlbumSet combines multiple media sets into one. It lists all sub -// media sets from the input album sets. -// This only handles SubMediaSets, not MediaItems. (That's all we need now) -public class ComboAlbumSet extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "ComboAlbumSet"; - private final MediaSet[] mSets; - private final String mName; - - public ComboAlbumSet(Path path, GalleryApp application, MediaSet[] mediaSets) { - super(path, nextVersionNumber()); - mSets = mediaSets; - for (MediaSet set : mSets) { - set.addContentListener(this); - } - mName = application.getResources().getString( - R.string.set_label_all_albums); - } - - @Override - public MediaSet getSubMediaSet(int index) { - for (MediaSet set : mSets) { - int size = set.getSubMediaSetCount(); - if (index < size) { - return set.getSubMediaSet(index); - } - index -= size; - } - return null; - } - - @Override - public int getSubMediaSetCount() { - int count = 0; - for (MediaSet set : mSets) { - count += set.getSubMediaSetCount(); - } - return count; - } - - @Override - public String getName() { - return mName; - } - - @Override - public boolean isLoading() { - for (int i = 0, n = mSets.length; i < n; ++i) { - if (mSets[i].isLoading()) return true; - } - return false; - } - - @Override - public long reload() { - boolean changed = false; - for (int i = 0, n = mSets.length; i < n; ++i) { - long version = mSets[i].reload(); - if (version > mDataVersion) changed = true; - } - if (changed) mDataVersion = nextVersionNumber(); - return mDataVersion; - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - @Override - public Future<Integer> requestSync(SyncListener listener) { - return requestSyncOnMultipleSets(mSets, listener); - } -} diff --git a/src/com/android/gallery3d/data/ComboSource.java b/src/com/android/gallery3d/data/ComboSource.java deleted file mode 100644 index 867d47e64..000000000 --- a/src/com/android/gallery3d/data/ComboSource.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 com.android.gallery3d.app.GalleryApp; - -class ComboSource extends MediaSource { - private static final int COMBO_ALBUMSET = 0; - private static final int COMBO_ALBUM = 1; - private GalleryApp mApplication; - private PathMatcher mMatcher; - - public ComboSource(GalleryApp application) { - super("combo"); - mApplication = application; - mMatcher = new PathMatcher(); - mMatcher.add("/combo/*", COMBO_ALBUMSET); - mMatcher.add("/combo/*/*", COMBO_ALBUM); - } - - // The only path we accept is "/combo/{set1, set2, ...} and /combo/item/{set1, set2, ...}" - @Override - public MediaObject createMediaObject(Path path) { - String[] segments = path.split(); - if (segments.length < 2) { - throw new RuntimeException("bad path: " + path); - } - - DataManager dataManager = mApplication.getDataManager(); - switch (mMatcher.match(path)) { - case COMBO_ALBUMSET: - return new ComboAlbumSet(path, mApplication, - dataManager.getMediaSetsFromString(segments[1])); - - case COMBO_ALBUM: - return new ComboAlbum(path, - dataManager.getMediaSetsFromString(segments[2]), segments[1]); - } - return null; - } -} diff --git a/src/com/android/gallery3d/data/ContentListener.java b/src/com/android/gallery3d/data/ContentListener.java deleted file mode 100644 index 5e2952685..000000000 --- a/src/com/android/gallery3d/data/ContentListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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; - -public interface ContentListener { - public void onContentDirty(); -}
\ No newline at end of file diff --git a/src/com/android/gallery3d/data/DataManager.java b/src/com/android/gallery3d/data/DataManager.java deleted file mode 100644 index 38865e9f1..000000000 --- a/src/com/android/gallery3d/data/DataManager.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * 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.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.app.StitchingChangeListener; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback; -import com.android.gallery3d.data.MediaSet.ItemConsumer; -import com.android.gallery3d.data.MediaSource.PathId; -import com.android.gallery3d.picasasource.PicasaSource; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map.Entry; -import java.util.WeakHashMap; - -// DataManager manages all media sets and media items in the system. -// -// Each MediaSet and MediaItem has a unique 64 bits id. The most significant -// 32 bits represents its parent, and the least significant 32 bits represents -// the self id. For MediaSet the self id is is globally unique, but for -// MediaItem it's unique only relative to its parent. -// -// To make sure the id is the same when the MediaSet is re-created, a child key -// is provided to obtainSetId() to make sure the same self id will be used as -// when the parent and key are the same. A sequence of child keys is called a -// path. And it's used to identify a specific media set even if the process is -// killed and re-created, so child keys should be stable identifiers. - -public class DataManager implements StitchingChangeListener { - public static final int INCLUDE_IMAGE = 1; - public static final int INCLUDE_VIDEO = 2; - public static final int INCLUDE_ALL = INCLUDE_IMAGE | INCLUDE_VIDEO; - public static final int INCLUDE_LOCAL_ONLY = 4; - public static final int INCLUDE_LOCAL_IMAGE_ONLY = - INCLUDE_LOCAL_ONLY | INCLUDE_IMAGE; - public static final int INCLUDE_LOCAL_VIDEO_ONLY = - INCLUDE_LOCAL_ONLY | INCLUDE_VIDEO; - public static final int INCLUDE_LOCAL_ALL_ONLY = - INCLUDE_LOCAL_ONLY | INCLUDE_IMAGE | INCLUDE_VIDEO; - - // Any one who would like to access data should require this lock - // to prevent concurrency issue. - public static final Object LOCK = new Object(); - - public static DataManager from(Context context) { - GalleryApp app = (GalleryApp) context.getApplicationContext(); - return app.getDataManager(); - } - - private static final String TAG = "DataManager"; - - // This is the path for the media set seen by the user at top level. - private static final String TOP_SET_PATH = "/combo/{/local/all,/picasa/all}"; - - private static final String TOP_IMAGE_SET_PATH = "/combo/{/local/image,/picasa/image}"; - - private static final String TOP_VIDEO_SET_PATH = - "/combo/{/local/video,/picasa/video}"; - - private static final String TOP_LOCAL_SET_PATH = "/local/all"; - - private static final String TOP_LOCAL_IMAGE_SET_PATH = "/local/image"; - - private static final String TOP_LOCAL_VIDEO_SET_PATH = "/local/video"; - - public static final Comparator<MediaItem> sDateTakenComparator = - new DateTakenComparator(); - - private static class DateTakenComparator implements Comparator<MediaItem> { - @Override - public int compare(MediaItem item1, MediaItem item2) { - return -Utils.compare(item1.getDateInMs(), item2.getDateInMs()); - } - } - - private final Handler mDefaultMainHandler; - - private GalleryApp mApplication; - private int mActiveCount = 0; - - private HashMap<Uri, NotifyBroker> mNotifierMap = - new HashMap<Uri, NotifyBroker>(); - - - private HashMap<String, MediaSource> mSourceMap = - new LinkedHashMap<String, MediaSource>(); - - public DataManager(GalleryApp application) { - mApplication = application; - mDefaultMainHandler = new Handler(application.getMainLooper()); - } - - public synchronized void initializeSourceMap() { - if (!mSourceMap.isEmpty()) return; - - // the order matters, the UriSource must come last - addSource(new LocalSource(mApplication)); - addSource(new PicasaSource(mApplication)); - addSource(new ComboSource(mApplication)); - addSource(new ClusterSource(mApplication)); - addSource(new FilterSource(mApplication)); - addSource(new SecureSource(mApplication)); - addSource(new UriSource(mApplication)); - addSource(new SnailSource(mApplication)); - - if (mActiveCount > 0) { - for (MediaSource source : mSourceMap.values()) { - source.resume(); - } - } - } - - public String getTopSetPath(int typeBits) { - - switch (typeBits) { - case INCLUDE_IMAGE: return TOP_IMAGE_SET_PATH; - case INCLUDE_VIDEO: return TOP_VIDEO_SET_PATH; - case INCLUDE_ALL: return TOP_SET_PATH; - case INCLUDE_LOCAL_IMAGE_ONLY: return TOP_LOCAL_IMAGE_SET_PATH; - case INCLUDE_LOCAL_VIDEO_ONLY: return TOP_LOCAL_VIDEO_SET_PATH; - case INCLUDE_LOCAL_ALL_ONLY: return TOP_LOCAL_SET_PATH; - default: throw new IllegalArgumentException(); - } - } - - // open for debug - void addSource(MediaSource source) { - if (source == null) return; - mSourceMap.put(source.getPrefix(), source); - } - - // A common usage of this method is: - // synchronized (DataManager.LOCK) { - // MediaObject object = peekMediaObject(path); - // if (object == null) { - // object = createMediaObject(...); - // } - // } - public MediaObject peekMediaObject(Path path) { - return path.getObject(); - } - - public MediaObject getMediaObject(Path path) { - synchronized (LOCK) { - MediaObject obj = path.getObject(); - if (obj != null) return obj; - - MediaSource source = mSourceMap.get(path.getPrefix()); - if (source == null) { - Log.w(TAG, "cannot find media source for path: " + path); - return null; - } - - try { - MediaObject object = source.createMediaObject(path); - if (object == null) { - Log.w(TAG, "cannot create media object: " + path); - } - return object; - } catch (Throwable t) { - Log.w(TAG, "exception in creating media object: " + path, t); - return null; - } - } - } - - public MediaObject getMediaObject(String s) { - return getMediaObject(Path.fromString(s)); - } - - public MediaSet getMediaSet(Path path) { - return (MediaSet) getMediaObject(path); - } - - public MediaSet getMediaSet(String s) { - return (MediaSet) getMediaObject(s); - } - - public MediaSet[] getMediaSetsFromString(String segment) { - String[] seq = Path.splitSequence(segment); - int n = seq.length; - MediaSet[] sets = new MediaSet[n]; - for (int i = 0; i < n; i++) { - sets[i] = getMediaSet(seq[i]); - } - return sets; - } - - // Maps a list of Paths to MediaItems, and invoke consumer.consume() - // for each MediaItem (may not be in the same order as the input list). - // An index number is also passed to consumer.consume() to identify - // the original position in the input list of the corresponding Path (plus - // startIndex). - public void mapMediaItems(ArrayList<Path> list, ItemConsumer consumer, - int startIndex) { - HashMap<String, ArrayList<PathId>> map = - new HashMap<String, ArrayList<PathId>>(); - - // Group the path by the prefix. - int n = list.size(); - for (int i = 0; i < n; i++) { - Path path = list.get(i); - String prefix = path.getPrefix(); - ArrayList<PathId> group = map.get(prefix); - if (group == null) { - group = new ArrayList<PathId>(); - map.put(prefix, group); - } - group.add(new PathId(path, i + startIndex)); - } - - // For each group, ask the corresponding media source to map it. - for (Entry<String, ArrayList<PathId>> entry : map.entrySet()) { - String prefix = entry.getKey(); - MediaSource source = mSourceMap.get(prefix); - source.mapMediaItems(entry.getValue(), consumer); - } - } - - // The following methods forward the request to the proper object. - public int getSupportedOperations(Path path) { - return getMediaObject(path).getSupportedOperations(); - } - - public void getPanoramaSupport(Path path, PanoramaSupportCallback callback) { - getMediaObject(path).getPanoramaSupport(callback); - } - - public void delete(Path path) { - getMediaObject(path).delete(); - } - - public void rotate(Path path, int degrees) { - getMediaObject(path).rotate(degrees); - } - - public Uri getContentUri(Path path) { - return getMediaObject(path).getContentUri(); - } - - public int getMediaType(Path path) { - return getMediaObject(path).getMediaType(); - } - - public Path findPathByUri(Uri uri, String type) { - if (uri == null) return null; - for (MediaSource source : mSourceMap.values()) { - Path path = source.findPathByUri(uri, type); - if (path != null) return path; - } - return null; - } - - public Path getDefaultSetOf(Path item) { - MediaSource source = mSourceMap.get(item.getPrefix()); - return source == null ? null : source.getDefaultSetOf(item); - } - - // Returns number of bytes used by cached pictures currently downloaded. - public long getTotalUsedCacheSize() { - long sum = 0; - for (MediaSource source : mSourceMap.values()) { - sum += source.getTotalUsedCacheSize(); - } - return sum; - } - - // Returns number of bytes used by cached pictures if all pending - // downloads and removals are completed. - public long getTotalTargetCacheSize() { - long sum = 0; - for (MediaSource source : mSourceMap.values()) { - sum += source.getTotalTargetCacheSize(); - } - return sum; - } - - public void registerChangeNotifier(Uri uri, ChangeNotifier notifier) { - NotifyBroker broker = null; - synchronized (mNotifierMap) { - broker = mNotifierMap.get(uri); - if (broker == null) { - broker = new NotifyBroker(mDefaultMainHandler); - mApplication.getContentResolver() - .registerContentObserver(uri, true, broker); - mNotifierMap.put(uri, broker); - } - } - broker.registerNotifier(notifier); - } - - public void resume() { - if (++mActiveCount == 1) { - for (MediaSource source : mSourceMap.values()) { - source.resume(); - } - } - } - - public void pause() { - if (--mActiveCount == 0) { - for (MediaSource source : mSourceMap.values()) { - source.pause(); - } - } - } - - private static class NotifyBroker extends ContentObserver { - private WeakHashMap<ChangeNotifier, Object> mNotifiers = - new WeakHashMap<ChangeNotifier, Object>(); - - public NotifyBroker(Handler handler) { - super(handler); - } - - public synchronized void registerNotifier(ChangeNotifier notifier) { - mNotifiers.put(notifier, null); - } - - @Override - public synchronized void onChange(boolean selfChange) { - for(ChangeNotifier notifier : mNotifiers.keySet()) { - notifier.onChange(selfChange); - } - } - } - - @Override - public void onStitchingQueued(Uri uri) { - // Do nothing. - } - - @Override - public void onStitchingResult(Uri uri) { - Path path = findPathByUri(uri, null); - if (path != null) { - MediaObject mediaObject = getMediaObject(path); - if (mediaObject != null) { - mediaObject.clearCachedPanoramaSupport(); - } - } - } - - @Override - public void onStitchingProgress(Uri uri, int progress) { - // Do nothing. - } -} diff --git a/src/com/android/gallery3d/data/DataSourceType.java b/src/com/android/gallery3d/data/DataSourceType.java deleted file mode 100644 index ab534d0c3..000000000 --- a/src/com/android/gallery3d/data/DataSourceType.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 com.android.gallery3d.util.MediaSetUtils; - -public final class DataSourceType { - public static final int TYPE_NOT_CATEGORIZED = 0; - public static final int TYPE_LOCAL = 1; - public static final int TYPE_PICASA = 2; - public static final int TYPE_CAMERA = 3; - - private static final Path PICASA_ROOT = Path.fromString("/picasa"); - private static final Path LOCAL_ROOT = Path.fromString("/local"); - - public static int identifySourceType(MediaSet set) { - if (set == null) { - return TYPE_NOT_CATEGORIZED; - } - - Path path = set.getPath(); - if (MediaSetUtils.isCameraSource(path)) return TYPE_CAMERA; - - Path prefix = path.getPrefixPath(); - - if (prefix == PICASA_ROOT) return TYPE_PICASA; - if (prefix == LOCAL_ROOT) return TYPE_LOCAL; - - return TYPE_NOT_CATEGORIZED; - } -} diff --git a/src/com/android/gallery3d/data/DecodeUtils.java b/src/com/android/gallery3d/data/DecodeUtils.java deleted file mode 100644 index fa709157d..000000000 --- a/src/com/android/gallery3d/data/DecodeUtils.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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.annotation.TargetApi; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; -import android.graphics.BitmapFactory.Options; -import android.graphics.BitmapRegionDecoder; -import android.os.Build; -import android.util.FloatMath; - -import com.android.gallery3d.common.ApiHelper; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.common.Utils; -import com.android.photos.data.GalleryBitmapPool; -import com.android.gallery3d.ui.Log; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.InputStream; - -public class DecodeUtils { - private static final String TAG = "DecodeUtils"; - - private static class DecodeCanceller implements CancelListener { - Options mOptions; - - public DecodeCanceller(Options options) { - mOptions = options; - } - - @Override - public void onCancel() { - mOptions.requestCancelDecode(); - } - } - - @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) - public static void setOptionsMutable(Options options) { - if (ApiHelper.HAS_OPTIONS_IN_MUTABLE) options.inMutable = true; - } - - public static Bitmap decode(JobContext jc, FileDescriptor fd, Options options) { - if (options == null) options = new Options(); - jc.setCancelListener(new DecodeCanceller(options)); - setOptionsMutable(options); - return ensureGLCompatibleBitmap( - BitmapFactory.decodeFileDescriptor(fd, null, options)); - } - - public static void decodeBounds(JobContext jc, FileDescriptor fd, - Options options) { - Utils.assertTrue(options != null); - options.inJustDecodeBounds = true; - jc.setCancelListener(new DecodeCanceller(options)); - BitmapFactory.decodeFileDescriptor(fd, null, options); - options.inJustDecodeBounds = false; - } - - public static Bitmap decode(JobContext jc, byte[] bytes, Options options) { - return decode(jc, bytes, 0, bytes.length, options); - } - - public static Bitmap decode(JobContext jc, byte[] bytes, int offset, - int length, Options options) { - if (options == null) options = new Options(); - jc.setCancelListener(new DecodeCanceller(options)); - setOptionsMutable(options); - return ensureGLCompatibleBitmap( - BitmapFactory.decodeByteArray(bytes, offset, length, options)); - } - - public static void decodeBounds(JobContext jc, byte[] bytes, int offset, - int length, Options options) { - Utils.assertTrue(options != null); - options.inJustDecodeBounds = true; - jc.setCancelListener(new DecodeCanceller(options)); - BitmapFactory.decodeByteArray(bytes, offset, length, options); - options.inJustDecodeBounds = false; - } - - public static Bitmap decodeThumbnail( - JobContext jc, String filePath, Options options, int targetSize, int type) { - FileInputStream fis = null; - try { - fis = new FileInputStream(filePath); - FileDescriptor fd = fis.getFD(); - return decodeThumbnail(jc, fd, options, targetSize, type); - } catch (Exception ex) { - Log.w(TAG, ex); - return null; - } finally { - Utils.closeSilently(fis); - } - } - - public static Bitmap decodeThumbnail( - JobContext jc, FileDescriptor fd, Options options, int targetSize, int type) { - if (options == null) options = new Options(); - jc.setCancelListener(new DecodeCanceller(options)); - - options.inJustDecodeBounds = true; - BitmapFactory.decodeFileDescriptor(fd, null, options); - if (jc.isCancelled()) return null; - - int w = options.outWidth; - int h = options.outHeight; - - if (type == MediaItem.TYPE_MICROTHUMBNAIL) { - // We center-crop the original image as it's micro thumbnail. In this case, - // we want to make sure the shorter side >= "targetSize". - float scale = (float) targetSize / Math.min(w, h); - options.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); - - // For an extremely wide image, e.g. 300x30000, we may got OOM when decoding - // it for TYPE_MICROTHUMBNAIL. So we add a max number of pixels limit here. - final int MAX_PIXEL_COUNT = 640000; // 400 x 1600 - if ((w / options.inSampleSize) * (h / options.inSampleSize) > MAX_PIXEL_COUNT) { - options.inSampleSize = BitmapUtils.computeSampleSize( - FloatMath.sqrt((float) MAX_PIXEL_COUNT / (w * h))); - } - } else { - // For screen nail, we only want to keep the longer side >= targetSize. - float scale = (float) targetSize / Math.max(w, h); - options.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); - } - - options.inJustDecodeBounds = false; - setOptionsMutable(options); - - Bitmap result = BitmapFactory.decodeFileDescriptor(fd, null, options); - if (result == null) return null; - - // We need to resize down if the decoder does not support inSampleSize - // (For example, GIF images) - float scale = (float) targetSize / (type == MediaItem.TYPE_MICROTHUMBNAIL - ? Math.min(result.getWidth(), result.getHeight()) - : Math.max(result.getWidth(), result.getHeight())); - - if (scale <= 0.5) result = BitmapUtils.resizeBitmapByScale(result, scale, true); - return ensureGLCompatibleBitmap(result); - } - - /** - * Decodes the bitmap from the given byte array if the image size is larger than the given - * requirement. - * - * Note: The returned image may be resized down. However, both width and height must be - * larger than the <code>targetSize</code>. - */ - public static Bitmap decodeIfBigEnough(JobContext jc, byte[] data, - Options options, int targetSize) { - if (options == null) options = new Options(); - jc.setCancelListener(new DecodeCanceller(options)); - - options.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(data, 0, data.length, options); - if (jc.isCancelled()) return null; - if (options.outWidth < targetSize || options.outHeight < targetSize) { - return null; - } - options.inSampleSize = BitmapUtils.computeSampleSizeLarger( - options.outWidth, options.outHeight, targetSize); - options.inJustDecodeBounds = false; - setOptionsMutable(options); - - return ensureGLCompatibleBitmap( - BitmapFactory.decodeByteArray(data, 0, data.length, options)); - } - - // TODO: This function should not be called directly from - // DecodeUtils.requestDecode(...), since we don't have the knowledge - // if the bitmap will be uploaded to GL. - public static Bitmap ensureGLCompatibleBitmap(Bitmap bitmap) { - if (bitmap == null || bitmap.getConfig() != null) return bitmap; - Bitmap newBitmap = bitmap.copy(Config.ARGB_8888, false); - bitmap.recycle(); - return newBitmap; - } - - public static BitmapRegionDecoder createBitmapRegionDecoder( - JobContext jc, byte[] bytes, int offset, int length, - boolean shareable) { - if (offset < 0 || length <= 0 || offset + length > bytes.length) { - throw new IllegalArgumentException(String.format( - "offset = %s, length = %s, bytes = %s", - offset, length, bytes.length)); - } - - try { - return BitmapRegionDecoder.newInstance( - bytes, offset, length, shareable); - } catch (Throwable t) { - Log.w(TAG, t); - return null; - } - } - - public static BitmapRegionDecoder createBitmapRegionDecoder( - JobContext jc, String filePath, boolean shareable) { - try { - return BitmapRegionDecoder.newInstance(filePath, shareable); - } catch (Throwable t) { - Log.w(TAG, t); - return null; - } - } - - public static BitmapRegionDecoder createBitmapRegionDecoder( - JobContext jc, FileDescriptor fd, boolean shareable) { - try { - return BitmapRegionDecoder.newInstance(fd, shareable); - } catch (Throwable t) { - Log.w(TAG, t); - return null; - } - } - - public static BitmapRegionDecoder createBitmapRegionDecoder( - JobContext jc, InputStream is, boolean shareable) { - try { - return BitmapRegionDecoder.newInstance(is, shareable); - } catch (Throwable t) { - // We often cancel the creating of bitmap region decoder, - // so just log one line. - Log.w(TAG, "requestCreateBitmapRegionDecoder: " + t); - return null; - } - } - - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static Bitmap decodeUsingPool(JobContext jc, byte[] data, int offset, - int length, BitmapFactory.Options options) { - if (options == null) options = new BitmapFactory.Options(); - if (options.inSampleSize < 1) options.inSampleSize = 1; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - options.inBitmap = (options.inSampleSize == 1) - ? findCachedBitmap(jc, data, offset, length, options) : null; - try { - Bitmap bitmap = decode(jc, data, offset, length, options); - if (options.inBitmap != null && options.inBitmap != bitmap) { - GalleryBitmapPool.getInstance().put(options.inBitmap); - options.inBitmap = null; - } - return bitmap; - } catch (IllegalArgumentException e) { - if (options.inBitmap == null) throw e; - - Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap"); - GalleryBitmapPool.getInstance().put(options.inBitmap); - options.inBitmap = null; - return decode(jc, data, offset, length, options); - } - } - - // This is the same as the method above except the source data comes - // from a file descriptor instead of a byte array. - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static Bitmap decodeUsingPool(JobContext jc, - FileDescriptor fileDescriptor, Options options) { - if (options == null) options = new BitmapFactory.Options(); - if (options.inSampleSize < 1) options.inSampleSize = 1; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - options.inBitmap = (options.inSampleSize == 1) - ? findCachedBitmap(jc, fileDescriptor, options) : null; - try { - Bitmap bitmap = DecodeUtils.decode(jc, fileDescriptor, options); - if (options.inBitmap != null && options.inBitmap != bitmap) { - GalleryBitmapPool.getInstance().put(options.inBitmap); - options.inBitmap = null; - } - return bitmap; - } catch (IllegalArgumentException e) { - if (options.inBitmap == null) throw e; - - Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap"); - GalleryBitmapPool.getInstance().put(options.inBitmap); - options.inBitmap = null; - return decode(jc, fileDescriptor, options); - } - } - - private static Bitmap findCachedBitmap(JobContext jc, byte[] data, - int offset, int length, Options options) { - decodeBounds(jc, data, offset, length, options); - return GalleryBitmapPool.getInstance().get(options.outWidth, options.outHeight); - } - - private static Bitmap findCachedBitmap(JobContext jc, FileDescriptor fileDescriptor, - Options options) { - decodeBounds(jc, fileDescriptor, options); - return GalleryBitmapPool.getInstance().get(options.outWidth, options.outHeight); - } -} diff --git a/src/com/android/gallery3d/data/DownloadCache.java b/src/com/android/gallery3d/data/DownloadCache.java deleted file mode 100644 index be7820b01..000000000 --- a/src/com/android/gallery3d/data/DownloadCache.java +++ /dev/null @@ -1,370 +0,0 @@ -/* - * 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.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.LruCache; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.data.DownloadEntry.Columns; -import com.android.gallery3d.util.Future; -import com.android.gallery3d.util.FutureListener; -import com.android.gallery3d.util.ThreadPool; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.io.File; -import java.net.URL; -import java.util.HashMap; -import java.util.HashSet; - -public class DownloadCache { - private static final String TAG = "DownloadCache"; - private static final int MAX_DELETE_COUNT = 16; - private static final int LRU_CAPACITY = 4; - - private static final String TABLE_NAME = DownloadEntry.SCHEMA.getTableName(); - - private static final String QUERY_PROJECTION[] = {Columns.ID, Columns.DATA}; - private static final String WHERE_HASH_AND_URL = String.format( - "%s = ? AND %s = ?", Columns.HASH_CODE, Columns.CONTENT_URL); - private static final int QUERY_INDEX_ID = 0; - private static final int QUERY_INDEX_DATA = 1; - - private static final String FREESPACE_PROJECTION[] = { - Columns.ID, Columns.DATA, Columns.CONTENT_URL, Columns.CONTENT_SIZE}; - private static final String FREESPACE_ORDER_BY = - String.format("%s ASC", Columns.LAST_ACCESS); - private static final int FREESPACE_IDNEX_ID = 0; - private static final int FREESPACE_IDNEX_DATA = 1; - private static final int FREESPACE_INDEX_CONTENT_URL = 2; - private static final int FREESPACE_INDEX_CONTENT_SIZE = 3; - - private static final String ID_WHERE = Columns.ID + " = ?"; - - private static final String SUM_PROJECTION[] = - {String.format("sum(%s)", Columns.CONTENT_SIZE)}; - private static final int SUM_INDEX_SUM = 0; - - private final LruCache<String, Entry> mEntryMap = - new LruCache<String, Entry>(LRU_CAPACITY); - private final HashMap<String, DownloadTask> mTaskMap = - new HashMap<String, DownloadTask>(); - private final File mRoot; - private final GalleryApp mApplication; - private final SQLiteDatabase mDatabase; - private final long mCapacity; - - private long mTotalBytes = 0; - private boolean mInitialized = false; - - public DownloadCache(GalleryApp application, File root, long capacity) { - mRoot = Utils.checkNotNull(root); - mApplication = Utils.checkNotNull(application); - mCapacity = capacity; - mDatabase = new DatabaseHelper(application.getAndroidContext()) - .getWritableDatabase(); - } - - private Entry findEntryInDatabase(String stringUrl) { - long hash = Utils.crc64Long(stringUrl); - String whereArgs[] = {String.valueOf(hash), stringUrl}; - Cursor cursor = mDatabase.query(TABLE_NAME, QUERY_PROJECTION, - WHERE_HASH_AND_URL, whereArgs, null, null, null); - try { - if (cursor.moveToNext()) { - File file = new File(cursor.getString(QUERY_INDEX_DATA)); - long id = cursor.getInt(QUERY_INDEX_ID); - Entry entry = null; - synchronized (mEntryMap) { - entry = mEntryMap.get(stringUrl); - if (entry == null) { - entry = new Entry(id, file); - mEntryMap.put(stringUrl, entry); - } - } - return entry; - } - } finally { - cursor.close(); - } - return null; - } - - public Entry download(JobContext jc, URL url) { - if (!mInitialized) initialize(); - - String stringUrl = url.toString(); - - // First find in the entry-pool - synchronized (mEntryMap) { - Entry entry = mEntryMap.get(stringUrl); - if (entry != null) { - updateLastAccess(entry.mId); - return entry; - } - } - - // Then, find it in database - TaskProxy proxy = new TaskProxy(); - synchronized (mTaskMap) { - Entry entry = findEntryInDatabase(stringUrl); - if (entry != null) { - updateLastAccess(entry.mId); - return entry; - } - - // Finally, we need to download the file .... - // First check if we are downloading it now ... - DownloadTask task = mTaskMap.get(stringUrl); - if (task == null) { // if not, start the download task now - task = new DownloadTask(stringUrl); - mTaskMap.put(stringUrl, task); - task.mFuture = mApplication.getThreadPool().submit(task, task); - } - task.addProxy(proxy); - } - - return proxy.get(jc); - } - - private void updateLastAccess(long id) { - ContentValues values = new ContentValues(); - values.put(Columns.LAST_ACCESS, System.currentTimeMillis()); - mDatabase.update(TABLE_NAME, values, - ID_WHERE, new String[] {String.valueOf(id)}); - } - - private synchronized void freeSomeSpaceIfNeed(int maxDeleteFileCount) { - if (mTotalBytes <= mCapacity) return; - Cursor cursor = mDatabase.query(TABLE_NAME, - FREESPACE_PROJECTION, null, null, null, null, FREESPACE_ORDER_BY); - try { - while (maxDeleteFileCount > 0 - && mTotalBytes > mCapacity && cursor.moveToNext()) { - long id = cursor.getLong(FREESPACE_IDNEX_ID); - String url = cursor.getString(FREESPACE_INDEX_CONTENT_URL); - long size = cursor.getLong(FREESPACE_INDEX_CONTENT_SIZE); - String path = cursor.getString(FREESPACE_IDNEX_DATA); - boolean containsKey; - synchronized (mEntryMap) { - containsKey = mEntryMap.containsKey(url); - } - if (!containsKey) { - --maxDeleteFileCount; - mTotalBytes -= size; - new File(path).delete(); - mDatabase.delete(TABLE_NAME, - ID_WHERE, new String[]{String.valueOf(id)}); - } else { - // skip delete, since it is being used - } - } - } finally { - cursor.close(); - } - } - - private synchronized long insertEntry(String url, File file) { - long size = file.length(); - mTotalBytes += size; - - ContentValues values = new ContentValues(); - String hashCode = String.valueOf(Utils.crc64Long(url)); - values.put(Columns.DATA, file.getAbsolutePath()); - values.put(Columns.HASH_CODE, hashCode); - values.put(Columns.CONTENT_URL, url); - values.put(Columns.CONTENT_SIZE, size); - values.put(Columns.LAST_UPDATED, System.currentTimeMillis()); - return mDatabase.insert(TABLE_NAME, "", values); - } - - private synchronized void initialize() { - if (mInitialized) return; - mInitialized = true; - if (!mRoot.isDirectory()) mRoot.mkdirs(); - if (!mRoot.isDirectory()) { - throw new RuntimeException("cannot create " + mRoot.getAbsolutePath()); - } - - Cursor cursor = mDatabase.query( - TABLE_NAME, SUM_PROJECTION, null, null, null, null, null); - mTotalBytes = 0; - try { - if (cursor.moveToNext()) { - mTotalBytes = cursor.getLong(SUM_INDEX_SUM); - } - } finally { - cursor.close(); - } - if (mTotalBytes > mCapacity) freeSomeSpaceIfNeed(MAX_DELETE_COUNT); - } - - private final class DatabaseHelper extends SQLiteOpenHelper { - public static final String DATABASE_NAME = "download.db"; - public static final int DATABASE_VERSION = 2; - - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - DownloadEntry.SCHEMA.createTables(db); - // Delete old files - for (File file : mRoot.listFiles()) { - if (!file.delete()) { - Log.w(TAG, "fail to remove: " + file.getAbsolutePath()); - } - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - //reset everything - DownloadEntry.SCHEMA.dropTables(db); - onCreate(db); - } - } - - public class Entry { - public File cacheFile; - protected long mId; - - Entry(long id, File cacheFile) { - mId = id; - this.cacheFile = Utils.checkNotNull(cacheFile); - } - } - - private class DownloadTask implements Job<File>, FutureListener<File> { - private HashSet<TaskProxy> mProxySet = new HashSet<TaskProxy>(); - private Future<File> mFuture; - private final String mUrl; - - public DownloadTask(String url) { - mUrl = Utils.checkNotNull(url); - } - - public void removeProxy(TaskProxy proxy) { - synchronized (mTaskMap) { - Utils.assertTrue(mProxySet.remove(proxy)); - if (mProxySet.isEmpty()) { - mFuture.cancel(); - mTaskMap.remove(mUrl); - } - } - } - - // should be used in synchronized block of mDatabase - public void addProxy(TaskProxy proxy) { - proxy.mTask = this; - mProxySet.add(proxy); - } - - @Override - public void onFutureDone(Future<File> future) { - File file = future.get(); - long id = 0; - if (file != null) { // insert to database - id = insertEntry(mUrl, file); - } - - if (future.isCancelled()) { - Utils.assertTrue(mProxySet.isEmpty()); - return; - } - - synchronized (mTaskMap) { - Entry entry = null; - synchronized (mEntryMap) { - if (file != null) { - entry = new Entry(id, file); - Utils.assertTrue(mEntryMap.put(mUrl, entry) == null); - } - } - for (TaskProxy proxy : mProxySet) { - proxy.setResult(entry); - } - mTaskMap.remove(mUrl); - freeSomeSpaceIfNeed(MAX_DELETE_COUNT); - } - } - - @Override - public File run(JobContext jc) { - // TODO: utilize etag - jc.setMode(ThreadPool.MODE_NETWORK); - File tempFile = null; - try { - URL url = new URL(mUrl); - tempFile = File.createTempFile("cache", ".tmp", mRoot); - // download from url to tempFile - jc.setMode(ThreadPool.MODE_NETWORK); - boolean downloaded = DownloadUtils.requestDownload(jc, url, tempFile); - jc.setMode(ThreadPool.MODE_NONE); - if (downloaded) return tempFile; - } catch (Exception e) { - Log.e(TAG, String.format("fail to download %s", mUrl), e); - } finally { - jc.setMode(ThreadPool.MODE_NONE); - } - if (tempFile != null) tempFile.delete(); - return null; - } - } - - public static class TaskProxy { - private DownloadTask mTask; - private boolean mIsCancelled = false; - private Entry mEntry; - - synchronized void setResult(Entry entry) { - if (mIsCancelled) return; - mEntry = entry; - notifyAll(); - } - - public synchronized Entry get(JobContext jc) { - jc.setCancelListener(new CancelListener() { - @Override - public void onCancel() { - mTask.removeProxy(TaskProxy.this); - synchronized (TaskProxy.this) { - mIsCancelled = true; - TaskProxy.this.notifyAll(); - } - } - }); - while (!mIsCancelled && mEntry == null) { - try { - wait(); - } catch (InterruptedException e) { - Log.w(TAG, "ignore interrupt", e); - } - } - jc.setCancelListener(null); - return mEntry; - } - } -} diff --git a/src/com/android/gallery3d/data/DownloadEntry.java b/src/com/android/gallery3d/data/DownloadEntry.java deleted file mode 100644 index 578523f73..000000000 --- a/src/com/android/gallery3d/data/DownloadEntry.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 com.android.gallery3d.common.Entry; -import com.android.gallery3d.common.EntrySchema; - - -@Entry.Table("download") -public class DownloadEntry extends Entry { - public static final EntrySchema SCHEMA = new EntrySchema(DownloadEntry.class); - - public static interface Columns extends Entry.Columns { - public static final String HASH_CODE = "hash_code"; - public static final String CONTENT_URL = "content_url"; - public static final String CONTENT_SIZE = "_size"; - public static final String ETAG = "etag"; - public static final String LAST_ACCESS = "last_access"; - public static final String LAST_UPDATED = "last_updated"; - public static final String DATA = "_data"; - } - - @Column(value = "hash_code", indexed = true) - public long hashCode; - - @Column("content_url") - public String contentUrl; - - @Column("_size") - public long contentSize; - - @Column("etag") - public String eTag; - - @Column(value = "last_access", indexed = true) - public long lastAccessTime; - - @Column(value = "last_updated") - public long lastUpdatedTime; - - @Column("_data") - public String path; - - @Override - public String toString() { - // Note: THIS IS REQUIRED. We used all the fields here. Otherwise, - // ProGuard will remove these UNUSED fields. However, these - // fields are needed to generate database. - return new StringBuilder() - .append("hash_code: ").append(hashCode).append(", ") - .append("content_url").append(contentUrl).append(", ") - .append("_size").append(contentSize).append(", ") - .append("etag").append(eTag).append(", ") - .append("last_access").append(lastAccessTime).append(", ") - .append("last_updated").append(lastUpdatedTime).append(",") - .append("_data").append(path) - .toString(); - } -} diff --git a/src/com/android/gallery3d/data/DownloadUtils.java b/src/com/android/gallery3d/data/DownloadUtils.java deleted file mode 100644 index 137898e91..000000000 --- a/src/com/android/gallery3d/data/DownloadUtils.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.net.URL; - -public class DownloadUtils { - private static final String TAG = "DownloadService"; - - public static boolean requestDownload(JobContext jc, URL url, File file) { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(file); - return download(jc, url, fos); - } catch (Throwable t) { - return false; - } finally { - Utils.closeSilently(fos); - } - } - - public static void dump(JobContext jc, InputStream is, OutputStream os) - throws IOException { - byte buffer[] = new byte[4096]; - int rc = is.read(buffer, 0, buffer.length); - final Thread thread = Thread.currentThread(); - jc.setCancelListener(new CancelListener() { - @Override - public void onCancel() { - thread.interrupt(); - } - }); - while (rc > 0) { - if (jc.isCancelled()) throw new InterruptedIOException(); - os.write(buffer, 0, rc); - rc = is.read(buffer, 0, buffer.length); - } - jc.setCancelListener(null); - Thread.interrupted(); // consume the interrupt signal - } - - public static boolean download(JobContext jc, URL url, OutputStream output) { - InputStream input = null; - try { - input = url.openStream(); - dump(jc, input, output); - return true; - } catch (Throwable t) { - Log.w(TAG, "fail to download", t); - return false; - } finally { - Utils.closeSilently(input); - } - } -}
\ No newline at end of file diff --git a/src/com/android/gallery3d/data/EmptyAlbumImage.java b/src/com/android/gallery3d/data/EmptyAlbumImage.java deleted file mode 100644 index 6f8c37c6b..000000000 --- a/src/com/android/gallery3d/data/EmptyAlbumImage.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.gallery3d.R; -import com.android.gallery3d.app.GalleryApp; - -public class EmptyAlbumImage extends ActionImage { - @SuppressWarnings("unused") - private static final String TAG = "EmptyAlbumImage"; - - public EmptyAlbumImage(Path path, GalleryApp application) { - super(path, application, R.drawable.placeholder_empty); - } - - @Override - public int getSupportedOperations() { - return super.getSupportedOperations() | SUPPORT_BACK; - } -} diff --git a/src/com/android/gallery3d/data/Exif.java b/src/com/android/gallery3d/data/Exif.java deleted file mode 100644 index 950e7de18..000000000 --- a/src/com/android/gallery3d/data/Exif.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2011 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.util.Log; - -import com.android.gallery3d.exif.ExifInterface; - -import java.io.IOException; -import java.io.InputStream; - -public class Exif { - private static final String TAG = "CameraExif"; - - // Returns the degrees in clockwise. Values are 0, 90, 180, or 270. - public static int getOrientation(InputStream is) { - if (is == null) { - return 0; - } - ExifInterface exif = new ExifInterface(); - try { - exif.readExif(is); - Integer val = exif.getTagIntValue(ExifInterface.TAG_ORIENTATION); - if (val == null) { - return 0; - } else { - return ExifInterface.getRotationForOrientationValue(val.shortValue()); - } - } catch (IOException e) { - Log.w(TAG, "Failed to read EXIF orientation", e); - return 0; - } - } -} diff --git a/src/com/android/gallery3d/data/Face.java b/src/com/android/gallery3d/data/Face.java deleted file mode 100644 index d2dc22bfc..000000000 --- a/src/com/android/gallery3d/data/Face.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2011 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.graphics.Rect; - -import com.android.gallery3d.common.Utils; - -import java.util.StringTokenizer; - -public class Face implements Comparable<Face> { - private String mName; - private String mPersonId; - private Rect mPosition; - - public Face(String name, String personId, String rect) { - mName = name; - mPersonId = personId; - Utils.assertTrue(mName != null && mPersonId != null && rect != null); - StringTokenizer tokenizer = new StringTokenizer(rect); - mPosition = new Rect(); - while (tokenizer.hasMoreElements()) { - mPosition.left = Integer.parseInt(tokenizer.nextToken()); - mPosition.top = Integer.parseInt(tokenizer.nextToken()); - mPosition.right = Integer.parseInt(tokenizer.nextToken()); - mPosition.bottom = Integer.parseInt(tokenizer.nextToken()); - } - } - - public Rect getPosition() { - return mPosition; - } - - public String getName() { - return mName; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Face) { - Face face = (Face) obj; - return mPersonId.equals(face.mPersonId); - } - return false; - } - - @Override - public int compareTo(Face another) { - return mName.compareTo(another.mName); - } -} diff --git a/src/com/android/gallery3d/data/FaceClustering.java b/src/com/android/gallery3d/data/FaceClustering.java deleted file mode 100644 index 819915edb..000000000 --- a/src/com/android/gallery3d/data/FaceClustering.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2011 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.Context; -import android.graphics.Rect; - -import com.android.gallery3d.R; -import com.android.gallery3d.picasasource.PicasaSource; - -import java.util.ArrayList; -import java.util.TreeMap; - -public class FaceClustering extends Clustering { - @SuppressWarnings("unused") - private static final String TAG = "FaceClustering"; - - private FaceCluster[] mClusters; - private String mUntaggedString; - private Context mContext; - - private class FaceCluster { - ArrayList<Path> mPaths = new ArrayList<Path>(); - String mName; - MediaItem mCoverItem; - Rect mCoverRegion; - int mCoverFaceIndex; - - public FaceCluster(String name) { - mName = name; - } - - public void add(MediaItem item, int faceIndex) { - Path path = item.getPath(); - mPaths.add(path); - Face[] faces = item.getFaces(); - if (faces != null) { - Face face = faces[faceIndex]; - if (mCoverItem == null) { - mCoverItem = item; - mCoverRegion = face.getPosition(); - mCoverFaceIndex = faceIndex; - } else { - Rect region = face.getPosition(); - if (mCoverRegion.width() < region.width() && - mCoverRegion.height() < region.height()) { - mCoverItem = item; - mCoverRegion = face.getPosition(); - mCoverFaceIndex = faceIndex; - } - } - } - } - - public int size() { - return mPaths.size(); - } - - public MediaItem getCover() { - if (mCoverItem != null) { - if (PicasaSource.isPicasaImage(mCoverItem)) { - return PicasaSource.getFaceItem(mContext, mCoverItem, mCoverFaceIndex); - } else { - return mCoverItem; - } - } - return null; - } - } - - public FaceClustering(Context context) { - mUntaggedString = context.getResources().getString(R.string.untagged); - mContext = context; - } - - @Override - public void run(MediaSet baseSet) { - final TreeMap<Face, FaceCluster> map = - new TreeMap<Face, FaceCluster>(); - final FaceCluster untagged = new FaceCluster(mUntaggedString); - - baseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - Face[] faces = item.getFaces(); - if (faces == null || faces.length == 0) { - untagged.add(item, -1); - return; - } - for (int j = 0; j < faces.length; j++) { - Face face = faces[j]; - FaceCluster cluster = map.get(face); - if (cluster == null) { - cluster = new FaceCluster(face.getName()); - map.put(face, cluster); - } - cluster.add(item, j); - } - } - }); - - int m = map.size(); - mClusters = map.values().toArray(new FaceCluster[m + ((untagged.size() > 0) ? 1 : 0)]); - if (untagged.size() > 0) { - mClusters[m] = untagged; - } - } - - @Override - public int getNumberOfClusters() { - return mClusters.length; - } - - @Override - public ArrayList<Path> getCluster(int index) { - return mClusters[index].mPaths; - } - - @Override - public String getClusterName(int index) { - return mClusters[index].mName; - } - - @Override - public MediaItem getClusterCover(int index) { - return mClusters[index].getCover(); - } -} diff --git a/src/com/android/gallery3d/data/FilterDeleteSet.java b/src/com/android/gallery3d/data/FilterDeleteSet.java deleted file mode 100644 index c76412ff8..000000000 --- a/src/com/android/gallery3d/data/FilterDeleteSet.java +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.ArrayList; - -// FilterDeleteSet filters a base MediaSet to remove some deletion items (we -// expect the number to be small). The user can use the following methods to -// add/remove deletion items: -// -// void addDeletion(Path path, int index); -// void removeDelection(Path path); -// void clearDeletion(); -// int getNumberOfDeletions(); -// -public class FilterDeleteSet extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "FilterDeleteSet"; - - private static final int REQUEST_ADD = 1; - private static final int REQUEST_REMOVE = 2; - private static final int REQUEST_CLEAR = 3; - - private static class Request { - int type; // one of the REQUEST_* constants - Path path; - int indexHint; - public Request(int type, Path path, int indexHint) { - this.type = type; - this.path = path; - this.indexHint = indexHint; - } - } - - private static class Deletion { - Path path; - int index; - public Deletion(Path path, int index) { - this.path = path; - this.index = index; - } - } - - // The underlying MediaSet - private final MediaSet mBaseSet; - - // Pending Requests - private ArrayList<Request> mRequests = new ArrayList<Request>(); - - // Deletions currently in effect, ordered by index - private ArrayList<Deletion> mCurrent = new ArrayList<Deletion>(); - - public FilterDeleteSet(Path path, MediaSet baseSet) { - super(path, INVALID_DATA_VERSION); - mBaseSet = baseSet; - mBaseSet.addContentListener(this); - } - - @Override - public boolean isCameraRoll() { - return mBaseSet.isCameraRoll(); - } - - @Override - public String getName() { - return mBaseSet.getName(); - } - - @Override - public int getMediaItemCount() { - return mBaseSet.getMediaItemCount() - mCurrent.size(); - } - - // Gets the MediaItems whose (post-deletion) index are in the range [start, - // start + count). Because we remove some of the MediaItems, the index need - // to be adjusted. - // - // For example, if there are 12 items in total. The deleted items are 3, 5, - // 10, and the the requested range is [3, 7]: - // - // The original index: 0 1 2 3 4 5 6 7 8 9 A B C - // The deleted items: X X X - // The new index: 0 1 2 3 4 5 6 7 8 9 - // Requested: * * * * * - // - // We need to figure out the [3, 7] actually maps to the original index 4, - // 6, 7, 8, 9. - // - // We can break the MediaItems into segments, each segment other than the - // last one ends in a deleted item. The difference between the new index and - // the original index increases with each segment: - // - // 0 1 2 X (new index = old index) - // 4 X (new index = old index - 1) - // 6 7 8 9 X (new index = old index - 2) - // B C (new index = old index - 3) - // - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - if (count <= 0) return new ArrayList<MediaItem>(); - - int end = start + count - 1; - int n = mCurrent.size(); - // Find the segment that "start" falls into. Count the number of items - // not yet deleted until it reaches "start". - int i = 0; - for (i = 0; i < n; i++) { - Deletion d = mCurrent.get(i); - if (d.index - i > start) break; - } - // Find the segment that "end" falls into. - int j = i; - for (; j < n; j++) { - Deletion d = mCurrent.get(j); - if (d.index - j > end) break; - } - - // Now get enough to cover deleted items in [start, end] - ArrayList<MediaItem> base = mBaseSet.getMediaItem(start + i, count + (j - i)); - - // Remove the deleted items. - for (int m = j - 1; m >= i; m--) { - Deletion d = mCurrent.get(m); - int k = d.index - (start + i); - base.remove(k); - } - return base; - } - - // We apply the pending requests in the mRequests to construct mCurrent in reload(). - @Override - public long reload() { - boolean newData = mBaseSet.reload() > mDataVersion; - synchronized (mRequests) { - if (!newData && mRequests.isEmpty()) { - return mDataVersion; - } - for (int i = 0; i < mRequests.size(); i++) { - Request r = mRequests.get(i); - switch (r.type) { - case REQUEST_ADD: { - // Add the path into mCurrent if there is no duplicate. - int n = mCurrent.size(); - int j; - for (j = 0; j < n; j++) { - if (mCurrent.get(j).path == r.path) break; - } - if (j == n) { - mCurrent.add(new Deletion(r.path, r.indexHint)); - } - break; - } - case REQUEST_REMOVE: { - // Remove the path from mCurrent. - int n = mCurrent.size(); - for (int j = 0; j < n; j++) { - if (mCurrent.get(j).path == r.path) { - mCurrent.remove(j); - break; - } - } - break; - } - case REQUEST_CLEAR: { - mCurrent.clear(); - break; - } - } - } - mRequests.clear(); - } - - if (!mCurrent.isEmpty()) { - // See if the elements in mCurrent can be found in the MediaSet. We - // don't want to search the whole mBaseSet, so we just search a - // small window that contains the index hints (plus some margin). - int minIndex = mCurrent.get(0).index; - int maxIndex = minIndex; - for (int i = 1; i < mCurrent.size(); i++) { - Deletion d = mCurrent.get(i); - minIndex = Math.min(d.index, minIndex); - maxIndex = Math.max(d.index, maxIndex); - } - - int n = mBaseSet.getMediaItemCount(); - int from = Math.max(minIndex - 5, 0); - int to = Math.min(maxIndex + 5, n); - ArrayList<MediaItem> items = mBaseSet.getMediaItem(from, to - from); - ArrayList<Deletion> result = new ArrayList<Deletion>(); - for (int i = 0; i < items.size(); i++) { - MediaItem item = items.get(i); - if (item == null) continue; - Path p = item.getPath(); - // Find the matching path in mCurrent, if found move it to result - for (int j = 0; j < mCurrent.size(); j++) { - Deletion d = mCurrent.get(j); - if (d.path == p) { - d.index = from + i; - result.add(d); - mCurrent.remove(j); - break; - } - } - } - mCurrent = result; - } - - mDataVersion = nextVersionNumber(); - return mDataVersion; - } - - private void sendRequest(int type, Path path, int indexHint) { - Request r = new Request(type, path, indexHint); - synchronized (mRequests) { - mRequests.add(r); - } - notifyContentChanged(); - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - public void addDeletion(Path path, int indexHint) { - sendRequest(REQUEST_ADD, path, indexHint); - } - - public void removeDeletion(Path path) { - sendRequest(REQUEST_REMOVE, path, 0 /* unused */); - } - - public void clearDeletion() { - sendRequest(REQUEST_CLEAR, null /* unused */ , 0 /* unused */); - } - - // Returns number of deletions _in effect_ (the number will only gets - // updated after a reload()). - public int getNumberOfDeletions() { - return mCurrent.size(); - } -} diff --git a/src/com/android/gallery3d/data/FilterEmptyPromptSet.java b/src/com/android/gallery3d/data/FilterEmptyPromptSet.java deleted file mode 100644 index b576e06d4..000000000 --- a/src/com/android/gallery3d/data/FilterEmptyPromptSet.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.ArrayList; - -public class FilterEmptyPromptSet extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "FilterEmptyPromptSet"; - - private ArrayList<MediaItem> mEmptyItem; - private MediaSet mBaseSet; - - public FilterEmptyPromptSet(Path path, MediaSet baseSet, MediaItem emptyItem) { - super(path, INVALID_DATA_VERSION); - mEmptyItem = new ArrayList<MediaItem>(1); - mEmptyItem.add(emptyItem); - mBaseSet = baseSet; - mBaseSet.addContentListener(this); - } - - @Override - public int getMediaItemCount() { - int itemCount = mBaseSet.getMediaItemCount(); - if (itemCount > 0) { - return itemCount; - } else { - return 1; - } - } - - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - int itemCount = mBaseSet.getMediaItemCount(); - if (itemCount > 0) { - return mBaseSet.getMediaItem(start, count); - } else if (start == 0 && count == 1) { - return mEmptyItem; - } else { - throw new ArrayIndexOutOfBoundsException(); - } - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - @Override - public boolean isLeafAlbum() { - return true; - } - - @Override - public boolean isCameraRoll() { - return mBaseSet.isCameraRoll(); - } - - @Override - public long reload() { - return mBaseSet.reload(); - } - - @Override - public String getName() { - return mBaseSet.getName(); - } -} diff --git a/src/com/android/gallery3d/data/FilterSource.java b/src/com/android/gallery3d/data/FilterSource.java deleted file mode 100644 index d689fe336..000000000 --- a/src/com/android/gallery3d/data/FilterSource.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 com.android.gallery3d.app.GalleryApp; - -public class FilterSource extends MediaSource { - @SuppressWarnings("unused") - private static final String TAG = "FilterSource"; - private static final int FILTER_BY_MEDIATYPE = 0; - private static final int FILTER_BY_DELETE = 1; - private static final int FILTER_BY_EMPTY = 2; - private static final int FILTER_BY_EMPTY_ITEM = 3; - private static final int FILTER_BY_CAMERA_SHORTCUT = 4; - private static final int FILTER_BY_CAMERA_SHORTCUT_ITEM = 5; - - public static final String FILTER_EMPTY_ITEM = "/filter/empty_prompt"; - public static final String FILTER_CAMERA_SHORTCUT = "/filter/camera_shortcut"; - private static final String FILTER_CAMERA_SHORTCUT_ITEM = "/filter/camera_shortcut_item"; - - private GalleryApp mApplication; - private PathMatcher mMatcher; - private MediaItem mEmptyItem; - private MediaItem mCameraShortcutItem; - - public FilterSource(GalleryApp application) { - super("filter"); - mApplication = application; - mMatcher = new PathMatcher(); - mMatcher.add("/filter/mediatype/*/*", FILTER_BY_MEDIATYPE); - mMatcher.add("/filter/delete/*", FILTER_BY_DELETE); - mMatcher.add("/filter/empty/*", FILTER_BY_EMPTY); - mMatcher.add(FILTER_EMPTY_ITEM, FILTER_BY_EMPTY_ITEM); - mMatcher.add(FILTER_CAMERA_SHORTCUT, FILTER_BY_CAMERA_SHORTCUT); - mMatcher.add(FILTER_CAMERA_SHORTCUT_ITEM, FILTER_BY_CAMERA_SHORTCUT_ITEM); - - mEmptyItem = new EmptyAlbumImage(Path.fromString(FILTER_EMPTY_ITEM), - mApplication); - mCameraShortcutItem = new CameraShortcutImage( - Path.fromString(FILTER_CAMERA_SHORTCUT_ITEM), mApplication); - } - - // The name we accept are: - // /filter/mediatype/k/{set} where k is the media type we want. - // /filter/delete/{set} - @Override - public MediaObject createMediaObject(Path path) { - int matchType = mMatcher.match(path); - DataManager dataManager = mApplication.getDataManager(); - switch (matchType) { - case FILTER_BY_MEDIATYPE: { - int mediaType = mMatcher.getIntVar(0); - String setsName = mMatcher.getVar(1); - MediaSet[] sets = dataManager.getMediaSetsFromString(setsName); - return new FilterTypeSet(path, dataManager, sets[0], mediaType); - } - case FILTER_BY_DELETE: { - String setsName = mMatcher.getVar(0); - MediaSet[] sets = dataManager.getMediaSetsFromString(setsName); - return new FilterDeleteSet(path, sets[0]); - } - case FILTER_BY_EMPTY: { - String setsName = mMatcher.getVar(0); - MediaSet[] sets = dataManager.getMediaSetsFromString(setsName); - return new FilterEmptyPromptSet(path, sets[0], mEmptyItem); - } - case FILTER_BY_EMPTY_ITEM: { - return mEmptyItem; - } - case FILTER_BY_CAMERA_SHORTCUT: { - return new SingleItemAlbum(path, mCameraShortcutItem); - } - case FILTER_BY_CAMERA_SHORTCUT_ITEM: { - return mCameraShortcutItem; - } - default: - throw new RuntimeException("bad path: " + path); - } - } -} diff --git a/src/com/android/gallery3d/data/FilterTypeSet.java b/src/com/android/gallery3d/data/FilterTypeSet.java deleted file mode 100644 index 477ef73ad..000000000 --- a/src/com/android/gallery3d/data/FilterTypeSet.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 java.util.ArrayList; - -// FilterTypeSet filters a base MediaSet according to a matching media type. -public class FilterTypeSet extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "FilterTypeSet"; - - private final DataManager mDataManager; - private final MediaSet mBaseSet; - private final int mMediaType; - private final ArrayList<Path> mPaths = new ArrayList<Path>(); - private final ArrayList<MediaSet> mAlbums = new ArrayList<MediaSet>(); - - public FilterTypeSet(Path path, DataManager dataManager, MediaSet baseSet, - int mediaType) { - super(path, INVALID_DATA_VERSION); - mDataManager = dataManager; - mBaseSet = baseSet; - mMediaType = mediaType; - mBaseSet.addContentListener(this); - } - - @Override - public String getName() { - return mBaseSet.getName(); - } - - @Override - public MediaSet getSubMediaSet(int index) { - return mAlbums.get(index); - } - - @Override - public int getSubMediaSetCount() { - return mAlbums.size(); - } - - @Override - public int getMediaItemCount() { - return mPaths.size(); - } - - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - return ClusterAlbum.getMediaItemFromPath( - mPaths, start, count, mDataManager); - } - - @Override - public long reload() { - if (mBaseSet.reload() > mDataVersion) { - updateData(); - mDataVersion = nextVersionNumber(); - } - return mDataVersion; - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - private void updateData() { - // Albums - mAlbums.clear(); - String basePath = "/filter/mediatype/" + mMediaType; - - for (int i = 0, n = mBaseSet.getSubMediaSetCount(); i < n; i++) { - MediaSet set = mBaseSet.getSubMediaSet(i); - String filteredPath = basePath + "/{" + set.getPath().toString() + "}"; - MediaSet filteredSet = mDataManager.getMediaSet(filteredPath); - filteredSet.reload(); - if (filteredSet.getMediaItemCount() > 0 - || filteredSet.getSubMediaSetCount() > 0) { - mAlbums.add(filteredSet); - } - } - - // Items - mPaths.clear(); - final int total = mBaseSet.getMediaItemCount(); - final Path[] buf = new Path[total]; - - mBaseSet.enumerateMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - if (item.getMediaType() == mMediaType) { - if (index < 0 || index >= total) return; - Path path = item.getPath(); - buf[index] = path; - } - } - }); - - for (int i = 0; i < total; i++) { - if (buf[i] != null) { - mPaths.add(buf[i]); - } - } - } - - @Override - public int getSupportedOperations() { - return SUPPORT_SHARE | SUPPORT_DELETE; - } - - @Override - public void delete() { - ItemConsumer consumer = new ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - if ((item.getSupportedOperations() & SUPPORT_DELETE) != 0) { - item.delete(); - } - } - }; - mDataManager.mapMediaItems(mPaths, consumer, 0); - } -} diff --git a/src/com/android/gallery3d/data/ImageCacheRequest.java b/src/com/android/gallery3d/data/ImageCacheRequest.java deleted file mode 100644 index 6cbc5c5ea..000000000 --- a/src/com/android/gallery3d/data/ImageCacheRequest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.graphics.Bitmap; -import android.graphics.BitmapFactory; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.data.BytesBufferPool.BytesBuffer; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -abstract class ImageCacheRequest implements Job<Bitmap> { - private static final String TAG = "ImageCacheRequest"; - - protected GalleryApp mApplication; - private Path mPath; - private int mType; - private int mTargetSize; - private long mTimeModified; - - public ImageCacheRequest(GalleryApp application, - Path path, long timeModified, int type, int targetSize) { - mApplication = application; - mPath = path; - mType = type; - mTargetSize = targetSize; - mTimeModified = timeModified; - } - - private String debugTag() { - return mPath + "," + mTimeModified + "," + - ((mType == MediaItem.TYPE_THUMBNAIL) ? "THUMB" : - (mType == MediaItem.TYPE_MICROTHUMBNAIL) ? "MICROTHUMB" : "?"); - } - - @Override - public Bitmap run(JobContext jc) { - ImageCacheService cacheService = mApplication.getImageCacheService(); - - BytesBuffer buffer = MediaItem.getBytesBufferPool().get(); - try { - boolean found = cacheService.getImageData(mPath, mTimeModified, mType, buffer); - if (jc.isCancelled()) return null; - if (found) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - Bitmap bitmap; - if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = DecodeUtils.decodeUsingPool(jc, - buffer.data, buffer.offset, buffer.length, options); - } else { - bitmap = DecodeUtils.decodeUsingPool(jc, - buffer.data, buffer.offset, buffer.length, options); - } - if (bitmap == null && !jc.isCancelled()) { - Log.w(TAG, "decode cached failed " + debugTag()); - } - return bitmap; - } - } finally { - MediaItem.getBytesBufferPool().recycle(buffer); - } - Bitmap bitmap = onDecodeOriginal(jc, mType); - if (jc.isCancelled()) return null; - - if (bitmap == null) { - Log.w(TAG, "decode orig failed " + debugTag()); - return null; - } - - if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = BitmapUtils.resizeAndCropCenter(bitmap, mTargetSize, true); - } else { - bitmap = BitmapUtils.resizeDownBySideLength(bitmap, mTargetSize, true); - } - if (jc.isCancelled()) return null; - - byte[] array = BitmapUtils.compressToBytes(bitmap); - if (jc.isCancelled()) return null; - - cacheService.putImageData(mPath, mTimeModified, mType, array); - return bitmap; - } - - public abstract Bitmap onDecodeOriginal(JobContext jc, int targetSize); -} diff --git a/src/com/android/gallery3d/data/ImageCacheService.java b/src/com/android/gallery3d/data/ImageCacheService.java deleted file mode 100644 index 1c7cb8c5e..000000000 --- a/src/com/android/gallery3d/data/ImageCacheService.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.Context; - -import com.android.gallery3d.common.BlobCache; -import com.android.gallery3d.common.BlobCache.LookupRequest; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.data.BytesBufferPool.BytesBuffer; -import com.android.gallery3d.util.CacheManager; -import com.android.gallery3d.util.GalleryUtils; - -import java.io.IOException; -import java.nio.ByteBuffer; - -public class ImageCacheService { - @SuppressWarnings("unused") - private static final String TAG = "ImageCacheService"; - - private static final String IMAGE_CACHE_FILE = "imgcache"; - private static final int IMAGE_CACHE_MAX_ENTRIES = 5000; - private static final int IMAGE_CACHE_MAX_BYTES = 200 * 1024 * 1024; - private static final int IMAGE_CACHE_VERSION = 7; - - private BlobCache mCache; - - public ImageCacheService(Context context) { - mCache = CacheManager.getCache(context, IMAGE_CACHE_FILE, - IMAGE_CACHE_MAX_ENTRIES, IMAGE_CACHE_MAX_BYTES, - IMAGE_CACHE_VERSION); - } - - /** - * Gets the cached image data for the given <code>path</code>, - * <code>timeModified</code> and <code>type</code>. - * - * The image data will be stored in <code>buffer.data</code>, started from - * <code>buffer.offset</code> for <code>buffer.length</code> bytes. If the - * buffer.data is not big enough, a new byte array will be allocated and returned. - * - * @return true if the image data is found; false if not found. - */ - public boolean getImageData(Path path, long timeModified, int type, BytesBuffer buffer) { - byte[] key = makeKey(path, timeModified, type); - long cacheKey = Utils.crc64Long(key); - try { - LookupRequest request = new LookupRequest(); - request.key = cacheKey; - request.buffer = buffer.data; - synchronized (mCache) { - if (!mCache.lookup(request)) return false; - } - if (isSameKey(key, request.buffer)) { - buffer.data = request.buffer; - buffer.offset = key.length; - buffer.length = request.length - buffer.offset; - return true; - } - } catch (IOException ex) { - // ignore. - } - return false; - } - - public void putImageData(Path path, long timeModified, int type, byte[] value) { - byte[] key = makeKey(path, timeModified, type); - long cacheKey = Utils.crc64Long(key); - ByteBuffer buffer = ByteBuffer.allocate(key.length + value.length); - buffer.put(key); - buffer.put(value); - synchronized (mCache) { - try { - mCache.insert(cacheKey, buffer.array()); - } catch (IOException ex) { - // ignore. - } - } - } - - public void clearImageData(Path path, long timeModified, int type) { - byte[] key = makeKey(path, timeModified, type); - long cacheKey = Utils.crc64Long(key); - synchronized (mCache) { - try { - mCache.clearEntry(cacheKey); - } catch (IOException ex) { - // ignore. - } - } - } - - private static byte[] makeKey(Path path, long timeModified, int type) { - return GalleryUtils.getBytes(path.toString() + "+" + timeModified + "+" + type); - } - - private static boolean isSameKey(byte[] key, byte[] buffer) { - int n = key.length; - if (buffer.length < n) { - return false; - } - for (int i = 0; i < n; ++i) { - if (key[i] != buffer[i]) { - return false; - } - } - return true; - } -} diff --git a/src/com/android/gallery3d/data/LocalAlbum.java b/src/com/android/gallery3d/data/LocalAlbum.java deleted file mode 100644 index 7b7015af6..000000000 --- a/src/com/android/gallery3d/data/LocalAlbum.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * 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(*)" }; - - 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<MediaItem> getMediaItem(int start, int count) { - DataManager dataManager = mApplication.getDataManager(); - Uri uri = mBaseUri.buildUpon() - .appendQueryParameter("limit", start + "," + count).build(); - ArrayList<MediaItem> list = new ArrayList<MediaItem>(); - 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<Integer> 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; - } - -} diff --git a/src/com/android/gallery3d/data/LocalAlbumSet.java b/src/com/android/gallery3d/data/LocalAlbumSet.java deleted file mode 100644 index b2b4b8c5d..000000000 --- a/src/com/android/gallery3d/data/LocalAlbumSet.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.net.Uri; -import android.os.Handler; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Video; - -import com.android.gallery3d.R; -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.data.BucketHelper.BucketEntry; -import com.android.gallery3d.util.Future; -import com.android.gallery3d.util.FutureListener; -import com.android.gallery3d.util.MediaSetUtils; -import com.android.gallery3d.util.ThreadPool; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.util.ArrayList; -import java.util.Comparator; - -// LocalAlbumSet lists all image or video albums in the local storage. -// The path should be "/local/image", "local/video" or "/local/all" -public class LocalAlbumSet extends MediaSet - implements FutureListener<ArrayList<MediaSet>> { - @SuppressWarnings("unused") - private static final String TAG = "LocalAlbumSet"; - - public static final Path PATH_ALL = Path.fromString("/local/all"); - public static final Path PATH_IMAGE = Path.fromString("/local/image"); - public static final Path PATH_VIDEO = Path.fromString("/local/video"); - - private static final Uri[] mWatchUris = - {Images.Media.EXTERNAL_CONTENT_URI, Video.Media.EXTERNAL_CONTENT_URI}; - - private final GalleryApp mApplication; - private final int mType; - private ArrayList<MediaSet> mAlbums = new ArrayList<MediaSet>(); - private final ChangeNotifier mNotifier; - private final String mName; - private final Handler mHandler; - private boolean mIsLoading; - - private Future<ArrayList<MediaSet>> mLoadTask; - private ArrayList<MediaSet> mLoadBuffer; - - public LocalAlbumSet(Path path, GalleryApp application) { - super(path, nextVersionNumber()); - mApplication = application; - mHandler = new Handler(application.getMainLooper()); - mType = getTypeFromPath(path); - mNotifier = new ChangeNotifier(this, mWatchUris, application); - mName = application.getResources().getString( - R.string.set_label_local_albums); - } - - private static int getTypeFromPath(Path path) { - String name[] = path.split(); - if (name.length < 2) { - throw new IllegalArgumentException(path.toString()); - } - return getTypeFromString(name[1]); - } - - @Override - public MediaSet getSubMediaSet(int index) { - return mAlbums.get(index); - } - - @Override - public int getSubMediaSetCount() { - return mAlbums.size(); - } - - @Override - public String getName() { - return mName; - } - - private static int findBucket(BucketEntry entries[], int bucketId) { - for (int i = 0, n = entries.length; i < n; ++i) { - if (entries[i].bucketId == bucketId) return i; - } - return -1; - } - - private class AlbumsLoader implements ThreadPool.Job<ArrayList<MediaSet>> { - - @Override - @SuppressWarnings("unchecked") - public ArrayList<MediaSet> run(JobContext jc) { - // Note: it will be faster if we only select media_type and bucket_id. - // need to test the performance if that is worth - BucketEntry[] entries = BucketHelper.loadBucketEntries( - jc, mApplication.getContentResolver(), mType); - - if (jc.isCancelled()) return null; - - int offset = 0; - // Move camera and download bucket to the front, while keeping the - // order of others. - int index = findBucket(entries, MediaSetUtils.CAMERA_BUCKET_ID); - if (index != -1) { - circularShiftRight(entries, offset++, index); - } - index = findBucket(entries, MediaSetUtils.DOWNLOAD_BUCKET_ID); - if (index != -1) { - circularShiftRight(entries, offset++, index); - } - - ArrayList<MediaSet> albums = new ArrayList<MediaSet>(); - DataManager dataManager = mApplication.getDataManager(); - for (BucketEntry entry : entries) { - MediaSet album = getLocalAlbum(dataManager, - mType, mPath, entry.bucketId, entry.bucketName); - albums.add(album); - } - return albums; - } - } - - private MediaSet getLocalAlbum( - DataManager manager, int type, Path parent, int id, String name) { - synchronized (DataManager.LOCK) { - Path path = parent.getChild(id); - MediaObject object = manager.peekMediaObject(path); - if (object != null) return (MediaSet) object; - switch (type) { - case MEDIA_TYPE_IMAGE: - return new LocalAlbum(path, mApplication, id, true, name); - case MEDIA_TYPE_VIDEO: - return new LocalAlbum(path, mApplication, id, false, name); - case MEDIA_TYPE_ALL: - Comparator<MediaItem> comp = DataManager.sDateTakenComparator; - return new LocalMergeAlbum(path, comp, new MediaSet[] { - getLocalAlbum(manager, MEDIA_TYPE_IMAGE, PATH_IMAGE, id, name), - getLocalAlbum(manager, MEDIA_TYPE_VIDEO, PATH_VIDEO, id, name)}, id); - } - throw new IllegalArgumentException(String.valueOf(type)); - } - } - - @Override - public synchronized boolean isLoading() { - return mIsLoading; - } - - @Override - // synchronized on this function for - // 1. Prevent calling reload() concurrently. - // 2. Prevent calling onFutureDone() and reload() concurrently - public synchronized long reload() { - if (mNotifier.isDirty()) { - if (mLoadTask != null) mLoadTask.cancel(); - mIsLoading = true; - mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this); - } - if (mLoadBuffer != null) { - mAlbums = mLoadBuffer; - mLoadBuffer = null; - for (MediaSet album : mAlbums) { - album.reload(); - } - mDataVersion = nextVersionNumber(); - } - return mDataVersion; - } - - @Override - public synchronized void onFutureDone(Future<ArrayList<MediaSet>> future) { - if (mLoadTask != future) return; // ignore, wait for the latest task - mLoadBuffer = future.get(); - mIsLoading = false; - if (mLoadBuffer == null) mLoadBuffer = new ArrayList<MediaSet>(); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyContentChanged(); - } - }); - } - - // For debug only. Fake there is a ContentObserver.onChange() event. - void fakeChange() { - mNotifier.fakeChange(); - } - - // Circular shift the array range from a[i] to a[j] (inclusive). That is, - // a[i] -> a[i+1] -> a[i+2] -> ... -> a[j], and a[j] -> a[i] - private static <T> void circularShiftRight(T[] array, int i, int j) { - T temp = array[j]; - for (int k = j; k > i; k--) { - array[k] = array[k - 1]; - } - array[i] = temp; - } -} diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java deleted file mode 100644 index cc70dd457..000000000 --- a/src/com/android/gallery3d/data/LocalImage.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * 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.annotation.TargetApi; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; -import android.net.Uri; -import android.os.Build; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.Images.ImageColumns; -import android.provider.MediaStore.MediaColumns; -import android.util.Log; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.app.PanoramaMetadataSupport; -import com.android.gallery3d.app.StitchingProgressManager; -import com.android.gallery3d.common.ApiHelper; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.exif.ExifInterface; -import com.android.gallery3d.exif.ExifTag; -import com.android.gallery3d.filtershow.tools.SaveImage; -import com.android.gallery3d.util.GalleryUtils; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; -import com.android.gallery3d.util.UpdateHelper; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; - -// LocalImage represents an image in the local storage. -public class LocalImage extends LocalMediaItem { - private static final String TAG = "LocalImage"; - - static final Path ITEM_PATH = Path.fromString("/local/image/item"); - - // Must preserve order between these indices and the order of the terms in - // the following PROJECTION array. - private static final int INDEX_ID = 0; - private static final int INDEX_CAPTION = 1; - private static final int INDEX_MIME_TYPE = 2; - private static final int INDEX_LATITUDE = 3; - private static final int INDEX_LONGITUDE = 4; - private static final int INDEX_DATE_TAKEN = 5; - private static final int INDEX_DATE_ADDED = 6; - private static final int INDEX_DATE_MODIFIED = 7; - private static final int INDEX_DATA = 8; - private static final int INDEX_ORIENTATION = 9; - private static final int INDEX_BUCKET_ID = 10; - private static final int INDEX_SIZE = 11; - private static final int INDEX_WIDTH = 12; - private static final int INDEX_HEIGHT = 13; - - static final String[] PROJECTION = { - ImageColumns._ID, // 0 - ImageColumns.TITLE, // 1 - ImageColumns.MIME_TYPE, // 2 - ImageColumns.LATITUDE, // 3 - ImageColumns.LONGITUDE, // 4 - ImageColumns.DATE_TAKEN, // 5 - ImageColumns.DATE_ADDED, // 6 - ImageColumns.DATE_MODIFIED, // 7 - ImageColumns.DATA, // 8 - ImageColumns.ORIENTATION, // 9 - ImageColumns.BUCKET_ID, // 10 - ImageColumns.SIZE, // 11 - "0", // 12 - "0" // 13 - }; - - static { - updateWidthAndHeightProjection(); - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private static void updateWidthAndHeightProjection() { - if (ApiHelper.HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT) { - PROJECTION[INDEX_WIDTH] = MediaColumns.WIDTH; - PROJECTION[INDEX_HEIGHT] = MediaColumns.HEIGHT; - } - } - - private final GalleryApp mApplication; - - public int rotation; - - private PanoramaMetadataSupport mPanoramaMetadata = new PanoramaMetadataSupport(this); - - public LocalImage(Path path, GalleryApp application, Cursor cursor) { - super(path, nextVersionNumber()); - mApplication = application; - loadFromCursor(cursor); - } - - public LocalImage(Path path, GalleryApp application, int id) { - super(path, nextVersionNumber()); - mApplication = application; - ContentResolver resolver = mApplication.getContentResolver(); - Uri uri = Images.Media.EXTERNAL_CONTENT_URI; - Cursor cursor = LocalAlbum.getItemCursor(resolver, uri, PROJECTION, id); - if (cursor == null) { - throw new RuntimeException("cannot get cursor for: " + path); - } - try { - if (cursor.moveToNext()) { - loadFromCursor(cursor); - } else { - throw new RuntimeException("cannot find data for: " + path); - } - } finally { - cursor.close(); - } - } - - private void loadFromCursor(Cursor cursor) { - id = cursor.getInt(INDEX_ID); - caption = cursor.getString(INDEX_CAPTION); - mimeType = cursor.getString(INDEX_MIME_TYPE); - latitude = cursor.getDouble(INDEX_LATITUDE); - longitude = cursor.getDouble(INDEX_LONGITUDE); - dateTakenInMs = cursor.getLong(INDEX_DATE_TAKEN); - dateAddedInSec = cursor.getLong(INDEX_DATE_ADDED); - dateModifiedInSec = cursor.getLong(INDEX_DATE_MODIFIED); - filePath = cursor.getString(INDEX_DATA); - rotation = cursor.getInt(INDEX_ORIENTATION); - bucketId = cursor.getInt(INDEX_BUCKET_ID); - fileSize = cursor.getLong(INDEX_SIZE); - width = cursor.getInt(INDEX_WIDTH); - height = cursor.getInt(INDEX_HEIGHT); - } - - @Override - protected boolean updateFromCursor(Cursor cursor) { - UpdateHelper uh = new UpdateHelper(); - id = uh.update(id, cursor.getInt(INDEX_ID)); - caption = uh.update(caption, cursor.getString(INDEX_CAPTION)); - mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE)); - latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE)); - longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE)); - dateTakenInMs = uh.update( - dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN)); - dateAddedInSec = uh.update( - dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED)); - dateModifiedInSec = uh.update( - dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED)); - filePath = uh.update(filePath, cursor.getString(INDEX_DATA)); - rotation = uh.update(rotation, cursor.getInt(INDEX_ORIENTATION)); - bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID)); - fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE)); - width = uh.update(width, cursor.getInt(INDEX_WIDTH)); - height = uh.update(height, cursor.getInt(INDEX_HEIGHT)); - return uh.isUpdated(); - } - - @Override - public Job<Bitmap> requestImage(int type) { - return new LocalImageRequest(mApplication, mPath, dateModifiedInSec, - type, filePath); - } - - public static class LocalImageRequest extends ImageCacheRequest { - private String mLocalFilePath; - - LocalImageRequest(GalleryApp application, Path path, long timeModified, - int type, String localFilePath) { - super(application, path, timeModified, type, - MediaItem.getTargetSize(type)); - mLocalFilePath = localFilePath; - } - - @Override - public Bitmap onDecodeOriginal(JobContext jc, final int type) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - int targetSize = MediaItem.getTargetSize(type); - - // try to decode from JPEG EXIF - if (type == MediaItem.TYPE_MICROTHUMBNAIL) { - ExifInterface exif = new ExifInterface(); - byte[] thumbData = null; - try { - exif.readExif(mLocalFilePath); - thumbData = exif.getThumbnail(); - } catch (FileNotFoundException e) { - Log.w(TAG, "failed to find file to read thumbnail: " + mLocalFilePath); - } catch (IOException e) { - Log.w(TAG, "failed to get thumbnail from: " + mLocalFilePath); - } - if (thumbData != null) { - Bitmap bitmap = DecodeUtils.decodeIfBigEnough( - jc, thumbData, options, targetSize); - if (bitmap != null) return bitmap; - } - } - - return DecodeUtils.decodeThumbnail(jc, mLocalFilePath, options, targetSize, type); - } - } - - @Override - public Job<BitmapRegionDecoder> requestLargeImage() { - return new LocalLargeImageRequest(filePath); - } - - public static class LocalLargeImageRequest - implements Job<BitmapRegionDecoder> { - String mLocalFilePath; - - public LocalLargeImageRequest(String localFilePath) { - mLocalFilePath = localFilePath; - } - - @Override - public BitmapRegionDecoder run(JobContext jc) { - return DecodeUtils.createBitmapRegionDecoder(jc, mLocalFilePath, false); - } - } - - @Override - public int getSupportedOperations() { - StitchingProgressManager progressManager = mApplication.getStitchingProgressManager(); - if (progressManager != null && progressManager.getProgress(getContentUri()) != null) { - return 0; // doesn't support anything while stitching! - } - int operation = SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_CROP - | SUPPORT_SETAS | SUPPORT_EDIT | SUPPORT_INFO; - if (BitmapUtils.isSupportedByRegionDecoder(mimeType)) { - operation |= SUPPORT_FULL_IMAGE; - } - - if (BitmapUtils.isRotationSupported(mimeType)) { - operation |= SUPPORT_ROTATE; - } - - if (GalleryUtils.isValidLocation(latitude, longitude)) { - operation |= SUPPORT_SHOW_ON_MAP; - } - return operation; - } - - @Override - public void getPanoramaSupport(PanoramaSupportCallback callback) { - mPanoramaMetadata.getPanoramaSupport(mApplication, callback); - } - - @Override - public void clearCachedPanoramaSupport() { - mPanoramaMetadata.clearCachedValues(); - } - - @Override - public void delete() { - GalleryUtils.assertNotInRenderThread(); - Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; - ContentResolver contentResolver = mApplication.getContentResolver(); - SaveImage.deleteAuxFiles(contentResolver, getContentUri()); - contentResolver.delete(baseUri, "_id=?", - new String[]{String.valueOf(id)}); - } - - @Override - public void rotate(int degrees) { - GalleryUtils.assertNotInRenderThread(); - Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; - ContentValues values = new ContentValues(); - int rotation = (this.rotation + degrees) % 360; - if (rotation < 0) rotation += 360; - - if (mimeType.equalsIgnoreCase("image/jpeg")) { - ExifInterface exifInterface = new ExifInterface(); - ExifTag tag = exifInterface.buildTag(ExifInterface.TAG_ORIENTATION, - ExifInterface.getOrientationValueForRotation(rotation)); - if(tag != null) { - exifInterface.setTag(tag); - try { - exifInterface.forceRewriteExif(filePath); - fileSize = new File(filePath).length(); - values.put(Images.Media.SIZE, fileSize); - } catch (FileNotFoundException e) { - Log.w(TAG, "cannot find file to set exif: " + filePath); - } catch (IOException e) { - Log.w(TAG, "cannot set exif data: " + filePath); - } - } else { - Log.w(TAG, "Could not build tag: " + ExifInterface.TAG_ORIENTATION); - } - } - - values.put(Images.Media.ORIENTATION, rotation); - mApplication.getContentResolver().update(baseUri, values, "_id=?", - new String[]{String.valueOf(id)}); - } - - @Override - public Uri getContentUri() { - Uri baseUri = Images.Media.EXTERNAL_CONTENT_URI; - return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); - } - - @Override - public int getMediaType() { - return MEDIA_TYPE_IMAGE; - } - - @Override - public MediaDetails getDetails() { - MediaDetails details = super.getDetails(); - details.addDetail(MediaDetails.INDEX_ORIENTATION, Integer.valueOf(rotation)); - if (MIME_TYPE_JPEG.equals(mimeType)) { - // ExifInterface returns incorrect values for photos in other format. - // For example, the width and height of an webp images is always '0'. - MediaDetails.extractExifInfo(details, filePath); - } - return details; - } - - @Override - public int getRotation() { - return rotation; - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getHeight() { - return height; - } - - @Override - public String getFilePath() { - return filePath; - } -} diff --git a/src/com/android/gallery3d/data/LocalMediaItem.java b/src/com/android/gallery3d/data/LocalMediaItem.java deleted file mode 100644 index 7e003cd3a..000000000 --- a/src/com/android/gallery3d/data/LocalMediaItem.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.database.Cursor; - -import com.android.gallery3d.util.GalleryUtils; - -import java.text.DateFormat; -import java.util.Date; - -// -// LocalMediaItem is an abstract class captures those common fields -// in LocalImage and LocalVideo. -// -public abstract class LocalMediaItem extends MediaItem { - - @SuppressWarnings("unused") - private static final String TAG = "LocalMediaItem"; - - // database fields - public int id; - public String caption; - public String mimeType; - public long fileSize; - public double latitude = INVALID_LATLNG; - public double longitude = INVALID_LATLNG; - public long dateTakenInMs; - public long dateAddedInSec; - public long dateModifiedInSec; - public String filePath; - public int bucketId; - public int width; - public int height; - - public LocalMediaItem(Path path, long version) { - super(path, version); - } - - @Override - public long getDateInMs() { - return dateTakenInMs; - } - - @Override - public String getName() { - return caption; - } - - @Override - public void getLatLong(double[] latLong) { - latLong[0] = latitude; - latLong[1] = longitude; - } - - abstract protected boolean updateFromCursor(Cursor cursor); - - public int getBucketId() { - return bucketId; - } - - protected void updateContent(Cursor cursor) { - if (updateFromCursor(cursor)) { - mDataVersion = nextVersionNumber(); - } - } - - @Override - public MediaDetails getDetails() { - MediaDetails details = super.getDetails(); - details.addDetail(MediaDetails.INDEX_PATH, filePath); - details.addDetail(MediaDetails.INDEX_TITLE, caption); - DateFormat formater = DateFormat.getDateTimeInstance(); - details.addDetail(MediaDetails.INDEX_DATETIME, - formater.format(new Date(dateModifiedInSec * 1000))); - details.addDetail(MediaDetails.INDEX_WIDTH, width); - details.addDetail(MediaDetails.INDEX_HEIGHT, height); - - if (GalleryUtils.isValidLocation(latitude, longitude)) { - details.addDetail(MediaDetails.INDEX_LOCATION, new double[] {latitude, longitude}); - } - if (fileSize > 0) details.addDetail(MediaDetails.INDEX_SIZE, fileSize); - return details; - } - - @Override - public String getMimeType() { - return mimeType; - } - - @Override - public long getSize() { - return fileSize; - } -} diff --git a/src/com/android/gallery3d/data/LocalMergeAlbum.java b/src/com/android/gallery3d/data/LocalMergeAlbum.java deleted file mode 100644 index f0b5e5726..000000000 --- a/src/com/android/gallery3d/data/LocalMergeAlbum.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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.net.Uri; -import android.provider.MediaStore; - -import com.android.gallery3d.common.ApiHelper; - -import java.lang.ref.SoftReference; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.NoSuchElementException; -import java.util.SortedMap; -import java.util.TreeMap; - -// MergeAlbum merges items from two or more MediaSets. It uses a Comparator to -// determine the order of items. The items are assumed to be sorted in the input -// media sets (with the same order that the Comparator uses). -// -// This only handles MediaItems, not SubMediaSets. -public class LocalMergeAlbum extends MediaSet implements ContentListener { - @SuppressWarnings("unused") - private static final String TAG = "LocalMergeAlbum"; - private static final int PAGE_SIZE = 64; - - private final Comparator<MediaItem> mComparator; - private final MediaSet[] mSources; - - private FetchCache[] mFetcher; - private int mSupportedOperation; - private int mBucketId; - - // mIndex maps global position to the position of each underlying media sets. - private TreeMap<Integer, int[]> mIndex = new TreeMap<Integer, int[]>(); - - public LocalMergeAlbum( - Path path, Comparator<MediaItem> comparator, MediaSet[] sources, int bucketId) { - super(path, INVALID_DATA_VERSION); - mComparator = comparator; - mSources = sources; - mBucketId = bucketId; - for (MediaSet set : mSources) { - set.addContentListener(this); - } - reload(); - } - - @Override - public boolean isCameraRoll() { - if (mSources.length == 0) return false; - for(MediaSet set : mSources) { - if (!set.isCameraRoll()) return false; - } - return true; - } - - private void updateData() { - ArrayList<MediaSet> matches = new ArrayList<MediaSet>(); - int supported = mSources.length == 0 ? 0 : MediaItem.SUPPORT_ALL; - mFetcher = new FetchCache[mSources.length]; - for (int i = 0, n = mSources.length; i < n; ++i) { - mFetcher[i] = new FetchCache(mSources[i]); - supported &= mSources[i].getSupportedOperations(); - } - mSupportedOperation = supported; - mIndex.clear(); - mIndex.put(0, new int[mSources.length]); - } - - private void invalidateCache() { - for (int i = 0, n = mSources.length; i < n; i++) { - mFetcher[i].invalidate(); - } - mIndex.clear(); - mIndex.put(0, new int[mSources.length]); - } - - @Override - public Uri getContentUri() { - String bucketId = String.valueOf(mBucketId); - if (ApiHelper.HAS_MEDIA_PROVIDER_FILES_TABLE) { - return MediaStore.Files.getContentUri("external").buildUpon() - .appendQueryParameter(LocalSource.KEY_BUCKET_ID, bucketId) - .build(); - } else { - // We don't have a single URL for a merged image before ICS - // So we used the image's URL as a substitute. - return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon() - .appendQueryParameter(LocalSource.KEY_BUCKET_ID, bucketId) - .build(); - } - } - - @Override - public String getName() { - return mSources.length == 0 ? "" : mSources[0].getName(); - } - - @Override - public int getMediaItemCount() { - return getTotalMediaItemCount(); - } - - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - - // First find the nearest mark position <= start. - SortedMap<Integer, int[]> head = mIndex.headMap(start + 1); - int markPos = head.lastKey(); - int[] subPos = head.get(markPos).clone(); - MediaItem[] slot = new MediaItem[mSources.length]; - - int size = mSources.length; - - // fill all slots - for (int i = 0; i < size; i++) { - slot[i] = mFetcher[i].getItem(subPos[i]); - } - - ArrayList<MediaItem> result = new ArrayList<MediaItem>(); - - for (int i = markPos; i < start + count; i++) { - int k = -1; // k points to the best slot up to now. - for (int j = 0; j < size; j++) { - if (slot[j] != null) { - if (k == -1 || mComparator.compare(slot[j], slot[k]) < 0) { - k = j; - } - } - } - - // If we don't have anything, all streams are exhausted. - if (k == -1) break; - - // Pick the best slot and refill it. - subPos[k]++; - if (i >= start) { - result.add(slot[k]); - } - slot[k] = mFetcher[k].getItem(subPos[k]); - - // Periodically leave a mark in the index, so we can come back later. - if ((i + 1) % PAGE_SIZE == 0) { - mIndex.put(i + 1, subPos.clone()); - } - } - - return result; - } - - @Override - public int getTotalMediaItemCount() { - int count = 0; - for (MediaSet set : mSources) { - count += set.getTotalMediaItemCount(); - } - return count; - } - - @Override - public long reload() { - boolean changed = false; - for (int i = 0, n = mSources.length; i < n; ++i) { - if (mSources[i].reload() > mDataVersion) changed = true; - } - if (changed) { - mDataVersion = nextVersionNumber(); - updateData(); - invalidateCache(); - } - return mDataVersion; - } - - @Override - public void onContentDirty() { - notifyContentChanged(); - } - - @Override - public int getSupportedOperations() { - return mSupportedOperation; - } - - @Override - public void delete() { - for (MediaSet set : mSources) { - set.delete(); - } - } - - @Override - public void rotate(int degrees) { - for (MediaSet set : mSources) { - set.rotate(degrees); - } - } - - private static class FetchCache { - private MediaSet mBaseSet; - private SoftReference<ArrayList<MediaItem>> mCacheRef; - private int mStartPos; - - public FetchCache(MediaSet baseSet) { - mBaseSet = baseSet; - } - - public void invalidate() { - mCacheRef = null; - } - - public MediaItem getItem(int index) { - boolean needLoading = false; - ArrayList<MediaItem> cache = null; - if (mCacheRef == null - || index < mStartPos || index >= mStartPos + PAGE_SIZE) { - needLoading = true; - } else { - cache = mCacheRef.get(); - if (cache == null) { - needLoading = true; - } - } - - if (needLoading) { - cache = mBaseSet.getMediaItem(index, PAGE_SIZE); - mCacheRef = new SoftReference<ArrayList<MediaItem>>(cache); - mStartPos = index; - } - - if (index < mStartPos || index >= mStartPos + cache.size()) { - return null; - } - - return cache.get(index - mStartPos); - } - } - - @Override - public boolean isLeafAlbum() { - return true; - } -} diff --git a/src/com/android/gallery3d/data/LocalSource.java b/src/com/android/gallery3d/data/LocalSource.java deleted file mode 100644 index a2e3d1443..000000000 --- a/src/com/android/gallery3d/data/LocalSource.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * 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.ContentProviderClient; -import android.content.ContentUris; -import android.content.UriMatcher; -import android.net.Uri; -import android.provider.MediaStore; - -import com.android.gallery3d.app.Gallery; -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.data.MediaSet.ItemConsumer; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; - -class LocalSource extends MediaSource { - - public static final String KEY_BUCKET_ID = "bucketId"; - - private GalleryApp mApplication; - private PathMatcher mMatcher; - private static final int NO_MATCH = -1; - private final UriMatcher mUriMatcher = new UriMatcher(NO_MATCH); - public static final Comparator<PathId> sIdComparator = new IdComparator(); - - private static final int LOCAL_IMAGE_ALBUMSET = 0; - private static final int LOCAL_VIDEO_ALBUMSET = 1; - private static final int LOCAL_IMAGE_ALBUM = 2; - private static final int LOCAL_VIDEO_ALBUM = 3; - private static final int LOCAL_IMAGE_ITEM = 4; - private static final int LOCAL_VIDEO_ITEM = 5; - private static final int LOCAL_ALL_ALBUMSET = 6; - private static final int LOCAL_ALL_ALBUM = 7; - - private static final String TAG = "LocalSource"; - - private ContentProviderClient mClient; - - public LocalSource(GalleryApp context) { - super("local"); - mApplication = context; - mMatcher = new PathMatcher(); - mMatcher.add("/local/image", LOCAL_IMAGE_ALBUMSET); - mMatcher.add("/local/video", LOCAL_VIDEO_ALBUMSET); - mMatcher.add("/local/all", LOCAL_ALL_ALBUMSET); - - mMatcher.add("/local/image/*", LOCAL_IMAGE_ALBUM); - mMatcher.add("/local/video/*", LOCAL_VIDEO_ALBUM); - mMatcher.add("/local/all/*", LOCAL_ALL_ALBUM); - mMatcher.add("/local/image/item/*", LOCAL_IMAGE_ITEM); - mMatcher.add("/local/video/item/*", LOCAL_VIDEO_ITEM); - - mUriMatcher.addURI(MediaStore.AUTHORITY, - "external/images/media/#", LOCAL_IMAGE_ITEM); - mUriMatcher.addURI(MediaStore.AUTHORITY, - "external/video/media/#", LOCAL_VIDEO_ITEM); - mUriMatcher.addURI(MediaStore.AUTHORITY, - "external/images/media", LOCAL_IMAGE_ALBUM); - mUriMatcher.addURI(MediaStore.AUTHORITY, - "external/video/media", LOCAL_VIDEO_ALBUM); - mUriMatcher.addURI(MediaStore.AUTHORITY, - "external/file", LOCAL_ALL_ALBUM); - } - - @Override - public MediaObject createMediaObject(Path path) { - GalleryApp app = mApplication; - switch (mMatcher.match(path)) { - case LOCAL_ALL_ALBUMSET: - case LOCAL_IMAGE_ALBUMSET: - case LOCAL_VIDEO_ALBUMSET: - return new LocalAlbumSet(path, mApplication); - case LOCAL_IMAGE_ALBUM: - return new LocalAlbum(path, app, mMatcher.getIntVar(0), true); - case LOCAL_VIDEO_ALBUM: - return new LocalAlbum(path, app, mMatcher.getIntVar(0), false); - case LOCAL_ALL_ALBUM: { - int bucketId = mMatcher.getIntVar(0); - DataManager dataManager = app.getDataManager(); - MediaSet imageSet = (MediaSet) dataManager.getMediaObject( - LocalAlbumSet.PATH_IMAGE.getChild(bucketId)); - MediaSet videoSet = (MediaSet) dataManager.getMediaObject( - LocalAlbumSet.PATH_VIDEO.getChild(bucketId)); - Comparator<MediaItem> comp = DataManager.sDateTakenComparator; - return new LocalMergeAlbum( - path, comp, new MediaSet[] {imageSet, videoSet}, bucketId); - } - case LOCAL_IMAGE_ITEM: - return new LocalImage(path, mApplication, mMatcher.getIntVar(0)); - case LOCAL_VIDEO_ITEM: - return new LocalVideo(path, mApplication, mMatcher.getIntVar(0)); - default: - throw new RuntimeException("bad path: " + path); - } - } - - private static int getMediaType(String type, int defaultType) { - if (type == null) return defaultType; - try { - int value = Integer.parseInt(type); - if ((value & (MEDIA_TYPE_IMAGE - | MEDIA_TYPE_VIDEO)) != 0) return value; - } catch (NumberFormatException e) { - Log.w(TAG, "invalid type: " + type, e); - } - return defaultType; - } - - // The media type bit passed by the intent - private static final int MEDIA_TYPE_ALL = 0; - private static final int MEDIA_TYPE_IMAGE = 1; - private static final int MEDIA_TYPE_VIDEO = 4; - - private Path getAlbumPath(Uri uri, int defaultType) { - int mediaType = getMediaType( - uri.getQueryParameter(Gallery.KEY_MEDIA_TYPES), - defaultType); - String bucketId = uri.getQueryParameter(KEY_BUCKET_ID); - int id = 0; - try { - id = Integer.parseInt(bucketId); - } catch (NumberFormatException e) { - Log.w(TAG, "invalid bucket id: " + bucketId, e); - return null; - } - switch (mediaType) { - case MEDIA_TYPE_IMAGE: - return Path.fromString("/local/image").getChild(id); - case MEDIA_TYPE_VIDEO: - return Path.fromString("/local/video").getChild(id); - default: - return Path.fromString("/local/all").getChild(id); - } - } - - @Override - public Path findPathByUri(Uri uri, String type) { - try { - switch (mUriMatcher.match(uri)) { - case LOCAL_IMAGE_ITEM: { - long id = ContentUris.parseId(uri); - return id >= 0 ? LocalImage.ITEM_PATH.getChild(id) : null; - } - case LOCAL_VIDEO_ITEM: { - long id = ContentUris.parseId(uri); - return id >= 0 ? LocalVideo.ITEM_PATH.getChild(id) : null; - } - case LOCAL_IMAGE_ALBUM: { - return getAlbumPath(uri, MEDIA_TYPE_IMAGE); - } - case LOCAL_VIDEO_ALBUM: { - return getAlbumPath(uri, MEDIA_TYPE_VIDEO); - } - case LOCAL_ALL_ALBUM: { - return getAlbumPath(uri, MEDIA_TYPE_ALL); - } - } - } catch (NumberFormatException e) { - Log.w(TAG, "uri: " + uri.toString(), e); - } - return null; - } - - @Override - public Path getDefaultSetOf(Path item) { - MediaObject object = mApplication.getDataManager().getMediaObject(item); - if (object instanceof LocalMediaItem) { - return Path.fromString("/local/all").getChild( - String.valueOf(((LocalMediaItem) object).getBucketId())); - } - return null; - } - - @Override - public void mapMediaItems(ArrayList<PathId> list, ItemConsumer consumer) { - ArrayList<PathId> imageList = new ArrayList<PathId>(); - ArrayList<PathId> videoList = new ArrayList<PathId>(); - int n = list.size(); - for (int i = 0; i < n; i++) { - PathId pid = list.get(i); - // We assume the form is: "/local/{image,video}/item/#" - // We don't use mMatcher for efficiency's reason. - Path parent = pid.path.getParent(); - if (parent == LocalImage.ITEM_PATH) { - imageList.add(pid); - } else if (parent == LocalVideo.ITEM_PATH) { - videoList.add(pid); - } - } - // TODO: use "files" table so we can merge the two cases. - processMapMediaItems(imageList, consumer, true); - processMapMediaItems(videoList, consumer, false); - } - - private void processMapMediaItems(ArrayList<PathId> list, - ItemConsumer consumer, boolean isImage) { - // Sort path by path id - Collections.sort(list, sIdComparator); - int n = list.size(); - for (int i = 0; i < n; ) { - PathId pid = list.get(i); - - // Find a range of items. - ArrayList<Integer> ids = new ArrayList<Integer>(); - int startId = Integer.parseInt(pid.path.getSuffix()); - ids.add(startId); - - int j; - for (j = i + 1; j < n; j++) { - PathId pid2 = list.get(j); - int curId = Integer.parseInt(pid2.path.getSuffix()); - if (curId - startId >= MediaSet.MEDIAITEM_BATCH_FETCH_COUNT) { - break; - } - ids.add(curId); - } - - MediaItem[] items = LocalAlbum.getMediaItemById( - mApplication, isImage, ids); - for(int k = i ; k < j; k++) { - PathId pid2 = list.get(k); - consumer.consume(pid2.id, items[k - i]); - } - - i = j; - } - } - - // This is a comparator which compares the suffix number in two Paths. - private static class IdComparator implements Comparator<PathId> { - @Override - public int compare(PathId p1, PathId p2) { - String s1 = p1.path.getSuffix(); - String s2 = p2.path.getSuffix(); - int len1 = s1.length(); - int len2 = s2.length(); - if (len1 < len2) { - return -1; - } else if (len1 > len2) { - return 1; - } else { - return s1.compareTo(s2); - } - } - } - - @Override - public void resume() { - mClient = mApplication.getContentResolver() - .acquireContentProviderClient(MediaStore.AUTHORITY); - } - - @Override - public void pause() { - mClient.release(); - mClient = null; - } -} diff --git a/src/com/android/gallery3d/data/LocalVideo.java b/src/com/android/gallery3d/data/LocalVideo.java deleted file mode 100644 index 4b8774ca4..000000000 --- a/src/com/android/gallery3d/data/LocalVideo.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * 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.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapRegionDecoder; -import android.net.Uri; -import android.provider.MediaStore.Video; -import android.provider.MediaStore.Video.VideoColumns; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.util.GalleryUtils; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; -import com.android.gallery3d.util.UpdateHelper; - -// LocalVideo represents a video in the local storage. -public class LocalVideo extends LocalMediaItem { - private static final String TAG = "LocalVideo"; - static final Path ITEM_PATH = Path.fromString("/local/video/item"); - - // Must preserve order between these indices and the order of the terms in - // the following PROJECTION array. - private static final int INDEX_ID = 0; - private static final int INDEX_CAPTION = 1; - private static final int INDEX_MIME_TYPE = 2; - private static final int INDEX_LATITUDE = 3; - private static final int INDEX_LONGITUDE = 4; - private static final int INDEX_DATE_TAKEN = 5; - private static final int INDEX_DATE_ADDED = 6; - private static final int INDEX_DATE_MODIFIED = 7; - private static final int INDEX_DATA = 8; - private static final int INDEX_DURATION = 9; - private static final int INDEX_BUCKET_ID = 10; - private static final int INDEX_SIZE = 11; - private static final int INDEX_RESOLUTION = 12; - - static final String[] PROJECTION = new String[] { - VideoColumns._ID, - VideoColumns.TITLE, - VideoColumns.MIME_TYPE, - VideoColumns.LATITUDE, - VideoColumns.LONGITUDE, - VideoColumns.DATE_TAKEN, - VideoColumns.DATE_ADDED, - VideoColumns.DATE_MODIFIED, - VideoColumns.DATA, - VideoColumns.DURATION, - VideoColumns.BUCKET_ID, - VideoColumns.SIZE, - VideoColumns.RESOLUTION, - }; - - private final GalleryApp mApplication; - - public int durationInSec; - - public LocalVideo(Path path, GalleryApp application, Cursor cursor) { - super(path, nextVersionNumber()); - mApplication = application; - loadFromCursor(cursor); - } - - public LocalVideo(Path path, GalleryApp context, int id) { - super(path, nextVersionNumber()); - mApplication = context; - ContentResolver resolver = mApplication.getContentResolver(); - Uri uri = Video.Media.EXTERNAL_CONTENT_URI; - Cursor cursor = LocalAlbum.getItemCursor(resolver, uri, PROJECTION, id); - if (cursor == null) { - throw new RuntimeException("cannot get cursor for: " + path); - } - try { - if (cursor.moveToNext()) { - loadFromCursor(cursor); - } else { - throw new RuntimeException("cannot find data for: " + path); - } - } finally { - cursor.close(); - } - } - - private void loadFromCursor(Cursor cursor) { - id = cursor.getInt(INDEX_ID); - caption = cursor.getString(INDEX_CAPTION); - mimeType = cursor.getString(INDEX_MIME_TYPE); - latitude = cursor.getDouble(INDEX_LATITUDE); - longitude = cursor.getDouble(INDEX_LONGITUDE); - dateTakenInMs = cursor.getLong(INDEX_DATE_TAKEN); - dateAddedInSec = cursor.getLong(INDEX_DATE_ADDED); - dateModifiedInSec = cursor.getLong(INDEX_DATE_MODIFIED); - filePath = cursor.getString(INDEX_DATA); - durationInSec = cursor.getInt(INDEX_DURATION) / 1000; - bucketId = cursor.getInt(INDEX_BUCKET_ID); - fileSize = cursor.getLong(INDEX_SIZE); - parseResolution(cursor.getString(INDEX_RESOLUTION)); - } - - private void parseResolution(String resolution) { - if (resolution == null) return; - int m = resolution.indexOf('x'); - if (m == -1) return; - try { - int w = Integer.parseInt(resolution.substring(0, m)); - int h = Integer.parseInt(resolution.substring(m + 1)); - width = w; - height = h; - } catch (Throwable t) { - Log.w(TAG, t); - } - } - - @Override - protected boolean updateFromCursor(Cursor cursor) { - UpdateHelper uh = new UpdateHelper(); - id = uh.update(id, cursor.getInt(INDEX_ID)); - caption = uh.update(caption, cursor.getString(INDEX_CAPTION)); - mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE)); - latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE)); - longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE)); - dateTakenInMs = uh.update( - dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN)); - dateAddedInSec = uh.update( - dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED)); - dateModifiedInSec = uh.update( - dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED)); - filePath = uh.update(filePath, cursor.getString(INDEX_DATA)); - durationInSec = uh.update( - durationInSec, cursor.getInt(INDEX_DURATION) / 1000); - bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID)); - fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE)); - return uh.isUpdated(); - } - - @Override - public Job<Bitmap> requestImage(int type) { - return new LocalVideoRequest(mApplication, getPath(), dateModifiedInSec, - type, filePath); - } - - public static class LocalVideoRequest extends ImageCacheRequest { - private String mLocalFilePath; - - LocalVideoRequest(GalleryApp application, Path path, long timeModified, - int type, String localFilePath) { - super(application, path, timeModified, type, - MediaItem.getTargetSize(type)); - mLocalFilePath = localFilePath; - } - - @Override - public Bitmap onDecodeOriginal(JobContext jc, int type) { - Bitmap bitmap = BitmapUtils.createVideoThumbnail(mLocalFilePath); - if (bitmap == null || jc.isCancelled()) return null; - return bitmap; - } - } - - @Override - public Job<BitmapRegionDecoder> requestLargeImage() { - throw new UnsupportedOperationException("Cannot regquest a large image" - + " to a local video!"); - } - - @Override - public int getSupportedOperations() { - return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM | SUPPORT_MUTE; - } - - @Override - public void delete() { - GalleryUtils.assertNotInRenderThread(); - Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI; - mApplication.getContentResolver().delete(baseUri, "_id=?", - new String[]{String.valueOf(id)}); - } - - @Override - public void rotate(int degrees) { - // TODO - } - - @Override - public Uri getContentUri() { - Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI; - return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); - } - - @Override - public Uri getPlayUri() { - return getContentUri(); - } - - @Override - public int getMediaType() { - return MEDIA_TYPE_VIDEO; - } - - @Override - public MediaDetails getDetails() { - MediaDetails details = super.getDetails(); - int s = durationInSec; - if (s > 0) { - details.addDetail(MediaDetails.INDEX_DURATION, GalleryUtils.formatDuration( - mApplication.getAndroidContext(), durationInSec)); - } - return details; - } - - @Override - public int getWidth() { - return width; - } - - @Override - public int getHeight() { - return height; - } - - @Override - public String getFilePath() { - return filePath; - } -} diff --git a/src/com/android/gallery3d/data/LocationClustering.java b/src/com/android/gallery3d/data/LocationClustering.java deleted file mode 100644 index 540322a33..000000000 --- a/src/com/android/gallery3d/data/LocationClustering.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * 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.Context; -import android.os.Handler; -import android.os.Looper; -import android.util.FloatMath; -import android.widget.Toast; - -import com.android.gallery3d.R; -import com.android.gallery3d.util.GalleryUtils; -import com.android.gallery3d.util.ReverseGeocoder; - -import java.util.ArrayList; - -class LocationClustering extends Clustering { - @SuppressWarnings("unused") - private static final String TAG = "LocationClustering"; - - private static final int MIN_GROUPS = 1; - private static final int MAX_GROUPS = 20; - private static final int MAX_ITERATIONS = 30; - - // If the total distance change is less than this ratio, stop iterating. - private static final float STOP_CHANGE_RATIO = 0.01f; - private Context mContext; - private ArrayList<ArrayList<SmallItem>> mClusters; - private ArrayList<String> mNames; - private String mNoLocationString; - private Handler mHandler; - - private static class Point { - public Point(double lat, double lng) { - latRad = Math.toRadians(lat); - lngRad = Math.toRadians(lng); - } - public Point() {} - public double latRad, lngRad; - } - - private static class SmallItem { - Path path; - double lat, lng; - } - - public LocationClustering(Context context) { - mContext = context; - mNoLocationString = mContext.getResources().getString(R.string.no_location); - mHandler = new Handler(Looper.getMainLooper()); - } - - @Override - public void run(MediaSet baseSet) { - final int total = baseSet.getTotalMediaItemCount(); - final SmallItem[] buf = new SmallItem[total]; - // Separate items to two sets: with or without lat-long. - final double[] latLong = new double[2]; - baseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - if (index < 0 || index >= total) return; - SmallItem s = new SmallItem(); - s.path = item.getPath(); - item.getLatLong(latLong); - s.lat = latLong[0]; - s.lng = latLong[1]; - buf[index] = s; - } - }); - - final ArrayList<SmallItem> withLatLong = new ArrayList<SmallItem>(); - final ArrayList<SmallItem> withoutLatLong = new ArrayList<SmallItem>(); - final ArrayList<Point> points = new ArrayList<Point>(); - for (int i = 0; i < total; i++) { - SmallItem s = buf[i]; - if (s == null) continue; - if (GalleryUtils.isValidLocation(s.lat, s.lng)) { - withLatLong.add(s); - points.add(new Point(s.lat, s.lng)); - } else { - withoutLatLong.add(s); - } - } - - ArrayList<ArrayList<SmallItem>> clusters = new ArrayList<ArrayList<SmallItem>>(); - - int m = withLatLong.size(); - if (m > 0) { - // cluster the items with lat-long - Point[] pointsArray = new Point[m]; - pointsArray = points.toArray(pointsArray); - int[] bestK = new int[1]; - int[] index = kMeans(pointsArray, bestK); - - for (int i = 0; i < bestK[0]; i++) { - clusters.add(new ArrayList<SmallItem>()); - } - - for (int i = 0; i < m; i++) { - clusters.get(index[i]).add(withLatLong.get(i)); - } - } - - ReverseGeocoder geocoder = new ReverseGeocoder(mContext); - mNames = new ArrayList<String>(); - boolean hasUnresolvedAddress = false; - mClusters = new ArrayList<ArrayList<SmallItem>>(); - for (ArrayList<SmallItem> cluster : clusters) { - String name = generateName(cluster, geocoder); - if (name != null) { - mNames.add(name); - mClusters.add(cluster); - } else { - // move cluster-i to no location cluster - withoutLatLong.addAll(cluster); - hasUnresolvedAddress = true; - } - } - - if (withoutLatLong.size() > 0) { - mNames.add(mNoLocationString); - mClusters.add(withoutLatLong); - } - - if (hasUnresolvedAddress) { - mHandler.post(new Runnable() { - @Override - public void run() { - Toast.makeText(mContext, R.string.no_connectivity, - Toast.LENGTH_LONG).show(); - } - }); - } - } - - private static String generateName(ArrayList<SmallItem> items, - ReverseGeocoder geocoder) { - ReverseGeocoder.SetLatLong set = new ReverseGeocoder.SetLatLong(); - - int n = items.size(); - for (int i = 0; i < n; i++) { - SmallItem item = items.get(i); - double itemLatitude = item.lat; - double itemLongitude = item.lng; - - if (set.mMinLatLatitude > itemLatitude) { - set.mMinLatLatitude = itemLatitude; - set.mMinLatLongitude = itemLongitude; - } - if (set.mMaxLatLatitude < itemLatitude) { - set.mMaxLatLatitude = itemLatitude; - set.mMaxLatLongitude = itemLongitude; - } - if (set.mMinLonLongitude > itemLongitude) { - set.mMinLonLatitude = itemLatitude; - set.mMinLonLongitude = itemLongitude; - } - if (set.mMaxLonLongitude < itemLongitude) { - set.mMaxLonLatitude = itemLatitude; - set.mMaxLonLongitude = itemLongitude; - } - } - - return geocoder.computeAddress(set); - } - - @Override - public int getNumberOfClusters() { - return mClusters.size(); - } - - @Override - public ArrayList<Path> getCluster(int index) { - ArrayList<SmallItem> items = mClusters.get(index); - ArrayList<Path> result = new ArrayList<Path>(items.size()); - for (int i = 0, n = items.size(); i < n; i++) { - result.add(items.get(i).path); - } - return result; - } - - @Override - public String getClusterName(int index) { - return mNames.get(index); - } - - // Input: n points - // Output: the best k is stored in bestK[0], and the return value is the - // an array which specifies the group that each point belongs (0 to k - 1). - private static int[] kMeans(Point points[], int[] bestK) { - int n = points.length; - - // min and max number of groups wanted - int minK = Math.min(n, MIN_GROUPS); - int maxK = Math.min(n, MAX_GROUPS); - - Point[] center = new Point[maxK]; // center of each group. - Point[] groupSum = new Point[maxK]; // sum of points in each group. - int[] groupCount = new int[maxK]; // number of points in each group. - int[] grouping = new int[n]; // The group assignment for each point. - - for (int i = 0; i < maxK; i++) { - center[i] = new Point(); - groupSum[i] = new Point(); - } - - // The score we want to minimize is: - // (sum of distance from each point to its group center) * sqrt(k). - float bestScore = Float.MAX_VALUE; - // The best group assignment up to now. - int[] bestGrouping = new int[n]; - // The best K up to now. - bestK[0] = 1; - - float lastDistance = 0; - float totalDistance = 0; - - for (int k = minK; k <= maxK; k++) { - // step 1: (arbitrarily) pick k points as the initial centers. - int delta = n / k; - for (int i = 0; i < k; i++) { - Point p = points[i * delta]; - center[i].latRad = p.latRad; - center[i].lngRad = p.lngRad; - } - - for (int iter = 0; iter < MAX_ITERATIONS; iter++) { - // step 2: assign each point to the nearest center. - for (int i = 0; i < k; i++) { - groupSum[i].latRad = 0; - groupSum[i].lngRad = 0; - groupCount[i] = 0; - } - totalDistance = 0; - - for (int i = 0; i < n; i++) { - Point p = points[i]; - float bestDistance = Float.MAX_VALUE; - int bestIndex = 0; - for (int j = 0; j < k; j++) { - float distance = (float) GalleryUtils.fastDistanceMeters( - p.latRad, p.lngRad, center[j].latRad, center[j].lngRad); - // We may have small non-zero distance introduced by - // floating point calculation, so zero out small - // distances less than 1 meter. - if (distance < 1) { - distance = 0; - } - if (distance < bestDistance) { - bestDistance = distance; - bestIndex = j; - } - } - grouping[i] = bestIndex; - groupCount[bestIndex]++; - groupSum[bestIndex].latRad += p.latRad; - groupSum[bestIndex].lngRad += p.lngRad; - totalDistance += bestDistance; - } - - // step 3: calculate new centers - for (int i = 0; i < k; i++) { - if (groupCount[i] > 0) { - center[i].latRad = groupSum[i].latRad / groupCount[i]; - center[i].lngRad = groupSum[i].lngRad / groupCount[i]; - } - } - - if (totalDistance == 0 || (Math.abs(lastDistance - totalDistance) - / totalDistance) < STOP_CHANGE_RATIO) { - break; - } - lastDistance = totalDistance; - } - - // step 4: remove empty groups and reassign group number - int reassign[] = new int[k]; - int realK = 0; - for (int i = 0; i < k; i++) { - if (groupCount[i] > 0) { - reassign[i] = realK++; - } - } - - // step 5: calculate the final score - float score = totalDistance * FloatMath.sqrt(realK); - - if (score < bestScore) { - bestScore = score; - bestK[0] = realK; - for (int i = 0; i < n; i++) { - bestGrouping[i] = reassign[grouping[i]]; - } - if (score == 0) { - break; - } - } - } - return bestGrouping; - } -} diff --git a/src/com/android/gallery3d/data/Log.java b/src/com/android/gallery3d/data/Log.java deleted file mode 100644 index 3384eb66c..000000000 --- a/src/com/android/gallery3d/data/Log.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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; - -public class Log { - public static int v(String tag, String msg) { - return android.util.Log.v(tag, msg); - } - public static int v(String tag, String msg, Throwable tr) { - return android.util.Log.v(tag, msg, tr); - } - public static int d(String tag, String msg) { - return android.util.Log.d(tag, msg); - } - public static int d(String tag, String msg, Throwable tr) { - return android.util.Log.d(tag, msg, tr); - } - public static int i(String tag, String msg) { - return android.util.Log.i(tag, msg); - } - public static int i(String tag, String msg, Throwable tr) { - return android.util.Log.i(tag, msg, tr); - } - public static int w(String tag, String msg) { - return android.util.Log.w(tag, msg); - } - public static int w(String tag, String msg, Throwable tr) { - return android.util.Log.w(tag, msg, tr); - } - public static int w(String tag, Throwable tr) { - return android.util.Log.w(tag, tr); - } - public static int e(String tag, String msg) { - return android.util.Log.e(tag, msg); - } - public static int e(String tag, String msg, Throwable tr) { - return android.util.Log.e(tag, msg, tr); - } -} diff --git a/src/com/android/gallery3d/data/MediaDetails.java b/src/com/android/gallery3d/data/MediaDetails.java deleted file mode 100644 index cac524b88..000000000 --- a/src/com/android/gallery3d/data/MediaDetails.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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 com.android.gallery3d.R; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.exif.ExifInterface; -import com.android.gallery3d.exif.ExifTag; -import com.android.gallery3d.exif.Rational; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.TreeMap; - -public class MediaDetails implements Iterable<Entry<Integer, Object>> { - @SuppressWarnings("unused") - private static final String TAG = "MediaDetails"; - - private TreeMap<Integer, Object> mDetails = new TreeMap<Integer, Object>(); - private HashMap<Integer, Integer> mUnits = new HashMap<Integer, Integer>(); - - public static final int INDEX_TITLE = 1; - public static final int INDEX_DESCRIPTION = 2; - public static final int INDEX_DATETIME = 3; - public static final int INDEX_LOCATION = 4; - public static final int INDEX_WIDTH = 5; - public static final int INDEX_HEIGHT = 6; - public static final int INDEX_ORIENTATION = 7; - public static final int INDEX_DURATION = 8; - public static final int INDEX_MIMETYPE = 9; - public static final int INDEX_SIZE = 10; - - // for EXIF - public static final int INDEX_MAKE = 100; - public static final int INDEX_MODEL = 101; - public static final int INDEX_FLASH = 102; - public static final int INDEX_FOCAL_LENGTH = 103; - public static final int INDEX_WHITE_BALANCE = 104; - public static final int INDEX_APERTURE = 105; - public static final int INDEX_SHUTTER_SPEED = 106; - public static final int INDEX_EXPOSURE_TIME = 107; - public static final int INDEX_ISO = 108; - - // Put this last because it may be long. - public static final int INDEX_PATH = 200; - - public static class FlashState { - private static int FLASH_FIRED_MASK = 1; - private static int FLASH_RETURN_MASK = 2 | 4; - private static int FLASH_MODE_MASK = 8 | 16; - private static int FLASH_FUNCTION_MASK = 32; - private static int FLASH_RED_EYE_MASK = 64; - private int mState; - - public FlashState(int state) { - mState = state; - } - - public boolean isFlashFired() { - return (mState & FLASH_FIRED_MASK) != 0; - } - } - - public void addDetail(int index, Object value) { - mDetails.put(index, value); - } - - public Object getDetail(int index) { - return mDetails.get(index); - } - - public int size() { - return mDetails.size(); - } - - @Override - public Iterator<Entry<Integer, Object>> iterator() { - return mDetails.entrySet().iterator(); - } - - public void setUnit(int index, int unit) { - mUnits.put(index, unit); - } - - public boolean hasUnit(int index) { - return mUnits.containsKey(index); - } - - public int getUnit(int index) { - return mUnits.get(index); - } - - private static void setExifData(MediaDetails details, ExifTag tag, - int key) { - if (tag != null) { - String value = null; - int type = tag.getDataType(); - if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) { - value = String.valueOf(tag.getValueAsRational(0).toDouble()); - } else if (type == ExifTag.TYPE_ASCII) { - value = tag.getValueAsString(); - } else { - value = String.valueOf(tag.forceGetValueAsLong(0)); - } - if (key == MediaDetails.INDEX_FLASH) { - MediaDetails.FlashState state = new MediaDetails.FlashState( - Integer.valueOf(value.toString())); - details.addDetail(key, state); - } else { - details.addDetail(key, value); - } - } - } - - public static void extractExifInfo(MediaDetails details, String filePath) { - - ExifInterface exif = new ExifInterface(); - try { - exif.readExif(filePath); - } catch (FileNotFoundException e) { - Log.w(TAG, "Could not find file to read exif: " + filePath, e); - } catch (IOException e) { - Log.w(TAG, "Could not read exif from file: " + filePath, e); - } - - setExifData(details, exif.getTag(ExifInterface.TAG_FLASH), - MediaDetails.INDEX_FLASH); - setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_WIDTH), - MediaDetails.INDEX_WIDTH); - setExifData(details, exif.getTag(ExifInterface.TAG_IMAGE_LENGTH), - MediaDetails.INDEX_HEIGHT); - setExifData(details, exif.getTag(ExifInterface.TAG_MAKE), - MediaDetails.INDEX_MAKE); - setExifData(details, exif.getTag(ExifInterface.TAG_MODEL), - MediaDetails.INDEX_MODEL); - setExifData(details, exif.getTag(ExifInterface.TAG_APERTURE_VALUE), - MediaDetails.INDEX_APERTURE); - setExifData(details, exif.getTag(ExifInterface.TAG_ISO_SPEED_RATINGS), - MediaDetails.INDEX_ISO); - setExifData(details, exif.getTag(ExifInterface.TAG_WHITE_BALANCE), - MediaDetails.INDEX_WHITE_BALANCE); - setExifData(details, exif.getTag(ExifInterface.TAG_EXPOSURE_TIME), - MediaDetails.INDEX_EXPOSURE_TIME); - ExifTag focalTag = exif.getTag(ExifInterface.TAG_FOCAL_LENGTH); - if (focalTag != null) { - details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH, - focalTag.getValueAsRational(0).toDouble()); - details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm); - } - } -} diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java deleted file mode 100644 index 59ea86551..000000000 --- a/src/com/android/gallery3d/data/MediaItem.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.graphics.Bitmap; -import android.graphics.BitmapRegionDecoder; - -import com.android.gallery3d.common.ApiHelper; -import com.android.gallery3d.ui.ScreenNail; -import com.android.gallery3d.util.ThreadPool.Job; - -// MediaItem represents an image or a video item. -public abstract class MediaItem extends MediaObject { - // NOTE: These type numbers are stored in the image cache, so it should not - // not be changed without resetting the cache. - public static final int TYPE_THUMBNAIL = 1; - public static final int TYPE_MICROTHUMBNAIL = 2; - - public static final int CACHED_IMAGE_QUALITY = 95; - - public static final int IMAGE_READY = 0; - public static final int IMAGE_WAIT = 1; - public static final int IMAGE_ERROR = -1; - - public static final String MIME_TYPE_JPEG = "image/jpeg"; - - private static final int BYTESBUFFE_POOL_SIZE = 4; - private static final int BYTESBUFFER_SIZE = 200 * 1024; - - private static int sMicrothumbnailTargetSize = 200; - private static final BytesBufferPool sMicroThumbBufferPool = - new BytesBufferPool(BYTESBUFFE_POOL_SIZE, BYTESBUFFER_SIZE); - - private static int sThumbnailTargetSize = 640; - - // TODO: fix default value for latlng and change this. - public static final double INVALID_LATLNG = 0f; - - public abstract Job<Bitmap> requestImage(int type); - public abstract Job<BitmapRegionDecoder> requestLargeImage(); - - public MediaItem(Path path, long version) { - super(path, version); - } - - public long getDateInMs() { - return 0; - } - - public String getName() { - return null; - } - - public void getLatLong(double[] latLong) { - latLong[0] = INVALID_LATLNG; - latLong[1] = INVALID_LATLNG; - } - - public String[] getTags() { - return null; - } - - public Face[] getFaces() { - return null; - } - - // The rotation of the full-resolution image. By default, it returns the value of - // getRotation(). - public int getFullImageRotation() { - return getRotation(); - } - - public int getRotation() { - return 0; - } - - public long getSize() { - return 0; - } - - public abstract String getMimeType(); - - public String getFilePath() { - return ""; - } - - // Returns width and height of the media item. - // Returns 0, 0 if the information is not available. - public abstract int getWidth(); - public abstract int getHeight(); - - // This is an alternative for requestImage() in PhotoPage. If this - // is implemented, you don't need to implement requestImage(). - public ScreenNail getScreenNail() { - return null; - } - - public static int getTargetSize(int type) { - switch (type) { - case TYPE_THUMBNAIL: - return sThumbnailTargetSize; - case TYPE_MICROTHUMBNAIL: - return sMicrothumbnailTargetSize; - default: - throw new RuntimeException( - "should only request thumb/microthumb from cache"); - } - } - - public static BytesBufferPool getBytesBufferPool() { - return sMicroThumbBufferPool; - } - - public static void setThumbnailSizes(int size, int microSize) { - sThumbnailTargetSize = size; - if (sMicrothumbnailTargetSize != microSize) { - sMicrothumbnailTargetSize = microSize; - } - } -} diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java deleted file mode 100644 index 270d4cf0b..000000000 --- a/src/com/android/gallery3d/data/MediaObject.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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.net.Uri; - -public abstract class MediaObject { - @SuppressWarnings("unused") - private static final String TAG = "MediaObject"; - public static final long INVALID_DATA_VERSION = -1; - - // These are the bits returned from getSupportedOperations(): - public static final int SUPPORT_DELETE = 1 << 0; - public static final int SUPPORT_ROTATE = 1 << 1; - public static final int SUPPORT_SHARE = 1 << 2; - public static final int SUPPORT_CROP = 1 << 3; - public static final int SUPPORT_SHOW_ON_MAP = 1 << 4; - public static final int SUPPORT_SETAS = 1 << 5; - public static final int SUPPORT_FULL_IMAGE = 1 << 6; - public static final int SUPPORT_PLAY = 1 << 7; - public static final int SUPPORT_CACHE = 1 << 8; - public static final int SUPPORT_EDIT = 1 << 9; - public static final int SUPPORT_INFO = 1 << 10; - public static final int SUPPORT_TRIM = 1 << 11; - public static final int SUPPORT_UNLOCK = 1 << 12; - public static final int SUPPORT_BACK = 1 << 13; - public static final int SUPPORT_ACTION = 1 << 14; - public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 15; - public static final int SUPPORT_MUTE = 1 << 16; - public static final int SUPPORT_ALL = 0xffffffff; - - // These are the bits returned from getMediaType(): - public static final int MEDIA_TYPE_UNKNOWN = 1; - public static final int MEDIA_TYPE_IMAGE = 2; - public static final int MEDIA_TYPE_VIDEO = 4; - public static final int MEDIA_TYPE_ALL = MEDIA_TYPE_IMAGE | MEDIA_TYPE_VIDEO; - - public static final String MEDIA_TYPE_IMAGE_STRING = "image"; - public static final String MEDIA_TYPE_VIDEO_STRING = "video"; - public static final String MEDIA_TYPE_ALL_STRING = "all"; - - // These are flags for cache() and return values for getCacheFlag(): - public static final int CACHE_FLAG_NO = 0; - public static final int CACHE_FLAG_SCREENNAIL = 1; - public static final int CACHE_FLAG_FULL = 2; - - // These are return values for getCacheStatus(): - public static final int CACHE_STATUS_NOT_CACHED = 0; - public static final int CACHE_STATUS_CACHING = 1; - public static final int CACHE_STATUS_CACHED_SCREENNAIL = 2; - public static final int CACHE_STATUS_CACHED_FULL = 3; - - private static long sVersionSerial = 0; - - protected long mDataVersion; - - protected final Path mPath; - - public interface PanoramaSupportCallback { - void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama, - boolean isPanorama360); - } - - public MediaObject(Path path, long version) { - path.setObject(this); - mPath = path; - mDataVersion = version; - } - - public Path getPath() { - return mPath; - } - - public int getSupportedOperations() { - return 0; - } - - public void getPanoramaSupport(PanoramaSupportCallback callback) { - callback.panoramaInfoAvailable(this, false, false); - } - - public void clearCachedPanoramaSupport() { - } - - public void delete() { - throw new UnsupportedOperationException(); - } - - public void rotate(int degrees) { - throw new UnsupportedOperationException(); - } - - public Uri getContentUri() { - String className = getClass().getName(); - Log.e(TAG, "Class " + className + "should implement getContentUri."); - Log.e(TAG, "The object was created from path: " + getPath()); - throw new UnsupportedOperationException(); - } - - public Uri getPlayUri() { - throw new UnsupportedOperationException(); - } - - public int getMediaType() { - return MEDIA_TYPE_UNKNOWN; - } - - public MediaDetails getDetails() { - MediaDetails details = new MediaDetails(); - return details; - } - - public long getDataVersion() { - return mDataVersion; - } - - public int getCacheFlag() { - return CACHE_FLAG_NO; - } - - public int getCacheStatus() { - throw new UnsupportedOperationException(); - } - - public long getCacheSize() { - throw new UnsupportedOperationException(); - } - - public void cache(int flag) { - throw new UnsupportedOperationException(); - } - - public static synchronized long nextVersionNumber() { - return ++MediaObject.sVersionSerial; - } - - public static int getTypeFromString(String s) { - if (MEDIA_TYPE_ALL_STRING.equals(s)) return MediaObject.MEDIA_TYPE_ALL; - if (MEDIA_TYPE_IMAGE_STRING.equals(s)) return MediaObject.MEDIA_TYPE_IMAGE; - if (MEDIA_TYPE_VIDEO_STRING.equals(s)) return MediaObject.MEDIA_TYPE_VIDEO; - throw new IllegalArgumentException(s); - } - - public static String getTypeString(int type) { - switch (type) { - case MEDIA_TYPE_IMAGE: return MEDIA_TYPE_IMAGE_STRING; - case MEDIA_TYPE_VIDEO: return MEDIA_TYPE_VIDEO_STRING; - case MEDIA_TYPE_ALL: return MEDIA_TYPE_ALL_STRING; - } - throw new IllegalArgumentException(); - } -} diff --git a/src/com/android/gallery3d/data/MediaSet.java b/src/com/android/gallery3d/data/MediaSet.java deleted file mode 100644 index 683aa6b32..000000000 --- a/src/com/android/gallery3d/data/MediaSet.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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 com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.Future; - -import java.util.ArrayList; -import java.util.WeakHashMap; - -// MediaSet is a directory-like data structure. -// It contains MediaItems and sub-MediaSets. -// -// The primary interface are: -// getMediaItemCount(), getMediaItem() and -// getSubMediaSetCount(), getSubMediaSet(). -// -// getTotalMediaItemCount() returns the number of all MediaItems, including -// those in sub-MediaSets. -public abstract class MediaSet extends MediaObject { - @SuppressWarnings("unused") - private static final String TAG = "MediaSet"; - - public static final int MEDIAITEM_BATCH_FETCH_COUNT = 500; - public static final int INDEX_NOT_FOUND = -1; - - public static final int SYNC_RESULT_SUCCESS = 0; - public static final int SYNC_RESULT_CANCELLED = 1; - public static final int SYNC_RESULT_ERROR = 2; - - /** Listener to be used with requestSync(SyncListener). */ - public static interface SyncListener { - /** - * Called when the sync task completed. Completion may be due to normal termination, - * an exception, or cancellation. - * - * @param mediaSet the MediaSet that's done with sync - * @param resultCode one of the SYNC_RESULT_* constants - */ - void onSyncDone(MediaSet mediaSet, int resultCode); - } - - public MediaSet(Path path, long version) { - super(path, version); - } - - public int getMediaItemCount() { - return 0; - } - - // Returns the media items in the range [start, start + count). - // - // The number of media items returned may be less than the specified count - // if there are not enough media items available. The number of - // media items available may not be consistent with the return value of - // getMediaItemCount() because the contents of database may have already - // changed. - public ArrayList<MediaItem> getMediaItem(int start, int count) { - return new ArrayList<MediaItem>(); - } - - public MediaItem getCoverMediaItem() { - ArrayList<MediaItem> items = getMediaItem(0, 1); - if (items.size() > 0) return items.get(0); - for (int i = 0, n = getSubMediaSetCount(); i < n; i++) { - MediaItem cover = getSubMediaSet(i).getCoverMediaItem(); - if (cover != null) return cover; - } - return null; - } - - public int getSubMediaSetCount() { - return 0; - } - - public MediaSet getSubMediaSet(int index) { - throw new IndexOutOfBoundsException(); - } - - public boolean isLeafAlbum() { - return false; - } - - public boolean isCameraRoll() { - return false; - } - - /** - * Method {@link #reload()} may process the loading task in background, this method tells - * its client whether the loading is still in process or not. - */ - public boolean isLoading() { - return false; - } - - public int getTotalMediaItemCount() { - int total = getMediaItemCount(); - for (int i = 0, n = getSubMediaSetCount(); i < n; i++) { - total += getSubMediaSet(i).getTotalMediaItemCount(); - } - return total; - } - - // TODO: we should have better implementation of sub classes - public int getIndexOfItem(Path path, int hint) { - // hint < 0 is handled below - // first, try to find it around the hint - int start = Math.max(0, - hint - MEDIAITEM_BATCH_FETCH_COUNT / 2); - ArrayList<MediaItem> list = getMediaItem( - start, MEDIAITEM_BATCH_FETCH_COUNT); - int index = getIndexOf(path, list); - if (index != INDEX_NOT_FOUND) return start + index; - - // try to find it globally - start = start == 0 ? MEDIAITEM_BATCH_FETCH_COUNT : 0; - list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT); - while (true) { - index = getIndexOf(path, list); - if (index != INDEX_NOT_FOUND) return start + index; - if (list.size() < MEDIAITEM_BATCH_FETCH_COUNT) return INDEX_NOT_FOUND; - start += MEDIAITEM_BATCH_FETCH_COUNT; - list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT); - } - } - - protected int getIndexOf(Path path, ArrayList<MediaItem> list) { - for (int i = 0, n = list.size(); i < n; ++i) { - // item could be null only in ClusterAlbum - MediaObject item = list.get(i); - if (item != null && item.mPath == path) return i; - } - return INDEX_NOT_FOUND; - } - - public abstract String getName(); - - private WeakHashMap<ContentListener, Object> mListeners = - new WeakHashMap<ContentListener, Object>(); - - // NOTE: The MediaSet only keeps a weak reference to the listener. The - // listener is automatically removed when there is no other reference to - // the listener. - public void addContentListener(ContentListener listener) { - mListeners.put(listener, null); - } - - public void removeContentListener(ContentListener listener) { - mListeners.remove(listener); - } - - // This should be called by subclasses when the content is changed. - public void notifyContentChanged() { - for (ContentListener listener : mListeners.keySet()) { - listener.onContentDirty(); - } - } - - // Reload the content. Return the current data version. reload() should be called - // in the same thread as getMediaItem(int, int) and getSubMediaSet(int). - public abstract long reload(); - - @Override - public MediaDetails getDetails() { - MediaDetails details = super.getDetails(); - details.addDetail(MediaDetails.INDEX_TITLE, getName()); - return details; - } - - // Enumerate all media items in this media set (including the ones in sub - // media sets), in an efficient order. ItemConsumer.consumer() will be - // called for each media item with its index. - public void enumerateMediaItems(ItemConsumer consumer) { - enumerateMediaItems(consumer, 0); - } - - public void enumerateTotalMediaItems(ItemConsumer consumer) { - enumerateTotalMediaItems(consumer, 0); - } - - public static interface ItemConsumer { - void consume(int index, MediaItem item); - } - - // The default implementation uses getMediaItem() for enumerateMediaItems(). - // Subclasses may override this and use more efficient implementations. - // Returns the number of items enumerated. - protected int enumerateMediaItems(ItemConsumer consumer, int startIndex) { - int total = getMediaItemCount(); - int start = 0; - while (start < total) { - int count = Math.min(MEDIAITEM_BATCH_FETCH_COUNT, total - start); - ArrayList<MediaItem> items = getMediaItem(start, count); - for (int i = 0, n = items.size(); i < n; i++) { - MediaItem item = items.get(i); - consumer.consume(startIndex + start + i, item); - } - start += count; - } - return total; - } - - // Recursively enumerate all media items under this set. - // Returns the number of items enumerated. - protected int enumerateTotalMediaItems( - ItemConsumer consumer, int startIndex) { - int start = 0; - start += enumerateMediaItems(consumer, startIndex); - int m = getSubMediaSetCount(); - for (int i = 0; i < m; i++) { - start += getSubMediaSet(i).enumerateTotalMediaItems( - consumer, startIndex + start); - } - return start; - } - - /** - * Requests sync on this MediaSet. It returns a Future object that can be used by the caller - * to query the status of the sync. The sync result code is one of the SYNC_RESULT_* constants - * defined in this class and can be obtained by Future.get(). - * - * Subclasses should perform sync on a different thread. - * - * The default implementation here returns a Future stub that does nothing and returns - * SYNC_RESULT_SUCCESS by get(). - */ - public Future<Integer> requestSync(SyncListener listener) { - listener.onSyncDone(this, SYNC_RESULT_SUCCESS); - return FUTURE_STUB; - } - - private static final Future<Integer> FUTURE_STUB = new Future<Integer>() { - @Override - public void cancel() {} - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return true; - } - - @Override - public Integer get() { - return SYNC_RESULT_SUCCESS; - } - - @Override - public void waitDone() {} - }; - - protected Future<Integer> requestSyncOnMultipleSets(MediaSet[] sets, SyncListener listener) { - return new MultiSetSyncFuture(sets, listener); - } - - private class MultiSetSyncFuture implements Future<Integer>, SyncListener { - @SuppressWarnings("hiding") - private static final String TAG = "Gallery.MultiSetSync"; - - private final SyncListener mListener; - private final Future<Integer> mFutures[]; - - private boolean mIsCancelled = false; - private int mResult = -1; - private int mPendingCount; - - @SuppressWarnings("unchecked") - MultiSetSyncFuture(MediaSet[] sets, SyncListener listener) { - mListener = listener; - mPendingCount = sets.length; - mFutures = new Future[sets.length]; - - synchronized (this) { - for (int i = 0, n = sets.length; i < n; ++i) { - mFutures[i] = sets[i].requestSync(this); - Log.d(TAG, " request sync: " + Utils.maskDebugInfo(sets[i].getName())); - } - } - } - - @Override - public synchronized void cancel() { - if (mIsCancelled) return; - mIsCancelled = true; - for (Future<Integer> future : mFutures) future.cancel(); - if (mResult < 0) mResult = SYNC_RESULT_CANCELLED; - } - - @Override - public synchronized boolean isCancelled() { - return mIsCancelled; - } - - @Override - public synchronized boolean isDone() { - return mPendingCount == 0; - } - - @Override - public synchronized Integer get() { - waitDone(); - return mResult; - } - - @Override - public synchronized void waitDone() { - try { - while (!isDone()) wait(); - } catch (InterruptedException e) { - Log.d(TAG, "waitDone() interrupted"); - } - } - - // SyncListener callback - @Override - public void onSyncDone(MediaSet mediaSet, int resultCode) { - SyncListener listener = null; - synchronized (this) { - if (resultCode == SYNC_RESULT_ERROR) mResult = SYNC_RESULT_ERROR; - --mPendingCount; - if (mPendingCount == 0) { - listener = mListener; - notifyAll(); - } - Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName()) - + " #pending=" + mPendingCount); - } - if (listener != null) listener.onSyncDone(MediaSet.this, mResult); - } - } -} diff --git a/src/com/android/gallery3d/data/MediaSource.java b/src/com/android/gallery3d/data/MediaSource.java deleted file mode 100644 index 95901283b..000000000 --- a/src/com/android/gallery3d/data/MediaSource.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.net.Uri; - -import com.android.gallery3d.data.MediaSet.ItemConsumer; - -import java.util.ArrayList; - -public abstract class MediaSource { - private static final String TAG = "MediaSource"; - private String mPrefix; - - protected MediaSource(String prefix) { - mPrefix = prefix; - } - - public String getPrefix() { - return mPrefix; - } - - public Path findPathByUri(Uri uri, String type) { - return null; - } - - public abstract MediaObject createMediaObject(Path path); - - public void pause() { - } - - public void resume() { - } - - public Path getDefaultSetOf(Path item) { - return null; - } - - public long getTotalUsedCacheSize() { - return 0; - } - - public long getTotalTargetCacheSize() { - return 0; - } - - public static class PathId { - public PathId(Path path, int id) { - this.path = path; - this.id = id; - } - public Path path; - public int id; - } - - // Maps a list of Paths (all belong to this MediaSource) to MediaItems, - // and invoke consumer.consume() for each MediaItem with the given id. - // - // This default implementation uses getMediaObject for each Path. Subclasses - // may override this and provide more efficient implementation (like - // batching the database query). - public void mapMediaItems(ArrayList<PathId> list, ItemConsumer consumer) { - int n = list.size(); - for (int i = 0; i < n; i++) { - PathId pid = list.get(i); - MediaObject obj; - synchronized (DataManager.LOCK) { - obj = pid.path.getObject(); - if (obj == null) { - try { - obj = createMediaObject(pid.path); - } catch (Throwable th) { - Log.w(TAG, "cannot create media object: " + pid.path, th); - } - } - } - if (obj != null) { - consumer.consume(pid.id, (MediaItem) obj); - } - } - } -} diff --git a/src/com/android/gallery3d/data/MtpClient.java b/src/com/android/gallery3d/data/MtpClient.java deleted file mode 100644 index 737b5b60d..000000000 --- a/src/com/android/gallery3d/data/MtpClient.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * 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.annotation.TargetApi; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.hardware.usb.UsbConstants; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; -import android.mtp.MtpDevice; -import android.mtp.MtpObjectInfo; -import android.mtp.MtpStorageInfo; -import android.util.Log; - -import com.android.gallery3d.common.ApiHelper; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * This class helps an application manage a list of connected MTP or PTP devices. - * It listens for MTP devices being attached and removed from the USB host bus - * and notifies the application when the MTP device list changes. - */ -@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1) -public class MtpClient { - - private static final String TAG = "MtpClient"; - - private static final String ACTION_USB_PERMISSION = - "android.mtp.MtpClient.action.USB_PERMISSION"; - - private final Context mContext; - private final UsbManager mUsbManager; - private final ArrayList<Listener> mListeners = new ArrayList<Listener>(); - // mDevices contains all MtpDevices that have been seen by our client, - // so we can inform when the device has been detached. - // mDevices is also used for synchronization in this class. - private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>(); - // List of MTP devices we should not try to open for which we are currently - // asking for permission to open. - private final ArrayList<String> mRequestPermissionDevices = new ArrayList<String>(); - // List of MTP devices we should not try to open. - // We add devices to this list if the user canceled a permission request or we were - // unable to open the device. - private final ArrayList<String> mIgnoredDevices = new ArrayList<String>(); - - private final PendingIntent mPermissionIntent; - - private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); - String deviceName = usbDevice.getDeviceName(); - - synchronized (mDevices) { - MtpDevice mtpDevice = mDevices.get(deviceName); - - if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { - if (mtpDevice == null) { - mtpDevice = openDeviceLocked(usbDevice); - } - if (mtpDevice != null) { - for (Listener listener : mListeners) { - listener.deviceAdded(mtpDevice); - } - } - } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { - if (mtpDevice != null) { - mDevices.remove(deviceName); - mRequestPermissionDevices.remove(deviceName); - mIgnoredDevices.remove(deviceName); - for (Listener listener : mListeners) { - listener.deviceRemoved(mtpDevice); - } - } - } else if (ACTION_USB_PERMISSION.equals(action)) { - mRequestPermissionDevices.remove(deviceName); - boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, - false); - Log.d(TAG, "ACTION_USB_PERMISSION: " + permission); - if (permission) { - if (mtpDevice == null) { - mtpDevice = openDeviceLocked(usbDevice); - } - if (mtpDevice != null) { - for (Listener listener : mListeners) { - listener.deviceAdded(mtpDevice); - } - } - } else { - // so we don't ask for permission again - mIgnoredDevices.add(deviceName); - } - } - } - } - }; - - /** - * An interface for being notified when MTP or PTP devices are attached - * or removed. In the current implementation, only PTP devices are supported. - */ - public interface Listener { - /** - * Called when a new device has been added - * - * @param device the new device that was added - */ - public void deviceAdded(MtpDevice device); - - /** - * Called when a new device has been removed - * - * @param device the device that was removed - */ - public void deviceRemoved(MtpDevice device); - } - - /** - * Tests to see if a {@link android.hardware.usb.UsbDevice} - * supports the PTP protocol (typically used by digital cameras) - * - * @param device the device to test - * @return true if the device is a PTP device. - */ - static public boolean isCamera(UsbDevice device) { - int count = device.getInterfaceCount(); - for (int i = 0; i < count; i++) { - UsbInterface intf = device.getInterface(i); - if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE && - intf.getInterfaceSubclass() == 1 && - intf.getInterfaceProtocol() == 1) { - return true; - } - } - return false; - } - - /** - * MtpClient constructor - * - * @param context the {@link android.content.Context} to use for the MtpClient - */ - public MtpClient(Context context) { - mContext = context; - mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE); - mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0); - IntentFilter filter = new IntentFilter(); - filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); - filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); - filter.addAction(ACTION_USB_PERMISSION); - context.registerReceiver(mUsbReceiver, filter); - } - - /** - * Opens the {@link android.hardware.usb.UsbDevice} for an MTP or PTP - * device and return an {@link android.mtp.MtpDevice} for it. - * - * @param usbDevice the device to open - * @return an MtpDevice for the device. - */ - private MtpDevice openDeviceLocked(UsbDevice usbDevice) { - String deviceName = usbDevice.getDeviceName(); - - // don't try to open devices that we have decided to ignore - // or are currently asking permission for - if (isCamera(usbDevice) && !mIgnoredDevices.contains(deviceName) - && !mRequestPermissionDevices.contains(deviceName)) { - if (!mUsbManager.hasPermission(usbDevice)) { - mUsbManager.requestPermission(usbDevice, mPermissionIntent); - mRequestPermissionDevices.add(deviceName); - } else { - UsbDeviceConnection connection = mUsbManager.openDevice(usbDevice); - if (connection != null) { - MtpDevice mtpDevice = new MtpDevice(usbDevice); - if (mtpDevice.open(connection)) { - mDevices.put(usbDevice.getDeviceName(), mtpDevice); - return mtpDevice; - } else { - // so we don't try to open it again - mIgnoredDevices.add(deviceName); - } - } else { - // so we don't try to open it again - mIgnoredDevices.add(deviceName); - } - } - } - return null; - } - - /** - * Closes all resources related to the MtpClient object - */ - public void close() { - mContext.unregisterReceiver(mUsbReceiver); - } - - /** - * Registers a {@link com.android.gallery3d.data.MtpClient.Listener} interface to receive - * notifications when MTP or PTP devices are added or removed. - * - * @param listener the listener to register - */ - public void addListener(Listener listener) { - synchronized (mDevices) { - if (!mListeners.contains(listener)) { - mListeners.add(listener); - } - } - } - - /** - * Unregisters a {@link com.android.gallery3d.data.MtpClient.Listener} interface. - * - * @param listener the listener to unregister - */ - public void removeListener(Listener listener) { - synchronized (mDevices) { - mListeners.remove(listener); - } - } - - /** - * Retrieves an {@link android.mtp.MtpDevice} object for the USB device - * with the given name. - * - * @param deviceName the name of the USB device - * @return the MtpDevice, or null if it does not exist - */ - public MtpDevice getDevice(String deviceName) { - synchronized (mDevices) { - return mDevices.get(deviceName); - } - } - - /** - * Retrieves an {@link android.mtp.MtpDevice} object for the USB device - * with the given ID. - * - * @param id the ID of the USB device - * @return the MtpDevice, or null if it does not exist - */ - public MtpDevice getDevice(int id) { - synchronized (mDevices) { - return mDevices.get(UsbDevice.getDeviceName(id)); - } - } - - /** - * Retrieves a list of all currently connected {@link android.mtp.MtpDevice}. - * - * @return the list of MtpDevices - */ - public List<MtpDevice> getDeviceList() { - synchronized (mDevices) { - // Query the USB manager since devices might have attached - // before we added our listener. - for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) { - if (mDevices.get(usbDevice.getDeviceName()) == null) { - openDeviceLocked(usbDevice); - } - } - - return new ArrayList<MtpDevice>(mDevices.values()); - } - } - - /** - * Retrieves a list of all {@link android.mtp.MtpStorageInfo} - * for the MTP or PTP device with the given USB device name - * - * @param deviceName the name of the USB device - * @return the list of MtpStorageInfo - */ - public List<MtpStorageInfo> getStorageList(String deviceName) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return null; - } - int[] storageIds = device.getStorageIds(); - if (storageIds == null) { - return null; - } - - int length = storageIds.length; - ArrayList<MtpStorageInfo> storageList = new ArrayList<MtpStorageInfo>(length); - for (int i = 0; i < length; i++) { - MtpStorageInfo info = device.getStorageInfo(storageIds[i]); - if (info == null) { - Log.w(TAG, "getStorageInfo failed"); - } else { - storageList.add(info); - } - } - return storageList; - } - - /** - * Retrieves the {@link android.mtp.MtpObjectInfo} for an object on - * the MTP or PTP device with the given USB device name with the given - * object handle - * - * @param deviceName the name of the USB device - * @param objectHandle handle of the object to query - * @return the MtpObjectInfo - */ - public MtpObjectInfo getObjectInfo(String deviceName, int objectHandle) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return null; - } - return device.getObjectInfo(objectHandle); - } - - /** - * Deletes an object on the MTP or PTP device with the given USB device name. - * - * @param deviceName the name of the USB device - * @param objectHandle handle of the object to delete - * @return true if the deletion succeeds - */ - public boolean deleteObject(String deviceName, int objectHandle) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return false; - } - return device.deleteObject(objectHandle); - } - - /** - * Retrieves a list of {@link android.mtp.MtpObjectInfo} for all objects - * on the MTP or PTP device with the given USB device name and given storage ID - * and/or object handle. - * If the object handle is zero, then all objects in the root of the storage unit - * will be returned. Otherwise, all immediate children of the object will be returned. - * If the storage ID is also zero, then all objects on all storage units will be returned. - * - * @param deviceName the name of the USB device - * @param storageId the ID of the storage unit to query, or zero for all - * @param objectHandle the handle of the parent object to query, or zero for the storage root - * @return the list of MtpObjectInfo - */ - public List<MtpObjectInfo> getObjectList(String deviceName, int storageId, int objectHandle) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return null; - } - if (objectHandle == 0) { - // all objects in root of storage - objectHandle = 0xFFFFFFFF; - } - int[] handles = device.getObjectHandles(storageId, 0, objectHandle); - if (handles == null) { - return null; - } - - int length = handles.length; - ArrayList<MtpObjectInfo> objectList = new ArrayList<MtpObjectInfo>(length); - for (int i = 0; i < length; i++) { - MtpObjectInfo info = device.getObjectInfo(handles[i]); - if (info == null) { - Log.w(TAG, "getObjectInfo failed"); - } else { - objectList.add(info); - } - } - return objectList; - } - - /** - * Returns the data for an object as a byte array. - * - * @param deviceName the name of the USB device containing the object - * @param objectHandle handle of the object to read - * @param objectSize the size of the object (this should match - * {@link android.mtp.MtpObjectInfo#getCompressedSize} - * @return the object's data, or null if reading fails - */ - public byte[] getObject(String deviceName, int objectHandle, int objectSize) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return null; - } - return device.getObject(objectHandle, objectSize); - } - - /** - * Returns the thumbnail data for an object as a byte array. - * - * @param deviceName the name of the USB device containing the object - * @param objectHandle handle of the object to read - * @return the object's thumbnail, or null if reading fails - */ - public byte[] getThumbnail(String deviceName, int objectHandle) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return null; - } - return device.getThumbnail(objectHandle); - } - - /** - * Copies the data for an object to a file in external storage. - * - * @param deviceName the name of the USB device containing the object - * @param objectHandle handle of the object to read - * @param destPath path to destination for the file transfer. - * This path should be in the external storage as defined by - * {@link android.os.Environment#getExternalStorageDirectory} - * @return true if the file transfer succeeds - */ - public boolean importFile(String deviceName, int objectHandle, String destPath) { - MtpDevice device = getDevice(deviceName); - if (device == null) { - return false; - } - return device.importFile(objectHandle, destPath); - } -} diff --git a/src/com/android/gallery3d/data/PanoramaMetadataJob.java b/src/com/android/gallery3d/data/PanoramaMetadataJob.java deleted file mode 100644 index ab99d6a81..000000000 --- a/src/com/android/gallery3d/data/PanoramaMetadataJob.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2012 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.Context; -import android.net.Uri; - -import com.android.gallery3d.util.LightCycleHelper; -import com.android.gallery3d.util.LightCycleHelper.PanoramaMetadata; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -public class PanoramaMetadataJob implements Job<PanoramaMetadata> { - Context mContext; - Uri mUri; - - public PanoramaMetadataJob(Context context, Uri uri) { - mContext = context; - mUri = uri; - } - - @Override - public PanoramaMetadata run(JobContext jc) { - return LightCycleHelper.getPanoramaMetadata(mContext, mUri); - } -} diff --git a/src/com/android/gallery3d/data/Path.java b/src/com/android/gallery3d/data/Path.java deleted file mode 100644 index fcae65e66..000000000 --- a/src/com/android/gallery3d/data/Path.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.IdentityCache; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -public class Path { - private static final String TAG = "Path"; - private static Path sRoot = new Path(null, "ROOT"); - - private final Path mParent; - private final String mSegment; - private WeakReference<MediaObject> mObject; - private IdentityCache<String, Path> mChildren; - - private Path(Path parent, String segment) { - mParent = parent; - mSegment = segment; - } - - public Path getChild(String segment) { - synchronized (Path.class) { - if (mChildren == null) { - mChildren = new IdentityCache<String, Path>(); - } else { - Path p = mChildren.get(segment); - if (p != null) return p; - } - - Path p = new Path(this, segment); - mChildren.put(segment, p); - return p; - } - } - - public Path getParent() { - synchronized (Path.class) { - return mParent; - } - } - - public Path getChild(int segment) { - return getChild(String.valueOf(segment)); - } - - public Path getChild(long segment) { - return getChild(String.valueOf(segment)); - } - - public void setObject(MediaObject object) { - synchronized (Path.class) { - Utils.assertTrue(mObject == null || mObject.get() == null); - mObject = new WeakReference<MediaObject>(object); - } - } - - MediaObject getObject() { - synchronized (Path.class) { - return (mObject == null) ? null : mObject.get(); - } - } - - @Override - // TODO: toString() should be more efficient, will fix it later - public String toString() { - synchronized (Path.class) { - StringBuilder sb = new StringBuilder(); - String[] segments = split(); - for (int i = 0; i < segments.length; i++) { - sb.append("/"); - sb.append(segments[i]); - } - return sb.toString(); - } - } - - public boolean equalsIgnoreCase (String p) { - String path = toString(); - return path.equalsIgnoreCase(p); - } - - public static Path fromString(String s) { - synchronized (Path.class) { - String[] segments = split(s); - Path current = sRoot; - for (int i = 0; i < segments.length; i++) { - current = current.getChild(segments[i]); - } - return current; - } - } - - public String[] split() { - synchronized (Path.class) { - int n = 0; - for (Path p = this; p != sRoot; p = p.mParent) { - n++; - } - String[] segments = new String[n]; - int i = n - 1; - for (Path p = this; p != sRoot; p = p.mParent) { - segments[i--] = p.mSegment; - } - return segments; - } - } - - public static String[] split(String s) { - int n = s.length(); - if (n == 0) return new String[0]; - if (s.charAt(0) != '/') { - throw new RuntimeException("malformed path:" + s); - } - ArrayList<String> segments = new ArrayList<String>(); - int i = 1; - while (i < n) { - int brace = 0; - int j; - for (j = i; j < n; j++) { - char c = s.charAt(j); - if (c == '{') ++brace; - else if (c == '}') --brace; - else if (brace == 0 && c == '/') break; - } - if (brace != 0) { - throw new RuntimeException("unbalanced brace in path:" + s); - } - segments.add(s.substring(i, j)); - i = j + 1; - } - String[] result = new String[segments.size()]; - segments.toArray(result); - return result; - } - - // Splits a string to an array of strings. - // For example, "{foo,bar,baz}" -> {"foo","bar","baz"}. - public static String[] splitSequence(String s) { - int n = s.length(); - if (s.charAt(0) != '{' || s.charAt(n-1) != '}') { - throw new RuntimeException("bad sequence: " + s); - } - ArrayList<String> segments = new ArrayList<String>(); - int i = 1; - while (i < n - 1) { - int brace = 0; - int j; - for (j = i; j < n - 1; j++) { - char c = s.charAt(j); - if (c == '{') ++brace; - else if (c == '}') --brace; - else if (brace == 0 && c == ',') break; - } - if (brace != 0) { - throw new RuntimeException("unbalanced brace in path:" + s); - } - segments.add(s.substring(i, j)); - i = j + 1; - } - String[] result = new String[segments.size()]; - segments.toArray(result); - return result; - } - - public String getPrefix() { - if (this == sRoot) return ""; - return getPrefixPath().mSegment; - } - - public Path getPrefixPath() { - synchronized (Path.class) { - Path current = this; - if (current == sRoot) { - throw new IllegalStateException(); - } - while (current.mParent != sRoot) { - current = current.mParent; - } - return current; - } - } - - public String getSuffix() { - // We don't need lock because mSegment is final. - return mSegment; - } - - // Below are for testing/debugging only - static void clearAll() { - synchronized (Path.class) { - sRoot = new Path(null, ""); - } - } - - static void dumpAll() { - dumpAll(sRoot, "", ""); - } - - static void dumpAll(Path p, String prefix1, String prefix2) { - synchronized (Path.class) { - MediaObject obj = p.getObject(); - Log.d(TAG, prefix1 + p.mSegment + ":" - + (obj == null ? "null" : obj.getClass().getSimpleName())); - if (p.mChildren != null) { - ArrayList<String> childrenKeys = p.mChildren.keys(); - int i = 0, n = childrenKeys.size(); - for (String key : childrenKeys) { - Path child = p.mChildren.get(key); - if (child == null) { - ++i; - continue; - } - Log.d(TAG, prefix2 + "|"); - if (++i < n) { - dumpAll(child, prefix2 + "+-- ", prefix2 + "| "); - } else { - dumpAll(child, prefix2 + "+-- ", prefix2 + " "); - } - } - } - } - } -} diff --git a/src/com/android/gallery3d/data/PathMatcher.java b/src/com/android/gallery3d/data/PathMatcher.java deleted file mode 100644 index 9c6b840d5..000000000 --- a/src/com/android/gallery3d/data/PathMatcher.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 java.util.ArrayList; -import java.util.HashMap; - -public class PathMatcher { - public static final int NOT_FOUND = -1; - - private ArrayList<String> mVariables = new ArrayList<String>(); - private Node mRoot = new Node(); - - public PathMatcher() { - mRoot = new Node(); - } - - public void add(String pattern, int kind) { - String[] segments = Path.split(pattern); - Node current = mRoot; - for (int i = 0; i < segments.length; i++) { - current = current.addChild(segments[i]); - } - current.setKind(kind); - } - - public int match(Path path) { - String[] segments = path.split(); - mVariables.clear(); - Node current = mRoot; - for (int i = 0; i < segments.length; i++) { - Node next = current.getChild(segments[i]); - if (next == null) { - next = current.getChild("*"); - if (next != null) { - mVariables.add(segments[i]); - } else { - return NOT_FOUND; - } - } - current = next; - } - return current.getKind(); - } - - public String getVar(int index) { - return mVariables.get(index); - } - - public int getIntVar(int index) { - return Integer.parseInt(mVariables.get(index)); - } - - public long getLongVar(int index) { - return Long.parseLong(mVariables.get(index)); - } - - private static class Node { - private HashMap<String, Node> mMap; - private int mKind = NOT_FOUND; - - Node addChild(String segment) { - if (mMap == null) { - mMap = new HashMap<String, Node>(); - } else { - Node node = mMap.get(segment); - if (node != null) return node; - } - - Node n = new Node(); - mMap.put(segment, n); - return n; - } - - Node getChild(String segment) { - if (mMap == null) return null; - return mMap.get(segment); - } - - void setKind(int kind) { - mKind = kind; - } - - int getKind() { - return mKind; - } - } -} diff --git a/src/com/android/gallery3d/data/SecureAlbum.java b/src/com/android/gallery3d/data/SecureAlbum.java deleted file mode 100644 index 204f848f8..000000000 --- a/src/com/android/gallery3d/data/SecureAlbum.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2012 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.Context; -import android.database.Cursor; -import android.net.Uri; -import android.provider.MediaStore.Images; -import android.provider.MediaStore.MediaColumns; -import android.provider.MediaStore.Video; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.app.StitchingChangeListener; -import com.android.gallery3d.util.MediaSetUtils; - -import java.util.ArrayList; - -// This class lists all media items added by the client. -public class SecureAlbum extends MediaSet implements StitchingChangeListener { - @SuppressWarnings("unused") - private static final String TAG = "SecureAlbum"; - private static final String[] PROJECTION = {MediaColumns._ID}; - private int mMinImageId = Integer.MAX_VALUE; // the smallest id of images - private int mMaxImageId = Integer.MIN_VALUE; // the biggest id in images - private int mMinVideoId = Integer.MAX_VALUE; // the smallest id of videos - private int mMaxVideoId = Integer.MIN_VALUE; // the biggest id of videos - // All the media items added by the client. - private ArrayList<Path> mAllItems = new ArrayList<Path>(); - // The types of items in mAllItems. True is video and false is image. - private ArrayList<Boolean> mAllItemTypes = new ArrayList<Boolean>(); - private ArrayList<Path> mExistingItems = new ArrayList<Path>(); - private Context mContext; - private DataManager mDataManager; - private static final Uri[] mWatchUris = - {Images.Media.EXTERNAL_CONTENT_URI, Video.Media.EXTERNAL_CONTENT_URI}; - private final ChangeNotifier mNotifier; - // A placeholder image in the end of secure album. When it is tapped, it - // will take the user to the lock screen. - private MediaItem mUnlockItem; - private boolean mShowUnlockItem; - - public SecureAlbum(Path path, GalleryApp application, MediaItem unlock) { - super(path, nextVersionNumber()); - mContext = application.getAndroidContext(); - mDataManager = application.getDataManager(); - mNotifier = new ChangeNotifier(this, mWatchUris, application); - mUnlockItem = unlock; - mShowUnlockItem = (!isCameraBucketEmpty(Images.Media.EXTERNAL_CONTENT_URI) - || !isCameraBucketEmpty(Video.Media.EXTERNAL_CONTENT_URI)); - } - - public void addMediaItem(boolean isVideo, int id) { - Path pathBase; - if (isVideo) { - pathBase = LocalVideo.ITEM_PATH; - mMinVideoId = Math.min(mMinVideoId, id); - mMaxVideoId = Math.max(mMaxVideoId, id); - } else { - pathBase = LocalImage.ITEM_PATH; - mMinImageId = Math.min(mMinImageId, id); - mMaxImageId = Math.max(mMaxImageId, id); - } - Path path = pathBase.getChild(id); - if (!mAllItems.contains(path)) { - mAllItems.add(path); - mAllItemTypes.add(isVideo); - mNotifier.fakeChange(); - } - } - - // The sequence is stitching items, local media items, and unlock image. - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - int existingCount = mExistingItems.size(); - if (start >= existingCount + 1) { - return new ArrayList<MediaItem>(); - } - - // Add paths of requested stitching items. - int end = Math.min(start + count, existingCount); - ArrayList<Path> subset = new ArrayList<Path>(mExistingItems.subList(start, end)); - - // Convert paths to media items. - final MediaItem[] buf = new MediaItem[end - start]; - ItemConsumer consumer = new ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - buf[index] = item; - } - }; - mDataManager.mapMediaItems(subset, consumer, 0); - ArrayList<MediaItem> result = new ArrayList<MediaItem>(end - start); - for (int i = 0; i < buf.length; i++) { - result.add(buf[i]); - } - if (mShowUnlockItem) result.add(mUnlockItem); - return result; - } - - @Override - public int getMediaItemCount() { - return (mExistingItems.size() + (mShowUnlockItem ? 1 : 0)); - } - - @Override - public String getName() { - return "secure"; - } - - @Override - public long reload() { - if (mNotifier.isDirty()) { - mDataVersion = nextVersionNumber(); - updateExistingItems(); - } - return mDataVersion; - } - - private ArrayList<Integer> queryExistingIds(Uri uri, int minId, int maxId) { - ArrayList<Integer> ids = new ArrayList<Integer>(); - if (minId == Integer.MAX_VALUE || maxId == Integer.MIN_VALUE) return ids; - - String[] selectionArgs = {String.valueOf(minId), String.valueOf(maxId)}; - Cursor cursor = mContext.getContentResolver().query(uri, PROJECTION, - "_id BETWEEN ? AND ?", selectionArgs, null); - if (cursor == null) return ids; - try { - while (cursor.moveToNext()) { - ids.add(cursor.getInt(0)); - } - } finally { - cursor.close(); - } - return ids; - } - - private boolean isCameraBucketEmpty(Uri baseUri) { - Uri uri = baseUri.buildUpon() - .appendQueryParameter("limit", "1").build(); - String[] selection = {String.valueOf(MediaSetUtils.CAMERA_BUCKET_ID)}; - Cursor cursor = mContext.getContentResolver().query(uri, PROJECTION, - "bucket_id = ?", selection, null); - if (cursor == null) return true; - try { - return (cursor.getCount() == 0); - } finally { - cursor.close(); - } - } - - private void updateExistingItems() { - if (mAllItems.size() == 0) return; - - // Query existing ids. - ArrayList<Integer> imageIds = queryExistingIds( - Images.Media.EXTERNAL_CONTENT_URI, mMinImageId, mMaxImageId); - ArrayList<Integer> videoIds = queryExistingIds( - Video.Media.EXTERNAL_CONTENT_URI, mMinVideoId, mMaxVideoId); - - // Construct the existing items list. - mExistingItems.clear(); - for (int i = mAllItems.size() - 1; i >= 0; i--) { - Path path = mAllItems.get(i); - boolean isVideo = mAllItemTypes.get(i); - int id = Integer.parseInt(path.getSuffix()); - if (isVideo) { - if (videoIds.contains(id)) mExistingItems.add(path); - } else { - if (imageIds.contains(id)) mExistingItems.add(path); - } - } - } - - @Override - public boolean isLeafAlbum() { - return true; - } - - @Override - public void onStitchingQueued(Uri uri) { - int id = Integer.parseInt(uri.getLastPathSegment()); - addMediaItem(false, id); - } - - @Override - public void onStitchingResult(Uri uri) { - } - - @Override - public void onStitchingProgress(Uri uri, final int progress) { - } -} diff --git a/src/com/android/gallery3d/data/SecureSource.java b/src/com/android/gallery3d/data/SecureSource.java deleted file mode 100644 index 6bc8cc295..000000000 --- a/src/com/android/gallery3d/data/SecureSource.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.gallery3d.app.GalleryApp; - -public class SecureSource extends MediaSource { - private GalleryApp mApplication; - private static PathMatcher mMatcher = new PathMatcher(); - private static final int SECURE_ALBUM = 0; - private static final int SECURE_UNLOCK = 1; - - static { - mMatcher.add("/secure/all/*", SECURE_ALBUM); - mMatcher.add("/secure/unlock", SECURE_UNLOCK); - } - - public SecureSource(GalleryApp context) { - super("secure"); - mApplication = context; - } - - public static boolean isSecurePath(String path) { - return (SECURE_ALBUM == mMatcher.match(Path.fromString(path))); - } - - @Override - public MediaObject createMediaObject(Path path) { - switch (mMatcher.match(path)) { - case SECURE_ALBUM: { - DataManager dataManager = mApplication.getDataManager(); - MediaItem unlock = (MediaItem) dataManager.getMediaObject( - "/secure/unlock"); - return new SecureAlbum(path, mApplication, unlock); - } - case SECURE_UNLOCK: - return new UnlockImage(path, mApplication); - default: - throw new RuntimeException("bad path: " + path); - } - } -} diff --git a/src/com/android/gallery3d/data/SingleItemAlbum.java b/src/com/android/gallery3d/data/SingleItemAlbum.java deleted file mode 100644 index a0093e0c3..000000000 --- a/src/com/android/gallery3d/data/SingleItemAlbum.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.ArrayList; - -public class SingleItemAlbum extends MediaSet { - @SuppressWarnings("unused") - private static final String TAG = "SingleItemAlbum"; - private final MediaItem mItem; - private final String mName; - - public SingleItemAlbum(Path path, MediaItem item) { - super(path, nextVersionNumber()); - mItem = item; - mName = "SingleItemAlbum("+mItem.getClass().getSimpleName()+")"; - } - - @Override - public int getMediaItemCount() { - return 1; - } - - @Override - public ArrayList<MediaItem> getMediaItem(int start, int count) { - ArrayList<MediaItem> result = new ArrayList<MediaItem>(); - - // If [start, start+count) contains the index 0, return the item. - if (start <= 0 && start + count > 0) { - result.add(mItem); - } - - return result; - } - - public MediaItem getItem() { - return mItem; - } - - @Override - public boolean isLeafAlbum() { - return true; - } - - @Override - public String getName() { - return mName; - } - - @Override - public long reload() { - return mDataVersion; - } -} diff --git a/src/com/android/gallery3d/data/SizeClustering.java b/src/com/android/gallery3d/data/SizeClustering.java deleted file mode 100644 index b809c841b..000000000 --- a/src/com/android/gallery3d/data/SizeClustering.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.Context; -import android.content.res.Resources; - -import com.android.gallery3d.R; - -import java.util.ArrayList; - -public class SizeClustering extends Clustering { - @SuppressWarnings("unused") - private static final String TAG = "SizeClustering"; - - private Context mContext; - private ArrayList<Path>[] mClusters; - private String[] mNames; - private long mMinSizes[]; - - private static final long MEGA_BYTES = 1024L*1024; - private static final long GIGA_BYTES = 1024L*1024*1024; - - private static final long[] SIZE_LEVELS = { - 0, - 1 * MEGA_BYTES, - 10 * MEGA_BYTES, - 100 * MEGA_BYTES, - 1 * GIGA_BYTES, - 2 * GIGA_BYTES, - 4 * GIGA_BYTES, - }; - - public SizeClustering(Context context) { - mContext = context; - } - - @SuppressWarnings("unchecked") - @Override - public void run(MediaSet baseSet) { - @SuppressWarnings("unchecked") - final ArrayList<Path>[] group = new ArrayList[SIZE_LEVELS.length]; - baseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - // Find the cluster this item belongs to. - long size = item.getSize(); - int i; - for (i = 0; i < SIZE_LEVELS.length - 1; i++) { - if (size < SIZE_LEVELS[i + 1]) { - break; - } - } - - ArrayList<Path> list = group[i]; - if (list == null) { - list = new ArrayList<Path>(); - group[i] = list; - } - list.add(item.getPath()); - } - }); - - int count = 0; - for (int i = 0; i < group.length; i++) { - if (group[i] != null) { - count++; - } - } - - mClusters = new ArrayList[count]; - mNames = new String[count]; - mMinSizes = new long[count]; - - Resources res = mContext.getResources(); - int k = 0; - // Go through group in the reverse order, so the group with the largest - // size will show first. - for (int i = group.length - 1; i >= 0; i--) { - if (group[i] == null) continue; - - mClusters[k] = group[i]; - if (i == 0) { - mNames[k] = String.format( - res.getString(R.string.size_below), getSizeString(i + 1)); - } else if (i == group.length - 1) { - mNames[k] = String.format( - res.getString(R.string.size_above), getSizeString(i)); - } else { - String minSize = getSizeString(i); - String maxSize = getSizeString(i + 1); - mNames[k] = String.format( - res.getString(R.string.size_between), minSize, maxSize); - } - mMinSizes[k] = SIZE_LEVELS[i]; - k++; - } - } - - private String getSizeString(int index) { - long bytes = SIZE_LEVELS[index]; - if (bytes >= GIGA_BYTES) { - return (bytes / GIGA_BYTES) + "GB"; - } else { - return (bytes / MEGA_BYTES) + "MB"; - } - } - - @Override - public int getNumberOfClusters() { - return mClusters.length; - } - - @Override - public ArrayList<Path> getCluster(int index) { - return mClusters[index]; - } - - @Override - public String getClusterName(int index) { - return mNames[index]; - } - - public long getMinSize(int index) { - return mMinSizes[index]; - } -} diff --git a/src/com/android/gallery3d/data/SnailAlbum.java b/src/com/android/gallery3d/data/SnailAlbum.java deleted file mode 100644 index 7bce7a695..000000000 --- a/src/com/android/gallery3d/data/SnailAlbum.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2012 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 java.util.concurrent.atomic.AtomicBoolean; - -// This is a simple MediaSet which contains only one MediaItem -- a SnailItem. -public class SnailAlbum extends SingleItemAlbum { - @SuppressWarnings("unused") - private static final String TAG = "SnailAlbum"; - private AtomicBoolean mDirty = new AtomicBoolean(false); - - public SnailAlbum(Path path, SnailItem item) { - super(path, item); - } - - @Override - public long reload() { - if (mDirty.compareAndSet(true, false)) { - ((SnailItem) getItem()).updateVersion(); - mDataVersion = nextVersionNumber(); - } - return mDataVersion; - } - - public void notifyChange() { - mDirty.set(true); - notifyContentChanged(); - } -} diff --git a/src/com/android/gallery3d/data/SnailItem.java b/src/com/android/gallery3d/data/SnailItem.java deleted file mode 100644 index 3586d2cab..000000000 --- a/src/com/android/gallery3d/data/SnailItem.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2012 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.graphics.Bitmap; -import android.graphics.BitmapRegionDecoder; - -import com.android.gallery3d.ui.ScreenNail; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -// SnailItem is a MediaItem which can provide a ScreenNail. This is -// used so we can show an foreign component (like an -// android.view.View) instead of a Bitmap. -public class SnailItem extends MediaItem { - @SuppressWarnings("unused") - private static final String TAG = "SnailItem"; - private ScreenNail mScreenNail; - - public SnailItem(Path path) { - super(path, nextVersionNumber()); - } - - @Override - public Job<Bitmap> requestImage(int type) { - // nothing to return - return new Job<Bitmap>() { - @Override - public Bitmap run(JobContext jc) { - return null; - } - }; - } - - @Override - public Job<BitmapRegionDecoder> requestLargeImage() { - // nothing to return - return new Job<BitmapRegionDecoder>() { - @Override - public BitmapRegionDecoder run(JobContext jc) { - return null; - } - }; - } - - // We do not provide requestImage or requestLargeImage, instead we - // provide a ScreenNail. - @Override - public ScreenNail getScreenNail() { - return mScreenNail; - } - - @Override - public String getMimeType() { - return ""; - } - - // Returns width and height of the media item. - // Returns 0, 0 if the information is not available. - @Override - public int getWidth() { - return 0; - } - - @Override - public int getHeight() { - return 0; - } - - ////////////////////////////////////////////////////////////////////////// - // Extra methods for SnailItem - ////////////////////////////////////////////////////////////////////////// - - public void setScreenNail(ScreenNail screenNail) { - mScreenNail = screenNail; - } - - public void updateVersion() { - mDataVersion = nextVersionNumber(); - } -} diff --git a/src/com/android/gallery3d/data/SnailSource.java b/src/com/android/gallery3d/data/SnailSource.java deleted file mode 100644 index 5c690ccdb..000000000 --- a/src/com/android/gallery3d/data/SnailSource.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.gallery3d.app.GalleryApp; - -public class SnailSource extends MediaSource { - @SuppressWarnings("unused") - private static final String TAG = "SnailSource"; - private static final int SNAIL_ALBUM = 0; - private static final int SNAIL_ITEM = 1; - - private GalleryApp mApplication; - private PathMatcher mMatcher; - private static int sNextId; - - public SnailSource(GalleryApp application) { - super("snail"); - mApplication = application; - mMatcher = new PathMatcher(); - mMatcher.add("/snail/set/*", SNAIL_ALBUM); - mMatcher.add("/snail/item/*", SNAIL_ITEM); - } - - // The only path we accept is "/snail/set/id" and "/snail/item/id" - @Override - public MediaObject createMediaObject(Path path) { - DataManager dataManager = mApplication.getDataManager(); - switch (mMatcher.match(path)) { - case SNAIL_ALBUM: - String itemPath = "/snail/item/" + mMatcher.getVar(0); - SnailItem item = - (SnailItem) dataManager.getMediaObject(itemPath); - return new SnailAlbum(path, item); - case SNAIL_ITEM: { - int id = mMatcher.getIntVar(0); - return new SnailItem(path); - } - } - return null; - } - - // Registers a new SnailAlbum containing a SnailItem and returns the id of - // them. You can obtain the Path of the SnailAlbum and SnailItem associated - // with the id by getSetPath and getItemPath(). - public static synchronized int newId() { - return sNextId++; - } - - public static Path getSetPath(int id) { - return Path.fromString("/snail/set").getChild(id); - } - - public static Path getItemPath(int id) { - return Path.fromString("/snail/item").getChild(id); - } -} diff --git a/src/com/android/gallery3d/data/TagClustering.java b/src/com/android/gallery3d/data/TagClustering.java deleted file mode 100644 index 407ca84c4..000000000 --- a/src/com/android/gallery3d/data/TagClustering.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.Context; - -import com.android.gallery3d.R; - -import java.util.ArrayList; -import java.util.Map; -import java.util.TreeMap; - -public class TagClustering extends Clustering { - @SuppressWarnings("unused") - private static final String TAG = "TagClustering"; - - private ArrayList<ArrayList<Path>> mClusters; - private String[] mNames; - private String mUntaggedString; - - public TagClustering(Context context) { - mUntaggedString = context.getResources().getString(R.string.untagged); - } - - @Override - public void run(MediaSet baseSet) { - final TreeMap<String, ArrayList<Path>> map = - new TreeMap<String, ArrayList<Path>>(); - final ArrayList<Path> untagged = new ArrayList<Path>(); - - baseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - Path path = item.getPath(); - - String[] tags = item.getTags(); - if (tags == null || tags.length == 0) { - untagged.add(path); - return; - } - for (int j = 0; j < tags.length; j++) { - String key = tags[j]; - ArrayList<Path> list = map.get(key); - if (list == null) { - list = new ArrayList<Path>(); - map.put(key, list); - } - list.add(path); - } - } - }); - - int m = map.size(); - mClusters = new ArrayList<ArrayList<Path>>(); - mNames = new String[m + ((untagged.size() > 0) ? 1 : 0)]; - int i = 0; - for (Map.Entry<String, ArrayList<Path>> entry : map.entrySet()) { - mNames[i++] = entry.getKey(); - mClusters.add(entry.getValue()); - } - if (untagged.size() > 0) { - mNames[i++] = mUntaggedString; - mClusters.add(untagged); - } - } - - @Override - public int getNumberOfClusters() { - return mClusters.size(); - } - - @Override - public ArrayList<Path> getCluster(int index) { - return mClusters.get(index); - } - - @Override - public String getClusterName(int index) { - return mNames[index]; - } -} diff --git a/src/com/android/gallery3d/data/TimeClustering.java b/src/com/android/gallery3d/data/TimeClustering.java deleted file mode 100644 index 35cbab1ee..000000000 --- a/src/com/android/gallery3d/data/TimeClustering.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * 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.Context; -import android.text.format.DateFormat; -import android.text.format.DateUtils; - -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.GalleryUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; - -public class TimeClustering extends Clustering { - @SuppressWarnings("unused") - private static final String TAG = "TimeClustering"; - - // If 2 items are greater than 25 miles apart, they will be in different - // clusters. - private static final int GEOGRAPHIC_DISTANCE_CUTOFF_IN_MILES = 20; - - // Do not want to split based on anything under 1 min. - private static final long MIN_CLUSTER_SPLIT_TIME_IN_MS = 60000L; - - // Disregard a cluster split time of anything over 2 hours. - private static final long MAX_CLUSTER_SPLIT_TIME_IN_MS = 7200000L; - - // Try and get around 9 clusters (best-effort for the common case). - private static final int NUM_CLUSTERS_TARGETED = 9; - - // Try and merge 2 clusters if they are both smaller than min cluster size. - // The min cluster size can range from 8 to 15. - private static final int MIN_MIN_CLUSTER_SIZE = 8; - private static final int MAX_MIN_CLUSTER_SIZE = 15; - - // Try and split a cluster if it is bigger than max cluster size. - // The max cluster size can range from 20 to 50. - private static final int MIN_MAX_CLUSTER_SIZE = 20; - private static final int MAX_MAX_CLUSTER_SIZE = 50; - - // Initially put 2 items in the same cluster as long as they are within - // 3 cluster frequencies of each other. - private static int CLUSTER_SPLIT_MULTIPLIER = 3; - - // The minimum change factor in the time between items to consider a - // partition. - // Example: (Item 3 - Item 2) / (Item 2 - Item 1). - private static final int MIN_PARTITION_CHANGE_FACTOR = 2; - - // Make the cluster split time of a large cluster half that of a regular - // cluster. - private static final int PARTITION_CLUSTER_SPLIT_TIME_FACTOR = 2; - - private Context mContext; - private ArrayList<Cluster> mClusters; - private String[] mNames; - private Cluster mCurrCluster; - - private long mClusterSplitTime = - (MIN_CLUSTER_SPLIT_TIME_IN_MS + MAX_CLUSTER_SPLIT_TIME_IN_MS) / 2; - private long mLargeClusterSplitTime = - mClusterSplitTime / PARTITION_CLUSTER_SPLIT_TIME_FACTOR; - private int mMinClusterSize = (MIN_MIN_CLUSTER_SIZE + MAX_MIN_CLUSTER_SIZE) / 2; - private int mMaxClusterSize = (MIN_MAX_CLUSTER_SIZE + MAX_MAX_CLUSTER_SIZE) / 2; - - - private static final Comparator<SmallItem> sDateComparator = - new DateComparator(); - - private static class DateComparator implements Comparator<SmallItem> { - @Override - public int compare(SmallItem item1, SmallItem item2) { - return -Utils.compare(item1.dateInMs, item2.dateInMs); - } - } - - public TimeClustering(Context context) { - mContext = context; - mClusters = new ArrayList<Cluster>(); - mCurrCluster = new Cluster(); - } - - @Override - public void run(MediaSet baseSet) { - final int total = baseSet.getTotalMediaItemCount(); - final SmallItem[] buf = new SmallItem[total]; - final double[] latLng = new double[2]; - - baseSet.enumerateTotalMediaItems(new MediaSet.ItemConsumer() { - @Override - public void consume(int index, MediaItem item) { - if (index < 0 || index >= total) return; - SmallItem s = new SmallItem(); - s.path = item.getPath(); - s.dateInMs = item.getDateInMs(); - item.getLatLong(latLng); - s.lat = latLng[0]; - s.lng = latLng[1]; - buf[index] = s; - } - }); - - ArrayList<SmallItem> items = new ArrayList<SmallItem>(total); - for (int i = 0; i < total; i++) { - if (buf[i] != null) { - items.add(buf[i]); - } - } - - Collections.sort(items, sDateComparator); - - int n = items.size(); - long minTime = 0; - long maxTime = 0; - for (int i = 0; i < n; i++) { - long t = items.get(i).dateInMs; - if (t == 0) continue; - if (minTime == 0) { - minTime = maxTime = t; - } else { - minTime = Math.min(minTime, t); - maxTime = Math.max(maxTime, t); - } - } - - setTimeRange(maxTime - minTime, n); - - for (int i = 0; i < n; i++) { - compute(items.get(i)); - } - - compute(null); - - int m = mClusters.size(); - mNames = new String[m]; - for (int i = 0; i < m; i++) { - mNames[i] = mClusters.get(i).generateCaption(mContext); - } - } - - @Override - public int getNumberOfClusters() { - return mClusters.size(); - } - - @Override - public ArrayList<Path> getCluster(int index) { - ArrayList<SmallItem> items = mClusters.get(index).getItems(); - ArrayList<Path> result = new ArrayList<Path>(items.size()); - for (int i = 0, n = items.size(); i < n; i++) { - result.add(items.get(i).path); - } - return result; - } - - @Override - public String getClusterName(int index) { - return mNames[index]; - } - - private void setTimeRange(long timeRange, int numItems) { - if (numItems != 0) { - int meanItemsPerCluster = numItems / NUM_CLUSTERS_TARGETED; - // Heuristic to get min and max cluster size - half and double the - // desired items per cluster. - mMinClusterSize = meanItemsPerCluster / 2; - mMaxClusterSize = meanItemsPerCluster * 2; - mClusterSplitTime = timeRange / numItems * CLUSTER_SPLIT_MULTIPLIER; - } - mClusterSplitTime = Utils.clamp(mClusterSplitTime, MIN_CLUSTER_SPLIT_TIME_IN_MS, MAX_CLUSTER_SPLIT_TIME_IN_MS); - mLargeClusterSplitTime = mClusterSplitTime / PARTITION_CLUSTER_SPLIT_TIME_FACTOR; - mMinClusterSize = Utils.clamp(mMinClusterSize, MIN_MIN_CLUSTER_SIZE, MAX_MIN_CLUSTER_SIZE); - mMaxClusterSize = Utils.clamp(mMaxClusterSize, MIN_MAX_CLUSTER_SIZE, MAX_MAX_CLUSTER_SIZE); - } - - private void compute(SmallItem currentItem) { - if (currentItem != null) { - int numClusters = mClusters.size(); - int numCurrClusterItems = mCurrCluster.size(); - boolean geographicallySeparateItem = false; - boolean itemAddedToCurrentCluster = false; - - // Determine if this item should go in the current cluster or be the - // start of a new cluster. - if (numCurrClusterItems == 0) { - mCurrCluster.addItem(currentItem); - } else { - SmallItem prevItem = mCurrCluster.getLastItem(); - if (isGeographicallySeparated(prevItem, currentItem)) { - mClusters.add(mCurrCluster); - geographicallySeparateItem = true; - } else if (numCurrClusterItems > mMaxClusterSize) { - splitAndAddCurrentCluster(); - } else if (timeDistance(prevItem, currentItem) < mClusterSplitTime) { - mCurrCluster.addItem(currentItem); - itemAddedToCurrentCluster = true; - } else if (numClusters > 0 && numCurrClusterItems < mMinClusterSize - && !mCurrCluster.mGeographicallySeparatedFromPrevCluster) { - mergeAndAddCurrentCluster(); - } else { - mClusters.add(mCurrCluster); - } - - // Creating a new cluster and adding the current item to it. - if (!itemAddedToCurrentCluster) { - mCurrCluster = new Cluster(); - if (geographicallySeparateItem) { - mCurrCluster.mGeographicallySeparatedFromPrevCluster = true; - } - mCurrCluster.addItem(currentItem); - } - } - } else { - if (mCurrCluster.size() > 0) { - int numClusters = mClusters.size(); - int numCurrClusterItems = mCurrCluster.size(); - - // The last cluster may potentially be too big or too small. - if (numCurrClusterItems > mMaxClusterSize) { - splitAndAddCurrentCluster(); - } else if (numClusters > 0 && numCurrClusterItems < mMinClusterSize - && !mCurrCluster.mGeographicallySeparatedFromPrevCluster) { - mergeAndAddCurrentCluster(); - } else { - mClusters.add(mCurrCluster); - } - mCurrCluster = new Cluster(); - } - } - } - - private void splitAndAddCurrentCluster() { - ArrayList<SmallItem> currClusterItems = mCurrCluster.getItems(); - int numCurrClusterItems = mCurrCluster.size(); - int secondPartitionStartIndex = getPartitionIndexForCurrentCluster(); - if (secondPartitionStartIndex != -1) { - Cluster partitionedCluster = new Cluster(); - for (int j = 0; j < secondPartitionStartIndex; j++) { - partitionedCluster.addItem(currClusterItems.get(j)); - } - mClusters.add(partitionedCluster); - partitionedCluster = new Cluster(); - for (int j = secondPartitionStartIndex; j < numCurrClusterItems; j++) { - partitionedCluster.addItem(currClusterItems.get(j)); - } - mClusters.add(partitionedCluster); - } else { - mClusters.add(mCurrCluster); - } - } - - private int getPartitionIndexForCurrentCluster() { - int partitionIndex = -1; - float largestChange = MIN_PARTITION_CHANGE_FACTOR; - ArrayList<SmallItem> currClusterItems = mCurrCluster.getItems(); - int numCurrClusterItems = mCurrCluster.size(); - int minClusterSize = mMinClusterSize; - - // Could be slightly more efficient here but this code seems cleaner. - if (numCurrClusterItems > minClusterSize + 1) { - for (int i = minClusterSize; i < numCurrClusterItems - minClusterSize; i++) { - SmallItem prevItem = currClusterItems.get(i - 1); - SmallItem currItem = currClusterItems.get(i); - SmallItem nextItem = currClusterItems.get(i + 1); - - long timeNext = nextItem.dateInMs; - long timeCurr = currItem.dateInMs; - long timePrev = prevItem.dateInMs; - - if (timeNext == 0 || timeCurr == 0 || timePrev == 0) continue; - - long diff1 = Math.abs(timeNext - timeCurr); - long diff2 = Math.abs(timeCurr - timePrev); - - float change = Math.max(diff1 / (diff2 + 0.01f), diff2 / (diff1 + 0.01f)); - if (change > largestChange) { - if (timeDistance(currItem, prevItem) > mLargeClusterSplitTime) { - partitionIndex = i; - largestChange = change; - } else if (timeDistance(nextItem, currItem) > mLargeClusterSplitTime) { - partitionIndex = i + 1; - largestChange = change; - } - } - } - } - return partitionIndex; - } - - private void mergeAndAddCurrentCluster() { - int numClusters = mClusters.size(); - Cluster prevCluster = mClusters.get(numClusters - 1); - ArrayList<SmallItem> currClusterItems = mCurrCluster.getItems(); - int numCurrClusterItems = mCurrCluster.size(); - if (prevCluster.size() < mMinClusterSize) { - for (int i = 0; i < numCurrClusterItems; i++) { - prevCluster.addItem(currClusterItems.get(i)); - } - mClusters.set(numClusters - 1, prevCluster); - } else { - mClusters.add(mCurrCluster); - } - } - - // Returns true if a, b are sufficiently geographically separated. - private static boolean isGeographicallySeparated(SmallItem itemA, SmallItem itemB) { - if (!GalleryUtils.isValidLocation(itemA.lat, itemA.lng) - || !GalleryUtils.isValidLocation(itemB.lat, itemB.lng)) { - return false; - } - - double distance = GalleryUtils.fastDistanceMeters( - Math.toRadians(itemA.lat), - Math.toRadians(itemA.lng), - Math.toRadians(itemB.lat), - Math.toRadians(itemB.lng)); - return (GalleryUtils.toMile(distance) > GEOGRAPHIC_DISTANCE_CUTOFF_IN_MILES); - } - - // Returns the time interval between the two items in milliseconds. - private static long timeDistance(SmallItem a, SmallItem b) { - return Math.abs(a.dateInMs - b.dateInMs); - } -} - -class SmallItem { - Path path; - long dateInMs; - double lat, lng; -} - -class Cluster { - @SuppressWarnings("unused") - private static final String TAG = "Cluster"; - private static final String MMDDYY_FORMAT = "MMddyy"; - - // This is for TimeClustering only. - public boolean mGeographicallySeparatedFromPrevCluster = false; - - private ArrayList<SmallItem> mItems = new ArrayList<SmallItem>(); - - public Cluster() { - } - - public void addItem(SmallItem item) { - mItems.add(item); - } - - public int size() { - return mItems.size(); - } - - public SmallItem getLastItem() { - int n = mItems.size(); - return (n == 0) ? null : mItems.get(n - 1); - } - - public ArrayList<SmallItem> getItems() { - return mItems; - } - - public String generateCaption(Context context) { - int n = mItems.size(); - long minTimestamp = 0; - long maxTimestamp = 0; - - for (int i = 0; i < n; i++) { - long t = mItems.get(i).dateInMs; - if (t == 0) continue; - if (minTimestamp == 0) { - minTimestamp = maxTimestamp = t; - } else { - minTimestamp = Math.min(minTimestamp, t); - maxTimestamp = Math.max(maxTimestamp, t); - } - } - if (minTimestamp == 0) return ""; - - String caption; - String minDay = DateFormat.format(MMDDYY_FORMAT, minTimestamp) - .toString(); - String maxDay = DateFormat.format(MMDDYY_FORMAT, maxTimestamp) - .toString(); - - if (minDay.substring(4).equals(maxDay.substring(4))) { - // The items are from the same year - show at least as - // much granularity as abbrev_all allows. - caption = DateUtils.formatDateRange(context, minTimestamp, - maxTimestamp, DateUtils.FORMAT_ABBREV_ALL); - - // Get a more granular date range string if the min and - // max timestamp are on the same day and from the - // current year. - if (minDay.equals(maxDay)) { - int flags = DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE; - // Contains the year only if the date does not - // correspond to the current year. - String dateRangeWithOptionalYear = DateUtils.formatDateTime( - context, minTimestamp, flags); - String dateRangeWithYear = DateUtils.formatDateTime( - context, minTimestamp, flags | DateUtils.FORMAT_SHOW_YEAR); - if (!dateRangeWithOptionalYear.equals(dateRangeWithYear)) { - // This means both dates are from the same year - // - show the time. - // Not enough room to display the time range. - // Pick the mid-point. - long midTimestamp = (minTimestamp + maxTimestamp) / 2; - caption = DateUtils.formatDateRange(context, midTimestamp, - midTimestamp, DateUtils.FORMAT_SHOW_TIME | flags); - } - } - } else { - // The items are not from the same year - only show - // month and year. - int flags = DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE; - caption = DateUtils.formatDateRange(context, minTimestamp, - maxTimestamp, flags); - } - - return caption; - } -} diff --git a/src/com/android/gallery3d/data/UnlockImage.java b/src/com/android/gallery3d/data/UnlockImage.java deleted file mode 100644 index ed3b485c4..000000000 --- a/src/com/android/gallery3d/data/UnlockImage.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 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 com.android.gallery3d.R; -import com.android.gallery3d.app.GalleryApp; - -public class UnlockImage extends ActionImage { - @SuppressWarnings("unused") - private static final String TAG = "UnlockImage"; - - public UnlockImage(Path path, GalleryApp application) { - super(path, application, R.drawable.placeholder_locked); - } - - @Override - public int getSupportedOperations() { - return super.getSupportedOperations() | SUPPORT_UNLOCK; - } -} diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java deleted file mode 100644 index e8875b572..000000000 --- a/src/com/android/gallery3d/data/UriImage.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory.Options; -import android.graphics.BitmapRegionDecoder; -import android.net.Uri; -import android.os.ParcelFileDescriptor; - -import com.android.gallery3d.app.GalleryApp; -import com.android.gallery3d.app.PanoramaMetadataSupport; -import com.android.gallery3d.common.BitmapUtils; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.util.ThreadPool.CancelListener; -import com.android.gallery3d.util.ThreadPool.Job; -import com.android.gallery3d.util.ThreadPool.JobContext; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; - -public class UriImage extends MediaItem { - private static final String TAG = "UriImage"; - - private static final int STATE_INIT = 0; - private static final int STATE_DOWNLOADING = 1; - private static final int STATE_DOWNLOADED = 2; - private static final int STATE_ERROR = -1; - - private final Uri mUri; - private final String mContentType; - - private DownloadCache.Entry mCacheEntry; - private ParcelFileDescriptor mFileDescriptor; - private int mState = STATE_INIT; - private int mWidth; - private int mHeight; - private int mRotation; - private PanoramaMetadataSupport mPanoramaMetadata = new PanoramaMetadataSupport(this); - - private GalleryApp mApplication; - - public UriImage(GalleryApp application, Path path, Uri uri, String contentType) { - super(path, nextVersionNumber()); - mUri = uri; - mApplication = Utils.checkNotNull(application); - mContentType = contentType; - } - - @Override - public Job<Bitmap> requestImage(int type) { - return new BitmapJob(type); - } - - @Override - public Job<BitmapRegionDecoder> requestLargeImage() { - return new RegionDecoderJob(); - } - - private void openFileOrDownloadTempFile(JobContext jc) { - int state = openOrDownloadInner(jc); - synchronized (this) { - mState = state; - if (mState != STATE_DOWNLOADED) { - if (mFileDescriptor != null) { - Utils.closeSilently(mFileDescriptor); - mFileDescriptor = null; - } - } - notifyAll(); - } - } - - private int openOrDownloadInner(JobContext jc) { - String scheme = mUri.getScheme(); - if (ContentResolver.SCHEME_CONTENT.equals(scheme) - || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme) - || ContentResolver.SCHEME_FILE.equals(scheme)) { - try { - if (MIME_TYPE_JPEG.equalsIgnoreCase(mContentType)) { - InputStream is = mApplication.getContentResolver() - .openInputStream(mUri); - mRotation = Exif.getOrientation(is); - Utils.closeSilently(is); - } - mFileDescriptor = mApplication.getContentResolver() - .openFileDescriptor(mUri, "r"); - if (jc.isCancelled()) return STATE_INIT; - return STATE_DOWNLOADED; - } catch (FileNotFoundException e) { - Log.w(TAG, "fail to open: " + mUri, e); - return STATE_ERROR; - } - } else { - try { - URL url = new URI(mUri.toString()).toURL(); - mCacheEntry = mApplication.getDownloadCache().download(jc, url); - if (jc.isCancelled()) return STATE_INIT; - if (mCacheEntry == null) { - Log.w(TAG, "download failed " + url); - return STATE_ERROR; - } - if (MIME_TYPE_JPEG.equalsIgnoreCase(mContentType)) { - InputStream is = new FileInputStream(mCacheEntry.cacheFile); - mRotation = Exif.getOrientation(is); - Utils.closeSilently(is); - } - mFileDescriptor = ParcelFileDescriptor.open( - mCacheEntry.cacheFile, ParcelFileDescriptor.MODE_READ_ONLY); - return STATE_DOWNLOADED; - } catch (Throwable t) { - Log.w(TAG, "download error", t); - return STATE_ERROR; - } - } - } - - private boolean prepareInputFile(JobContext jc) { - jc.setCancelListener(new CancelListener() { - @Override - public void onCancel() { - synchronized (this) { - notifyAll(); - } - } - }); - - while (true) { - synchronized (this) { - if (jc.isCancelled()) return false; - if (mState == STATE_INIT) { - mState = STATE_DOWNLOADING; - // Then leave the synchronized block and continue. - } else if (mState == STATE_ERROR) { - return false; - } else if (mState == STATE_DOWNLOADED) { - return true; - } else /* if (mState == STATE_DOWNLOADING) */ { - try { - wait(); - } catch (InterruptedException ex) { - // ignored. - } - continue; - } - } - // This is only reached for STATE_INIT->STATE_DOWNLOADING - openFileOrDownloadTempFile(jc); - } - } - - private class RegionDecoderJob implements Job<BitmapRegionDecoder> { - @Override - public BitmapRegionDecoder run(JobContext jc) { - if (!prepareInputFile(jc)) return null; - BitmapRegionDecoder decoder = DecodeUtils.createBitmapRegionDecoder( - jc, mFileDescriptor.getFileDescriptor(), false); - mWidth = decoder.getWidth(); - mHeight = decoder.getHeight(); - return decoder; - } - } - - private class BitmapJob implements Job<Bitmap> { - private int mType; - - protected BitmapJob(int type) { - mType = type; - } - - @Override - public Bitmap run(JobContext jc) { - if (!prepareInputFile(jc)) return null; - int targetSize = MediaItem.getTargetSize(mType); - Options options = new Options(); - options.inPreferredConfig = Config.ARGB_8888; - Bitmap bitmap = DecodeUtils.decodeThumbnail(jc, - mFileDescriptor.getFileDescriptor(), options, targetSize, mType); - - if (jc.isCancelled() || bitmap == null) { - return null; - } - - if (mType == MediaItem.TYPE_MICROTHUMBNAIL) { - bitmap = BitmapUtils.resizeAndCropCenter(bitmap, targetSize, true); - } else { - bitmap = BitmapUtils.resizeDownBySideLength(bitmap, targetSize, true); - } - return bitmap; - } - } - - @Override - public int getSupportedOperations() { - int supported = SUPPORT_EDIT | SUPPORT_SETAS; - if (isSharable()) supported |= SUPPORT_SHARE; - if (BitmapUtils.isSupportedByRegionDecoder(mContentType)) { - supported |= SUPPORT_FULL_IMAGE; - } - return supported; - } - - @Override - public void getPanoramaSupport(PanoramaSupportCallback callback) { - mPanoramaMetadata.getPanoramaSupport(mApplication, callback); - } - - @Override - public void clearCachedPanoramaSupport() { - mPanoramaMetadata.clearCachedValues(); - } - - private boolean isSharable() { - // We cannot grant read permission to the receiver since we put - // the data URI in EXTRA_STREAM instead of the data part of an intent - // And there are issues in MediaUploader and Bluetooth file sender to - // share a general image data. So, we only share for local file. - return ContentResolver.SCHEME_FILE.equals(mUri.getScheme()); - } - - @Override - public int getMediaType() { - return MEDIA_TYPE_IMAGE; - } - - @Override - public Uri getContentUri() { - return mUri; - } - - @Override - public MediaDetails getDetails() { - MediaDetails details = super.getDetails(); - if (mWidth != 0 && mHeight != 0) { - details.addDetail(MediaDetails.INDEX_WIDTH, mWidth); - details.addDetail(MediaDetails.INDEX_HEIGHT, mHeight); - } - if (mContentType != null) { - details.addDetail(MediaDetails.INDEX_MIMETYPE, mContentType); - } - if (ContentResolver.SCHEME_FILE.equals(mUri.getScheme())) { - String filePath = mUri.getPath(); - details.addDetail(MediaDetails.INDEX_PATH, filePath); - MediaDetails.extractExifInfo(details, filePath); - } - return details; - } - - @Override - public String getMimeType() { - return mContentType; - } - - @Override - protected void finalize() throws Throwable { - try { - if (mFileDescriptor != null) { - Utils.closeSilently(mFileDescriptor); - } - } finally { - super.finalize(); - } - } - - @Override - public int getWidth() { - return 0; - } - - @Override - public int getHeight() { - return 0; - } - - @Override - public int getRotation() { - return mRotation; - } -} diff --git a/src/com/android/gallery3d/data/UriSource.java b/src/com/android/gallery3d/data/UriSource.java deleted file mode 100644 index f66bacd7b..000000000 --- a/src/com/android/gallery3d/data/UriSource.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.net.Uri; -import android.webkit.MimeTypeMap; - -import com.android.gallery3d.app.GalleryApp; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; - -class UriSource extends MediaSource { - @SuppressWarnings("unused") - private static final String TAG = "UriSource"; - private static final String IMAGE_TYPE_PREFIX = "image/"; - private static final String IMAGE_TYPE_ANY = "image/*"; - private static final String CHARSET_UTF_8 = "utf-8"; - - private GalleryApp mApplication; - - public UriSource(GalleryApp context) { - super("uri"); - mApplication = context; - } - - @Override - public MediaObject createMediaObject(Path path) { - String segment[] = path.split(); - if (segment.length != 3) { - throw new RuntimeException("bad path: " + path); - } - try { - String uri = URLDecoder.decode(segment[1], CHARSET_UTF_8); - String type = URLDecoder.decode(segment[2], CHARSET_UTF_8); - return new UriImage(mApplication, path, Uri.parse(uri), type); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } - - private String getMimeType(Uri uri) { - if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { - String extension = - MimeTypeMap.getFileExtensionFromUrl(uri.toString()); - String type = MimeTypeMap.getSingleton() - .getMimeTypeFromExtension(extension.toLowerCase()); - if (type != null) return type; - } - // Assume the type is image if the type cannot be resolved - // This could happen for "http" URI. - String type = mApplication.getContentResolver().getType(uri); - if (type == null) type = "image/*"; - return type; - } - - @Override - public Path findPathByUri(Uri uri, String type) { - String mimeType = getMimeType(uri); - - // Try to find a most specific type but it has to be started with "image/" - if ((type == null) || (IMAGE_TYPE_ANY.equals(type) - && mimeType.startsWith(IMAGE_TYPE_PREFIX))) { - type = mimeType; - } - - if (type.startsWith(IMAGE_TYPE_PREFIX)) { - try { - return Path.fromString("/uri/" - + URLEncoder.encode(uri.toString(), CHARSET_UTF_8) - + "/" +URLEncoder.encode(type, CHARSET_UTF_8)); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - } - // We have no clues that it is an image - return null; - } -} |