1 /* 2 * Copyright (C) 2009 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.cooliris.media; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.DataInputStream; 21 import java.io.IOException; 22 import java.net.URISyntaxException; 23 24 import android.content.Context; 25 import android.graphics.Bitmap; 26 import android.graphics.BitmapFactory; 27 import android.net.Uri; 28 import android.provider.MediaStore; 29 import android.util.Log; 30 import android.os.Process; 31 32 import com.cooliris.cache.CacheService; 33 34 public final class MediaItemTexture extends Texture { 35 public static final int MAX_FACES = 1; 36 private static final String TAG = "MediaItemTexture"; 37 private static final int CACHE_HEADER_SIZE = 12; 38 39 private final Config mConfig; 40 private final MediaItem mItem; 41 private Context mContext; 42 private boolean mIsRetrying; 43 private boolean mCached; 44 45 public static final class Config { 46 public int thumbnailWidth; 47 public int thumbnailHeight; 48 } 49 50 public MediaItemTexture(Context context, Config config, MediaItem item) { 51 mConfig = config; 52 mContext = context; 53 mItem = item; 54 mCached = computeCache(); 55 } 56 57 private boolean computeCache() { 58 final Config config = mConfig; 59 final MediaItem item = mItem; 60 DiskCache cache = null; 61 MediaSet parentMediaSet = item.mParentMediaSet; 62 if (config != null && parentMediaSet != null && parentMediaSet.mDataSource != null) { 63 cache = parentMediaSet.mDataSource.getThumbnailCache(); 64 if (cache == LocalDataSource.sThumbnailCache) { 65 if (item.mMimeType != null && item.mMimeType.contains("video")) { 66 cache = LocalDataSource.sThumbnailCacheVideo; 67 } 68 } 69 } 70 if (cache == null) { 71 return false; 72 } 73 synchronized (cache) { 74 long id = parentMediaSet.mPicasaAlbumId == Shared.INVALID ? Utils.Crc64Long(item.mFilePath) : item.mId; 75 return cache.isDataAvailable(id, item.mDateModifiedInSec * 1000); 76 } 77 } 78 79 @Override 80 public boolean isUncachedVideo() { 81 if (isCached()) 82 return false; 83 if (mItem.mParentMediaSet == null || mItem.mMimeType == null) 84 return false; 85 if (mItem.mParentMediaSet.mPicasaAlbumId == Shared.INVALID && mItem.mMimeType.contains("video")) { 86 return true; 87 } else { 88 return false; 89 } 90 } 91 92 @Override 93 public boolean isCached() { 94 return mCached; 95 } 96 97 protected Bitmap load(RenderView view) { 98 final Config config = mConfig; 99 final MediaItem item = mItem; 100 101 // Special case for non-MediaStore content URIs, do not cache the 102 // thumbnail. 103 String uriString = item.mContentUri; 104 if (uriString != null) { 105 Uri uri = Uri.parse(uriString); 106 if (uri.getScheme().equals("content") && !uri.getAuthority().equals("media")) { 107 try { 108 return UriTexture.createFromUri(mContext, item.mThumbnailUri, 128, 128, 0, null); 109 } catch (IOException e) { 110 return null; 111 } catch (URISyntaxException e) { 112 return null; 113 } 114 } 115 } 116 117 // Look up the thumbnail in the disk cache. 118 if (config == null) { 119 Bitmap retVal = null; 120 try { 121 if (mItem.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) { 122 Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 123 try { 124 // We first dirty the cache if the timestamp has changed 125 DiskCache cache = null; 126 MediaSet parentMediaSet = item.mParentMediaSet; 127 if (parentMediaSet != null && parentMediaSet.mDataSource != null) { 128 cache = parentMediaSet.mDataSource.getThumbnailCache(); 129 if (cache == LocalDataSource.sThumbnailCache) { 130 if (item.mMimeType != null && item.mMimeType.contains("video")) { 131 cache = LocalDataSource.sThumbnailCacheVideo; 132 } 133 final long crc64 = Utils.Crc64Long(item.mFilePath); 134 if (!cache.isDataAvailable(crc64, item.mDateModifiedInSec * 1000)) { 135 UriTexture.invalidateCache(crc64, UriTexture.MAX_RESOLUTION); 136 } 137 } 138 } 139 retVal = UriTexture.createFromUri(mContext, mItem.mContentUri, UriTexture.MAX_RESOLUTION, 140 UriTexture.MAX_RESOLUTION, Utils.Crc64Long(item.mFilePath), null); 141 } catch (IOException e) { 142 ; 143 } catch (URISyntaxException e) { 144 ; 145 } 146 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 147 } else { 148 Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 149 new Thread() { 150 public void run() { 151 try { 152 Thread.sleep(5000); 153 } catch (InterruptedException e) { 154 ; 155 } 156 try { 157 MediaStore.Video.Thumbnails.cancelThumbnailRequest(mContext.getContentResolver(), mItem.mId); 158 } catch (Exception e) { 159 ; 160 } 161 } 162 }.start(); 163 retVal = MediaStore.Video.Thumbnails.getThumbnail(mContext.getContentResolver(), mItem.mId, 164 MediaStore.Video.Thumbnails.MINI_KIND, null); 165 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 166 } 167 } catch (OutOfMemoryError e) { 168 Log.i(TAG, "Bitmap creation fail, outofmemory"); 169 view.handleLowMemory(); 170 try { 171 if (!mIsRetrying) { 172 Thread.sleep(1000); 173 mIsRetrying = true; 174 retVal = load(view); 175 } 176 } catch (InterruptedException eInterrupted) { 177 178 } 179 } 180 return retVal; 181 } else { 182 byte[] data = null; 183 MediaSet parentMediaSet = item.mParentMediaSet; 184 if (parentMediaSet != null && !parentMediaSet.mIsLocal) { 185 DiskCache thumbnailCache = parentMediaSet.mDataSource.getThumbnailCache(); 186 data = thumbnailCache.get(item.mId, 0); 187 if (data == null) { 188 // We need to generate the cache. 189 try { 190 Bitmap retVal = UriTexture.createFromUri(mContext, item.mThumbnailUri, 256, 256, 0, null); 191 data = CacheService.writeBitmapToCache(thumbnailCache, item.mId, item.mId, retVal, config.thumbnailWidth, 192 config.thumbnailHeight, item.mDateModifiedInSec * 1000); 193 } catch (IOException e) { 194 return null; 195 } catch (URISyntaxException e) { 196 return null; 197 } 198 } 199 } else { 200 long dateToUse = (item.mDateAddedInSec > item.mDateModifiedInSec) ? item.mDateAddedInSec : item.mDateModifiedInSec; 201 data = CacheService.queryThumbnail(mContext, Utils.Crc64Long(item.mFilePath), item.mId, 202 item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO, dateToUse * 1000); 203 } 204 if (data != null) { 205 try { 206 // Parse record header. 207 final ByteArrayInputStream cacheInput = new ByteArrayInputStream(data); 208 final DataInputStream dataInput = new DataInputStream(cacheInput); 209 item.mThumbnailId = dataInput.readLong(); 210 item.mThumbnailFocusX = dataInput.readShort(); 211 item.mThumbnailFocusY = dataInput.readShort(); 212 // Decode the thumbnail. 213 final BitmapFactory.Options options = new BitmapFactory.Options(); 214 options.inDither = true; 215 options.inScaled = false; 216 options.inPreferredConfig = Bitmap.Config.RGB_565; 217 final Bitmap bitmap = BitmapFactory.decodeByteArray(data, CACHE_HEADER_SIZE, data.length - CACHE_HEADER_SIZE, 218 options); 219 return bitmap; 220 } catch (IOException e) { 221 // Fall through to regenerate the cached thumbnail. 222 } 223 } 224 } 225 return null; 226 } 227 } 228