MediaSet.java revision f9a0a4306d589b4a4e20554fed512a603426bfa1
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.gallery3d.data; 18 19import com.android.gallery3d.util.Future; 20 21import java.util.ArrayList; 22import java.util.WeakHashMap; 23 24// MediaSet is a directory-like data structure. 25// It contains MediaItems and sub-MediaSets. 26// 27// The primary interface are: 28// getMediaItemCount(), getMediaItem() and 29// getSubMediaSetCount(), getSubMediaSet(). 30// 31// getTotalMediaItemCount() returns the number of all MediaItems, including 32// those in sub-MediaSets. 33public abstract class MediaSet extends MediaObject { 34 public static final int MEDIAITEM_BATCH_FETCH_COUNT = 500; 35 public static final int INDEX_NOT_FOUND = -1; 36 37 public MediaSet(Path path, long version) { 38 super(path, version); 39 } 40 41 public int getMediaItemCount() { 42 return 0; 43 } 44 45 // Returns the media items in the range [start, start + count). 46 // 47 // The number of media items returned may be less than the specified count 48 // if there are not enough media items available. The number of 49 // media items available may not be consistent with the return value of 50 // getMediaItemCount() because the contents of database may have already 51 // changed. 52 public ArrayList<MediaItem> getMediaItem(int start, int count) { 53 return new ArrayList<MediaItem>(); 54 } 55 56 public int getSubMediaSetCount() { 57 return 0; 58 } 59 60 public MediaSet getSubMediaSet(int index) { 61 throw new IndexOutOfBoundsException(); 62 } 63 64 public boolean isLeafAlbum() { 65 return false; 66 } 67 68 public int getTotalMediaItemCount() { 69 int total = getMediaItemCount(); 70 for (int i = 0, n = getSubMediaSetCount(); i < n; i++) { 71 total += getSubMediaSet(i).getTotalMediaItemCount(); 72 } 73 return total; 74 } 75 76 // TODO: we should have better implementation of sub classes 77 public int getIndexOfItem(Path path, int hint) { 78 // hint < 0 is handled below 79 // first, try to find it around the hint 80 int start = Math.max(0, 81 hint - MEDIAITEM_BATCH_FETCH_COUNT / 2); 82 ArrayList<MediaItem> list = getMediaItem( 83 start, MEDIAITEM_BATCH_FETCH_COUNT); 84 int index = getIndexOf(path, list); 85 if (index != INDEX_NOT_FOUND) return start + index; 86 87 // try to find it globally 88 start = start == 0 ? MEDIAITEM_BATCH_FETCH_COUNT : 0; 89 list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT); 90 while (true) { 91 index = getIndexOf(path, list); 92 if (index != INDEX_NOT_FOUND) return start + index; 93 if (list.size() < MEDIAITEM_BATCH_FETCH_COUNT) return INDEX_NOT_FOUND; 94 start += MEDIAITEM_BATCH_FETCH_COUNT; 95 list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT); 96 } 97 } 98 99 protected int getIndexOf(Path path, ArrayList<MediaItem> list) { 100 for (int i = 0, n = list.size(); i < n; ++i) { 101 if (list.get(i).mPath == path) return i; 102 } 103 return INDEX_NOT_FOUND; 104 } 105 106 public abstract String getName(); 107 108 private WeakHashMap<ContentListener, Object> mListeners = 109 new WeakHashMap<ContentListener, Object>(); 110 111 // NOTE: The MediaSet only keeps a weak reference to the listener. The 112 // listener is automatically removed when there is no other reference to 113 // the listener. 114 public void addContentListener(ContentListener listener) { 115 if (mListeners.containsKey(listener)) { 116 throw new IllegalArgumentException(); 117 } 118 mListeners.put(listener, null); 119 } 120 121 public void removeContentListener(ContentListener listener) { 122 if (!mListeners.containsKey(listener)) { 123 throw new IllegalArgumentException(); 124 } 125 mListeners.remove(listener); 126 } 127 128 // This should be called by subclasses when the content is changed. 129 public void notifyContentChanged() { 130 for (ContentListener listener : mListeners.keySet()) { 131 listener.onContentDirty(); 132 } 133 } 134 135 // Reload the content. Return the current data version. reload() should be called 136 // in the same thread as getMediaItem(int, int) and getSubMediaSet(int). 137 public abstract long reload(); 138 139 @Override 140 public MediaDetails getDetails() { 141 MediaDetails details = super.getDetails(); 142 details.addDetail(MediaDetails.INDEX_TITLE, getName()); 143 return details; 144 } 145 146 // Enumerate all media items in this media set (including the ones in sub 147 // media sets), in an efficient order. ItemConsumer.consumer() will be 148 // called for each media item with its index. 149 public void enumerateMediaItems(ItemConsumer consumer) { 150 enumerateMediaItems(consumer, 0); 151 } 152 153 public void enumerateTotalMediaItems(ItemConsumer consumer) { 154 enumerateTotalMediaItems(consumer, 0); 155 } 156 157 public static interface ItemConsumer { 158 void consume(int index, MediaItem item); 159 } 160 161 // The default implementation uses getMediaItem() for enumerateMediaItems(). 162 // Subclasses may override this and use more efficient implementations. 163 // Returns the number of items enumerated. 164 protected int enumerateMediaItems(ItemConsumer consumer, int startIndex) { 165 int total = getMediaItemCount(); 166 int start = 0; 167 while (start < total) { 168 int count = Math.min(MEDIAITEM_BATCH_FETCH_COUNT, total - start); 169 ArrayList<MediaItem> items = getMediaItem(start, count); 170 for (int i = 0, n = items.size(); i < n; i++) { 171 MediaItem item = items.get(i); 172 consumer.consume(startIndex + start + i, item); 173 } 174 start += count; 175 } 176 return total; 177 } 178 179 // Recursively enumerate all media items under this set. 180 // Returns the number of items enumerated. 181 protected int enumerateTotalMediaItems( 182 ItemConsumer consumer, int startIndex) { 183 int start = 0; 184 start += enumerateMediaItems(consumer, startIndex); 185 int m = getSubMediaSetCount(); 186 for (int i = 0; i < m; i++) { 187 start += getSubMediaSet(i).enumerateTotalMediaItems( 188 consumer, startIndex + start); 189 } 190 return start; 191 } 192 193 public Future<Void> requestSync() { 194 return FUTURE_STUB; 195 } 196 197 private static final Future<Void> FUTURE_STUB = new Future<Void>() { 198 @Override 199 public void cancel() {} 200 201 @Override 202 public boolean isCancelled() { 203 return false; 204 } 205 206 @Override 207 public boolean isDone() { 208 return true; 209 } 210 211 @Override 212 public Void get() { 213 return null; 214 } 215 216 @Override 217 public void waitDone() {} 218 }; 219} 220