1 /* 2 * Copyright (C) 2015 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 package com.android.messaging.ui.photoviewer; 17 18 import android.content.Context; 19 import android.graphics.drawable.Drawable; 20 import android.net.Uri; 21 import android.support.rastermill.FrameSequenceDrawable; 22 import androidx.loader.content.AsyncTaskLoader; 23 24 import com.android.ex.photo.PhotoViewController; 25 import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface; 26 import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult; 27 import com.android.messaging.datamodel.media.ImageRequestDescriptor; 28 import com.android.messaging.datamodel.media.ImageResource; 29 import com.android.messaging.datamodel.media.MediaRequest; 30 import com.android.messaging.datamodel.media.MediaResourceManager; 31 import com.android.messaging.datamodel.media.UriImageRequestDescriptor; 32 import com.android.messaging.util.ImageUtils; 33 34 /** 35 * Loader for the bitmap of a photo. 36 */ 37 public class BuglePhotoBitmapLoader extends AsyncTaskLoader<BitmapResult> 38 implements PhotoBitmapLoaderInterface { 39 private String mPhotoUri; 40 private ImageResource mImageResource; 41 // The drawable that is currently "in use" and being presented to the user. This drawable 42 // should never exist without the image resource backing it. 43 private Drawable mDrawable; 44 BuglePhotoBitmapLoader(Context context, String photoUri)45 public BuglePhotoBitmapLoader(Context context, String photoUri) { 46 super(context); 47 mPhotoUri = photoUri; 48 } 49 50 @Override setPhotoUri(String photoUri)51 public void setPhotoUri(String photoUri) { 52 mPhotoUri = photoUri; 53 } 54 55 @Override loadInBackground()56 public BitmapResult loadInBackground() { 57 final BitmapResult result = new BitmapResult(); 58 final Context context = getContext(); 59 if (context != null && mPhotoUri != null) { 60 final ImageRequestDescriptor descriptor = 61 new UriImageRequestDescriptor(Uri.parse(mPhotoUri), 62 PhotoViewController.sMaxPhotoSize, PhotoViewController.sMaxPhotoSize, 63 true /* allowCompression */, false /* isStatic */, 64 false /* cropToCircle */, 65 ImageUtils.DEFAULT_CIRCLE_BACKGROUND_COLOR /* circleBackgroundColor */, 66 ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */); 67 final MediaRequest<ImageResource> imageRequest = 68 descriptor.buildSyncMediaRequest(context); 69 final ImageResource imageResource = 70 MediaResourceManager.get().requestMediaResourceSync(imageRequest); 71 if (imageResource != null) { 72 setImageResource(imageResource); 73 result.status = BitmapResult.STATUS_SUCCESS; 74 result.drawable = mImageResource.getDrawable(context.getResources()); 75 } else { 76 releaseImageResource(); 77 result.status = BitmapResult.STATUS_EXCEPTION; 78 } 79 } else { 80 result.status = BitmapResult.STATUS_EXCEPTION; 81 } 82 return result; 83 } 84 85 /** 86 * Called when there is new data to deliver to the client. The super class will take care of 87 * delivering it; the implementation here just adds a little more logic. 88 */ 89 @Override deliverResult(BitmapResult result)90 public void deliverResult(BitmapResult result) { 91 final Drawable drawable = result != null ? result.drawable : null; 92 if (isReset()) { 93 // An async query came in while the loader is stopped. We don't need the result. 94 releaseDrawable(drawable); 95 return; 96 } 97 98 // We are now going to display this drawable so set to mDrawable 99 mDrawable = drawable; 100 101 if (isStarted()) { 102 // If the Loader is currently started, we can immediately deliver its results. 103 super.deliverResult(result); 104 } 105 } 106 107 /** 108 * Handles a request to start the Loader. 109 */ 110 @Override onStartLoading()111 protected void onStartLoading() { 112 if (mDrawable != null) { 113 // If we currently have a result available, deliver it 114 // immediately. 115 final BitmapResult result = new BitmapResult(); 116 result.status = BitmapResult.STATUS_SUCCESS; 117 result.drawable = mDrawable; 118 deliverResult(result); 119 } 120 121 if (takeContentChanged() || (mImageResource == null)) { 122 // If the data has changed since the last time it was loaded 123 // or is not currently available, start a load. 124 forceLoad(); 125 } 126 } 127 128 /** 129 * Handles a request to stop the Loader. 130 */ 131 @Override onStopLoading()132 protected void onStopLoading() { 133 // Attempt to cancel the current load task if possible. 134 cancelLoad(); 135 } 136 137 /** 138 * Handles a request to cancel a load. 139 */ 140 @Override onCanceled(BitmapResult result)141 public void onCanceled(BitmapResult result) { 142 super.onCanceled(result); 143 144 // At this point we can release the resources associated with 'drawable' if needed. 145 if (result != null) { 146 releaseDrawable(result.drawable); 147 } 148 } 149 150 /** 151 * Handles a request to completely reset the Loader. 152 */ 153 @Override onReset()154 protected void onReset() { 155 super.onReset(); 156 157 // Ensure the loader is stopped 158 onStopLoading(); 159 160 releaseImageResource(); 161 } 162 releaseDrawable(Drawable drawable)163 private void releaseDrawable(Drawable drawable) { 164 if (drawable != null && drawable instanceof FrameSequenceDrawable 165 && !((FrameSequenceDrawable) drawable).isDestroyed()) { 166 ((FrameSequenceDrawable) drawable).destroy(); 167 } 168 169 } 170 setImageResource(final ImageResource resource)171 private void setImageResource(final ImageResource resource) { 172 if (mImageResource != resource) { 173 // Clear out any information for what is currently used 174 releaseImageResource(); 175 mImageResource = resource; 176 // No need to add ref since a ref is already reserved as a result of 177 // requestMediaResourceSync. 178 } 179 } 180 releaseImageResource()181 private void releaseImageResource() { 182 // If we are getting rid of the imageResource backing the drawable, we must also 183 // destroy the drawable before releasing it. 184 releaseDrawable(mDrawable); 185 mDrawable = null; 186 187 if (mImageResource != null) { 188 mImageResource.release(); 189 } 190 mImageResource = null; 191 } 192 }