/* * Copyright (C) 2012 The Android Open Source Project * * 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 com.nf2m.util; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.nf2m.BuildConfig; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import logger.Log; /** * A simple subclass of {@link ImageResizer} that fetches and resizes images fetched from UriObserver URL. */ public class ImageFetcher extends ImageResizer { private static final String TAG = "ImageFetcher"; private static final int ALBUM_CACHE_SIZE = 10 * 1024 * 1024; // 10MB private static final String ALBUM_CACHE_DIR = "album"; private static final int IO_BUFFER_SIZE = 8 * 1024; @Nullable private DiskLruCache mAlbumDiskCache; private File mAlbumCacheDir; private boolean mAlbumDiskCacheStarting = true; private final Object mAlbumDiskCacheLock = new Object(); private static final int DISK_CACHE_INDEX = 0; /** * Initialize providing UriObserver target image width and height for the processing images. * * @param context * @param imageWidth * @param imageHeight */ public ImageFetcher(@NonNull Context context, int imageWidth, int imageHeight) { super(context, imageWidth, imageHeight); init(context); } /** * Initialize providing UriObserver single target image size (used for both width and height); * * @param context * @param imageSize */ public ImageFetcher(@NonNull Context context, int imageSize) { super(context, imageSize); init(context); } private void init(@NonNull Context context) { mAlbumCacheDir = ImageCache.getDiskCacheDir(context, ALBUM_CACHE_DIR); } @Override protected void initDiskCacheInternal() { super.initDiskCacheInternal(); initAlbumDiskCache(); } private void initAlbumDiskCache() { if (!mAlbumCacheDir.exists()) { mAlbumCacheDir.mkdirs(); } synchronized (mAlbumDiskCacheLock) { if (ImageCache.getUsableSpace(mAlbumCacheDir) > ALBUM_CACHE_SIZE) { try { mAlbumDiskCache = DiskLruCache.open(mAlbumCacheDir, 1, 1, ALBUM_CACHE_SIZE); if (BuildConfig.DEBUG) { Log.d(TAG, "HTTP cache initialized"); } } catch (IOException e) { mAlbumDiskCache = null; } } mAlbumDiskCacheStarting = false; mAlbumDiskCacheLock.notifyAll(); } } @Override protected void clearCacheInternal() { super.clearCacheInternal(); synchronized (mAlbumDiskCacheLock) { if (mAlbumDiskCache != null && !mAlbumDiskCache.isClosed()) { try { mAlbumDiskCache.delete(); if (BuildConfig.DEBUG) { Log.d(TAG, "HTTP cache cleared"); } } catch (IOException e) { Log.e(TAG, "clearCacheInternal - " + e); } mAlbumDiskCache = null; mAlbumDiskCacheStarting = true; initAlbumDiskCache(); } } } @Override protected void flushCacheInternal() { super.flushCacheInternal(); synchronized (mAlbumDiskCacheLock) { if (mAlbumDiskCache != null) { try { mAlbumDiskCache.flush(); if (BuildConfig.DEBUG) { Log.d(TAG, "HTTP cache flushed"); } } catch (IOException e) { Log.e(TAG, "flush - " + e); } } } } @Override protected void closeCacheInternal() { super.closeCacheInternal(); synchronized (mAlbumDiskCacheLock) { if (mAlbumDiskCache != null) { try { if (!mAlbumDiskCache.isClosed()) { mAlbumDiskCache.close(); mAlbumDiskCache = null; if (BuildConfig.DEBUG) { Log.d(TAG, "HTTP cache closed"); } } } catch (IOException e) { Log.e(TAG, "closeCacheInternal - " + e); } } } } /** * The main process method, which will be called by the ImageWorker in the AsyncTask background * thread. * * @param data The data to load the bitmap, in this case, UriObserver regular http URL * @return The downloaded and resized bitmap */ @Nullable private Bitmap processBitmap(@NonNull String data) { if (BuildConfig.DEBUG) { Log.d(TAG, "processBitmap - " + data); } final String key = ImageCache.hashKeyForDisk(data); FileDescriptor fileDescriptor = null; FileInputStream fileInputStream = null; DiskLruCache.Snapshot snapshot; synchronized (mAlbumDiskCacheLock) { // Wait for disk cache to initialize while (mAlbumDiskCacheStarting) { try { mAlbumDiskCacheLock.wait(); } catch (InterruptedException ignored) { } } if (mAlbumDiskCache != null) { try { snapshot = mAlbumDiskCache.get(key); if (snapshot == null) { DiskLruCache.Editor editor = mAlbumDiskCache.edit(key); if (editor != null) { if (saveAlbumToCache(data, editor.newOutputStream(DISK_CACHE_INDEX))) { editor.commit(); } else { editor.abort(); } } snapshot = mAlbumDiskCache.get(key); } if (snapshot != null) { fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX); fileDescriptor = fileInputStream.getFD(); } } catch (@NonNull IOException | IllegalStateException e) { Log.e(TAG, "processBitmap - " + e); } finally { if (fileDescriptor == null && fileInputStream != null) { try { fileInputStream.close(); } catch (IOException ignored) { } } } } } Bitmap bitmap = null; if (fileDescriptor != null) { bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, mImageWidth, mImageHeight, getImageCache()); } if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException ignored) { } } return bitmap; } @Nullable @Override protected Bitmap processBitmap(Object data) { return processBitmap(String.valueOf(data)); } /** * Download UriObserver bitmap from UriObserver URL and write the content to an output stream. * * @param urlString The URL to fetch * @return true if successful, false otherwise */ private boolean saveAlbumToCache(String urlString, @NonNull OutputStream outputStream) { BufferedOutputStream out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE); Bitmap bitmap = BitmapFactory.decodeFile(urlString); if (bitmap != null) { bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); try { out.close(); } catch (IOException e) { e.printStackTrace(); } return true; } try { out.close(); } catch (IOException e) { e.printStackTrace(); } return false; } }