/*
* Copyright (C) 2014 Arpit Khurana <arpitkh96@gmail.com>
*
* This file is part of Amaze File Manager.
*
* Amaze File Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.amaze.filemanager.ui.icons;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v4.content.ContextCompat;
import android.widget.ImageView;
import com.amaze.filemanager.R;
import com.amaze.filemanager.database.CloudHandler;
import com.amaze.filemanager.utils.CloudUtil;
import com.amaze.filemanager.utils.OTGUtil;
import com.amaze.filemanager.utils.OpenMode;
import com.amaze.filemanager.utils.Utils;
import com.cloudrail.si.interfaces.CloudStorage;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import jcifs.smb.SmbFileInputStream;
import static com.amaze.filemanager.activities.MainActivity.dataUtils;
/**
* A class that holds icons for a more efficient access.
*/
public class IconHolder {
private static final int MAX_CACHE = 500;
private static final int MSG_LOAD = 1;
private static final int MSG_LOADED = 2;
private static final int MSG_DESTROY = 3;
private final Map<String, Bitmap> mIcons; // Themes based
private final Map<String, Bitmap> mAppIcons; // App based
private Map<ImageView, String> mRequests;
private final Context mContext;
private final boolean mUseThumbs;
private HandlerThread mWorkerThread;
private Handler mWorkerHandler;
private static class LoadResult {
String fso;
Bitmap result;
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_LOADED:
processResult((LoadResult) msg.obj);
sendEmptyMessageDelayed(MSG_DESTROY, 3000);
break;
case MSG_DESTROY:
shutdownWorker();
break;
}
}
private synchronized void processResult(LoadResult result) {
// Cache the new drawable
final String filePath =(result.fso);
mAppIcons.put(filePath, result.result);
// find the request for it
for (Map.Entry<ImageView, String> entry : mRequests.entrySet()) {
final ImageView imageView = entry.getKey();
final String fso = entry.getValue();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (Objects.equals(fso, result.fso)) {
imageView.setImageBitmap(result.result);
mRequests.remove(imageView);
break;
}
} else {
if (fso.equals(result.fso)) {
imageView.setImageBitmap(result.result);
mRequests.remove(imageView);
break;
}
}
}
}
};
/**
* Constructor of <code>IconHolder</code>.
*
* @param useThumbs If thumbs of images, videos, apps, ... should be returned
* instead of the default icon.
*/
int px;
public IconHolder(Context context, boolean useThumbs,boolean grid) {
super();
this.mContext = context;
this.mUseThumbs = useThumbs;
this.mRequests = new HashMap<>();
this.mIcons = new HashMap<>();
this.mAppIcons = new LinkedHashMap<String, Bitmap>(MAX_CACHE, .75F, true) {
private static final long serialVersionUID = 1L;
@Override
protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
return size() > MAX_CACHE;
}
};
Resources res=mContext.getResources();
int dp=50;
if(grid){dp=150;}
px = (int)(dp * (res.getDisplayMetrics().densityDpi / 160));
}
/**
* Method that returns a drawable reference of a icon.
*
* @param resid The resource identifier
* @return Drawable The drawable icon reference
*/
/**
* Method that returns a drawable reference of a FileSystemObject.
*
* @param iconView View to load the drawable into
* @param fso The FileSystemObject reference
* @param defaultIcon Drawable to be used in case no specific one could be found
* @return Drawable The drawable reference
*/
public void loadDrawable(final ImageView iconView, final String fso, Drawable defaultIcon) {
if (!mUseThumbs) {
return;
}
// Is cached?
final String filePath = fso;
if (this.mAppIcons.containsKey(filePath)) {
iconView.setImageBitmap(this.mAppIcons.get(filePath));
return;
}
new Thread(new Runnable() {
@Override
public void run() {
mHandler.removeMessages(MSG_DESTROY);
if (mWorkerThread == null || mWorkerHandler==null) {
mWorkerThread = new HandlerThread("IconHolderLoader");
mWorkerThread.start();
mWorkerHandler = new WorkerHandler(mWorkerThread.getLooper());
}
mRequests.put(iconView, fso);
Message msg = mWorkerHandler.obtainMessage(MSG_LOAD, fso);
msg.sendToTarget();
}
}).start();
}
/**
* Cancel loading of a drawable for a certain ImageView.
*/
public void cancelLoad(ImageView view) {
String fso = mRequests.get(view);
if (fso != null && mWorkerHandler != null) {
mWorkerHandler.removeMessages(MSG_LOAD, fso);
}
mRequests.remove(view);
}
private class WorkerHandler extends Handler {
WorkerHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_LOAD:
String fso = (String) msg.obj;
Bitmap d = loadDrawable(fso);
if (d != null) {
LoadResult result = new LoadResult();
result.fso = fso;
result.result = d;
mHandler.obtainMessage(MSG_LOADED, result).sendToTarget();
}
break;
}
}
}
private Bitmap loadDrawable(String fso) {
final String filePath = (fso);
try {
if (Icons.isApk(filePath)) {
return getAppDrawable(fso);
}else if(Icons.isPicture(filePath)){
return loadImage(fso);
}else if(Icons.isVideo(filePath))
return getVideoDrawable(fso);
} catch (OutOfMemoryError outOfMemoryError) {
cleanup();
shutdownWorker();
}
return null;
}
private Bitmap getVideoDrawable(String path) throws OutOfMemoryError{
try {
Bitmap thumb = ThumbnailUtils.createVideoThumbnail(path,
MediaStore.Images.Thumbnails.MINI_KIND);
return thumb;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private Bitmap getAppDrawable(String path) throws OutOfMemoryError {
Bitmap bitsat;
try {
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageArchiveInfo(path, 0);
// // the secret are these two lines....
pi.applicationInfo.sourceDir = path;
pi.applicationInfo.publicSourceDir = path;
// //
Drawable d = pi.applicationInfo.loadIcon(pm);
Bitmap d1 = null;
d1 = ((BitmapDrawable) d).getBitmap();
bitsat = d1;
} catch (Exception e) {
Drawable apk = ContextCompat.getDrawable(mContext, R.drawable.ic_doc_apk_grid);
Bitmap apk1 = ((BitmapDrawable) apk).getBitmap();
bitsat = apk1;
}
return bitsat;
}
private Bitmap loadImage(String path) throws OutOfMemoryError {
Bitmap bitsat;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig= Bitmap.Config.ARGB_8888;
options.inJustDecodeBounds = true;
Bitmap b = BitmapFactory.decodeFile(path, options);
options.inSampleSize = Utils.calculateInSampleSize(options, px, px);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bit;
if(path.startsWith("smb:/")) {
bit = BitmapFactory.decodeStream(new SmbFileInputStream(path));
} else if (path.startsWith(CloudHandler.CLOUD_PREFIX_DROPBOX)) {
CloudStorage cloudStorageDropbox = dataUtils.getAccount(OpenMode.DROPBOX);
bit = BitmapFactory.decodeStream(cloudStorageDropbox.getThumbnail(CloudUtil
.stripPath(OpenMode.DROPBOX, path)));
} else if (path.startsWith(CloudHandler.CLOUD_PREFIX_BOX)) {
CloudStorage cloudStorageBox = dataUtils.getAccount(OpenMode.BOX);
bit = BitmapFactory.decodeStream(cloudStorageBox.getThumbnail(CloudUtil
.stripPath(OpenMode.BOX, path)));
} else if (path.startsWith(CloudHandler.CLOUD_PREFIX_GOOGLE_DRIVE)) {
CloudStorage cloudStorageGDrive = dataUtils.getAccount(OpenMode.GDRIVE);
bit = BitmapFactory.decodeStream(cloudStorageGDrive.getThumbnail(CloudUtil
.stripPath(OpenMode.GDRIVE, path)));
} else if (path.startsWith(CloudHandler.CLOUD_PREFIX_ONE_DRIVE)) {
CloudStorage cloudStorageOneDrive = dataUtils.getAccount(OpenMode.ONEDRIVE);
bit = BitmapFactory.decodeStream(cloudStorageOneDrive.getThumbnail(CloudUtil
.stripPath(OpenMode.ONEDRIVE, path)));
} else if (path.startsWith(OTGUtil.PREFIX_OTG)) {
bit = BitmapFactory.decodeStream(OTGUtil.getDocumentFilesList(path, mContext).get(0).getInputStream(mContext));
}
else bit= BitmapFactory.decodeFile(path, options);
bitsat = bit;// decodeFile(path);//.createScaledBitmap(bits,imageViewReference.get().getHeight(),imageViewReference.get().getWidth(),true);
} catch (Exception e) {
Drawable img = ContextCompat.getDrawable(mContext, R.drawable.ic_doc_image);
Bitmap img1 = ((BitmapDrawable) img).getBitmap();
bitsat = img1;
}return bitsat;
}
/**
* Method that returns a thumbnail of the album folder
*
* @param albumId The album identifier
* @return Drawable The drawable or null if cannot be extracted
*/
/**
* Shut down worker thread
*/
private void shutdownWorker() {
if (mWorkerThread != null) {
mWorkerThread.getLooper().quit();
mWorkerHandler = null;
mWorkerThread = null;
}
}
/**
* Free any resources used by this instance
*/
public void cleanup() {
this.mRequests.clear();
this.mIcons.clear();
this.mAppIcons.clear();
shutdownWorker();
}
}