diff --git a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookDetailActivity.java b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookDetailActivity.java index 3f2fc7f..674bf7f 100644 --- a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookDetailActivity.java +++ b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookDetailActivity.java @@ -30,7 +30,6 @@ import android.support.v7.app.AppCompatActivity; import android.text.Layout; import android.text.TextUtils; -import android.util.Log; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -46,8 +45,6 @@ import com.example.kaushiknsanji.bookslibrary.utils.TextAppearanceUtility; import com.example.kaushiknsanji.bookslibrary.workers.ImageDownloaderFragment; -import java.text.ParseException; - /** * Activity class that displays the {@link BookInfo} Item data selected in the List/Grid View. * The Item data is received via an Intent and the corresponding view components in the @@ -417,17 +414,12 @@ private void updatePagesInfo(int pageCount, String bookType) { * retrieved from the Item's {@link BookInfo} Object */ private void updateBookImage(String imageLinkForDetailInfo) { - if (!TextUtils.isEmpty(imageLinkForDetailInfo)) { - //Loading the Image when link is present - ImageDownloaderFragment - .newInstance(this.getSupportFragmentManager(), mBookImageView.getId()) - .executeAndUpdate(mBookImageView, - imageLinkForDetailInfo, - mBookImageView.getId()); - } else { - //Resetting to the default Book image when link is absent - mBookImageView.setImageResource(R.drawable.ic_book); - } + //Loading the Image when link is present + ImageDownloaderFragment + .newInstance(this.getSupportFragmentManager(), mBookImageView.getId()) + .executeAndUpdate(mBookImageView, + imageLinkForDetailInfo, + mBookImageView.getId()); } /** diff --git a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookImageActivity.java b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookImageActivity.java index db2af88..59e25ff 100644 --- a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookImageActivity.java +++ b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/BookImageActivity.java @@ -21,7 +21,6 @@ import android.support.annotation.Nullable; import android.support.v4.app.NavUtils; import android.support.v7.app.AppCompatActivity; -import android.text.TextUtils; import android.view.MenuItem; import android.widget.ImageView; @@ -56,19 +55,13 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { //Loading the Image to be shown from the Intent received Intent bookImageIntent = getIntent(); - String imageLinkForBookImageInfo = bookImageIntent.getStringExtra(BOOK_INFO_ITEM_IMAGE_STR_KEY); - if (!TextUtils.isEmpty(imageLinkForBookImageInfo)) { - //Loading the Image when link is present - ImageDownloaderFragment - .newInstance(getSupportFragmentManager(), bookImageView.getId()) - .executeAndUpdate(bookImageView, - imageLinkForBookImageInfo, - bookImageView.getId()); - } else { - //Resetting to the default Book image when link is absent - //(This case can only occur if there is a problem with network connectivity) - bookImageView.setImageResource(R.drawable.ic_book); - } + + //Loading the Image when link is present + ImageDownloaderFragment + .newInstance(getSupportFragmentManager(), bookImageView.getId()) + .executeAndUpdate(bookImageView, + bookImageIntent.getStringExtra(BOOK_INFO_ITEM_IMAGE_STR_KEY), + bookImageView.getId()); } diff --git a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/utils/ImageUtility.java b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/utils/ImageUtility.java index 1fc0e60..76a5000 100644 --- a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/utils/ImageUtility.java +++ b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/utils/ImageUtility.java @@ -22,8 +22,6 @@ import android.text.TextUtils; import android.util.Log; -import com.example.kaushiknsanji.bookslibrary.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 diff --git a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloader.java b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloader.java index c29973e..c23f9f4 100644 --- a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloader.java +++ b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloader.java @@ -18,8 +18,12 @@ import android.content.Context; import android.graphics.Bitmap; +import android.support.annotation.Nullable; import android.support.v4.content.AsyncTaskLoader; +import android.text.TextUtils; +import android.util.Log; +import com.example.kaushiknsanji.bookslibrary.cache.BitmapImageCache; import com.example.kaushiknsanji.bookslibrary.utils.ImageUtility; import com.example.kaushiknsanji.bookslibrary.utils.NetworkUtility; @@ -31,6 +35,9 @@ */ public class ImageDownloader extends AsyncTaskLoader { + //Constant used for logs + private static final String LOG_TAG = ImageDownloader.class.getSimpleName(); + //Stores the Image URL String from which the Image needs to be downloaded private String mImageURLStr; @@ -42,9 +49,9 @@ public class ImageDownloader extends AsyncTaskLoader { * * @param context used to retrieve the application context. * @param imageURLStr String containing the Image URL from - * which the image needs to be downloaded + * which the image needs to be downloaded. Can be {@code null}. */ - public ImageDownloader(Context context, String imageURLStr) { + public ImageDownloader(Context context, @Nullable String imageURLStr) { super(context); mImageURLStr = imageURLStr; } @@ -58,15 +65,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 @@ -171,9 +199,8 @@ public void onCanceled(Bitmap data) { */ private void releaseResources() { //Invalidating the Loader data - if (mDownloadedBitmap != null) { - mDownloadedBitmap = null; - } + mDownloadedBitmap = null; + mImageURLStr = null; } /** @@ -181,6 +208,7 @@ private void releaseResources() { * * @return The Image URL String from which this loader downloads the image */ + @Nullable public String getImageURLStr() { return mImageURLStr; } diff --git a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloaderFragment.java b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloaderFragment.java index 4641ed1..60c61dc 100644 --- a/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloaderFragment.java +++ b/app/src/main/java/com/example/kaushiknsanji/bookslibrary/workers/ImageDownloaderFragment.java @@ -17,14 +17,18 @@ package com.example.kaushiknsanji.bookslibrary.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.bookslibrary.R; @@ -83,44 +87,122 @@ 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; - //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); + 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."); + } + + //Retrieving the loader at the loaderId if any + ImageDownloader imageDownloader = getImageDownloader(loaderId, loaderManager); + + //Resetting the ImageView to the default Book Image for lazy loading + mImageView.setImageResource(R.drawable.ic_book); + + //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 Book Image for lazy loading - mImageView.setImageResource(R.drawable.ic_book); + //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; } /**