/*
* Copyright 2015 OpenMarket Ltd
*
* 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 org.matrix.androidsdk.db;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import org.matrix.androidsdk.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.ImageView;
import org.matrix.androidsdk.HomeserverConnectionConfig;
import org.matrix.androidsdk.listeners.IMXMediaDownloadListener;
import org.matrix.androidsdk.listeners.IMXMediaUploadListener;
import org.matrix.androidsdk.listeners.MXMediaDownloadListener;
import org.matrix.androidsdk.rest.model.EncryptedFileInfo;
import org.matrix.androidsdk.util.ContentManager;
import org.matrix.androidsdk.util.ContentUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.concurrent.RejectedExecutionException;
public class MXMediasCache {
private static final String LOG_TAG = "MXMediasCache";
/**
* The medias folders.
*/
private static final String MXMEDIA_STORE_FOLDER = "MXMediaStore";
private static final String MXMEDIA_STORE_MEMBER_THUMBNAILS_FOLDER = "MXMemberThumbnailsStore";
private static final String MXMEDIA_STORE_IMAGES_FOLDER = "Images";
private static final String MXMEDIA_STORE_OTHERS_FOLDER = "Others";
/**
* The content manager
*/
private ContentManager mContentManager = null;
/**
* The medias folders list.
*/
private File mMediasFolderFile = null;
private File mImagesFolderFile = null;
private File mOthersFolderFile = null;
private File mThumbnailsFolderFile = null;
/**
* Clear the former medias cache.
* The dirtree has been updated.
*
* @param directory The upper directory file.
*/
private void cleanFormerMediasCache(File directory) {
File[] files = directory.listFiles();
if (null != files) {
for(int i=0; i<files.length; i++) {
if(!files[i].isDirectory()) {
String fileName = files[i].getName();
// remove standard medias
if (fileName.endsWith(".jpeg") || fileName.endsWith(".jpg") || fileName.endsWith(".tmp") || fileName.endsWith(".gif")) {
files[i].delete();
}
}
}
}
}
/**
* Constructor
* @param contentManager the content manager.
* @param userID the account user Id.
* @param context the context
*/
public MXMediasCache(ContentManager contentManager, String userID, Context context) {
mContentManager = contentManager;
File mediaBaseFolderFile = new File(context.getApplicationContext().getFilesDir(), MXMEDIA_STORE_FOLDER);
if (!mediaBaseFolderFile.exists()) {
cleanFormerMediasCache(context.getApplicationContext().getFilesDir());
mediaBaseFolderFile.mkdirs();
}
// create the dir tree
mMediasFolderFile = new File(mediaBaseFolderFile, userID);
mImagesFolderFile = new File(mMediasFolderFile, MXMEDIA_STORE_IMAGES_FOLDER);
mOthersFolderFile = new File(mMediasFolderFile, MXMEDIA_STORE_OTHERS_FOLDER);
mThumbnailsFolderFile = new File(mediaBaseFolderFile, MXMEDIA_STORE_MEMBER_THUMBNAILS_FOLDER);
}
/**
* Returns the mediasFolder files.
* Creates it if it does not exist
*
* @return the medias folder file.
*/
private File getMediasFolderFile() {
if (!mMediasFolderFile.exists()) {
mMediasFolderFile.mkdirs();
}
return mMediasFolderFile;
}
/**
* Returns the folder file for a dedicated mimetype.
* Creates it if it does not exist
*
* @param mimeType the media mimetype.
* @return the folder file.
*/
private File getFolderFile(String mimeType) {
File file;
//
if ((null == mimeType) || mimeType.startsWith("image/")) {
file = mImagesFolderFile;
} else {
file = mOthersFolderFile;
}
if (!file.exists()) {
file.mkdirs();
}
return file;
}
/**
* Returns the thumbnails folder.
* Creates it if it does not exist
*
* @return the thumbnails folder file.
*/
private File getThumbnailsFolderFile() {
if (!mThumbnailsFolderFile.exists()) {
mThumbnailsFolderFile.mkdirs();
}
return mThumbnailsFolderFile;
}
/**
* Recursive method to compute a directory sie
* @param directory the directory.
* @return the directory size in bytes.
*/
private long cacheSize(File directory) {
long size = 0;
File[] files = directory.listFiles();
if (null != files) {
for(int i=0; i<files.length; i++) {
File file = files[i];
if(!file.isDirectory()) {
size += file.length();
} else {
size += cacheSize(file);
}
}
}
return size;
}
/**
* Compute the medias cache size
*
* @return the medias cache size in bytes
*/
public long cacheSize() {
return cacheSize(getMediasFolderFile());
}
/**
* Clear the medias caches.
*/
public void clear() {
ContentUtils.deleteDirectory(getMediasFolderFile());
// clear the media cache
MXMediaDownloadWorkerTask.clearBitmapsCache();
// cancel pending uploads.
MXMediaUploadWorkerTask.cancelPendingUploads();
}
/**
* The thumbnails cached is not cleared when logging out a session
* because many sessions share the same thumbnails.
* This method must be called when performing an application logout
* i.e. logging out of all sessions.
*/
public static void clearThumbnailsCache(Context applicationContext) {
ContentUtils.deleteDirectory(new File(new File(applicationContext.getApplicationContext().getFilesDir(), MXMediasCache.MXMEDIA_STORE_FOLDER), MXMEDIA_STORE_MEMBER_THUMBNAILS_FOLDER));
}
/**
* Convert matrix url into http one.
*
* @param url the matrix url
* @param width the expected image width
* @param height the expected image height
* @return the URL to access the described resource.
*/
private String downloadableUrl(String url, int width, int height) {
// check if the Url is a matrix one
if ((null != url) && url.startsWith(ContentManager.MATRIX_CONTENT_URI_SCHEME)) {
if ((width > 0) && (height > 0)) {
return mContentManager.getDownloadableThumbnailUrl(url, width, height, ContentManager.METHOD_SCALE);
} else {
return mContentManager.getDownloadableUrl(url);
}
} else {
return url;
}
}
/**
* Provide the thumbnail file.
* @param url the thumbnail url/
* @param size the thumbnail size.
* @return the File if it exits.
*/
public File thumbnailCacheFile(String url, int size) {
// sanity check
if (null == url) {
return null;
}
String filename = MXMediaDownloadWorkerTask.buildFileName(downloadableUrl(url, size, size), "image/jpeg");
try {
File file = new File(getThumbnailsFolderFile(), filename);
if (file.exists()) {
return file;
}
} catch (Exception e) {
Log.e(LOG_TAG, "thumbnailCacheFile failed " + e.getLocalizedMessage());
}
return null;
}
/**
* Return the cache file name for a media defined by its URL and its mimetype.
* @param url the media url
* @param mimeType the mime type
* @return the media file it is found
*/
public File mediaCacheFile(String url, String mimeType) {
return mediaCacheFile(url, -1, -1, mimeType);
}
/**
* Return the cache file name for a media defined by its URL and its mimetype.
* @param url the media URL
* @param width the media width
* @param height the media height
* @param mimeType the media mime type
* @return the media file it is found
*/
public File mediaCacheFile(String url, int width, int height, String mimeType) {
// sanity check
if (null == url) {
return null;
}
String filename = (url.startsWith("file:")) ? url : MXMediaDownloadWorkerTask.buildFileName(downloadableUrl(url, width, height), mimeType);
try {
// already a local file
if (filename.startsWith("file:")) {
Uri uri = Uri.parse(filename);
filename = uri.getLastPathSegment();
}
File file = new File(getFolderFile(mimeType), filename);
if (file.exists()) {
return file;
}
} catch (Exception e) {
Log.e(LOG_TAG, "mediaCacheFile failed " + e.getLocalizedMessage());
}
return null;
}
/**
* Save a bitmap to the local cache
* it could be used for unsent media to allow them to be resent.
*
* @param bitmap the bitmap to save
* @param defaultFileName the filename is provided, if null, a filename will be generated
* @return the media cache URL
*/
public String saveBitmap(Bitmap bitmap, String defaultFileName) {
String filename = "file" + System.currentTimeMillis() + ".jpg";
String cacheURL = null;
try {
if (null != defaultFileName) {
File file = new File(getFolderFile(null), defaultFileName);
file.delete();
filename = Uri.fromFile(file).getLastPathSegment();
}
File file = new File(getFolderFile(null), filename);
FileOutputStream fos = new FileOutputStream(file.getPath());
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
cacheURL = Uri.fromFile(file).toString();
} catch (Exception e) {
Log.e(LOG_TAG, "saveBitmap failed " + e.getLocalizedMessage());
}
return cacheURL;
}
/**
* Save a media to the local cache
* it could be used for unsent media to allow them to be resent.
*
* @param stream the file stream to save
* @param defaultFileName the filename is provided, if null, a filename will be generated
* @param mimeType the mime type.
* @return the media cache URL
*/
public String saveMedia(InputStream stream, String defaultFileName, String mimeType) {
String filename = defaultFileName;
if (null == filename) {
filename = "file" + System.currentTimeMillis();
if (null != mimeType) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
if (null == extension) {
if (mimeType.lastIndexOf("/") >= 0) {
extension = mimeType.substring(mimeType.lastIndexOf("/") +1);
}
}
if (!TextUtils.isEmpty(extension)) {
filename += "." + extension;
}
}
}
String cacheURL = null;
try {
File file = new File(getFolderFile(mimeType), filename);
// if the file exits, delete it
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(file.getPath());
try {
byte[] buf = new byte[1024 * 32];
int len;
while ((len = stream.read(buf)) != -1) {
fos.write(buf, 0, len);
}
} catch (Exception e) {
Log.e(LOG_TAG, "saveMedia failed " + e.getLocalizedMessage());
}
fos.flush();
fos.close();
stream.close();
cacheURL = Uri.fromFile(file).toString();
} catch (Exception e) {
Log.e(LOG_TAG, "saveMedia failed " + e.getLocalizedMessage());
}
return cacheURL;
}
/**
* Replace a media cache by a file content.
*
* @param mediaUrl the mediaUrl
* @param mimeType the mimeType.
* @param fileUrl the file which replaces the cached media.
*/
public void saveFileMediaForUrl(String mediaUrl, String fileUrl, String mimeType) {
saveFileMediaForUrl(mediaUrl, fileUrl, -1, -1, mimeType);
}
/**
* Replace a media cache by a file content.
* MediaUrl is the same model as the one used in loadBitmap.
*
* @param mediaUrl the mediaUrl
* @param fileUrl the file which replaces the cached media.
* @param width the expected image width
* @param height the expected image height
* @param mimeType the mimeType.
*/
public void saveFileMediaForUrl(String mediaUrl, String fileUrl, int width, int height, String mimeType) {
saveFileMediaForUrl(mediaUrl, fileUrl, width, height, mimeType, false);
}
/**
* Copy or Replace a media cache by a file content.
* MediaUrl is the same model as the one used in loadBitmap.
*
* @param mediaUrl the mediaUrl
* @param fileUrl the file which replaces the cached media.
* @param width the expected image width
* @param height the expected image height
* @param mimeType the mimeType.
* @param keepSource keep the source file
*/
public void saveFileMediaForUrl(String mediaUrl, String fileUrl, int width, int height, String mimeType, boolean keepSource) {
String downloadableUrl = downloadableUrl(mediaUrl, width, height);
String filename = MXMediaDownloadWorkerTask.buildFileName(downloadableUrl, mimeType);
try {
// delete the current content
File destFile = new File(getFolderFile(mimeType), filename);
if (destFile.exists()) {
try {
destFile.delete();
} catch (Exception e) {
Log.e(LOG_TAG, "saveFileMediaForUrl delete failed " + e.getLocalizedMessage());
}
}
Uri uri = Uri.parse(fileUrl);
File srcFile = new File(uri.getPath());
if (keepSource) {
InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(destFile);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
} else {
srcFile.renameTo(destFile);
}
} catch (Exception e) {
Log.e(LOG_TAG, "saveFileMediaForUrl failed " + e.getLocalizedMessage());
}
}
/**
* Load an avatar thumbnail.
* The imageView image is updated when the bitmap is loaded or downloaded.
*
* @param hsConfig the home server config.
* @param imageView Ihe imageView to update with the image.
* @param url the image url
* @param side the avatar thumbnail side
* @return a download identifier if the image is not cached.
*/
public String loadAvatarThumbnail(HomeserverConnectionConfig hsConfig, ImageView imageView, String url, int side) {
return loadBitmap(imageView.getContext(), hsConfig, imageView, url, side, side, 0, ExifInterface.ORIENTATION_UNDEFINED, null, getThumbnailsFolderFile(), null);
}
/**
* Load an avatar thumbnail.
* The imageView image is updated when the bitmap is loaded or downloaded.
*
* @param hsConfig the home server config.
* @param imageView Ihe imageView to update with the image.
* @param url the image url
* @param side the avatar thumbnail side
* @param aDefaultAvatar the avatar to use when the Url is not reachable.
* @return a download identifier if the image is not cached.
*/
public String loadAvatarThumbnail(HomeserverConnectionConfig hsConfig, ImageView imageView, String url, int side, Bitmap aDefaultAvatar) {
return loadBitmap(imageView.getContext(), hsConfig, imageView, url, side, side, 0, ExifInterface.ORIENTATION_UNDEFINED, null, getThumbnailsFolderFile(), aDefaultAvatar, null);
}
/**
* Tells if the avatar is cached
* @param url the avatar url to test
* @return true if the avatar bitmap is cached.
*/
public boolean isAvatarThumbnailCached(String url, int side) {
return MXMediaDownloadWorkerTask.isUrlCached(downloadableUrl(url, side, side));
}
/**
* Tells if the media URL is unreachable.
* @param url the url to test.
* @return true if the media URL is unreachable.
*/
public static boolean isMediaUrlUnreachable(String url) {
return MXMediaDownloadWorkerTask.isMediaUrlUnreachable(url);
}
/**
* Load a bitmap from the url.
* The imageView image is updated when the bitmap is loaded or downloaded.
*
* @param hsConfig The home server config.
* @param imageView The imageView to update with the image.
* @param url the image url
* @param rotationAngle the rotation angle (degrees)
* @param orientation the orientation (ExifInterface.ORIENTATION_XXX value)
* @param mimeType the mimeType.
* @param encryptionInfo the encryption file info
* @return a download identifier if the image is not cached.
*/
public String loadBitmap(HomeserverConnectionConfig hsConfig, ImageView imageView, String url, int rotationAngle, int orientation, String mimeType, EncryptedFileInfo encryptionInfo) {
return loadBitmap(hsConfig, imageView, url, -1, -1, rotationAngle, orientation, mimeType, encryptionInfo);
}
/**
* Load a bitmap from the url.
* The imageView image is updated when the bitmap is loaded or downloaded.
*
* @param hsConfig The home server config.
* @param context The context
* @param url the image url
* @param rotationAngle the rotation angle (degrees)
* @param orientation the orientation (ExifInterface.ORIENTATION_XXX value)
* @param mimeType the mimeType.
* @param encryptionInfo the encryption file info
* @return a download identifier if the image is not cached.
*/
public String loadBitmap(Context context, HomeserverConnectionConfig hsConfig, String url, int rotationAngle, int orientation, String mimeType, EncryptedFileInfo encryptionInfo) {
return loadBitmap(context, hsConfig, null, url, -1, -1, rotationAngle, orientation, mimeType, getFolderFile(mimeType), encryptionInfo);
}
/**
* Load a bitmap from an url.
* The imageView image is updated when the bitmap is loaded or downloaded.
* The width/height parameters are optional. If they are > 0, download a thumbnail.
* rotationAngle is set to Integer.MAX_VALUE when undefined : the EXIF metadata must be checked.
*
* @param hsConfig The home server config.
* @param imageView The imageView to fill when the image is downloaded
* @param url the image url
* @param width the expected image width
* @param height the expected image height
* @param rotationAngle the rotation angle (degrees)
* @param orientation the orientation (ExifInterface.ORIENTATION_XXX value)
* @param mimeType the mimeType.
* @param encryptionInfo the encryption file info
* @return a download identifier if the image is not cached
*/
public String loadBitmap(HomeserverConnectionConfig hsConfig, ImageView imageView, String url, int width, int height, int rotationAngle, int orientation, String mimeType, EncryptedFileInfo encryptionInfo) {
return loadBitmap(imageView.getContext(), hsConfig, imageView, url, width, height, rotationAngle, orientation, mimeType, getFolderFile(mimeType), encryptionInfo);
}
// some tasks have been stacked because there are too many running ones.
private final ArrayList<MXMediaDownloadWorkerTask> mSuspendedTasks = new ArrayList<>();
/**
* Returns the download ID from the media URL.
*
* @param url the media url
* @return the download ID
*/
public String downloadIdFromUrl(String url) {
return downloadableUrl(url, -1, -1);
}
/**
* Download a media.
* @param context the application context
* @param hsConfig the home server config.
* @param url the media url
* @param mimeType the media mimetype
* @param encryptionInfo the encryption information
* @return the download identifier.
*/
public String downloadMedia(Context context, HomeserverConnectionConfig hsConfig, String url, String mimeType, EncryptedFileInfo encryptionInfo) {
// sanity checks
if ((null == mimeType) || (null == url) || (null == context)) {
return null;
}
// is the media already downloaded ?
if (null != mediaCacheFile(url, mimeType)) {
return null;
}
String downloadableUrl = downloadableUrl(url, -1, -1);
// is the media downloading ?
if (null != MXMediaDownloadWorkerTask.getMediaDownloadWorkerTask(downloadableUrl)) {
return downloadableUrl;
}
// download it in background
MXMediaDownloadWorkerTask task = new MXMediaDownloadWorkerTask(context, hsConfig, getFolderFile(mimeType), downloadableUrl, mimeType, encryptionInfo);
// avoid crash if there are too many running task
try {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Integer[])null);
} catch (RejectedExecutionException e) {
// too many tasks have been launched
synchronized (mSuspendedTasks) {
task.cancel(true);
// create a new task from the existing one
task = new MXMediaDownloadWorkerTask(task);
mSuspendedTasks.add(task);
// privacy
//Log.e(LOG_TAG, "Suspend the task " + task.getUrl());
Log.e(LOG_TAG, "Suspend the task " );
}
} catch (Exception e) {
Log.e(LOG_TAG, "downloadMedia failed " + e.getLocalizedMessage());
}
return downloadableUrl;
}
/**
* Start any suspended task
*/
private void launchSuspendedTask() {
synchronized (mSuspendedTasks)
{
// some task have been suspended because there were too many running ones ?
if (mSuspendedTasks.size() > 0) {
if (mSuspendedTasks.size() > 0) {
MXMediaDownloadWorkerTask task = mSuspendedTasks.get(0);
// privacy
//Log.d(LOG_TAG, "Restart the task " + task.getUrl());
Log.d(LOG_TAG, "Restart a task ");
// avoid crash if there are too many running task
try {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Integer[])null);
mSuspendedTasks.remove(task);
} catch (RejectedExecutionException e) {
task.cancel(true);
mSuspendedTasks.remove(task);
// create a new task from the existing one
task = new MXMediaDownloadWorkerTask(task);
mSuspendedTasks.add(task);
// privacy
//Log.d(LOG_TAG, "Suspend again the task " + task.getUrl() + " - " + task.getStatus());
Log.d(LOG_TAG, "Suspend again the task " + task.getStatus());
} catch (Exception e) {
Log.d(LOG_TAG, "Try to Restart a task fails " + e.getMessage());
}
}
}
}
}
/**
* Handler to post events on UI thread
*/
private static Handler mUIHandler = null;
/**
* The default bitmap to use when the media cannot be retrieved.
*/
private static Bitmap mDefaultBitmap = null;
/**
* Load a bitmap from an url.
* The imageView image is updated when the bitmap is loaded or downloaded.
* The width/height parameters are optional. If they are > 0, download a thumbnail.
*
* The rotation angle is checked first.
* If rotationAngle is set to Integer.MAX_VALUE, check the orientation is defined to a valid value.
* If the orientation is defined, request the properly oriented image to the server
*
*
* @param context the context
* @param hsConfig the home server config
* @param imageView the imageView to fill when the image is downloaded
* @param url the image url
* @param width the expected image width
* @param height the expected image height
* @param rotationAngle the rotation angle (degrees)
* @param orientation the orientation (ExifInterface.ORIENTATION_XXX value)
* @param mimeType the mimeType.
* @param folderFile the folder where the media should be stored
* @param encryptionInfo the encryption file information.
* @return a download identifier if the image is not cached
*/
public String loadBitmap(Context context, HomeserverConnectionConfig hsConfig, final ImageView imageView, String url, int width, int height, int rotationAngle, int orientation, String mimeType, File folderFile, EncryptedFileInfo encryptionInfo) {
return loadBitmap(context, hsConfig, imageView, url, width, height, rotationAngle, orientation, mimeType, folderFile, null, encryptionInfo);
}
/**
* Load a bitmap from an url.
* The imageView image is updated when the bitmap is loaded or downloaded.
* The width/height parameters are optional. If they are > 0, download a thumbnail.
*
* The rotation angle is checked first.
* If rotationAngle is set to Integer.MAX_VALUE, check the orientation is defined to a valid value.
* If the orientation is defined, request the properly oriented image to the server
*
*
* @param context the context
* @param hsConfig the home server config
* @param imageView the imageView to fill when the image is downloaded
* @param url the image url
* @param width the expected image width
* @param height the expected image height
* @param rotationAngle the rotation angle (degrees)
* @param orientation the orientation (ExifInterface.ORIENTATION_XXX value)
* @param mimeType the mimeType.
* @param folderFile the folder where the media should be stored
* @param aDefaultBitmap the default bitmap to use when the url media cannot be retrieved.
* @param encryptionInfo the file encryption info
* @return a download identifier if the image is not cached
*/
public String loadBitmap(Context context, HomeserverConnectionConfig hsConfig, final ImageView imageView, String url, int width, int height, int rotationAngle, int orientation, String mimeType, File folderFile, Bitmap aDefaultBitmap, EncryptedFileInfo encryptionInfo) {
if (null == url) {
return null;
}
// request invalid bitmap size
if ((0 == width) || (0 == height)) {
return null;
}
if (null == mDefaultBitmap) {
mDefaultBitmap = BitmapFactory.decodeResource(context.getResources(), android.R.drawable.ic_menu_gallery);
}
Bitmap defaultBimap = (null == aDefaultBitmap) ? mDefaultBitmap : aDefaultBitmap;
String downloadableUrl;
// it is not possible to resize an encrypted image
if (null == encryptionInfo) {
downloadableUrl = downloadableUrl(url, width, height);
} else {
downloadableUrl = downloadableUrl(url, -1, -1);
}
// the thumbnail params are ignored when encrypted
if ((null == encryptionInfo) && (rotationAngle == Integer.MAX_VALUE) && (orientation != ExifInterface.ORIENTATION_UNDEFINED) && (orientation != ExifInterface.ORIENTATION_NORMAL)) {
if (downloadableUrl.indexOf("?") != -1) {
downloadableUrl += "&apply_orientation=true";
} else {
downloadableUrl += "?apply_orientation=true";
}
}
final String fDownloadableUrl = downloadableUrl;
if (null != imageView) {
imageView.setTag(fDownloadableUrl);
}
// if the mime type is not provided, assume it is a jpeg file
if (null == mimeType) {
mimeType = "image/jpeg";
}
// check if the bitmap is already cached
final Bitmap bitmap = (MXMediaDownloadWorkerTask.isMediaUrlUnreachable(downloadableUrl)) ? defaultBimap : MXMediaDownloadWorkerTask.bitmapForURL(context.getApplicationContext(), folderFile, downloadableUrl, rotationAngle, mimeType);
if (null != bitmap) {
if (null != imageView) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
// display it
imageView.setBackgroundColor(Color.TRANSPARENT);
imageView.setImageBitmap(bitmap);
} else {
// init
if (null == mUIHandler) {
mUIHandler = new Handler(Looper.getMainLooper());
}
// handle any thread management
// the image should be loaded from any thread
mUIHandler.post(new Runnable() {
@Override
public void run() {
if (TextUtils.equals(fDownloadableUrl, (String) imageView.getTag())) {
// display it
imageView.setBackgroundColor(Color.TRANSPARENT);
imageView.setImageBitmap(bitmap);
}
}
});
}
}
downloadableUrl = null;
} else {
MXMediaDownloadWorkerTask currentTask = MXMediaDownloadWorkerTask.getMediaDownloadWorkerTask(downloadableUrl);
if (null != currentTask) {
if (null != imageView) {
currentTask.addImageView(imageView);
}
} else {
// download it in background
MXMediaDownloadWorkerTask task = new MXMediaDownloadWorkerTask(context, hsConfig, folderFile, downloadableUrl, rotationAngle, mimeType, encryptionInfo);
if (null != imageView) {
task.addImageView(imageView);
}
task.setDefaultBitmap(defaultBimap);
// check at the end of the download, if a suspended task can be launched again.
task.addDownloadListener(new MXMediaDownloadListener() {
@Override
public void onDownloadComplete(String downloadId) {
MXMediasCache.this.launchSuspendedTask();
}
});
// avoid crash if there are too many running task
try {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Integer[])null);
} catch (RejectedExecutionException e) {
// too many tasks have been launched
synchronized (mSuspendedTasks) {
task.cancel(true);
// create a new task from the existing one
task = new MXMediaDownloadWorkerTask(task);
mSuspendedTasks.add(task);
// privacy
//Log.e(LOG_TAG, "Suspend the task " + task.getUrl());
Log.e(LOG_TAG, "Suspend a task ");
}
} catch (Exception e) {
Log.e(LOG_TAG, "loadBitmap failed " + e.getLocalizedMessage());
}
}
}
return downloadableUrl;
}
/**
* Returns the download progress (percentage).
* @param downloadId the downloadId provided by loadBitmap;
* @return the download progress
*/
public int getProgressValueForDownloadId(String downloadId) {
MXMediaDownloadWorkerTask currentTask = MXMediaDownloadWorkerTask.getMediaDownloadWorkerTask(downloadId);
if (null != currentTask) {
return currentTask.getProgress();
}
return -1;
}
/**
* Returns the download stats for a dedicated download id.
* @param downloadId the downloadId provided by loadBitmap;
* @return the download stats
*/
public IMXMediaDownloadListener.DownloadStats getStatsForDownloadId(String downloadId) {
MXMediaDownloadWorkerTask currentTask = MXMediaDownloadWorkerTask.getMediaDownloadWorkerTask(downloadId);
if (null != currentTask) {
return currentTask.getDownloadStats();
}
return null;
}
/**
* Add a download listener for an downloadId.
* @param downloadId The uploadId.
* @param listener the download listener.
*/
public void addDownloadListener(String downloadId, IMXMediaDownloadListener listener) {
MXMediaDownloadWorkerTask currentTask = MXMediaDownloadWorkerTask.getMediaDownloadWorkerTask(downloadId);
if (null != currentTask) {
currentTask.addDownloadListener(listener);
}
}
/**
* Cancel a download.
* @param downloadId the download id.
*/
public void cancelDownload(String downloadId) {
MXMediaDownloadWorkerTask currentTask = MXMediaDownloadWorkerTask.getMediaDownloadWorkerTask(downloadId);
if (null != currentTask) {
currentTask.cancelDownload();
}
}
/**
* Upload a file
* @param contentStream the stream to upload
* @param filename the dst fileanme
* @param mimeType the mimetype
* @param uploadId the upload id
* @param listener the upload progress listener
*/
public void uploadContent(InputStream contentStream, String filename, String mimeType, String uploadId, IMXMediaUploadListener listener) {
try {
new MXMediaUploadWorkerTask(mContentManager, contentStream, mimeType, uploadId, filename, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} catch (Exception e) {
// cannot start the task
if (null != listener) {
listener.onUploadError(uploadId, -1, null);
}
}
}
/**
* Returns the upload progress (percentage) for a dedicated uploadId
* @param uploadId The uploadId.
* @return the upload percentage. -1 means there is no pending upload.
*/
public int getProgressValueForUploadId(String uploadId) {
MXMediaUploadWorkerTask uploadTask = MXMediaUploadWorkerTask.getMediaDUploadWorkerTask(uploadId);
if (null != uploadTask) {
return uploadTask.getProgress();
}
return -1;
}
/**
* Returns the upload stats for a dedicated uploadId
* @param uploadId The uploadId.
* @return the upload stats
*/
public IMXMediaUploadListener.UploadStats getStatsForUploadId(String uploadId) {
MXMediaUploadWorkerTask uploadTask = MXMediaUploadWorkerTask.getMediaDUploadWorkerTask(uploadId);
if (null != uploadTask) {
return uploadTask.getStats();
}
return null;
}
/**
* Add an upload listener for an uploadId.
* @param uploadId The uploadId.
* @param listener the upload listener
*/
public void addUploadListener(String uploadId, IMXMediaUploadListener listener) {
MXMediaUploadWorkerTask uploadTask = MXMediaUploadWorkerTask.getMediaDUploadWorkerTask(uploadId);
if (null != uploadTask) {
uploadTask.addListener(listener);
}
}
/**
* Cancel an upload.
* @param uploadId the upload Id
*/
public void cancelUpload(String uploadId) {
MXMediaUploadWorkerTask uploadTask = MXMediaUploadWorkerTask.getMediaDUploadWorkerTask(uploadId);
if (null != uploadTask) {
uploadTask.cancelUpload();
}
}
}