diff --git a/app/src/main/java/com/example/kaushiknsanji/novalines/utils/ImageUtility.java b/app/src/main/java/com/example/kaushiknsanji/novalines/utils/ImageUtility.java index 5b2234e..2a9128d 100644 --- a/app/src/main/java/com/example/kaushiknsanji/novalines/utils/ImageUtility.java +++ b/app/src/main/java/com/example/kaushiknsanji/novalines/utils/ImageUtility.java @@ -22,8 +22,6 @@ import android.text.TextUtils; import android.util.Log; -import com.example.kaushiknsanji.novalines.cache.BitmapImageCache; - import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -97,11 +95,6 @@ public static Bitmap downloadFromURL(String imageURLStr) { Log.e(LOG_TAG, "Error occurred while closing the Image Input Stream\n", e); } } - - //Adding the Bitmap to Memory Cache if generated - if (bitmap != null) { - BitmapImageCache.addBitmapToCache(imageURLStr, bitmap); - } } //Returning the Bitmap Image downloaded @@ -219,10 +212,10 @@ private static byte[] getImageByteArrayForProcessing(InputStream imageInputStrea //Declaring Byte Array with a buffer size to buffer the content read byte[] byteBuff = new byte[4096]; - int bytesRead = 0; //Initializing and defaulting the Bytes read to 0 + int bytesRead; //Saves the Bytes read //Declaring the Byte Array to be returned for processing - byte[] imageByteArray = null; + byte[] imageByteArray; //Writing the Bytes read to the ByteArrayOutputStream try { diff --git a/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloader.java b/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloader.java index 1f8197a..c1c9f82 100644 --- a/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloader.java +++ b/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloader.java @@ -19,7 +19,10 @@ import android.content.Context; import android.graphics.Bitmap; import android.support.v4.content.AsyncTaskLoader; +import android.text.TextUtils; +import android.util.Log; +import com.example.kaushiknsanji.novalines.cache.BitmapImageCache; import com.example.kaushiknsanji.novalines.utils.ImageUtility; import com.example.kaushiknsanji.novalines.utils.NetworkUtility; @@ -31,6 +34,9 @@ */ public class ImageDownloader extends AsyncTaskLoader { + //Constant used for logs + private static final String LOG_TAG = ImageDownloader.class.getSimpleName(); + //Integer Constant of the Loader public final static int IMAGE_LOADER = 5; @@ -61,15 +67,36 @@ public ImageDownloader(Context context, String imageURLStr) { */ @Override public Bitmap loadInBackground() { - //Proceeding to download when the Internet Connectivity is established - if (NetworkUtility.isNetworkConnected(getContext())) { - //Downloading the Image from URL and returning the Bitmap - Bitmap downloadedBitmap = ImageUtility.downloadFromURL(mImageURLStr); - if (downloadedBitmap != null) { - //Uploading the Bitmap to GPU for caching in background thread (for faster loads) - downloadedBitmap.prepareToDraw(); + if (!TextUtils.isEmpty(mImageURLStr)) { + //When we have the Image URL + try { + //Proceeding to download when the Internet Connectivity is established + if (NetworkUtility.isNetworkConnected(getContext())) { + //Looking up for the Image in Memory Cache + Bitmap cachedBitmap = BitmapImageCache.getBitmapFromCache(mImageURLStr); + if (cachedBitmap != null) { + //When Bitmap image was present in Memory Cache, return the Bitmap retrieved + return cachedBitmap; + } else { + //When Bitmap image was NOT present in Memory Cache, download the Image from URL + Bitmap downloadedBitmap = ImageUtility.downloadFromURL(mImageURLStr); + if (downloadedBitmap != null) { + //On Successful download + + //Uploading the Bitmap to GPU for caching in background thread (for faster loads) + downloadedBitmap.prepareToDraw(); + + //Adding the downloaded Bitmap to cache + BitmapImageCache.addBitmapToCache(mImageURLStr, downloadedBitmap); + + return downloadedBitmap; //Returning the Bitmap downloaded + } + } + } + } catch (Exception e) { + Log.e(LOG_TAG, "loadInBackground: Failed while downloading the bitmap for the URL " + + mImageURLStr, e); } - return downloadedBitmap; //Returning the Bitmap downloaded } //For all else, returning null @@ -174,9 +201,8 @@ public void onCanceled(Bitmap data) { */ private void releaseResources() { //Invalidating the Loader data - if (mDownloadedBitmap != null) { - mDownloadedBitmap = null; - } + mDownloadedBitmap = null; + mImageURLStr = null; } /** diff --git a/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloaderFragment.java b/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloaderFragment.java index 686bc49..828ee44 100644 --- a/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloaderFragment.java +++ b/app/src/main/java/com/example/kaushiknsanji/novalines/workers/ImageDownloaderFragment.java @@ -16,14 +16,18 @@ package com.example.kaushiknsanji.novalines.workers; +import android.content.Context; +import android.content.ContextWrapper; import android.graphics.Bitmap; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; +import android.text.TextUtils; import android.widget.ImageView; import com.example.kaushiknsanji.novalines.R; @@ -82,47 +86,125 @@ public static ImageDownloaderFragment newInstance(FragmentManager fragmentManage * if necessary * * @param imageView The ImageView Component on which the Image needs to be updated - * @param imageURLStr String containing the Image URL whose Image needs to be downloaded and updated + * @param imageURLStr String containing the Image URL whose Image needs to be downloaded and updated. Can be {@code null}. * @param loaderId Integer identifier used while creating this Fragment */ - public void executeAndUpdate(ImageView imageView, String imageURLStr, int loaderId) { + public void executeAndUpdate(ImageView imageView, @Nullable String imageURLStr, int loaderId) { + //Delegating to other overloaded method with the derived instance for LoaderManager + executeAndUpdate(imageView, imageURLStr, loaderId, obtainLoaderManager(imageView)); + } + + /** + * Method that loads the Image from Memory Cache or downloads the Image from the URL passed + * if necessary + * + * @param imageView The ImageView Component on which the Image needs to be updated + * @param imageURLStr String containing the Image URL whose Image needs to be downloaded and updated. Can be {@code null}. + * @param loaderId Integer identifier used while creating this Fragment + * @param loaderManager Instance of {@link LoaderManager} to use for downloading the image + */ + public void executeAndUpdate(ImageView imageView, @Nullable String imageURLStr, int loaderId, LoaderManager loaderManager) { //Saving the parameters passed mImageView = imageView; mImageURLStr = imageURLStr; + if (loaderManager == null) { + //When we do not have the LoaderManager instance for downloading the Image + //throw a Runtime Exception + throw new IllegalStateException("LoaderManager is not attached."); + } + //Normalizing the loaderId to start from the ImageDownloader's base ID loaderId += ImageDownloader.IMAGE_LOADER; - //Looking up for the Image in Memory Cache for the given URL - Bitmap bitmap = BitmapImageCache.getBitmapFromCache(mImageURLStr); - if (bitmap != null) { - //When Bitmap image was present in Memory Cache, update the ImageView - mImageView.setImageBitmap(bitmap); + //Retrieving the loader at the loaderId if any + ImageDownloader imageDownloader = getImageDownloader(loaderId, loaderManager); + + //Resetting the ImageView to the default News Thumbnail Image for lazy loading + mImageView.setImageResource(R.drawable.ic_news_thumbnail); + + //Boolean to check if we need to restart the loader + boolean isNewImageURLStr = false; + if (imageDownloader != null) { + //When we have a previously registered loader + + //Setting the Loader to be restarted when the Image URL passed is + //not the same as that of the loader + isNewImageURLStr = !TextUtils.equals(mImageURLStr, imageDownloader.getImageURLStr()); + } + + if (isNewImageURLStr) { + //Restarting the Loader when the ImageURL is new + loaderManager.restartLoader(loaderId, null, this); } else { - //Resetting the ImageView to the default News Thumbnail Image for lazy loading - mImageView.setImageResource(R.drawable.ic_news_thumbnail); + //Invoking the Loader AS-IS if the ImageURL is the same + //or if the Loader is not yet registered with the loaderId passed + loaderManager.initLoader(loaderId, null, this); + } - //Starting the download when Bitmap image is not available from Memory Cache: START - LoaderManager loaderManager = ((FragmentActivity) mImageView.getContext()).getSupportLoaderManager(); - boolean isNewImageURLStr = false; //Boolean to check if we need to restart the loader - Loader loader = loaderManager.getLoader(loaderId); //Getting the loader at the loaderId - if (loader instanceof ImageDownloader) { - //Validating the loader and casting to ImageDownloader - ImageDownloader imageDownloader = (ImageDownloader) loader; - //Checking for inequality of the Image URL with the one from the loader - isNewImageURLStr = !mImageURLStr.equals(imageDownloader.getImageURLStr()); - } - - if (isNewImageURLStr) { - //Restarting the Loader when the ImageURL is new - loaderManager.restartLoader(loaderId, null, this); - } else { - //Invoking the Loader AS-IS if the ImageURL is the same - //or if the Loader is not yet registered with the loaderId passed - loaderManager.initLoader(loaderId, null, this); - } - //Starting the download when Bitmap image is not available from Memory Cache: END + } + + /** + * Method that returns the instance of the {@link ImageDownloader} for the + * Loader Id {@code loaderId} passed. + * + * @param loaderId The Id of the Loader whose Loader instance needs to be looked up + * @param loaderManager Instance of {@link LoaderManager} + * @return Instance of {@link ImageDownloader} if found; else {@code null} + */ + @Nullable + private ImageDownloader getImageDownloader(int loaderId, LoaderManager loaderManager) { + //Getting the loader at the loaderId + Loader loader = loaderManager.getLoader(loaderId); + if (loader instanceof ImageDownloader) { + //Returning the ImageDownloader instance + return (ImageDownloader) loader; + } else { + //Returning NULL when not found + return null; + } + } + + /** + * Method that retrieves the {@link FragmentActivity} instance required + * for obtaining the {@link LoaderManager} instance. + * + * @param context The {@link Context} to retrieve the Activity from. + * @return Instance of the {@link FragmentActivity} is any; else {@code null} + */ + @Nullable + private FragmentActivity obtainActivity(@Nullable Context context) { + if (context == null) { + //Return Null when Null + return null; + } else if (context instanceof FragmentActivity) { + //Return the FragmentActivity instance when Context is of type FragmentActivity + return (FragmentActivity) context; + } else if (context instanceof ContextWrapper) { + //Recall with the Base Context when Context is of type ContextWrapper + return obtainActivity(((ContextWrapper) context).getBaseContext()); + } + //Returning Null when we could not derive the Activity instance from the given Context + return null; + } + + /** + * Method that retrieves the {@link LoaderManager} instance for the given view {@code imageView} + * + * @param imageView The {@link ImageView} to retrieve the {@link LoaderManager} from. + * @return Instance of {@link LoaderManager} when the {@link FragmentActivity} was derivable + * from {@code imageView}; else {@code null} + */ + @Nullable + private LoaderManager obtainLoaderManager(ImageView imageView) { + //Obtaining the Activity from the ImageView + FragmentActivity activity = obtainActivity(imageView.getContext()); + if (activity != null) { + //When we have the Activity, return the LoaderManager instance + return activity.getSupportLoaderManager(); } + //Returning Null when Activity could not be derived from ImageView + return null; } /**