/*
* ownCloud Android client application
*
* @author Tobias Kaminsky
* @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
package com.owncloud.android.datamodel;
import android.accounts.Account;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadataRetriever;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.Display;
import android.view.MenuItem;
import android.view.WindowManager;
import android.widget.ImageView;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.model.ServerFileInterface;
import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile;
import com.owncloud.android.ui.TextDrawable;
import com.owncloud.android.ui.adapter.DiskLruImageCache;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.utils.BitmapUtils;
import com.owncloud.android.utils.ConnectivityUtils;
import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimeTypeUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
import java.util.List;
/**
* Manager for concurrent access to thumbnails cache.
*/
public final class ThumbnailsCacheManager {
public static final String PREFIX_RESIZED_IMAGE = "r";
public static final String PREFIX_THUMBNAIL = "t";
private static final String TAG = ThumbnailsCacheManager.class.getSimpleName();
private static final String PNG_MIMETYPE = "image/png";
private static final String CACHE_FOLDER = "thumbnailCache";
public static final String AVATAR = "avatar";
private static final String ETAG = "ETag";
private static final Object mThumbnailsDiskCacheLock = new Object();
private static DiskLruImageCache mThumbnailCache;
private static boolean mThumbnailCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 200; // 200MB
private static final CompressFormat mCompressFormat = CompressFormat.JPEG;
private static final int mCompressQuality = 70;
private static OwnCloudClient mClient;
public static final Bitmap mDefaultImg = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(),
R.drawable.file_image);
public static final Bitmap mDefaultVideo = BitmapFactory.decodeResource(MainApp.getAppContext().getResources(),
R.drawable.file_movie);
private ThumbnailsCacheManager() {
}
public static class InitDiskCacheTask extends AsyncTask {
@Override
protected Void doInBackground(File... params) {
synchronized (mThumbnailsDiskCacheLock) {
mThumbnailCacheStarting = true;
if (mThumbnailCache == null) {
try {
File cacheDir = MainApp.getAppContext().getCacheDir();
if (cacheDir == null) {
throw new FileNotFoundException("Thumbnail cache could not be opened");
}
String cachePath = cacheDir.getPath() + File.separator + CACHE_FOLDER;
Log_OC.d(TAG, "thumbnail cache dir: " + cachePath);
File diskCacheDir = new File(cachePath);
// migrate from external cache to internal cache
File oldCacheDir = MainApp.getAppContext().getExternalCacheDir();
if (oldCacheDir != null && oldCacheDir.exists()) {
String cacheOldPath = oldCacheDir.getPath() + File.separator + CACHE_FOLDER;
File diskOldCacheDir = new File(cacheOldPath);
FileStorageUtils.copyDirs(diskOldCacheDir, diskCacheDir);
FileStorageUtils.deleteRecursive(diskOldCacheDir);
}
mThumbnailCache = new DiskLruImageCache(diskCacheDir, DISK_CACHE_SIZE, mCompressFormat,
mCompressQuality);
} catch (Exception e) {
Log_OC.d(TAG, e.getMessage());
mThumbnailCache = null;
}
}
mThumbnailCacheStarting = false; // Finished initialization
mThumbnailsDiskCacheLock.notifyAll(); // Wake any waiting threads
}
return null;
}
}
/**
* Converts size of file icon from dp to pixel
* @return int
*/
private static int getThumbnailDimension(){
// Converts dp to pixel
Resources r = MainApp.getAppContext().getResources();
return Math.round(r.getDimension(R.dimen.file_icon_size_grid));
}
/**
* Converts dimension of screen as point
*
* @return Point
*/
private static Point getScreenDimension() {
WindowManager wm = (WindowManager) MainApp.getAppContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
return point;
}
/**
* Add thumbnail to cache
* @param imageKey: thumb key
* @param bitmap: image for extracting thumbnail
* @param path: image path
* @param pxW: thumbnail width in pixel
* @param pxH: thumbnail height in pixel
* @return Bitmap
*/
private static Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int pxW, int pxH){
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH);
// Rotate image, obeying exif tag
thumbnail = BitmapUtils.rotateImage(thumbnail,path);
// Add thumbnail to cache
addBitmapToCache(imageKey, thumbnail);
return thumbnail;
}
public static void addBitmapToCache(String key, Bitmap bitmap) {
synchronized (mThumbnailsDiskCacheLock) {
if (mThumbnailCache != null) {
mThumbnailCache.put(key, bitmap);
}
}
}
public static boolean containsBitmap(String key) {
return mThumbnailCache.containsKey(key);
}
public static Bitmap getBitmapFromDiskCache(String key) {
synchronized (mThumbnailsDiskCacheLock) {
// Wait while disk cache is started from background thread
while (mThumbnailCacheStarting) {
try {
mThumbnailsDiskCacheLock.wait();
} catch (InterruptedException e) {
Log_OC.e(TAG, "Wait in mThumbnailsDiskCacheLock was interrupted", e);
}
}
if (mThumbnailCache != null) {
return mThumbnailCache.getBitmap(key);
}
}
return null;
}
public static class ResizedImageGenerationTask extends AsyncTask