/*
* Copyright (C) 2013 Antarix Tandon
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sufficientlysecure.htmltextview;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.text.Html.ImageGetter;
import android.text.Spannable;
import android.text.style.ImageSpan;
import android.widget.TextView;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader.ImageContainer;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.goodev.discourse.App;
import org.goodev.discourse.R;
import org.goodev.discourse.span.ImageClickableSpan;
import org.goodev.discourse.utils.ImageLoader;
import org.goodev.discourse.utils.L;
import org.goodev.discourse.utils.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
public class UrlImageGetter implements ImageGetter {
public ArrayList<ImageContainer> mImageContainers = new ArrayList<ImageLoader.ImageContainer>();
Context mCtx;
TextView mTextView;
Drawable mPlaseHolder;
int mDefaultHeight;
int mDefaultWidth;
ImageLoader mImageLoader;
/**
* Construct the URLImageParser which will execute AsyncTask and refresh the container
*/
public UrlImageGetter(TextView t, Context c, ImageLoader loader) {
mCtx = c;
t.setTag(R.id.poste_image_getter, this);
mTextView = t;
mImageLoader = loader;
// TODO 帖子内容中的默认图片
mPlaseHolder = c.getResources().getDrawable(R.drawable.ic_logo);
mDefaultHeight = mPlaseHolder.getIntrinsicHeight();
mDefaultWidth = mPlaseHolder.getIntrinsicWidth();
mPlaseHolder.setBounds(0, 0, mDefaultWidth, mDefaultHeight);
}
@Override
public Drawable getDrawable(String source) {
source = checkUrl(source);
UrlDrawable urlDrawable = new UrlDrawable();
ImageContainer ic = mImageLoader.getForImageSpan(this, source, mTextView, new ImageCallback(mCtx.getResources(), urlDrawable));
// TODO 当帖子滚动屏幕外的时候 如何和ImageView一样 取消前面没用的下载请求???
if (ic != null && ic.getBitmap() != null) {
BitmapDrawable drawable = new BitmapDrawable(mCtx.getResources(), ic.getBitmap());
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
return drawable;
}
if (ic != null) {
mImageContainers.add(ic);
}
// get the actual source
// ImageGetterAsyncTask asyncTask = new ImageGetterAsyncTask(urlDrawable);
// asyncTask.execute(source);
// return reference to URLDrawable where I will change with actual image from
// the src tag
return urlDrawable;
}
private String checkUrl(String source) {
if (source.startsWith(Utils.SLASH2)) {
source = Utils.AVATAR_HTTP_PREFIX + source;
} else if (source.startsWith(Utils.SLASH)) {
// 用户上传的图片 地址为相对地址: /upload/ddd.
source = App.getSiteUrl() + source.substring(1);
}
return source;
}
class ImageCallback implements com.android.volley.toolbox.ImageLoader.ImageListener {
private final UrlDrawable mDrawable;
private final Resources mRes;
public ImageCallback(Resources res, UrlDrawable d) {
mDrawable = d;
mRes = res;
}
@Override
public void onErrorResponse(VolleyError error) {
// TODO 显示错误图片?
}
@Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (mTextView.getTag(R.id.poste_image_getter) == UrlImageGetter.this && response.getBitmap() != null) {
BitmapDrawable drawable = new BitmapDrawable(mRes, response.getBitmap());
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
String src = response.getRequestUrl();
// redraw the image by invalidating the container
// 由于下面重新设置了ImageSpan,所以这里invalidate就不必要了吧
// urlDrawable.invalidateSelf();
// UrlImageGetter.this.mTextView.invalidate();
// TODO 这种方式基本完美解决显示图片问题, blog?
// 把提取到下面显示的图片在放出来? 然后点击任何图片 进入到 帖子图片浏览界面。。?
TextView v = UrlImageGetter.this.mTextView;
CharSequence text = v.getText();
if (!(text instanceof Spannable)) {
return;
}
Spannable spanText = (Spannable) text;
@SuppressWarnings("unchecked") ArrayList<String> imgs = (ArrayList<String>) v.getTag(R.id.poste_image_data);
ImageSpan[] imageSpans = spanText.getSpans(0, spanText.length(), ImageSpan.class);
L.i("%b X Recycled %b src: %s", isImmediate, response.getBitmap().isRecycled(), src);
for (ImageSpan imgSpan : imageSpans) {
int start = spanText.getSpanStart(imgSpan);
int end = spanText.getSpanEnd(imgSpan);
L.i("%d-%d :%s", start, end, imgSpan.getSource());
String url = imgSpan.getSource();
url = checkUrl(url);
if (src.equals(url)) {
spanText.removeSpan(imgSpan);
ImageSpan is = new ImageSpan(drawable, src, ImageSpan.ALIGN_BASELINE);
spanText.setSpan(is, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (imgs != null && imgs.contains(src)) {
ImageClickableSpan ics = new ImageClickableSpan(src);
spanText.setSpan(ics, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
v.setText(spanText);
}
}
}
public class ImageGetterAsyncTask extends AsyncTask<String, Void, Drawable> {
private final UrlDrawable urlDrawable;
private String src;
public ImageGetterAsyncTask(UrlDrawable d) {
urlDrawable = d;
}
@Override
protected Drawable doInBackground(String... params) {
String source = params[0];
src = source;
return fetchDrawable(source);
}
@Override
protected void onPostExecute(Drawable result) {
if (result == null) {
// TODO 设置默认错误图片?
return;
}
// set the correct bound according to the result from HTTP call
int w = result.getIntrinsicWidth();
int h = result.getIntrinsicHeight();
urlDrawable.setBounds(0, 0, 0 + w, 0 + h);
L.i("%d X %d", w, h);
// change the reference of the current drawable to the result
// from the HTTP call
urlDrawable.drawable = result;
// redraw the image by invalidating the container
// 由于下面重新设置了ImageSpan,所以这里invalidate就不必要了吧
// urlDrawable.invalidateSelf();
// UrlImageGetter.this.mTextView.invalidate();
// TODO 这种方式基本完美解决显示图片问题, blog?
// 把提取到下面显示的图片在放出来? 然后点击任何图片 进入到 帖子图片浏览界面。。?
TextView v = UrlImageGetter.this.mTextView;
Spannable spanText = (Spannable) v.getText();
@SuppressWarnings("unchecked") ArrayList<String> imgs = (ArrayList<String>) v.getTag(R.id.poste_image_data);
ImageSpan[] imageSpans = spanText.getSpans(0, spanText.length(), ImageSpan.class);
for (ImageSpan imgSpan : imageSpans) {
int start = spanText.getSpanStart(imgSpan);
int end = spanText.getSpanEnd(imgSpan);
L.i("%d-%d :%s", start, end, imgSpan.getSource());
if (src.equals(imgSpan.getSource())) {
spanText.removeSpan(imgSpan);
ImageSpan is = new ImageSpan(result, src, ImageSpan.ALIGN_BASELINE);
spanText.setSpan(is, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (imgs != null && imgs.contains(src)) {
ImageClickableSpan ics = new ImageClickableSpan(src);
spanText.setSpan(ics, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
// For ICS
// UrlImageGetter.this.container.setMinimumHeight(
// (UrlImageGetter.this.container.getHeight() + result.getIntrinsicHeight()));
// UrlImageGetter.this.container.requestLayout();
// UrlImageGetter.this.container.invalidate();
}
/**
* Get the Drawable from URL
*
* @param urlString
* @return
*/
public Drawable fetchDrawable(String urlString) {
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromResourceStream(App.getContext().getResources(), null, is, "src");
L.d("%d----------X------ %d--%s", drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), urlString);
drawable.setBounds(0, 0, 0 + drawable.getIntrinsicWidth(), 0 + drawable.getIntrinsicHeight());
return drawable;
} catch (Exception e) {
return null;
}
}
private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}
@SuppressWarnings("deprecation")
public class UrlDrawable extends BitmapDrawable {
public UrlDrawable() {
setBounds(0, 0, mDefaultWidth, mDefaultHeight);
} // the drawable that you need to set, you could set the initial drawing
// with the loading image if you need to
protected Drawable drawable = mPlaseHolder;
@Override
public void draw(Canvas canvas) {
// override the draw to facilitate refresh function later
if (drawable != null) {
drawable.draw(canvas);
}
}
@Override
public int getIntrinsicWidth() {
return drawable.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return drawable.getIntrinsicHeight();
}
}
}