1 /* 2 * Copyright (C) 2011 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 17 package com.android.gallery3d.gadget; 18 19 import android.graphics.Bitmap; 20 import android.net.Uri; 21 import android.os.Binder; 22 23 import com.android.gallery3d.common.Utils; 24 import com.android.gallery3d.data.ContentListener; 25 import com.android.gallery3d.data.DataManager; 26 import com.android.gallery3d.data.MediaItem; 27 import com.android.gallery3d.data.MediaObject; 28 import com.android.gallery3d.data.MediaSet; 29 import com.android.gallery3d.data.Path; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 34 public class MediaSetSource implements WidgetSource, ContentListener { 35 private static final String TAG = "MediaSetSource"; 36 37 private DataManager mDataManager; 38 private Path mAlbumPath; 39 40 private WidgetSource mSource; 41 42 private MediaSet mRootSet; 43 private ContentListener mListener; 44 45 public MediaSetSource(DataManager manager, String albumPath) { 46 MediaSet mediaSet = (MediaSet) manager.getMediaObject(albumPath); 47 if (mediaSet != null) { 48 mSource = new CheckedMediaSetSource(mediaSet); 49 return; 50 } 51 52 // Initialize source to an empty source until the album path can be resolved 53 mDataManager = Utils.checkNotNull(manager); 54 mAlbumPath = Path.fromString(albumPath); 55 mSource = new EmptySource(); 56 monitorRootPath(); 57 } 58 59 @Override 60 public int size() { 61 return mSource.size(); 62 } 63 64 @Override 65 public Bitmap getImage(int index) { 66 return mSource.getImage(index); 67 } 68 69 @Override 70 public Uri getContentUri(int index) { 71 return mSource.getContentUri(index); 72 } 73 74 @Override 75 public synchronized void setContentListener(ContentListener listener) { 76 if (mRootSet != null) { 77 mListener = listener; 78 } else { 79 mSource.setContentListener(listener); 80 } 81 } 82 83 @Override 84 public void reload() { 85 mSource.reload(); 86 } 87 88 @Override 89 public void close() { 90 mSource.close(); 91 } 92 93 @Override 94 public void onContentDirty() { 95 resolveAlbumPath(); 96 } 97 98 private void monitorRootPath() { 99 String rootPath = mDataManager.getTopSetPath(DataManager.INCLUDE_ALL); 100 mRootSet = (MediaSet) mDataManager.getMediaObject(rootPath); 101 mRootSet.addContentListener(this); 102 } 103 104 private synchronized void resolveAlbumPath() { 105 if (mDataManager == null) return; 106 MediaSet mediaSet = (MediaSet) mDataManager.getMediaObject(mAlbumPath); 107 if (mediaSet != null) { 108 // Clear the reference instead of removing the listener 109 // to get around a concurrent modification exception. 110 mRootSet = null; 111 112 mSource = new CheckedMediaSetSource(mediaSet); 113 if (mListener != null) { 114 mListener.onContentDirty(); 115 mSource.setContentListener(mListener); 116 mListener = null; 117 } 118 mDataManager = null; 119 mAlbumPath = null; 120 } 121 } 122 123 private static class CheckedMediaSetSource implements WidgetSource, ContentListener { 124 private static final int CACHE_SIZE = 32; 125 126 @SuppressWarnings("unused") 127 private static final String TAG = "CheckedMediaSetSource"; 128 129 private MediaSet mSource; 130 private MediaItem mCache[] = new MediaItem[CACHE_SIZE]; 131 private int mCacheStart; 132 private int mCacheEnd; 133 private long mSourceVersion = MediaObject.INVALID_DATA_VERSION; 134 135 private ContentListener mContentListener; 136 137 public CheckedMediaSetSource(MediaSet source) { 138 mSource = Utils.checkNotNull(source); 139 mSource.addContentListener(this); 140 } 141 142 @Override 143 public void close() { 144 mSource.removeContentListener(this); 145 } 146 147 private void ensureCacheRange(int index) { 148 if (index >= mCacheStart && index < mCacheEnd) return; 149 150 long token = Binder.clearCallingIdentity(); 151 try { 152 mCacheStart = index; 153 ArrayList<MediaItem> items = mSource.getMediaItem(mCacheStart, CACHE_SIZE); 154 mCacheEnd = mCacheStart + items.size(); 155 items.toArray(mCache); 156 } finally { 157 Binder.restoreCallingIdentity(token); 158 } 159 } 160 161 @Override 162 public synchronized Uri getContentUri(int index) { 163 ensureCacheRange(index); 164 if (index < mCacheStart || index >= mCacheEnd) return null; 165 return mCache[index - mCacheStart].getContentUri(); 166 } 167 168 @Override 169 public synchronized Bitmap getImage(int index) { 170 ensureCacheRange(index); 171 if (index < mCacheStart || index >= mCacheEnd) return null; 172 return WidgetUtils.createWidgetBitmap(mCache[index - mCacheStart]); 173 } 174 175 @Override 176 public void reload() { 177 long version = mSource.reload(); 178 if (mSourceVersion != version) { 179 mSourceVersion = version; 180 mCacheStart = 0; 181 mCacheEnd = 0; 182 Arrays.fill(mCache, null); 183 } 184 } 185 186 @Override 187 public void setContentListener(ContentListener listener) { 188 mContentListener = listener; 189 } 190 191 @Override 192 public int size() { 193 long token = Binder.clearCallingIdentity(); 194 try { 195 return mSource.getMediaItemCount(); 196 } finally { 197 Binder.restoreCallingIdentity(token); 198 } 199 } 200 201 @Override 202 public void onContentDirty() { 203 if (mContentListener != null) mContentListener.onContentDirty(); 204 } 205 } 206 207 private static class EmptySource implements WidgetSource { 208 209 @Override 210 public int size() { 211 return 0; 212 } 213 214 @Override 215 public Bitmap getImage(int index) { 216 throw new UnsupportedOperationException(); 217 } 218 219 @Override 220 public Uri getContentUri(int index) { 221 throw new UnsupportedOperationException(); 222 } 223 224 @Override 225 public void setContentListener(ContentListener listener) {} 226 227 @Override 228 public void reload() {} 229 230 @Override 231 public void close() {} 232 } 233 } 234