PageRenderTime 45ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/discourse2/src/main/java/org/sufficientlysecure/htmltextview/UrlImageGetter.java

https://gitlab.com/adamlwalker/android-discourse
Java | 286 lines | 193 code | 37 blank | 56 comment | 28 complexity | e958dacd2b7abe26c141e30d9100bb0d MD5 | raw file
  1. /*
  2. * Copyright (C) 2013 Antarix Tandon
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.sufficientlysecure.htmltextview;
  17. import android.content.Context;
  18. import android.content.res.Resources;
  19. import android.graphics.Canvas;
  20. import android.graphics.drawable.BitmapDrawable;
  21. import android.graphics.drawable.Drawable;
  22. import android.os.AsyncTask;
  23. import android.text.Html.ImageGetter;
  24. import android.text.Spannable;
  25. import android.text.style.ImageSpan;
  26. import android.widget.TextView;
  27. import com.android.volley.VolleyError;
  28. import com.android.volley.toolbox.ImageLoader.ImageContainer;
  29. import org.apache.http.HttpResponse;
  30. import org.apache.http.client.methods.HttpGet;
  31. import org.apache.http.impl.client.DefaultHttpClient;
  32. import org.goodev.discourse.App;
  33. import org.goodev.discourse.R;
  34. import org.goodev.discourse.span.ImageClickableSpan;
  35. import org.goodev.discourse.utils.ImageLoader;
  36. import org.goodev.discourse.utils.L;
  37. import org.goodev.discourse.utils.Utils;
  38. import java.io.IOException;
  39. import java.io.InputStream;
  40. import java.net.MalformedURLException;
  41. import java.util.ArrayList;
  42. public class UrlImageGetter implements ImageGetter {
  43. public ArrayList<ImageContainer> mImageContainers = new ArrayList<ImageLoader.ImageContainer>();
  44. Context mCtx;
  45. TextView mTextView;
  46. Drawable mPlaseHolder;
  47. int mDefaultHeight;
  48. int mDefaultWidth;
  49. ImageLoader mImageLoader;
  50. /**
  51. * Construct the URLImageParser which will execute AsyncTask and refresh the container
  52. */
  53. public UrlImageGetter(TextView t, Context c, ImageLoader loader) {
  54. mCtx = c;
  55. t.setTag(R.id.poste_image_getter, this);
  56. mTextView = t;
  57. mImageLoader = loader;
  58. // TODO 帖子内容中的默认图片
  59. mPlaseHolder = c.getResources().getDrawable(R.drawable.ic_logo);
  60. mDefaultHeight = mPlaseHolder.getIntrinsicHeight();
  61. mDefaultWidth = mPlaseHolder.getIntrinsicWidth();
  62. mPlaseHolder.setBounds(0, 0, mDefaultWidth, mDefaultHeight);
  63. }
  64. @Override
  65. public Drawable getDrawable(String source) {
  66. source = checkUrl(source);
  67. UrlDrawable urlDrawable = new UrlDrawable();
  68. ImageContainer ic = mImageLoader.getForImageSpan(this, source, mTextView, new ImageCallback(mCtx.getResources(), urlDrawable));
  69. // TODO 当帖子滚动屏幕外的时候 如何和ImageView一样 取消前面没用的下载请求???
  70. if (ic != null && ic.getBitmap() != null) {
  71. BitmapDrawable drawable = new BitmapDrawable(mCtx.getResources(), ic.getBitmap());
  72. drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
  73. return drawable;
  74. }
  75. if (ic != null) {
  76. mImageContainers.add(ic);
  77. }
  78. // get the actual source
  79. // ImageGetterAsyncTask asyncTask = new ImageGetterAsyncTask(urlDrawable);
  80. // asyncTask.execute(source);
  81. // return reference to URLDrawable where I will change with actual image from
  82. // the src tag
  83. return urlDrawable;
  84. }
  85. private String checkUrl(String source) {
  86. if (source.startsWith(Utils.SLASH2)) {
  87. source = Utils.AVATAR_HTTP_PREFIX + source;
  88. } else if (source.startsWith(Utils.SLASH)) {
  89. // 用户上传的图片 地址为相对地址: /upload/ddd.
  90. source = App.getSiteUrl() + source.substring(1);
  91. }
  92. return source;
  93. }
  94. class ImageCallback implements com.android.volley.toolbox.ImageLoader.ImageListener {
  95. private final UrlDrawable mDrawable;
  96. private final Resources mRes;
  97. public ImageCallback(Resources res, UrlDrawable d) {
  98. mDrawable = d;
  99. mRes = res;
  100. }
  101. @Override
  102. public void onErrorResponse(VolleyError error) {
  103. // TODO 显示错误图片?
  104. }
  105. @Override
  106. public void onResponse(ImageContainer response, boolean isImmediate) {
  107. if (mTextView.getTag(R.id.poste_image_getter) == UrlImageGetter.this && response.getBitmap() != null) {
  108. BitmapDrawable drawable = new BitmapDrawable(mRes, response.getBitmap());
  109. drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
  110. String src = response.getRequestUrl();
  111. // redraw the image by invalidating the container
  112. // 由于下面重新设置了ImageSpan,所以这里invalidate就不必要了吧
  113. // urlDrawable.invalidateSelf();
  114. // UrlImageGetter.this.mTextView.invalidate();
  115. // TODO 这种方式基本完美解决显示图片问题, blog?
  116. // 把提取到下面显示的图片在放出来? 然后点击任何图片 进入到 帖子图片浏览界面。。?
  117. TextView v = UrlImageGetter.this.mTextView;
  118. CharSequence text = v.getText();
  119. if (!(text instanceof Spannable)) {
  120. return;
  121. }
  122. Spannable spanText = (Spannable) text;
  123. @SuppressWarnings("unchecked") ArrayList<String> imgs = (ArrayList<String>) v.getTag(R.id.poste_image_data);
  124. ImageSpan[] imageSpans = spanText.getSpans(0, spanText.length(), ImageSpan.class);
  125. L.i("%b X Recycled %b src: %s", isImmediate, response.getBitmap().isRecycled(), src);
  126. for (ImageSpan imgSpan : imageSpans) {
  127. int start = spanText.getSpanStart(imgSpan);
  128. int end = spanText.getSpanEnd(imgSpan);
  129. L.i("%d-%d :%s", start, end, imgSpan.getSource());
  130. String url = imgSpan.getSource();
  131. url = checkUrl(url);
  132. if (src.equals(url)) {
  133. spanText.removeSpan(imgSpan);
  134. ImageSpan is = new ImageSpan(drawable, src, ImageSpan.ALIGN_BASELINE);
  135. spanText.setSpan(is, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  136. }
  137. if (imgs != null && imgs.contains(src)) {
  138. ImageClickableSpan ics = new ImageClickableSpan(src);
  139. spanText.setSpan(ics, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  140. }
  141. }
  142. v.setText(spanText);
  143. }
  144. }
  145. }
  146. public class ImageGetterAsyncTask extends AsyncTask<String, Void, Drawable> {
  147. private final UrlDrawable urlDrawable;
  148. private String src;
  149. public ImageGetterAsyncTask(UrlDrawable d) {
  150. urlDrawable = d;
  151. }
  152. @Override
  153. protected Drawable doInBackground(String... params) {
  154. String source = params[0];
  155. src = source;
  156. return fetchDrawable(source);
  157. }
  158. @Override
  159. protected void onPostExecute(Drawable result) {
  160. if (result == null) {
  161. // TODO 设置默认错误图片?
  162. return;
  163. }
  164. // set the correct bound according to the result from HTTP call
  165. int w = result.getIntrinsicWidth();
  166. int h = result.getIntrinsicHeight();
  167. urlDrawable.setBounds(0, 0, 0 + w, 0 + h);
  168. L.i("%d X %d", w, h);
  169. // change the reference of the current drawable to the result
  170. // from the HTTP call
  171. urlDrawable.drawable = result;
  172. // redraw the image by invalidating the container
  173. // 由于下面重新设置了ImageSpan,所以这里invalidate就不必要了吧
  174. // urlDrawable.invalidateSelf();
  175. // UrlImageGetter.this.mTextView.invalidate();
  176. // TODO 这种方式基本完美解决显示图片问题, blog?
  177. // 把提取到下面显示的图片在放出来? 然后点击任何图片 进入到 帖子图片浏览界面。。?
  178. TextView v = UrlImageGetter.this.mTextView;
  179. Spannable spanText = (Spannable) v.getText();
  180. @SuppressWarnings("unchecked") ArrayList<String> imgs = (ArrayList<String>) v.getTag(R.id.poste_image_data);
  181. ImageSpan[] imageSpans = spanText.getSpans(0, spanText.length(), ImageSpan.class);
  182. for (ImageSpan imgSpan : imageSpans) {
  183. int start = spanText.getSpanStart(imgSpan);
  184. int end = spanText.getSpanEnd(imgSpan);
  185. L.i("%d-%d :%s", start, end, imgSpan.getSource());
  186. if (src.equals(imgSpan.getSource())) {
  187. spanText.removeSpan(imgSpan);
  188. ImageSpan is = new ImageSpan(result, src, ImageSpan.ALIGN_BASELINE);
  189. spanText.setSpan(is, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  190. }
  191. if (imgs != null && imgs.contains(src)) {
  192. ImageClickableSpan ics = new ImageClickableSpan(src);
  193. spanText.setSpan(ics, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  194. }
  195. }
  196. // For ICS
  197. // UrlImageGetter.this.container.setMinimumHeight(
  198. // (UrlImageGetter.this.container.getHeight() + result.getIntrinsicHeight()));
  199. // UrlImageGetter.this.container.requestLayout();
  200. // UrlImageGetter.this.container.invalidate();
  201. }
  202. /**
  203. * Get the Drawable from URL
  204. *
  205. * @param urlString
  206. * @return
  207. */
  208. public Drawable fetchDrawable(String urlString) {
  209. try {
  210. InputStream is = fetch(urlString);
  211. Drawable drawable = Drawable.createFromResourceStream(App.getContext().getResources(), null, is, "src");
  212. L.d("%d----------X------ %d--%s", drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), urlString);
  213. drawable.setBounds(0, 0, 0 + drawable.getIntrinsicWidth(), 0 + drawable.getIntrinsicHeight());
  214. return drawable;
  215. } catch (Exception e) {
  216. return null;
  217. }
  218. }
  219. private InputStream fetch(String urlString) throws MalformedURLException, IOException {
  220. DefaultHttpClient httpClient = new DefaultHttpClient();
  221. HttpGet request = new HttpGet(urlString);
  222. HttpResponse response = httpClient.execute(request);
  223. return response.getEntity().getContent();
  224. }
  225. }
  226. @SuppressWarnings("deprecation")
  227. public class UrlDrawable extends BitmapDrawable {
  228. public UrlDrawable() {
  229. setBounds(0, 0, mDefaultWidth, mDefaultHeight);
  230. } // the drawable that you need to set, you could set the initial drawing
  231. // with the loading image if you need to
  232. protected Drawable drawable = mPlaseHolder;
  233. @Override
  234. public void draw(Canvas canvas) {
  235. // override the draw to facilitate refresh function later
  236. if (drawable != null) {
  237. drawable.draw(canvas);
  238. }
  239. }
  240. @Override
  241. public int getIntrinsicWidth() {
  242. return drawable.getIntrinsicWidth();
  243. }
  244. @Override
  245. public int getIntrinsicHeight() {
  246. return drawable.getIntrinsicHeight();
  247. }
  248. }
  249. }