/** * galaxy inc. * meetup client for android */ package com.galaxy.meetup.client.android.service; import java.io.File; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import WriteReviewOperation.MediaRef; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Handler; import android.support.v4.util.LruCache; import android.util.Log; import com.galaxy.meetup.client.android.EsApplication; import com.galaxy.meetup.client.android.R; import com.galaxy.meetup.client.android.content.EsAvatarData; import com.galaxy.meetup.client.android.content.cache.EsMediaCache; import com.galaxy.meetup.client.util.EsLog; import com.galaxy.meetup.client.util.GifImage; import com.galaxy.meetup.client.util.ImageUtils; /** * * @author sihai * */ public class ImageResourceManager extends ResourceManager { private static ImageResourceManager sInstance; private final Map mActiveResources = new HashMap(); private AvatarIdentifier mAvatarIdPool; private final boolean mBitmapPackingEnabled; private final LruCache mImageCache; private int mLargeImageThreshold; private MediaIdentifier mMediaIdPool; private ResourceDownloader mResourceDownloader; private ImageResourceManager(Context context) { super(context); mAvatarIdPool = null; mMediaIdPool = null; Resources resources = context.getApplicationContext().getResources(); int i; boolean flag; if(EsApplication.sMemoryClass >= 48) i = Math.max(resources.getInteger(R.integer.config_image_cache_max_bytes_decoded_large), 1024 * (1024 * (EsApplication.sMemoryClass / 4))); else i = resources.getInteger(R.integer.config_image_cache_max_bytes_decoded_small); mLargeImageThreshold = i / 3; mImageCache = new LruCache(i) { protected final void entryRemoved(boolean flag1, Object obj, Object obj1, Object obj2) { ImageResourceIdentifier imageresourceidentifier = (ImageResourceIdentifier)obj; ImageResource imageresource = (ImageResource)obj1; if(flag1) { if(imageresource.isDebugLogEnabled()) imageresource.logDebug((new StringBuilder("Evicted image from cache: ")).append(imageresourceidentifier).toString()); imageresource.recycle(); } } protected final int sizeOf(Object obj, Object obj1) { return ((ImageResource)obj1).getSizeInBytes(); } }; if(android.os.Build.VERSION.SDK_INT < 11) flag = true; else flag = false; mBitmapPackingEnabled = flag; } static String buildShortFileName(String s) { long l = 0x3ffffffffffe5L; int i = s.length(); for(int j = 0; j < i; j++) l = 31L * l + (long)s.charAt(j); return Long.toHexString(0xfffffffffffffffL & l >> 4); } public static synchronized ImageResourceManager getInstance(Context context) { ImageResourceManager imageresourcemanager; if(sInstance == null) sInstance = new ImageResourceManager(context.getApplicationContext()); imageresourcemanager = sInstance; return imageresourcemanager; } private Resource getMedia(MediaRef mediaref, int i, int j, int k, int l, ResourceConsumer resourceconsumer) { MediaIdentifier mediaidentifier; MediaResource mediaresource; if(mMediaIdPool != null) { mediaidentifier = mMediaIdPool; mMediaIdPool = mMediaIdPool.getNextInPool(); mediaidentifier.setNextInPool(null); } else { mediaidentifier = new MediaIdentifier(); } mediaidentifier.init(l, mediaref, i, j, k); mediaresource = (MediaResource)mActiveResources.get(mediaidentifier); if(mediaresource == null) { mediaresource = (MediaResource)mImageCache.get(mediaidentifier); if(mediaresource != null) { if(mediaresource.isDebugLogEnabled()) mediaresource.logDebug((new StringBuilder("getMedia [CACHED]: ")).append(mediaidentifier).toString()); mImageCache.remove(mediaidentifier); } else { mediaresource = new MediaResource(this, mediaidentifier); if(mediaresource.isDebugLogEnabled()) mediaresource.logDebug((new StringBuilder("getMedia [NOT CACHED]: ")).append(mediaidentifier).toString()); } mActiveResources.put(mediaidentifier, mediaresource); } else { if(mediaresource.isDebugLogEnabled()) mediaresource.logDebug((new StringBuilder("getMedia [ACTIVE]: ")).append(mediaidentifier).toString()); mediaidentifier.setNextInPool(mMediaIdPool); mMediaIdPool = mediaidentifier; } mediaresource.register(resourceconsumer); return mediaresource; } public final Resource getAvatar(String s, int i, boolean flag, ResourceConsumer resourceconsumer) { AvatarIdentifier avataridentifier; Object obj; if(mAvatarIdPool != null) { avataridentifier = mAvatarIdPool; mAvatarIdPool = mAvatarIdPool.getNextInPool(); avataridentifier.setNextInPool(null); } else { avataridentifier = new AvatarIdentifier(); } avataridentifier.init(s, i, true); obj = (ImageResource)mActiveResources.get(avataridentifier); if(obj == null) { obj = (ImageResource)mImageCache.get(avataridentifier); if(obj != null) { if(((ImageResource) (obj)).isDebugLogEnabled()) ((ImageResource) (obj)).logDebug((new StringBuilder("getAvatar [CACHED]: ")).append(avataridentifier).toString()); mImageCache.remove(avataridentifier); } else { obj = new UrlImageResource(this, avataridentifier, ImageUtils.getCroppedAndResizedUrl(EsAvatarData.getAvatarSizeInPx(getContext(), i), s)); if(((ImageResource) (obj)).isDebugLogEnabled()) ((ImageResource) (obj)).logDebug((new StringBuilder("getAvatar [NOT CACHED]: ")).append(avataridentifier).toString()); } mActiveResources.put(avataridentifier, obj); } else { if(((ImageResource) (obj)).isDebugLogEnabled()) ((ImageResource) (obj)).logDebug((new StringBuilder("getAvatar [ACTIVE]: ")).append(avataridentifier).toString()); avataridentifier.setNextInPool(mAvatarIdPool); mAvatarIdPool = avataridentifier; } ((ImageResource) (obj)).register(resourceconsumer); return ((Resource) (obj)); } public final Resource getMedia(MediaRef mediaref, int i, int j, int k, ResourceConsumer resourceconsumer) { return getMedia(mediaref, 0, i, j, k, resourceconsumer); } public final Resource getMedia(MediaRef mediaref, int i, int j, ResourceConsumer resourceconsumer) { return getMedia(mediaref, i, 0, 0, j, resourceconsumer); } public final Resource getMedia(MediaRef mediaref, int i, ResourceConsumer resourceconsumer) { return getMedia(mediaref, i, 0, resourceconsumer); } protected final ResourceDownloader getResourceDownloader() { if(mResourceDownloader == null) mResourceDownloader = new ResourceDownloader(getContext(), new Handler()); return mResourceDownloader; } public final void onEnvironmentChanged() { NetworkInfo networkinfo; if(mActiveResources.size() != 0) if((networkinfo = ((ConnectivityManager)getContext().getSystemService("connectivity")).getActiveNetworkInfo()) != null && networkinfo.isConnected()) { Iterator iterator = mActiveResources.values().iterator(); while(iterator.hasNext()) { Resource resource = (Resource)iterator.next(); if(resource.getStatus() == 5) { deliverResourceStatus(resource, 2); loadResource(resource); } } } } public final void onFirstConsumerRegistered(Resource resource) { if(!mActiveResources.containsKey(resource.getIdentifier())) throw new IllegalStateException((new StringBuilder("Resource is not active: ")).append(resource.getIdentifier()).toString()); ImageResource imageresource = (ImageResource)resource; switch(imageresource.getStatus()) { case 0: if(imageresource.isDebugLogEnabled()) imageresource.logDebug((new StringBuilder("Requesting image load: ")).append(imageresource.mId).toString()); imageresource.mStatus = 2; loadResource(resource); break; case 1: break; case 2: throw new IllegalStateException((new StringBuilder("Illegal resource state: ")).append(resource.getStatusAsString()).toString()); case 3: throw new IllegalStateException((new StringBuilder("Illegal resource state: ")).append(resource.getStatusAsString()).toString()); case 4: throw new IllegalStateException((new StringBuilder("Illegal resource state: ")).append(resource.getStatusAsString()).toString()); case 5: if(imageresource.isDebugLogEnabled()) imageresource.logDebug((new StringBuilder("Requesting image load: ")).append(imageresource.mId).toString()); imageresource.mStatus = 2; loadResource(resource); break; case 6: throw new IllegalStateException((new StringBuilder("Illegal resource state: ")).append(resource.getStatusAsString()).toString()); case 7: break; case 8: if(imageresource.isDebugLogEnabled()) imageresource.logDebug((new StringBuilder("Requesting image load: ")).append(imageresource.mId).toString()); imageresource.mStatus = 2; loadResource(resource); break; case 9: if(mBitmapPackingEnabled) imageresource.unpack(); else resource.mStatus = 1; break; default: throw new IllegalStateException((new StringBuilder("Illegal resource state: ")).append(resource.getStatusAsString()).toString()); } } public final void onLastConsumerUnregistered(Resource resource) { ImageResource imageresource = (ImageResource)resource; ImageResourceIdentifier imageresourceidentifier = (ImageResourceIdentifier)imageresource.mId; if(imageresource.isDebugLogEnabled()) imageresource.logDebug((new StringBuilder("Deactivating image resource: ")).append(imageresourceidentifier).toString()); int status = imageresource.getStatus(); if(status == 2 || status == 3) { imageresource.mStatus = 8; if(mResourceDownloader != null) mResourceDownloader.cancelDownload(imageresource); } mActiveResources.remove(imageresourceidentifier); if(status == 1 && (2 & imageresourceidentifier.mFlags) == 0) { boolean flag; if(!(((Resource) (imageresource)).mResource instanceof Bitmap) || !mBitmapPackingEnabled) flag = true; else if(((Bitmap)((Resource) (imageresource)).mResource).getConfig() != null) flag = true; else flag = false; if(flag) { int j = imageresource.getSizeInBytes(); boolean flag1; if(j == -1 || j >= mLargeImageThreshold) flag1 = true; else flag1 = false; if(!flag1) { if(mBitmapPackingEnabled) imageresource.pack(); mImageCache.put(imageresourceidentifier, imageresource); } } } } public final void verifyEmpty() { if(!mActiveResources.isEmpty()) { Log.i("ImageResourceManager", (new StringBuilder("ImageResourceManager contains ")).append(mActiveResources.size()).append(" resources").toString()); Resource resource; for(Iterator iterator = mActiveResources.values().iterator(); iterator.hasNext(); EsLog.writeToLog(4, "ImageResourceManager", (new StringBuilder()).append(resource.toString()).append("\n").toString())) resource = (Resource)iterator.next(); } } private static abstract class ImageResourceIdentifier extends Resource.ResourceIdentifier { protected int mFlags; public abstract String getShortFileName(); public final void init(int i) { mFlags = i; } } private abstract class UrlImageResourceIdentifier extends ImageResourceIdentifier { protected String mUrl; protected final void init(int i, String s) { super.init(0); mUrl = s; } } private final class AvatarIdentifier extends UrlImageResourceIdentifier { private int mHashCode; private AvatarIdentifier mNextInPool; private boolean mRounded; private int mSizeCategory; public final boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AvatarIdentifier)) { return false; } AvatarIdentifier avataridentifier = (AvatarIdentifier) obj; if (mSizeCategory != avataridentifier.mSizeCategory || mRounded != avataridentifier.mRounded || !mUrl.equals(avataridentifier.mUrl)) { return false; } return true; } public final AvatarIdentifier getNextInPool() { return mNextInPool; } public final String getShortFileName() { StringBuilder stringbuilder; stringbuilder = new StringBuilder(); stringbuilder.append(ImageResourceManager.buildShortFileName(mUrl)); if (0 == mSizeCategory) { stringbuilder.append("-at"); } else if (1 == mSizeCategory) { stringbuilder.append("-as"); } else if (2 == mSizeCategory) { stringbuilder.append("-am"); } else { } return stringbuilder.toString(); } public final int hashCode() { if(mHashCode == 0) { mHashCode = mUrl.hashCode() ^ mSizeCategory; if(mRounded) mHashCode = 1 + mHashCode; } return mHashCode; } public final void init(String s, int i, boolean flag) { super.init(0, s); mSizeCategory = i; mRounded = flag; mHashCode = 0; } public final void setNextInPool(AvatarIdentifier avataridentifier) { mNextInPool = avataridentifier; } public final String toString() { String s = null; if (0 == mSizeCategory) { s = "tiny"; } else if (1 == mSizeCategory) { s = "small"; } else if (2 == mSizeCategory) { s = "medium"; } else { } StringBuilder stringbuilder = (new StringBuilder("{")).append(mUrl).append(" (").append(s).append(")"); String s1; if(mRounded) s1 = " (rounded)"; else s1 = ""; return stringbuilder.append(s1).append("}").toString(); } } private final class MediaIdentifier extends ImageResourceIdentifier { private int mHashCode; private int mHeight; private MediaRef mMediaRef; private MediaIdentifier mNextInPool; private int mSizeCategory; private int mWidth; public final MediaIdentifier getNextInPool() { return mNextInPool; } public final void init(int i, MediaRef mediaref, int j, int k, int l) { init(i); mMediaRef = mediaref; mSizeCategory = j; mWidth = k; mHeight = l; mHashCode = 0; } public final void setNextInPool(MediaIdentifier mediaidentifier) { mNextInPool = mediaidentifier; } public final String getShortFileName() { StringBuilder stringbuilder = new StringBuilder(); if(mMediaRef.hasUrl()) stringbuilder.append(ImageResourceManager.buildShortFileName(mMediaRef.getUrl())); else if(mMediaRef.hasLocalUri()) stringbuilder.append(ImageResourceManager.buildShortFileName(mMediaRef.getLocalUri().toString())); else throw new IllegalStateException("A media ref should have a URI"); switch(mSizeCategory) { case 0: stringbuilder.append('-').append(mWidth).append('x').append(mHeight); break; case 1: if((4 & mFlags) != 0) stringbuilder.append("-a"); break; case 2: stringbuilder.append("-t"); break; case 3: stringbuilder.append("-b"); break; case 4: stringbuilder.append("-p"); break; case 5: stringbuilder.append("-l"); break; default: if((4 & mFlags) != 0) stringbuilder.append("-a"); break; } return stringbuilder.toString(); } public final boolean equals(Object obj) { if(obj == this) { return true; } if(!(obj instanceof MediaIdentifier)) { return false; } MediaIdentifier mediaidentifier = (MediaIdentifier)obj; if(mSizeCategory != mediaidentifier.mSizeCategory || mFlags != mediaidentifier.mFlags || !mMediaRef.equals(mediaidentifier.mMediaRef) || mWidth != mediaidentifier.mWidth || mHeight != mediaidentifier.mHeight) { return false; } return true; } public final int hashCode() { if(mHashCode == 0) mHashCode = mMediaRef.hashCode() + mSizeCategory + mFlags; return mHashCode; } public final String toString() { String s = null; switch(mSizeCategory){ case 0: s = (new StringBuilder()).append(mWidth).append("x").append(mHeight).toString(); break; case 1: s = "full"; break; case 2: s = "thumbnail"; break; case 3: s = "large"; break; case 4: s = "portrait"; break; case 5: s = "landscape"; break; default: break; } return s; } } private static final class MediaResource extends ImageResource { public MediaResource(ImageResourceManager imageresourcemanager, MediaIdentifier mediaidentifier) { super(imageresourcemanager, mediaidentifier); } protected final void downloadResource() { MediaIdentifier mediaidentifier = (MediaIdentifier)mId; int sizeCategory = mediaidentifier.mSizeCategory; int width = mediaidentifier.mWidth; int height = mediaidentifier.mHeight; MediaRef mediaref = mediaidentifier.mMediaRef; if(mediaref.hasLocalUri()) { Uri uri = mediaref.getLocalUri(); String s = uri.getScheme(); if(s != null && !s.startsWith("http")) { ResourceDownloader.loadLocalResource(mManager.getContext(), this, uri, sizeCategory, width, height); return; } } ResourceDownloader resourcedownloader = ((ImageResourceManager)mManager).getResourceDownloader(); boolean flag; if((4 & mediaidentifier.mFlags) != 0) flag = true; else flag = false; resourcedownloader.downloadResource(this, mediaref, sizeCategory, width, height, flag); } } private static abstract class ImageResource extends Resource { protected final String mCacheDir; protected final String mCacheFile; protected ImageResource(ImageResourceManager imageresourcemanager, ImageResourceIdentifier imageresourceidentifier) { super(imageresourcemanager, imageresourceidentifier); mCacheFile = imageresourceidentifier.getShortFileName(); mCacheDir = mCacheFile.substring(0, 1); } protected abstract void downloadResource(); public final void deliverData(byte abyte0[], boolean flag) { ImageResourceIdentifier imageresourceidentifier = (ImageResourceIdentifier)mId; boolean flag1; if((2 & imageresourceidentifier.mFlags) != 0) flag1 = true; else if((1 & imageresourceidentifier.mFlags) != 0) flag1 = false; else flag1 = flag; if(flag1) { if(isDebugLogEnabled()) logDebug((new StringBuilder("Saving image in file cache: ")).append(mId).toString()); EsMediaCache.write(mManager.getContext(), mCacheDir, mCacheFile, abyte0); } if(mStatus == 2 || mStatus == 3) { if((2 & imageresourceidentifier.mFlags) != 0) { if(isDebugLogEnabled()) logDebug((new StringBuilder("Image decoding disabled. Delivering null to consumers: ")).append(mId).toString()); mManager.deliverResourceContent(this, 1, null); return; } if((4 & imageresourceidentifier.mFlags) != 0 && GifImage.isGif(abyte0)) { mManager.deliverResourceContent(this, 1, new GifImage(abyte0)); return; } try { Bitmap bitmap = BitmapFactory.decodeByteArray(abyte0, 0, abyte0.length); if(bitmap != null && (mId instanceof AvatarIdentifier) && ((AvatarIdentifier)mId).mRounded) { Bitmap bitmap1 = ImageUtils.getRoundedBitmap(mManager.getContext(), bitmap); if(bitmap != bitmap1) { bitmap.recycle(); bitmap = bitmap1; } } if(bitmap != null) { if(isDebugLogEnabled()) logDebug((new StringBuilder("Delivering image to consumers: ")).append(mId).toString()); mManager.deliverResourceContent(this, 1, bitmap); } else { if(isDebugLogEnabled()) logDebug((new StringBuilder("Bad image; cannot decode: ")).append(mId).toString()); mManager.deliverResourceStatus(this, 6); } } catch(OutOfMemoryError outofmemoryerror) { if(isDebugLogEnabled()) logDebug((new StringBuilder("Out of memory while decoding image: ")).append(mId).toString()); mManager.deliverResourceContent(this, 7, null); } } else { if(isDebugLogEnabled()) logDebug((new StringBuilder("Request no longer needed, not delivering: ")).append(mId).append(", status: ").append(getStatusAsString()).toString()); } } public final File getCacheFileName() { File file; if((1 & ((ImageResourceIdentifier)mId).mFlags) != 0) file = null; else file = EsMediaCache.getMediaCacheFile(mManager.getContext(), mCacheDir, mCacheFile); return file; } public final int getSizeInBytes() { int i = -1; if (null == mResource) { return i; } if (mResource instanceof Bitmap) { Bitmap bitmap = (Bitmap) mResource; i = bitmap.getRowBytes() * bitmap.getHeight(); } else if (mResource instanceof GifImage) i = ((GifImage) mResource).getSizeEstimate(); else if (mResource instanceof PackedBitmap) i = ((PackedBitmap) mResource).sizeInBytes; return i; } public final void load() { ImageResourceIdentifier imageresourceidentifier = (ImageResourceIdentifier)mId; if((2 & imageresourceidentifier.mFlags) != 0 && EsMediaCache.exists(mManager.getContext(), mCacheDir, mCacheFile)) { mManager.deliverResourceContent(this, 1, null); } else { int i = 1 & imageresourceidentifier.mFlags; byte abyte0[] = null; if(i == 0) abyte0 = EsMediaCache.read(mManager.getContext(), mCacheDir, mCacheFile); if(abyte0 != null) { if(isDebugLogEnabled()) logDebug((new StringBuilder("Loaded image from file: ")).append(mId).toString()); deliverData(abyte0, false); } else { if(isDebugLogEnabled()) logDebug((new StringBuilder("Requesting network download: ")).append(mId).toString()); mManager.deliverResourceStatus(this, 3); downloadResource(); } } } public final void pack() { if(mStatus == 1 && (mResource instanceof Bitmap)) { Bitmap bitmap = (Bitmap)mResource; android.graphics.Bitmap.Config config = bitmap.getConfig(); if(config != null) try { PackedBitmap packedbitmap = new PackedBitmap(); packedbitmap.config = config; packedbitmap.width = bitmap.getWidth(); packedbitmap.height = bitmap.getHeight(); packedbitmap.sizeInBytes = bitmap.getRowBytes() * bitmap.getHeight(); packedbitmap.buffer = ByteBuffer.allocate(packedbitmap.sizeInBytes); bitmap.copyPixelsToBuffer(packedbitmap.buffer); bitmap.recycle(); mResource = packedbitmap; mStatus = 9; } catch(OutOfMemoryError outofmemoryerror) { mStatus = 7; } } } public final void recycle() { if(mResource instanceof Bitmap) ((Bitmap)mResource).recycle(); super.recycle(); } public final void unpack() { PackedBitmap packedbitmap; if(mStatus != 9 || !(mResource instanceof PackedBitmap)) return; try { packedbitmap = (PackedBitmap)mResource; Bitmap bitmap = Bitmap.createBitmap(packedbitmap.width, packedbitmap.height, packedbitmap.config); packedbitmap.buffer.rewind(); bitmap.copyPixelsFromBuffer(packedbitmap.buffer); mResource = bitmap; mStatus = 1; } catch (OutOfMemoryError outofmemoryerror) { mResource = null; mStatus = 7; } } } private static final class UrlImageResource extends ImageResource { private String mDownloadUrl; public UrlImageResource(ImageResourceManager imageresourcemanager, UrlImageResourceIdentifier urlimageresourceidentifier, String s) { super(imageresourcemanager, urlimageresourceidentifier); mDownloadUrl = s; } protected final void downloadResource() { ((ImageResourceManager)mManager).getResourceDownloader().downloadResource(this, mDownloadUrl); } } private static final class PackedBitmap { public ByteBuffer buffer; public android.graphics.Bitmap.Config config; public int height; public int sizeInBytes; public int width; private PackedBitmap() { } } }