package org.zarroboogs.weibo.support.asyncdrawable; import org.zarroboogs.utils.ImageUtility; import org.zarroboogs.utils.file.FileDownloaderHttpHelper; import org.zarroboogs.utils.file.FileLocationMethod; import org.zarroboogs.utils.file.FileManager; import org.zarroboogs.weibo.BeeboApplication; import org.zarroboogs.weibo.R; import org.zarroboogs.weibo.asynctask.MyAsyncTask; import org.zarroboogs.weibo.bean.MessageBean; import org.zarroboogs.weibo.bean.UserBean; import org.zarroboogs.weibo.fragment.base.AbsBaseTimeLineFragment; import org.zarroboogs.weibo.hot.hean.HotMblogBean; import org.zarroboogs.weibo.setting.SettingUtils; import org.zarroboogs.weibo.support.utils.ThemeUtility; import android.annotation.TargetApi; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.View; import android.view.animation.Animation; import android.widget.ImageView; import java.lang.ref.WeakReference; import java.util.ArrayList; @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public class TimeLineBitmapDownloader { private int defaultPictureResId; private Handler handler; static volatile boolean pauseDownloadWork = false; static final Object pauseDownloadWorkLock = new Object(); static volatile boolean pauseReadWork = false; static final Object pauseReadWorkLock = new Object(); private static final Object lock = new Object(); private static TimeLineBitmapDownloader instance; private TimeLineBitmapDownloader(Handler handler) { this.handler = handler; this.defaultPictureResId = ThemeUtility.getResourceId(R.attr.listview_pic_bg); } public static TimeLineBitmapDownloader getInstance() { synchronized (lock) { if (instance == null) { instance = new TimeLineBitmapDownloader(new Handler(Looper.getMainLooper())); } } return instance; } public static void refreshThemePictureBackground() { synchronized (lock) { instance = new TimeLineBitmapDownloader(new Handler(Looper.getMainLooper())); } } /** * Pause any ongoing background work. This can be used as a temporary measure to improve * performance. For example background work could be paused when a ListView or GridView is being * scrolled using a {@link android.widget.AbsListView.OnScrollListener} to keep scrolling * smooth. * <p/> * If work is paused, be sure setPauseDownloadWork(false) is called again before your fragment * or activity is destroyed (for example during {@link android.app.Activity#onPause()}), or * there is a risk the background thread will never finish. */ public void setPauseDownloadWork(boolean pauseWork) { synchronized (pauseDownloadWorkLock) { TimeLineBitmapDownloader.pauseDownloadWork = pauseWork; if (!TimeLineBitmapDownloader.pauseDownloadWork) { pauseDownloadWorkLock.notifyAll(); } } } public void setPauseReadWork(boolean pauseWork) { synchronized (pauseReadWorkLock) { TimeLineBitmapDownloader.pauseReadWork = pauseWork; if (!TimeLineBitmapDownloader.pauseReadWork) { pauseReadWorkLock.notifyAll(); } } } protected Bitmap getBitmapFromMemCache(String key) { if (TextUtils.isEmpty(key)) { return null; } else { return BeeboApplication.getInstance().getBitmapCache().get(key); } } public void downloadAvatar(ImageView view, UserBean user) { downloadAvatar(view, user, false); } public void downloadAvatar(ImageView view, UserBean user, AbsBaseTimeLineFragment fragment) { boolean isFling = fragment.isListViewFling(); downloadAvatar(view, user, isFling); } public void downloadAvatar(ImageView view, UserBean user, boolean isFling) { if (user == null) { view.setImageResource(defaultPictureResId); return; } String url; FileLocationMethod method; if (SettingUtils.getEnableBigAvatar()) { url = user.getAvatar_large(); method = FileLocationMethod.avatar_large; } else { url = user.getAvatar_large(); method = FileLocationMethod.avatar_small; } displayImageView(view, url, method, isFling, false); } public void downContentPic(ImageView view, MessageBean msg, AbsBaseTimeLineFragment fragment) { String picUrl; boolean isFling = fragment.isListViewFling(); if (SettingUtils.getEnableBigPic()) { picUrl = msg.getOriginal_pic(); displayImageView(view, picUrl, FileLocationMethod.picture_large, isFling, false); } else { picUrl = msg.getThumbnail_pic(); displayImageView(view, picUrl, FileLocationMethod.picture_thumbnail, isFling, false); } } public void displayMultiPicture(IWeiboDrawable view, String picUrl, FileLocationMethod method, AbsBaseTimeLineFragment fragment) { boolean isFling = fragment.isListViewFling(); display(view, picUrl, method, isFling, true); } public void displayMultiPicture(IWeiboDrawable view, String picUrl, FileLocationMethod method) { display(view, picUrl, method, false, true); } public void downContentPic(IWeiboDrawable view, HotMblogBean msg, AbsBaseTimeLineFragment fragment) { String picUrl; boolean isFling = fragment.isListViewFling(); if (SettingUtils.getEnableBigPic()) { picUrl = msg.getOriginal_pic(); display(view, picUrl, FileLocationMethod.picture_large, isFling, false); } else { picUrl = msg.getThumbnail_pic(); display(view, picUrl, FileLocationMethod.picture_thumbnail, isFling, false); } } public void downContentPic(IWeiboDrawable view, MessageBean msg, AbsBaseTimeLineFragment fragment) { String picUrl; boolean isFling = fragment.isListViewFling(); if (SettingUtils.getEnableBigPic()) { picUrl = msg.getOriginal_pic(); display(view, picUrl, FileLocationMethod.picture_large, isFling, false); } else { picUrl = msg.getThumbnail_pic(); display(view, picUrl, FileLocationMethod.picture_thumbnail, isFling, false); } } /** * when user open weibo detail, the activity will setResult to previous Activity, timeline will * refresh at the time user press back button to display the latest repost count and comment * count. But sometimes, weibo detail's pictures are very large that bitmap memory cache has * cleared those timeline bitmap to save memory, app have to read bitmap from sd card again, * then app play annoying animation , this method will check whether we should read again or * not. */ private boolean shouldReloadPicture(ImageView view, String urlKey) { if (urlKey.equals(view.getTag()) && view.getDrawable() != null && view.getDrawable() instanceof BitmapDrawable && (view.getDrawable() != null && ((BitmapDrawable) view.getDrawable()).getBitmap() != null)) { // AppLogger.d("shouldReloadPicture=false"); return false; } else { view.setTag(null); // AppLogger.d("shouldReloadPicture=true"); return true; } } private void displayImageView(final ImageView view, final String urlKey, final FileLocationMethod method, boolean isFling, boolean isMultiPictures) { view.clearAnimation(); if (!shouldReloadPicture(view, urlKey)) { return; } final Bitmap bitmap = getBitmapFromMemCache(urlKey); if (bitmap != null) { view.setImageBitmap(bitmap); view.setTag(urlKey); if (view.getAlpha() != 1.0f) { view.setAlpha(1.0f); } cancelPotentialDownload(urlKey, view); } else { if (isFling) { view.setImageResource(defaultPictureResId); return; } if (!cancelPotentialDownload(urlKey, view)) { return; } final LocalOrNetworkChooseWorker newTask = new LocalOrNetworkChooseWorker(view, urlKey, method, isMultiPictures); PictureBitmapDrawable downloadedDrawable = new PictureBitmapDrawable(newTask); view.setImageDrawable(downloadedDrawable); // listview fast scroll performance handler.postDelayed(new Runnable() { @Override public void run() { if (getBitmapDownloaderTask(view) == newTask) { newTask.executeOnNormal(); } return; } }, 400); } } private void display(final IWeiboDrawable view, final String urlKey, final FileLocationMethod method, boolean isFling, boolean isMultiPictures) { view.getImageView().clearAnimation(); if (!shouldReloadPicture(view.getImageView(), urlKey)) { return; } final Bitmap bitmap = getBitmapFromMemCache(urlKey); if (bitmap != null) { view.setImageBitmap(bitmap); view.getImageView().setTag(urlKey); if (view.getProgressBar() != null) { view.getProgressBar().setVisibility(View.INVISIBLE); } if (view.getImageView().getAlpha() != 1.0f) { view.getImageView().setAlpha(1.0f); } view.setGifFlag(ImageUtility.isThisPictureGif(urlKey)); cancelPotentialDownload(urlKey, view.getImageView()); } else { if (isFling) { view.getImageView().setImageResource(defaultPictureResId); if (view.getProgressBar() != null) { view.getProgressBar().setVisibility(View.INVISIBLE); } view.setGifFlag(ImageUtility.isThisPictureGif(urlKey)); return; } if (!cancelPotentialDownload(urlKey, view.getImageView())) { return; } final LocalOrNetworkChooseWorker newTask = new LocalOrNetworkChooseWorker(view, urlKey, method, isMultiPictures); PictureBitmapDrawable downloadedDrawable = new PictureBitmapDrawable(newTask); view.setImageDrawable(downloadedDrawable); // listview fast scroll performance handler.postDelayed(new Runnable() { @Override public void run() { if (getBitmapDownloaderTask(view.getImageView()) == newTask) { newTask.executeOnNormal(); } return; } }, 400); } } public void totalStopLoadPicture() { } private static boolean cancelPotentialDownload(String url, ImageView imageView) { IPictureWorker bitmapDownloaderTask = getBitmapDownloaderTask(imageView); if (bitmapDownloaderTask != null) { String bitmapUrl = bitmapDownloaderTask.getUrl(); if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { if (bitmapDownloaderTask instanceof MyAsyncTask) { ((MyAsyncTask) bitmapDownloaderTask).cancel(true); } } else { return false; } } return true; } private static IPictureWorker getBitmapDownloaderTask(ImageView imageView) { if (imageView != null) { Drawable drawable = imageView.getDrawable(); if (drawable instanceof PictureBitmapDrawable) { PictureBitmapDrawable downloadedDrawable = (PictureBitmapDrawable) drawable; return downloadedDrawable.getBitmapDownloaderTask(); } } return null; } public void display(final ImageView imageView, final int width, final int height, final String url, final FileLocationMethod method) { ArrayList<ImageView> imageViewArrayList = new ArrayList<ImageView>(); imageViewArrayList.add(imageView); display(imageViewArrayList, width, height, url, method, null); } public void display(final ArrayList<ImageView> imageView, final int width, final int height, final String url, final FileLocationMethod method, final ArrayList<Animation> animations) { if (TextUtils.isEmpty(url)) { return; } final Bitmap bitmap = getBitmapFromMemCache(url); if (bitmap != null && bitmap.getHeight() == height && bitmap.getWidth() == width) { for (int i = 0; i < imageView.size(); i++) { ImageView imageView1 = imageView.get(i); imageView1.setImageDrawable(new BitmapDrawable(BeeboApplication.getInstance().getResources(), bitmap)); if (animations != null && animations.size() > i) { Animation animation = animations.get(i); imageView1.startAnimation(animation); } } return; } new MyAsyncTask<Void, Bitmap, Bitmap>() { @Override protected Bitmap doInBackground(Void... params) { Bitmap bitmap = null; String path = FileManager.getFilePathFromUrl(url, method); if (!(ImageUtility.isThisBitmapCanRead(path) && TaskCache.isThisUrlTaskFinished(url))) { boolean downloaded = TaskCache.waitForPictureDownload(url, null, FileManager.generateDownloadFileName(url), method); if (downloaded) { path = FileManager.getFilePathFromUrl(url, method); } } if (!TextUtils.isEmpty(path)) { bitmap = ImageUtility.readNormalPic(path, width, height); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (bitmap != null) { BeeboApplication.getInstance().getBitmapCache().put(url, bitmap); for (int i = 0; i < imageView.size(); i++) { ImageView imageView1 = imageView.get(i); imageView1.setImageDrawable(new BitmapDrawable(BeeboApplication.getInstance().getResources(), bitmap)); if (animations != null && animations.size() > i) { Animation animation = animations.get(i); imageView1.startAnimation(animation); } } } } }.executeOnExecutor(MyAsyncTask.THREAD_POOL_EXECUTOR); } public static class DownloadCallback { public void onSubmitJobButNotBegin() { } public void onSubmitJobButAlreadyBegin() { } public void onBegin() { } public void onUpdate(int value, int max) { } public void onComplete(String localPath) { } } public void download(final Activity activity, final String url, final FileLocationMethod method, final DownloadCallback callback) { downloadInner(activity, url, method, callback); } public void download(final Fragment fragment, final String url, final FileLocationMethod method, final DownloadCallback callback) { downloadInner(fragment, url, method, callback); } private void downloadInner(final Object object, final String url, final FileLocationMethod method, final DownloadCallback callback) { if (TextUtils.isEmpty(url)) { return; } if (TaskCache.isThisUrlTaskFinished(url)) { callback.onSubmitJobButNotBegin(); } else { callback.onSubmitJobButAlreadyBegin(); } new MyAsyncTask<Void, Integer, String>() { WeakReference<Object> activityRef; @Override protected void onPreExecute() { super.onPreExecute(); activityRef = new WeakReference<Object>(object); callback.onBegin(); } @Override protected String doInBackground(Void... params) { String path = FileManager.getFilePathFromUrl(url, method); if (!(ImageUtility.isThisBitmapCanRead(path) && TaskCache.isThisUrlTaskFinished(url))) { boolean downloaded = TaskCache.waitForPictureDownload(url, new FileDownloaderHttpHelper.DownloadListener() { @Override public void pushProgress(final int progress, final int max) { handler.post(new Runnable() { @Override public void run() { onProgressUpdate(progress, max); } }); } @Override public void completed() { } @Override public void cancel() { } }, FileManager.getFilePathFromUrl(url, method), method); if (downloaded) { path = FileManager.getFilePathFromUrl(url, method); } } return path; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if (!isComponentLifeCycleFinished()) { int progress = values[0]; int max = values[1]; callback.onUpdate(progress, max); } } @Override protected void onPostExecute(String value) { super.onPostExecute(value); if (!isComponentLifeCycleFinished()) { callback.onComplete(value); } } boolean isComponentLifeCycleFinished() { Object object = activityRef.get(); if (object == null) { return true; } if (object instanceof Fragment) { Fragment fragment = (Fragment) object; if (fragment.getActivity() == null) { return true; } } else if (object instanceof Activity) { Activity activity = (Activity) object; if (activity.isDestroyed()) { return true; } } return false; } }.executeOnExecutor(MyAsyncTask.THREAD_POOL_EXECUTOR); } }