Home | History | Annotate | Download | only in media
      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();
     72         HttpConnectionParams.setStaleCheckingEnabled(params, false);
     73         HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
     74         HttpConnectionParams.setSoTimeout(params, CONNECTION_TIMEOUT);
     75         HttpClientParams.setRedirecting(params, true);
     76         HttpProtocolParams.setUserAgent(params, USER_AGENT);
     77         HTTP_PARAMS = params;
     78 
     79         // Register HTTP protocol.
     80         SCHEME_REGISTRY = new SchemeRegistry();
     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);
     88         uri_cache.mkdirs();
     89     }
     90 
     91     public UriTexture(String imageUri) {
     92         mUri = imageUri;
     93     }
     94 
     95     public void setCacheId(long id) {
     96         mCacheId = id;
     97     }
     98 
     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 
    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 
    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
    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 
    243     public static final String createFilePathFromCrc64(long crc64, int maxResolution) {
    244         return URI_CACHE + crc64 + "_" + maxResolution + ".cache";
    245     }
    246 
    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 
    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 
    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 
    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 
    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
    342     public void finalize() {
    343         if (mConnectionManager != null) {
    344             mConnectionManager.shutdown();
    345         }
    346     }
    347 }
    348