summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/widget/LocalPhotoSource.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/widget/LocalPhotoSource.java')
-rw-r--r--src/com/android/gallery3d/widget/LocalPhotoSource.java202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/widget/LocalPhotoSource.java b/src/com/android/gallery3d/widget/LocalPhotoSource.java
new file mode 100644
index 000000000..de16a7129
--- /dev/null
+++ b/src/com/android/gallery3d/widget/LocalPhotoSource.java
@@ -0,0 +1,202 @@
+/*
+ * 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.widget;
+
+import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.ContentListener;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.Path;
+import com.android.gallery3d.util.GalleryUtils;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Handler;
+import android.provider.MediaStore.Images.Media;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+
+public class LocalPhotoSource implements WidgetSource {
+
+ private static final String TAG = "LocalPhotoSource";
+
+ private static final int MAX_PHOTO_COUNT = 128;
+
+ /* Static fields used to query for the correct set of images */
+ private static final Uri CONTENT_URI = Media.EXTERNAL_CONTENT_URI;
+ private static final String DATE_TAKEN = Media.DATE_TAKEN;
+ private static final String[] PROJECTION = {Media._ID};
+ private static final String[] COUNT_PROJECTION = {"count(*)"};
+ /* We don't want to include the download directory */
+ private static final String SELECTION =
+ String.format("%s != %s", Media.BUCKET_ID, getDownloadBucketId());
+ private static final String ORDER = String.format("%s DESC", DATE_TAKEN);
+
+ private Context mContext;
+ private ArrayList<Long> mPhotos = new ArrayList<Long>();
+ private ContentListener mContentListener;
+ private ContentObserver mContentObserver;
+ private boolean mContentDirty = true;
+ private DataManager mDataManager;
+ private static final Path LOCAL_IMAGE_ROOT = Path.fromString("/local/image/item");
+
+ public LocalPhotoSource(Context context) {
+ mContext = context;
+ mDataManager = ((GalleryApp) context.getApplicationContext()).getDataManager();
+ mContentObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mContentDirty = true;
+ if (mContentListener != null) mContentListener.onContentDirty();
+ }
+ };
+ mContext.getContentResolver()
+ .registerContentObserver(CONTENT_URI, true, mContentObserver);
+ }
+
+ public void close() {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public Uri getContentUri(int index) {
+ if (index < mPhotos.size()) {
+ return CONTENT_URI.buildUpon()
+ .appendPath(String.valueOf(mPhotos.get(index)))
+ .build();
+ }
+ return null;
+ }
+
+ @Override
+ public Bitmap getImage(int index) {
+ if (index >= mPhotos.size()) return null;
+ long id = mPhotos.get(index);
+ MediaItem image = (MediaItem)
+ mDataManager.getMediaObject(LOCAL_IMAGE_ROOT.getChild(id));
+ if (image == null) return null;
+
+ return WidgetUtils.createWidgetBitmap(image);
+ }
+
+ private int[] getExponentialIndice(int total, int count) {
+ Random random = new Random();
+ if (count > total) count = total;
+ HashSet<Integer> selected = new HashSet<Integer>(count);
+ while (selected.size() < count) {
+ int row = (int)(-Math.log(random.nextDouble()) * total / 2);
+ if (row < total) selected.add(row);
+ }
+ int values[] = new int[count];
+ int index = 0;
+ for (int value : selected) {
+ values[index++] = value;
+ }
+ return values;
+ }
+
+ private int getPhotoCount(ContentResolver resolver) {
+ Cursor cursor = resolver.query(
+ CONTENT_URI, COUNT_PROJECTION, SELECTION, null, null);
+ if (cursor == null) return 0;
+ try {
+ Utils.assertTrue(cursor.moveToNext());
+ return cursor.getInt(0);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private boolean isContentSound(int totalCount) {
+ if (mPhotos.size() < Math.min(totalCount, MAX_PHOTO_COUNT)) return false;
+ if (mPhotos.size() == 0) return true; // totalCount is also 0
+
+ StringBuilder builder = new StringBuilder();
+ for (Long imageId : mPhotos) {
+ if (builder.length() > 0) builder.append(",");
+ builder.append(imageId);
+ }
+ Cursor cursor = mContext.getContentResolver().query(
+ CONTENT_URI, COUNT_PROJECTION,
+ String.format("%s in (%s)", Media._ID, builder.toString()),
+ null, null);
+ if (cursor == null) return false;
+ try {
+ Utils.assertTrue(cursor.moveToNext());
+ return cursor.getInt(0) == mPhotos.size();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void reload() {
+ if (!mContentDirty) return;
+ mContentDirty = false;
+
+ ContentResolver resolver = mContext.getContentResolver();
+ int photoCount = getPhotoCount(resolver);
+ if (isContentSound(photoCount)) return;
+
+ int choosedIds[] = getExponentialIndice(photoCount, MAX_PHOTO_COUNT);
+ Arrays.sort(choosedIds);
+
+ mPhotos.clear();
+ Cursor cursor = mContext.getContentResolver().query(
+ CONTENT_URI, PROJECTION, SELECTION, null, ORDER);
+ if (cursor == null) return;
+ try {
+ for (int index : choosedIds) {
+ if (cursor.moveToPosition(index)) {
+ mPhotos.add(cursor.getLong(0));
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ @Override
+ public int size() {
+ reload();
+ return mPhotos.size();
+ }
+
+ /**
+ * Builds the bucket ID for the public external storage Downloads directory
+ * @return the bucket ID
+ */
+ private static int getDownloadBucketId() {
+ String downloadsPath = Environment
+ .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ .getAbsolutePath();
+ return GalleryUtils.getBucketId(downloadsPath);
+ }
+
+ @Override
+ public void setContentListener(ContentListener listener) {
+ mContentListener = listener;
+ }
+}