| /* |
| * Copyright (C) 2009 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.cooliris.media; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.DataInputStream; |
| import java.io.IOException; |
| import java.net.URISyntaxException; |
| |
| import android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.BitmapFactory; |
| import android.net.Uri; |
| import android.provider.MediaStore; |
| import android.util.Log; |
| import android.os.Process; |
| |
| import com.cooliris.cache.CacheService; |
| |
| public final class MediaItemTexture extends Texture { |
| public static final int MAX_FACES = 1; |
| private static final String TAG = "MediaItemTexture"; |
| private static final int CACHE_HEADER_SIZE = 12; |
| |
| private final Config mConfig; |
| private final MediaItem mItem; |
| private Context mContext; |
| private boolean mIsRetrying; |
| private boolean mCached; |
| |
| public static final class Config { |
| public int thumbnailWidth; |
| public int thumbnailHeight; |
| } |
| |
| public MediaItemTexture(Context context, Config config, MediaItem item) { |
| mConfig = config; |
| mContext = context; |
| mItem = item; |
| mCached = computeCache(); |
| } |
| |
| private boolean computeCache() { |
| final Config config = mConfig; |
| final MediaItem item = mItem; |
| DiskCache cache = null; |
| MediaSet parentMediaSet = item.mParentMediaSet; |
| if (config != null && parentMediaSet != null && parentMediaSet.mDataSource != null) { |
| cache = parentMediaSet.mDataSource.getThumbnailCache(); |
| if (cache == LocalDataSource.sThumbnailCache) { |
| if (item.mMimeType != null && item.mMimeType.contains("video")) { |
| cache = LocalDataSource.sThumbnailCacheVideo; |
| } |
| } |
| } |
| if (cache == null) { |
| return false; |
| } |
| synchronized (cache) { |
| long id = parentMediaSet.mPicasaAlbumId == Shared.INVALID ? Utils.Crc64Long(item.mFilePath) : item.mId; |
| return cache.isDataAvailable(id, item.mDateModifiedInSec * 1000); |
| } |
| } |
| |
| @Override |
| public boolean isUncachedVideo() { |
| if (isCached()) |
| return false; |
| if (mItem.mParentMediaSet == null || mItem.mMimeType == null) |
| return false; |
| if (mItem.mParentMediaSet.mPicasaAlbumId == Shared.INVALID && mItem.mMimeType.contains("video")) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| @Override |
| public boolean isCached() { |
| return mCached; |
| } |
| |
| protected Bitmap load(RenderView view) { |
| final Config config = mConfig; |
| final MediaItem item = mItem; |
| |
| // Special case for non-MediaStore content URIs, do not cache the |
| // thumbnail. |
| String uriString = item.mContentUri; |
| if (uriString != null) { |
| Uri uri = Uri.parse(uriString); |
| if (uri.getScheme().equals("content") && !uri.getAuthority().equals("media")) { |
| try { |
| return UriTexture.createFromUri(mContext, item.mThumbnailUri, 128, 128, 0, null); |
| } catch (IOException e) { |
| return null; |
| } catch (URISyntaxException e) { |
| return null; |
| } |
| } |
| } |
| |
| // Look up the thumbnail in the disk cache. |
| if (config == null) { |
| Bitmap retVal = null; |
| try { |
| if (mItem.getMediaType() == MediaItem.MEDIA_TYPE_IMAGE) { |
| Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); |
| try { |
| // We first dirty the cache if the timestamp has changed |
| DiskCache cache = null; |
| MediaSet parentMediaSet = item.mParentMediaSet; |
| if (parentMediaSet != null && parentMediaSet.mDataSource != null) { |
| cache = parentMediaSet.mDataSource.getThumbnailCache(); |
| if (cache == LocalDataSource.sThumbnailCache) { |
| if (item.mMimeType != null && item.mMimeType.contains("video")) { |
| cache = LocalDataSource.sThumbnailCacheVideo; |
| } |
| final long crc64 = Utils.Crc64Long(item.mFilePath); |
| if (!cache.isDataAvailable(crc64, item.mDateModifiedInSec * 1000)) { |
| UriTexture.invalidateCache(crc64, UriTexture.MAX_RESOLUTION); |
| } |
| } |
| } |
| retVal = UriTexture.createFromUri(mContext, mItem.mContentUri, UriTexture.MAX_RESOLUTION, |
| UriTexture.MAX_RESOLUTION, Utils.Crc64Long(item.mFilePath), null); |
| } catch (IOException e) { |
| ; |
| } catch (URISyntaxException e) { |
| ; |
| } |
| Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); |
| } else { |
| Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); |
| new Thread() { |
| public void run() { |
| try { |
| Thread.sleep(5000); |
| } catch (InterruptedException e) { |
| ; |
| } |
| try { |
| MediaStore.Video.Thumbnails.cancelThumbnailRequest(mContext.getContentResolver(), mItem.mId); |
| } catch (Exception e) { |
| ; |
| } |
| } |
| }.start(); |
| retVal = MediaStore.Video.Thumbnails.getThumbnail(mContext.getContentResolver(), mItem.mId, |
| MediaStore.Video.Thumbnails.MINI_KIND, null); |
| Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); |
| } |
| } catch (OutOfMemoryError e) { |
| Log.i(TAG, "Bitmap creation fail, outofmemory"); |
| view.handleLowMemory(); |
| try { |
| if (!mIsRetrying) { |
| Thread.sleep(1000); |
| mIsRetrying = true; |
| retVal = load(view); |
| } |
| } catch (InterruptedException eInterrupted) { |
| |
| } |
| } |
| return retVal; |
| } else { |
| byte[] data = null; |
| MediaSet parentMediaSet = item.mParentMediaSet; |
| if (parentMediaSet != null && !parentMediaSet.mIsLocal) { |
| DiskCache thumbnailCache = parentMediaSet.mDataSource.getThumbnailCache(); |
| data = thumbnailCache.get(item.mId, 0); |
| if (data == null) { |
| // We need to generate the cache. |
| try { |
| Bitmap retVal = UriTexture.createFromUri(mContext, item.mThumbnailUri, 256, 256, 0, null); |
| data = CacheService.writeBitmapToCache(thumbnailCache, item.mId, item.mId, retVal, config.thumbnailWidth, |
| config.thumbnailHeight, item.mDateModifiedInSec * 1000); |
| } catch (IOException e) { |
| return null; |
| } catch (URISyntaxException e) { |
| return null; |
| } |
| } |
| } else { |
| long dateToUse = (item.mDateAddedInSec > item.mDateModifiedInSec) ? item.mDateAddedInSec : item.mDateModifiedInSec; |
| data = CacheService.queryThumbnail(mContext, Utils.Crc64Long(item.mFilePath), item.mId, |
| item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO, dateToUse * 1000); |
| } |
| if (data != null) { |
| try { |
| // Parse record header. |
| final ByteArrayInputStream cacheInput = new ByteArrayInputStream(data); |
| final DataInputStream dataInput = new DataInputStream(cacheInput); |
| item.mThumbnailId = dataInput.readLong(); |
| item.mThumbnailFocusX = dataInput.readShort(); |
| item.mThumbnailFocusY = dataInput.readShort(); |
| // Decode the thumbnail. |
| final BitmapFactory.Options options = new BitmapFactory.Options(); |
| options.inDither = true; |
| options.inScaled = false; |
| options.inPreferredConfig = Bitmap.Config.RGB_565; |
| final Bitmap bitmap = BitmapFactory.decodeByteArray(data, CACHE_HEADER_SIZE, data.length - CACHE_HEADER_SIZE, |
| options); |
| return bitmap; |
| } catch (IOException e) { |
| // Fall through to regenerate the cached thumbnail. |
| } |
| } |
| } |
| return null; |
| } |
| } |