/* * Overchan Android (Meta Imageboard Client) * Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package nya.miku.wishmaster.ui.presentation; import java.lang.ref.WeakReference; import java.util.concurrent.Executor; import nya.miku.wishmaster.api.ChanModule; import nya.miku.wishmaster.api.interfaces.CancellableTask; import nya.miku.wishmaster.api.util.CryptoUtils; import nya.miku.wishmaster.cache.BitmapCache; import nya.miku.wishmaster.common.MainApplication; import nya.miku.wishmaster.ui.settings.ApplicationSettings.StaticSettingsContainer; import nya.miku.wishmaster.ui.settings.Wifi; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.View; /** * Обработчик-згрузчик картинок в тэгах <img>. * Может использоваться для загрузки картинок внутри постов (например, смайлики на 1 апреля). * @author miku-nyan * */ public class AsyncImageGetter implements HtmlParser.ImageGetter { private static final Bitmap EMPTY_BMP = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); private final Resources res; private final BitmapCache bmpCache; private final ChanModule chan; private WeakReference<Executor> executor; private CancellableTask task; private WeakReference<View> view; private Handler handler; private StaticSettingsContainer staticSettings; private final int maxSize; /** * Конструктор * @param res объект ресурсов * @param maxSizeRes ID ресурса (dimen) с максимальным размером картинки (по большей из сторон) * @param bmpCache объект кэша картинок (Bitmap Cache) * @param chan объект ChanModule для текущего чана * @param executor асинхронный исполнитель * @param task объект отменяемой задачи * @param view видждет (контейнер), обновляемый после успешной загрузки. (сохраняется слабая ссылка во избежание утечки памяти) * @param handler Handler основного UI потока */ public AsyncImageGetter(Resources res, int maxSizeRes, BitmapCache bmpCache, ChanModule chan, Executor executor, CancellableTask task, View view, Handler handler, StaticSettingsContainer staticSettings) { this.res = res; this.bmpCache = bmpCache; this.chan = chan; this.executor = new WeakReference<>(executor); this.task = task; this.view = new WeakReference<>(view); this.handler = handler; this.staticSettings = staticSettings; this.maxSize = res.getDimensionPixelSize(maxSizeRes); } /** * Установить новые значения ссылок на объекты * @param executor асинхронный исполнитель * @param task объект отменяемой задачи * @param view видждет (контейнер), обновляемый после успешной загрузки. (сохраняется слабая ссылка во избежание утечки памяти) * @param handler Handler основного UI потока */ public void setObjects(Executor executor, CancellableTask task, View view, Handler handler, StaticSettingsContainer staticSettings) { this.executor = new WeakReference<>(executor); this.task = task; this.view = new WeakReference<>(view); this.handler = handler; this.staticSettings = staticSettings; } @Override public Drawable getDrawable(String source) { String hash = CryptoUtils.computeMD5(chan.getChanName().concat(source)); Bitmap fromCache = bmpCache.getFromCache(hash); if (fromCache != null) { Drawable drawable = new BitmapDrawable(res, fromCache); drawable.setBounds(0, 0, Math.min(drawable.getIntrinsicWidth(), maxSize), Math.min(drawable.getIntrinsicHeight(), maxSize)); return drawable; } MutableBmpDrawable drawable = new MutableBmpDrawable(MainApplication.getInstance().resources, EMPTY_BMP); drawable.setBounds(0, 0, maxSize, maxSize); final boolean canDownload; switch (staticSettings.downloadThumbnails) { case ALWAYS: canDownload = true; break; case WIFI_ONLY: canDownload = Wifi.isConnected(); break; default: canDownload = false; break; } if (canDownload) { Executor executor = this.executor.get(); if (executor != null) executor.execute(new Downloader(hash, source, drawable, task)); } return drawable; } private class Downloader implements Runnable { private final String hash; private final String url; private final MutableBmpDrawable drawable; private final CancellableTask task; public Downloader(String hash, String url, MutableBmpDrawable drawable, CancellableTask task) { this.hash = hash; this.url = url; this.drawable = drawable; this.task = task; } @Override public void run() { Bitmap fromInternet = bmpCache.download(hash, url, maxSize, chan, task); if (fromInternet != null) { Drawable newDrawable = new BitmapDrawable(res, fromInternet); int left = Math.max(0, maxSize - newDrawable.getIntrinsicWidth()); int top = Math.max(0, maxSize - newDrawable.getIntrinsicHeight()); newDrawable.setBounds(left, top, maxSize, maxSize); drawable.setDrawable(newDrawable); if (task != null && task.isCancelled()) return; if (handler == null || view.get() == null) return; handler.post(new Runnable() { @Override public void run() { View v = view.get(); if (v == null) return; v.invalidate(); } }); } } } private class MutableBmpDrawable extends BitmapDrawable { private Drawable drawable; public void setDrawable(Drawable drawable){ this.drawable = drawable; } public MutableBmpDrawable(Resources res, Bitmap bitmap) { super(res, bitmap); } @Override public void draw(Canvas canvas){ if (drawable != null) { drawable.draw(canvas); } else { super.draw(canvas); } } } }