/* * 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.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 { public static final int MEDIAITEM_BATCH_FETCH_COUNT = 500; public static final int INDEX_NOT_FOUND = -1; 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 getMediaItem(int start, int count) { return new ArrayList(); } public int getSubMediaSetCount() { return 0; } public MediaSet getSubMediaSet(int index) { throw new IndexOutOfBoundsException(); } public boolean isLeafAlbum() { 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 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 list) { for (int i = 0, n = list.size(); i < n; ++i) { if (list.get(i).mPath == path) return i; } return INDEX_NOT_FOUND; } public abstract String getName(); private WeakHashMap mListeners = new WeakHashMap(); // 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) { if (mListeners.containsKey(listener)) { throw new IllegalArgumentException(); } mListeners.put(listener, null); } public void removeContentListener(ContentListener listener) { if (!mListeners.containsKey(listener)) { throw new IllegalArgumentException(); } 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 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; } public Future requestSync() { return FUTURE_STUB; } private static final Future FUTURE_STUB = new Future() { @Override public void cancel() {} @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return true; } @Override public Void get() { return null; } @Override public void waitDone() {} }; }