aboutsummaryrefslogtreecommitdiffstats
path: root/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java')
-rw-r--r--src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java
new file mode 100644
index 0000000..4228abf
--- /dev/null
+++ b/src/org/cyanogenmod/wallpapers/photophase/MediaPictureDiscoverer.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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 org.cyanogenmod.wallpapers.photophase;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.AsyncTask.Status;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import org.cyanogenmod.wallpapers.photophase.preferences.PreferencesProvider.Preferences;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A class that load asynchronously the paths of all media stored in the device.
+ * This class only seek at the specified paths
+ */
+public class MediaPictureDiscoverer {
+
+ private static final String TAG = "MediaPictureDiscoverer";
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * An interface that is called when new data is ready.
+ */
+ public interface OnMediaPictureDiscoveredListener {
+ /**
+ * Called when the starting to fetch data
+ *
+ * @param userRequest If the user requested this media discovery
+ */
+ void onStartMediaDiscovered(boolean userRequest);
+ /**
+ * Called when the all the data is ready
+ *
+ * @param images All the images paths found
+ * @param userRequest If the user requested this media discovery
+ */
+ void onEndMediaDiscovered(File[] images, boolean userRequest);
+ /**
+ * Called when the partial data is ready
+ *
+ * @param images All the images paths found
+ * @param userRequest If the user requested this media discovery
+ */
+ void onPartialMediaDiscovered(File[] images, boolean userRequest);
+ }
+
+ /**
+ * The asynchronous task for query the MediaStore
+ */
+ private class AsyncDiscoverTask extends AsyncTask<Void, File, List<File>> {
+
+ private final ContentResolver mFinalContentResolver;
+ private final OnMediaPictureDiscoveredListener mFinalCallback;
+ private final Set<String> mFilter;
+ private final Set<String> mLastAlbums;
+ private final Set<String> mNewAlbums;
+ private final boolean mIsAutoSelectNewAlbums;
+ private final boolean mUserRequest;
+
+ /**
+ * Constructor of <code>AsyncDiscoverTask</code>
+ *
+ * @param cr The {@link ContentResolver}
+ * @param cb The {@link OnMediaPictureDiscoveredListener} listener
+ * @param userRequest If the request was generated by the user
+ */
+ public AsyncDiscoverTask(
+ ContentResolver cr, OnMediaPictureDiscoveredListener cb, boolean userRequest) {
+ super();
+ mFinalContentResolver = cr;
+ mFinalCallback = cb;
+ mFilter = Preferences.Media.getSelectedMedia();
+ mLastAlbums = Preferences.Media.getLastDiscorevedAlbums();
+ mIsAutoSelectNewAlbums = Preferences.Media.isAutoSelectNewAlbums();
+ mNewAlbums = new HashSet<String>();
+ mUserRequest = userRequest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected List<File> doInBackground(Void...params) {
+ try {
+ // Start progress
+ publishProgress(new File[]{});
+
+ // The columns to read
+ final String[] projection = {MediaStore.MediaColumns.DATA};
+
+ // Query external content
+ List<File> paths =
+ getPictures(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ projection,
+ null,
+ null);
+ if (DEBUG) {
+ int cc = paths.size();
+ Log.v(TAG, "Pictures found (" + cc + "):");
+ for (int i = 0; i < cc; i++) {
+ Log.v(TAG, "\t" + paths.get(i));
+ }
+ }
+ return paths;
+
+ } catch (Exception e) {
+ Log.e(TAG, "AsyncDiscoverTask failed.", e);
+
+ // Return and empty list
+ return new ArrayList<File>();
+ } finally {
+ // Save the filter (could have new albums)
+ Preferences.Media.setSelectedMedia(mContext, mFilter);
+ Preferences.Media.setLastDiscorevedAlbums(mContext, mNewAlbums);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onProgressUpdate(File... values) {
+ if (mFinalCallback != null) {
+ if (values == null || values.length == 0) {
+ mFinalCallback.onStartMediaDiscovered(mUserRequest);
+ } else {
+ mFinalCallback.onPartialMediaDiscovered(values, mUserRequest);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPostExecute(List<File> result) {
+ if (mFinalCallback != null) {
+ mFinalCallback.onEndMediaDiscovered(result.toArray(new File[result.size()]), mUserRequest);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onCancelled(List<File> result) {
+ // Nothing found
+ if (mFinalCallback != null) {
+ // Overwrite the user request setting. If the task is cancelled then
+ // there is no notification to send to the user
+ mFinalCallback.onEndMediaDiscovered(new File[]{}, false);
+ }
+ }
+
+ /**
+ * Method that return all the media store pictures for the content uri
+ *
+ * @param uri The content uri where to search
+ * @param projection The field data to return
+ * @param where A filter
+ * @param args The filter arguments
+ * @return List<File> The pictures found
+ */
+ private List<File> getPictures(
+ Uri uri, String[] projection, String where, String[] args) {
+ long start = System.currentTimeMillis();
+ List<File> paths = new ArrayList<File>();
+ List<File> partial = new ArrayList<File>();
+ Cursor c = mFinalContentResolver.query(uri, projection, where, args, null);
+ if (c != null) {
+ try {
+ int i = 0;
+ while (c.moveToNext()) {
+ // Only valid files (those i can read)
+ String p = c.getString(0);
+ if (p != null) {
+ File f = new File(p);
+ catalog(f);
+
+ // Check if is a valid filter
+ if (matchFilter(f)) {
+ paths.add(f);
+ partial.add(f);
+ }
+ }
+
+ // Publish partial data
+ if (i % 5 == 0 && partial.size() > 0) {
+ publishProgress(partial.toArray(new File[partial.size()]));
+ partial.clear();
+ }
+ i++;
+ }
+ } finally {
+ try {
+ c.close();
+ } catch (Exception e) {
+ // Ignore: handle exception
+ }
+ }
+ }
+ long end = System.currentTimeMillis();
+ if (DEBUG) Log.v(TAG, "Media reloaded in " + (end - start) + " miliseconds");
+ return paths;
+ }
+
+ /**
+ * Method that checks if the picture match the preferences filter
+ *
+ * @param picture The picture to check
+ * @return boolean whether the picture match the filter
+ */
+ private boolean matchFilter(File picture) {
+ return mFilter.contains(picture.getAbsolutePath()) ||
+ mFilter.contains(picture.getParentFile().getAbsolutePath());
+ }
+
+ /**
+ * Method that catalog the file (set its album and determine if is a new album)
+ *
+ * @param f The file to catalog
+ */
+ private void catalog(File f) {
+ File parent = f.getParentFile();
+ String albumPath = parent.getAbsolutePath();
+
+ // Add to new albums
+ mNewAlbums.add(albumPath);
+
+ // Is a new album?
+ if (!mLastAlbums.contains(albumPath)) {
+ // Is in the filter?
+ if (mIsAutoSelectNewAlbums && !mFilter.contains(albumPath)) {
+ // Add the album to the selected filter
+ mFilter.add(parent.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ /*package*/ final Context mContext;
+ private final OnMediaPictureDiscoveredListener mCallback;
+
+ private AsyncDiscoverTask mTask;
+
+ /**
+ * Constructor of <code>MediaPictureDiscoverer</code>.
+ *
+ * @param ctx The current context
+ * @param callback A callback to returns the data when it gets ready
+ */
+ public MediaPictureDiscoverer(Context ctx, OnMediaPictureDiscoveredListener callback) {
+ super();
+ mContext = ctx;
+ mCallback = callback;
+ }
+
+ /**
+ * Method that request a new reload of the media store picture data.
+ *
+ * @param userRequest If the request was generated by the user
+ */
+ public synchronized void discover(boolean userRequest) {
+ if (mTask != null && mTask.getStatus().compareTo(Status.FINISHED) != 0 &&
+ !mTask.isCancelled()) {
+ mTask.cancel(true);
+ }
+ mTask = new AsyncDiscoverTask(mContext.getContentResolver(), mCallback, userRequest);
+ mTask.execute();
+ }
+
+ /**
+ * Method that destroy the references of this class
+ */
+ public void recycle() {
+ if (mTask != null && !mTask.isCancelled()) {
+ mTask.cancel(true);
+ }
+ }
+
+}