package com.amaze.filemanager.services;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.text.format.Formatter;
import com.amaze.filemanager.R;
import com.amaze.filemanager.activities.MainActivity;
import com.amaze.filemanager.filesystem.BaseFile;
import com.amaze.filemanager.filesystem.FileUtil;
import com.amaze.filemanager.filesystem.HFile;
import com.amaze.filemanager.utils.CryptUtil;
import com.amaze.filemanager.utils.DataPackage;
import com.amaze.filemanager.utils.OpenMode;
import com.amaze.filemanager.utils.ProgressHandler;
import com.amaze.filemanager.utils.ServiceWatcherUtil;
import java.util.ArrayList;
/**
* Created by vishal on 8/4/17.
*/
public class EncryptService extends Service {
public static final String TAG_SOURCE = "crypt_source"; // source file to encrypt or decrypt
public static final String TAG_DECRYPT_PATH = "decrypt_path";
public static final String TAG_OPEN_MODE = "open_mode";
public static final String TAG_CRYPT_MODE = "crypt_mode"; // ordinal of type of service
// expected (encryption or decryption)
private static final int ID_NOTIFICATION = 27978;
public static final String TAG_BROADCAST_CRYPT_CANCEL = "crypt_cancel";
// list of data packages which contains progress
private ArrayList<DataPackage> dataPackages = new ArrayList<>();
private NotificationManager notificationManager;
private NotificationCompat.Builder notificationBuilder;
private Context context;
private IBinder mBinder = new LocalBinder();
private ProgressHandler progressHandler;
private ServiceWatcherUtil serviceWatcherUtil;
private long totalSize = 0l;
private OpenMode openMode;
private String decryptPath;
private BaseFile baseFile;
private CryptEnum cryptEnum;
private ArrayList<HFile> failedOps = new ArrayList<>();
private ProgressListener progressListener;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
registerReceiver(cancelReceiver, new IntentFilter(TAG_BROADCAST_CRYPT_CANCEL));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
baseFile = intent.getParcelableExtra(TAG_SOURCE);
cryptEnum = CryptEnum.values()[intent.getIntExtra(TAG_CRYPT_MODE, CryptEnum.ENCRYPT.ordinal())];
openMode = OpenMode.values()[intent.getIntExtra(TAG_OPEN_MODE, OpenMode.UNKNOWN.ordinal())];
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
notificationIntent.putExtra(MainActivity.KEY_INTENT_PROCESS_VIEWER, true);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setContentIntent(pendingIntent);
if (cryptEnum == CryptEnum.ENCRYPT) {
// we have to encrypt the source
notificationBuilder.setContentTitle(getResources().getString(R.string.crypt_encrypting));
notificationBuilder.setSmallIcon(R.drawable.ic_folder_lock_white_36dp);
} else {
decryptPath = intent.getStringExtra(TAG_DECRYPT_PATH);
notificationBuilder.setContentTitle(getResources().getString(R.string.crypt_decrypting));
notificationBuilder.setSmallIcon(R.drawable.ic_folder_lock_open_white_36dp);
}
startForeground(ID_NOTIFICATION, notificationBuilder.build());
new BackgroundTask().execute();
return START_STICKY;
}
class BackgroundTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
if (baseFile.isDirectory()) totalSize = baseFile.folderSize(context);
else totalSize = baseFile.length(context);
progressHandler = new ProgressHandler(1, totalSize);
progressHandler.setProgressListener(new ProgressHandler.ProgressListener() {
@Override
public void onProgressed(String fileName, int sourceFiles, int sourceProgress,
long totalSize, long writtenSize, int speed) {
publishResults(fileName, sourceFiles, sourceProgress, totalSize, writtenSize, speed);
}
});
serviceWatcherUtil = new ServiceWatcherUtil(progressHandler, totalSize);
DataPackage dataPackage = new DataPackage();
dataPackage.setName(baseFile.getName());
dataPackage.setSourceFiles(1);
dataPackage.setSourceProgress(1);
dataPackage.setTotal(totalSize);
dataPackage.setByteProgress(0);
dataPackage.setSpeedRaw(0);
dataPackage.setMove(cryptEnum==CryptEnum.ENCRYPT ? false : true); // we're using encrypt as
// move flag false
dataPackage.setCompleted(false);
putDataPackage(dataPackage);
if (FileUtil.checkFolder(baseFile.getPath(), context) == 1) {
serviceWatcherUtil.watch();
if (cryptEnum == CryptEnum.ENCRYPT) {
// we're here to encrypt
try {
new CryptUtil(context, baseFile, progressHandler, failedOps);
} catch (Exception e) {
e.printStackTrace();
failedOps.add(baseFile);
}
} else {
// we're here to decrypt, we'll decrypt at a custom path.
// the path is to the same directory as in encrypted one in normal case
// and the cache directory in case we're here because of the viewer
try {
new CryptUtil(context, baseFile, decryptPath, progressHandler, failedOps);
} catch (Exception e) {
e.printStackTrace();
failedOps.add(baseFile);
}
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
serviceWatcherUtil.stopWatch();
generateNotification(failedOps, cryptEnum==CryptEnum.ENCRYPT ? false : true);
Intent intent = new Intent("loadlist");
sendBroadcast(intent);
stopSelf();
}
}
private void publishResults(String fileName, int sourceFiles, int sourceProgress,
long totalSize, long writtenSize, int speed) {
if (!progressHandler.getCancelled()) {
//notification
float progressPercent = ((float) writtenSize/totalSize)*100;
notificationBuilder.setProgress(100, Math.round(progressPercent), false);
notificationBuilder.setOngoing(true);
int title = R.string.crypt_encrypting;
if (cryptEnum == CryptEnum.DECRYPT) title = R.string.crypt_decrypting;
notificationBuilder.setContentTitle(context.getResources().getString(title));
notificationBuilder.setContentText(fileName + " " + Formatter.formatFileSize(context,
writtenSize) + "/" +
Formatter.formatFileSize(context, totalSize));
notificationManager.notify(ID_NOTIFICATION, notificationBuilder.build());
if (writtenSize == totalSize || totalSize == 0) {
notificationBuilder.setContentText("");
notificationBuilder.setOngoing(false);
notificationBuilder.setAutoCancel(true);
notificationManager.notify(ID_NOTIFICATION, notificationBuilder.build());
publishCompletedResult();
}
//for processviewer
DataPackage intent = new DataPackage();
intent.setName(fileName);
intent.setSourceFiles(sourceFiles);
intent.setSourceProgress(sourceProgress);
intent.setTotal(totalSize);
intent.setByteProgress(writtenSize);
intent.setSpeedRaw(speed);
intent.setMove(cryptEnum==CryptEnum.ENCRYPT ? false : true);
intent.setCompleted(false);
putDataPackage(intent);
if(progressListener!=null) {
progressListener.onUpdate(intent);
if(false) progressListener.refresh();
}
} else publishCompletedResult();
}
public void publishCompletedResult(){
try {
notificationManager.cancel(ID_NOTIFICATION);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
public EncryptService getService() {
return EncryptService.this;
}
}
@Override
public void onDestroy() {
super.onDestroy();
this.unregisterReceiver(cancelReceiver);
}
/**
* Displays a notification, sends intent and cancels progress if there were some failures
* in copy progress
* @param failedOps
* @param move
*/
void generateNotification(ArrayList<HFile> failedOps, boolean move) {
notificationManager.cancelAll();
if(failedOps.size()==0)return;
NotificationCompat.Builder mBuilder=new NotificationCompat.Builder(context);
mBuilder.setContentTitle(context.getString(R.string.operationunsuccesful));
mBuilder.setContentText(context.getString(R.string.copy_error).replace("%s",
move ? context.getString(R.string.crypt_encrypted).toLowerCase() :
context.getString(R.string.crypt_decrypted).toLowerCase()));
mBuilder.setAutoCancel(true);
progressHandler.setCancelled(true);
Intent intent= new Intent(this, MainActivity.class);
intent.putExtra(MainActivity.TAG_INTENT_FILTER_FAILED_OPS, failedOps);
intent.putExtra("move", move);
PendingIntent pIntent = PendingIntent.getActivity(this, 101, intent,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pIntent);
if (cryptEnum == CryptEnum.ENCRYPT) {
mBuilder.setSmallIcon(R.drawable.ic_folder_lock_white_36dp);
} else {
mBuilder.setSmallIcon(R.drawable.ic_folder_lock_open_white_36dp);
}
notificationManager.notify(741,mBuilder.build());
intent=new Intent(MainActivity.TAG_INTENT_FILTER_GENERAL);
intent.putExtra(MainActivity.TAG_INTENT_FILTER_FAILED_OPS, failedOps);
intent.putExtra(TAG_CRYPT_MODE, move);
sendBroadcast(intent);
}
public enum CryptEnum {
ENCRYPT,
DECRYPT
}
private BroadcastReceiver cancelReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//cancel operation
progressHandler.setCancelled(true);
}
};
public interface ProgressListener {
void onUpdate(DataPackage dataPackage);
void refresh();
}
public void setProgressListener(ProgressListener progressListener) {
this.progressListener = progressListener;
}
/**
* Returns the {@link #dataPackages} list which contains
* data to be transferred to {@link com.amaze.filemanager.fragments.ProcessViewer}
* Method call is synchronized so as to avoid modifying the list
* by {@link ServiceWatcherUtil#handlerThread} while {@link MainActivity#runOnUiThread(Runnable)}
* is executing the callbacks in {@link com.amaze.filemanager.fragments.ProcessViewer}
* @return
*/
public synchronized DataPackage getDataPackage(int index) {
return this.dataPackages.get(index);
}
public synchronized int getDataPackageSize() {
return this.dataPackages.size();
}
/**
* Puts a {@link DataPackage} into a list
* Method call is synchronized so as to avoid modifying the list
* by {@link ServiceWatcherUtil#handlerThread} while {@link MainActivity#runOnUiThread(Runnable)}
* is executing the callbacks in {@link com.amaze.filemanager.fragments.ProcessViewer}
* @param dataPackage
*/
private synchronized void putDataPackage(DataPackage dataPackage) {
this.dataPackages.add(dataPackage);
}
}