1 /* 2 * Copyright (C) 2009 The Android Open Source Project 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 17 package com.cooliris.media; 18 19 import java.io.BufferedInputStream; 20 import java.io.BufferedOutputStream; 21 import java.io.File; 22 import java.io.FileInputStream; 23 import java.io.FileNotFoundException; 24 import java.io.FileOutputStream; 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.net.URI; 28 import java.net.URISyntaxException; 29 import java.net.URL; 30 import java.net.URLConnection; 31 32 import org.apache.http.HttpEntity; 33 import org.apache.http.HttpResponse; 34 import org.apache.http.client.methods.HttpGet; 35 import org.apache.http.client.methods.HttpUriRequest; 36 import org.apache.http.client.params.HttpClientParams; 37 import org.apache.http.conn.ClientConnectionManager; 38 import org.apache.http.conn.scheme.PlainSocketFactory; 39 import org.apache.http.conn.scheme.Scheme; 40 import org.apache.http.conn.scheme.SchemeRegistry; 41 import org.apache.http.impl.client.DefaultHttpClient; 42 import org.apache.http.impl.conn.*; 43 import org.apache.http.params.BasicHttpParams; 44 import org.apache.http.params.HttpConnectionParams; 45 import org.apache.http.params.HttpParams; 46 import org.apache.http.params.HttpProtocolParams; 47 48 import android.content.ContentResolver; 49 import android.content.Context; 50 import android.graphics.Bitmap; 51 import android.graphics.BitmapFactory; 52 import android.net.Uri; 53 import android.util.Log; 54 55 import com.cooliris.cache.CacheService; 56 57 public class UriTexture extends Texture { 58 public static final int MAX_RESOLUTION = 1024; 59 private static final String TAG = "UriTexture"; 60 protected String mUri; 61 protected long mCacheId; 62 private static final int MAX_RESOLUTION_A = MAX_RESOLUTION; 63 private static final int MAX_RESOLUTION_B = MAX_RESOLUTION; 64 public static final String URI_CACHE = CacheService.getCachePath("hires-image-cache"); 65 private static final String USER_AGENT = "Cooliris-ImageDownload"; 66 private static final int CONNECTION_TIMEOUT = 20000; // ms. 67 public static final HttpParams HTTP_PARAMS; 68 public static final SchemeRegistry SCHEME_REGISTRY; 69 static { 70 // Prepare HTTP parameters. 71 HttpParams params = new BasicHttpParams(); HttpConnectionParams.setStaleCheckingEnabled(params, false)72 HttpConnectionParams.setStaleCheckingEnabled(params, false); HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT)73 HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); HttpConnectionParams.setSoTimeout(params, CONNECTION_TIMEOUT)74 HttpConnectionParams.setSoTimeout(params, CONNECTION_TIMEOUT); HttpClientParams.setRedirecting(params, true)75 HttpClientParams.setRedirecting(params, true); HttpProtocolParams.setUserAgent(params, USER_AGENT)76 HttpProtocolParams.setUserAgent(params, USER_AGENT); 77 HTTP_PARAMS = params; 78 79 // Register HTTP protocol. 80 SCHEME_REGISTRY = new SchemeRegistry(); SCHEME_REGISTRY.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80))81 SCHEME_REGISTRY.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 82 } 83 84 private SingleClientConnManager mConnectionManager; 85 86 static { 87 File uri_cache = new File(URI_CACHE); uri_cache.mkdirs()88 uri_cache.mkdirs(); 89 } 90 UriTexture(String imageUri)91 public UriTexture(String imageUri) { 92 mUri = imageUri; 93 } 94 setCacheId(long id)95 public void setCacheId(long id) { 96 mCacheId = id; 97 } 98 computeSampleSize(InputStream stream, int maxResolutionX, int maxResolutionY)99 private static int computeSampleSize(InputStream stream, int maxResolutionX, 100 int maxResolutionY) { 101 BitmapFactory.Options options = new BitmapFactory.Options(); 102 options.inJustDecodeBounds = true; 103 BitmapFactory.decodeStream(stream, null, options); 104 int maxNumOfPixels = maxResolutionX * maxResolutionY; 105 int minSideLength = Math.min(maxResolutionX, maxResolutionY) / 2; 106 return Utils.computeSampleSize(options, minSideLength, maxNumOfPixels); 107 } 108 createFromUri(Context context, String uri, int maxResolutionX, int maxResolutionY, long cacheId, ClientConnectionManager connectionManager)109 public static final Bitmap createFromUri(Context context, String uri, int maxResolutionX, int maxResolutionY, long cacheId, 110 ClientConnectionManager connectionManager) throws IOException, URISyntaxException, OutOfMemoryError { 111 final BitmapFactory.Options options = new BitmapFactory.Options(); 112 options.inScaled = false; 113 options.inPreferredConfig = Bitmap.Config.RGB_565; 114 options.inDither = true; 115 long crc64 = 0; 116 Bitmap bitmap = null; 117 if (uri.startsWith(ContentResolver.SCHEME_CONTENT)) { 118 // We need the filepath for the given content uri 119 crc64 = cacheId; 120 } else { 121 crc64 = Utils.Crc64Long(uri); 122 } 123 bitmap = createFromCache(crc64, maxResolutionX); 124 if (bitmap != null) { 125 return bitmap; 126 } 127 final boolean local = uri.startsWith(ContentResolver.SCHEME_CONTENT) || uri.startsWith("file://"); 128 129 // Get the input stream for computing the sample size. 130 BufferedInputStream bufferedInput = null; 131 if (uri.startsWith(ContentResolver.SCHEME_CONTENT) || 132 uri.startsWith(ContentResolver.SCHEME_FILE)) { 133 // Get the stream from a local file. 134 bufferedInput = new BufferedInputStream(context.getContentResolver() 135 .openInputStream(Uri.parse(uri)), 16384); 136 } else { 137 // Get the stream from a remote URL. 138 bufferedInput = createInputStreamFromRemoteUrl(uri, connectionManager); 139 } 140 141 // Compute the sample size, i.e., not decoding real pixels. 142 if (bufferedInput != null) { 143 options.inSampleSize = computeSampleSize(bufferedInput, maxResolutionX, maxResolutionY); 144 } else { 145 return null; 146 } 147 148 // Get the input stream again for decoding it to a bitmap. 149 bufferedInput = null; 150 if (uri.startsWith(ContentResolver.SCHEME_CONTENT) || 151 uri.startsWith(ContentResolver.SCHEME_FILE)) { 152 // Get the stream from a local file. 153 bufferedInput = new BufferedInputStream(context.getContentResolver() 154 .openInputStream(Uri.parse(uri)), 16384); 155 } else { 156 // Get the stream from a remote URL. 157 bufferedInput = createInputStreamFromRemoteUrl(uri, connectionManager); 158 } 159 160 // Decode bufferedInput to a bitmap. 161 if (bufferedInput != null) { 162 options.inDither = false; 163 options.inJustDecodeBounds = false; 164 Thread timeoutThread = new Thread("BitmapTimeoutThread") { 165 public void run() { 166 try { 167 Thread.sleep(6000); 168 options.requestCancelDecode(); 169 } catch (InterruptedException e) { 170 } 171 } 172 }; 173 timeoutThread.start(); 174 175 bitmap = BitmapFactory.decodeStream(bufferedInput, null, options); 176 } 177 178 if ((options.inSampleSize > 1 || !local) && bitmap != null) { 179 writeToCache(crc64, bitmap, maxResolutionX / options.inSampleSize); 180 } 181 return bitmap; 182 } 183 createInputStreamFromRemoteUrl( String uri, ClientConnectionManager connectionManager)184 private static final BufferedInputStream createInputStreamFromRemoteUrl( 185 String uri, ClientConnectionManager connectionManager) { 186 InputStream contentInput = null; 187 if (connectionManager == null) { 188 try { 189 URL url = new URI(uri).toURL(); 190 URLConnection conn = url.openConnection(); 191 conn.connect(); 192 contentInput = conn.getInputStream(); 193 } catch (Exception e) { 194 Log.w(TAG, "Request failed: " + uri); 195 e.printStackTrace(); 196 return null; 197 } 198 } else { 199 // We create a cancelable http request from the client 200 final DefaultHttpClient mHttpClient = new DefaultHttpClient(connectionManager, HTTP_PARAMS); 201 HttpUriRequest request = new HttpGet(uri); 202 // Execute the HTTP request. 203 HttpResponse httpResponse = null; 204 try { 205 httpResponse = mHttpClient.execute(request); 206 HttpEntity entity = httpResponse.getEntity(); 207 if (entity != null) { 208 // Wrap the entity input stream in a GZIP decoder if 209 // necessary. 210 contentInput = entity.getContent(); 211 } 212 } catch (Exception e) { 213 Log.w(TAG, "Request failed: " + request.getURI()); 214 return null; 215 } 216 } 217 if (contentInput != null) { 218 return new BufferedInputStream(contentInput, 4096); 219 } else { 220 return null; 221 } 222 } 223 224 @Override load(RenderView view)225 protected Bitmap load(RenderView view) { 226 Bitmap bitmap = null; 227 if (mUri == null) 228 return bitmap; 229 try { 230 if (mUri.startsWith("http://")) { 231 if (!isCached(Utils.Crc64Long(mUri), MAX_RESOLUTION_A)) { 232 mConnectionManager = new SingleClientConnManager(HTTP_PARAMS, SCHEME_REGISTRY); 233 } 234 } 235 bitmap = createFromUri(view.getContext(), mUri, MAX_RESOLUTION_A, MAX_RESOLUTION_B, mCacheId, mConnectionManager); 236 } catch (Exception e2) { 237 Log.e(TAG, "Unable to load image from URI " + mUri); 238 e2.printStackTrace(); 239 } 240 return bitmap; 241 } 242 createFilePathFromCrc64(long crc64, int maxResolution)243 public static final String createFilePathFromCrc64(long crc64, int maxResolution) { 244 return URI_CACHE + crc64 + "_" + maxResolution + ".cache"; 245 } 246 isCached(long crc64, int maxResolution)247 public static boolean isCached(long crc64, int maxResolution) { 248 String file = null; 249 if (crc64 != 0) { 250 file = createFilePathFromCrc64(crc64, maxResolution); 251 try { 252 new FileInputStream(file); 253 return true; 254 } catch (FileNotFoundException e) { 255 return false; 256 } 257 } 258 return false; 259 } 260 createFromCache(long crc64, int maxResolution)261 public static Bitmap createFromCache(long crc64, int maxResolution) { 262 try { 263 String file = null; 264 Bitmap bitmap = null; 265 final BitmapFactory.Options options = new BitmapFactory.Options(); 266 options.inScaled = false; 267 options.inPreferredConfig = Bitmap.Config.RGB_565; 268 options.inDither = true; 269 if (crc64 != 0) { 270 file = createFilePathFromCrc64(crc64, maxResolution); 271 bitmap = BitmapFactory.decodeFile(file, options); 272 } 273 return bitmap; 274 } catch (Exception e) { 275 return null; 276 } 277 } 278 writeHttpDataInDirectory(Context context, String uri, String path)279 public static String writeHttpDataInDirectory(Context context, String uri, String path) { 280 long crc64 = Utils.Crc64Long(uri); 281 if (!isCached(crc64, 1024)) { 282 Bitmap bitmap; 283 try { 284 bitmap = UriTexture.createFromUri(context, uri, 1024, 1024, crc64, null); 285 } catch (OutOfMemoryError e) { 286 return null; 287 } catch (IOException e) { 288 return null; 289 } catch (URISyntaxException e) { 290 return null; 291 } 292 bitmap.recycle(); 293 } 294 String fileString = createFilePathFromCrc64(crc64, 1024); 295 try { 296 File file = new File(fileString); 297 if (file.exists()) { 298 // We write a copy of this file 299 String newPath = path + (path.endsWith("/") ? "" : "/") + crc64 + ".jpg"; 300 File newFile = new File(newPath); 301 Utils.Copy(file, newFile); 302 return newPath; 303 } 304 return null; 305 } catch (Exception e) { 306 return null; 307 } 308 } 309 writeToCache(long crc64, Bitmap bitmap, int maxResolution)310 public static void writeToCache(long crc64, Bitmap bitmap, int maxResolution) { 311 String file = createFilePathFromCrc64(crc64, maxResolution); 312 if (bitmap != null && file != null && crc64 != 0) { 313 try { 314 File fileC = new File(file); 315 fileC.createNewFile(); 316 final FileOutputStream fos = new FileOutputStream(fileC); 317 final BufferedOutputStream bos = new BufferedOutputStream(fos, 16384); 318 bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); 319 bos.flush(); 320 bos.close(); 321 fos.close(); 322 } catch (Exception e) { 323 324 } 325 } 326 } 327 invalidateCache(long crc64, int maxResolution)328 public static void invalidateCache(long crc64, int maxResolution) { 329 String file = createFilePathFromCrc64(crc64, maxResolution); 330 if (file != null && crc64 != 0) { 331 try { 332 File fileC = new File(file); 333 fileC.delete(); 334 } catch (Exception e) { 335 336 } 337 } 338 339 } 340 341 @Override finalize()342 public void finalize() { 343 if (mConnectionManager != null) { 344 mConnectionManager.shutdown(); 345 } 346 } 347 } 348