/*
* Copyright (C) 2015 Actor LLC. <https://actor.im>
*/
package im.actor.core.modules.file;
import java.util.ArrayList;
import java.util.HashMap;
import im.actor.core.entity.FileReference;
import im.actor.core.modules.ModuleContext;
import im.actor.core.modules.file.entity.Downloaded;
import im.actor.core.modules.ModuleActor;
import im.actor.core.util.RandomUtils;
import im.actor.core.viewmodel.UploadFileCallback;
import im.actor.runtime.Log;
import im.actor.runtime.actors.ActorRef;
import im.actor.runtime.actors.Props;
import im.actor.runtime.actors.messages.PoisonPill;
import im.actor.runtime.files.FileSystemReference;
public class UploadManager extends ModuleActor {
private static final String TAG = "UploadManager";
private static final int SIM_MAX_UPLOADS = 2;
private final boolean LOG;
private ArrayList<QueueItem> queue = new ArrayList<>();
private HashMap<Long, ArrayList<UploadFileCallback>> callbacks = new HashMap<>();
public UploadManager(ModuleContext context) {
super(context);
this.LOG = context.getConfiguration().isEnableFilesLogging();
}
// Tasks
public void startUpload(long rid, String descriptor, String fileName, ActorRef requestActor) {
if (LOG) {
Log.d(TAG, "Starting upload #" + rid + " with descriptor " + descriptor);
}
QueueItem queueItem = new QueueItem(rid, descriptor, fileName, requestActor);
queueItem.isStopped = false;
queue.add(queueItem);
checkQueue();
}
public void stopUpload(long rid) {
if (LOG) {
Log.d(TAG, "Stopping upload #" + rid);
}
QueueItem queueItem = findItem(rid);
if (queueItem == null) {
if (LOG) {
Log.d(TAG, "- Not present in queue");
}
} else {
if (queueItem.isStarted) {
if (LOG) {
Log.d(TAG, "- Stopping actor");
}
queueItem.taskRef.send(PoisonPill.INSTANCE);
queueItem.taskRef = null;
queueItem.isStarted = false;
}
queue.remove(queueItem);
ArrayList<UploadFileCallback> clist = callbacks.get(queueItem.rid);
if (clist != null) {
for (final UploadFileCallback callback : clist) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
}
}
}
checkQueue();
}
public void bindUpload(long rid, final UploadFileCallback callback) {
if (LOG) {
Log.d(TAG, "Bind upload #" + rid);
}
QueueItem queueItem = findItem(rid);
if (queueItem == null) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
} else {
if (queueItem.isStopped) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
} else {
final float progress = queueItem.progress;
im.actor.runtime.Runtime.dispatch(() -> callback.onUploading(progress));
}
}
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist == null) {
clist = new ArrayList<>();
callbacks.put(rid, clist);
}
clist.add(callback);
}
public void unbindUpload(long rid, UploadFileCallback callback) {
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist != null) {
clist.remove(callback);
}
}
public void requestState(long rid, final UploadFileCallback callback) {
QueueItem queueItem = findItem(rid);
if (queueItem == null) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
} else {
if (queueItem.isStopped) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
} else {
final float progress = queueItem.progress;
im.actor.runtime.Runtime.dispatch(() -> callback.onUploading(progress));
}
}
}
public void resumeUpload(long rid) {
QueueItem queueItem = findItem(rid);
if (queueItem != null) {
if (queueItem.isStarted) {
return;
}
if (queueItem.isStopped) {
queueItem.isStopped = false;
}
queueItem.progress = 0;
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist != null) {
for (final UploadFileCallback callback : clist) {
im.actor.runtime.Runtime.dispatch(() -> callback.onUploading(0));
}
}
checkQueue();
}
}
public void pauseUpload(long rid) {
QueueItem queueItem = findItem(rid);
if (queueItem != null) {
if (queueItem.isStarted) {
queueItem.taskRef.send(PoisonPill.INSTANCE);
queueItem.taskRef = null;
queueItem.isStarted = false;
}
queueItem.isStopped = true;
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist != null) {
for (final UploadFileCallback callback : clist) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
}
}
}
}
// Queue processing
public void onUploadTaskError(long rid) {
if (LOG) {
Log.d(TAG, "Upload #" + rid + " error");
}
QueueItem queueItem = findItem(rid);
if (queueItem == null) {
if (LOG) {
Log.d(TAG, "- Nothing found");
}
return;
}
if (!queueItem.isStarted) {
return;
}
queueItem.taskRef.send(PoisonPill.INSTANCE);
queueItem.isStopped = true;
queueItem.isStarted = false;
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist != null) {
for (final UploadFileCallback callback : clist) {
im.actor.runtime.Runtime.dispatch(() -> callback.onNotUploading());
}
}
queueItem.requestActor.send(new UploadError(rid));
checkQueue();
}
public void onUploadTaskProgress(long rid, final float progress) {
if (LOG) {
Log.d(TAG, "Upload #" + rid + " progress " + progress);
}
QueueItem queueItem = findItem(rid);
if (queueItem == null) {
return;
}
if (!queueItem.isStarted) {
return;
}
queueItem.progress = progress;
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist != null) {
for (final UploadFileCallback callback : clist) {
im.actor.runtime.Runtime.dispatch(() -> callback.onUploading(progress));
}
}
}
public void onUploadTaskComplete(long rid, FileReference fileReference, FileSystemReference reference) {
if (LOG) {
Log.d(TAG, "Upload #" + rid + " complete");
}
QueueItem queueItem = findItem(rid);
if (queueItem == null) {
return;
}
if (!queueItem.isStarted) {
return;
}
queue.remove(queueItem);
queueItem.taskRef.send(PoisonPill.INSTANCE);
// Saving reference to uploaded file
context().getFilesModule().getDownloadedEngine().addOrUpdateItem(new Downloaded(fileReference.getFileId(),
fileReference.getFileSize(), reference.getDescriptor()));
ArrayList<UploadFileCallback> clist = callbacks.get(rid);
if (clist != null) {
for (final UploadFileCallback callback : clist) {
im.actor.runtime.Runtime.dispatch(() -> callback.onUploaded());
}
}
queueItem.requestActor.send(new UploadCompleted(rid, fileReference));
checkQueue();
}
private void checkQueue() {
if (LOG) {
Log.d(TAG, "- Checking queue");
}
int activeUploads = 0;
for (QueueItem queueItem : queue) {
if (queueItem.isStarted) {
activeUploads++;
}
}
if (activeUploads >= SIM_MAX_UPLOADS) {
if (LOG) {
Log.d(TAG, "- Already have max number of simultaneous uploads");
}
return;
}
QueueItem pendingQueue = null;
for (QueueItem queueItem : queue) {
if (!queueItem.isStarted && !queueItem.isStopped) {
pendingQueue = queueItem;
break;
}
}
if (pendingQueue == null) {
if (LOG) {
Log.d(TAG, "- No work for uploading");
}
return;
}
if (LOG) {
Log.d(TAG, "- Starting upload file #" + pendingQueue.fileDescriptor);
}
pendingQueue.isStarted = true;
final QueueItem finalPendingQueue = pendingQueue;
pendingQueue.taskRef = system().actorOf(Props.create(() -> new UploadTask(finalPendingQueue.rid, finalPendingQueue.fileDescriptor,
finalPendingQueue.fileName, self(), context())).changeDispatcher("heavy"), "actor/upload/task_" + RandomUtils.nextRid());
}
private QueueItem findItem(long rid) {
for (QueueItem q : queue) {
if (q.rid == rid) {
return q;
}
}
return null;
}
private class QueueItem {
private long rid;
private String fileDescriptor;
private boolean isStopped;
private boolean isStarted;
private float progress;
private ActorRef taskRef;
private ActorRef requestActor;
private String fileName;
private QueueItem(long rid, String fileDescriptor, String fileName, ActorRef requestActor) {
this.rid = rid;
this.fileDescriptor = fileDescriptor;
this.requestActor = requestActor;
this.fileName = fileName;
}
}
//region Messages
@Override
public void onReceive(Object message) {
if (message instanceof StartUpload) {
StartUpload startUpload = (StartUpload) message;
startUpload(startUpload.getRid(), startUpload.getFileDescriptor(),
startUpload.getFileName(), sender());
} else if (message instanceof StopUpload) {
StopUpload cancelUpload = (StopUpload) message;
stopUpload(cancelUpload.getRid());
} else if (message instanceof UploadTaskError) {
UploadTaskError uploadTaskError = (UploadTaskError) message;
onUploadTaskError(uploadTaskError.getRid());
} else if (message instanceof UploadTaskProgress) {
UploadTaskProgress taskProgress = (UploadTaskProgress) message;
onUploadTaskProgress(taskProgress.getRid(), taskProgress.getProgress());
} else if (message instanceof UploadTaskComplete) {
UploadTaskComplete taskComplete = (UploadTaskComplete) message;
onUploadTaskComplete(taskComplete.getRid(), taskComplete.getLocation(),
taskComplete.getReference());
} else if (message instanceof BindUpload) {
BindUpload bindUpload = (BindUpload) message;
bindUpload(bindUpload.getRid(), bindUpload.getCallback());
} else if (message instanceof UnbindUpload) {
UnbindUpload unbindUpload = (UnbindUpload) message;
unbindUpload(unbindUpload.getRid(), unbindUpload.getCallback());
} else if (message instanceof RequestState) {
RequestState requestState = (RequestState) message;
requestState(requestState.getRid(), requestState.getCallback());
} else if (message instanceof PauseUpload) {
PauseUpload pauseUpload = (PauseUpload) message;
pauseUpload(pauseUpload.getRid());
} else if (message instanceof ResumeUpload) {
ResumeUpload resumeUpload = (ResumeUpload) message;
resumeUpload(resumeUpload.getRid());
} else {
super.onReceive(message);
}
}
public static class StartUpload {
private long rid;
private String fileDescriptor;
private String fileName;
public StartUpload(long rid, String fileDescriptor, String fileName) {
this.rid = rid;
this.fileDescriptor = fileDescriptor;
this.fileName = fileName;
}
public long getRid() {
return rid;
}
public String getFileDescriptor() {
return fileDescriptor;
}
public String getFileName() {
return fileName;
}
}
public static class BindUpload {
private long rid;
private UploadFileCallback callback;
public BindUpload(long rid, UploadFileCallback callback) {
this.rid = rid;
this.callback = callback;
}
public long getRid() {
return rid;
}
public UploadFileCallback getCallback() {
return callback;
}
}
public static class UnbindUpload {
private long rid;
private UploadFileCallback callback;
public UnbindUpload(long rid, UploadFileCallback callback) {
this.rid = rid;
this.callback = callback;
}
public long getRid() {
return rid;
}
public UploadFileCallback getCallback() {
return callback;
}
}
public static class StopUpload {
private long rid;
public StopUpload(long rid) {
this.rid = rid;
}
public long getRid() {
return rid;
}
}
public static class UploadTaskError {
private long rid;
public UploadTaskError(long rid) {
this.rid = rid;
}
public long getRid() {
return rid;
}
}
public static class UploadTaskProgress {
private long rid;
private float progress;
public UploadTaskProgress(long rid, float progress) {
this.rid = rid;
this.progress = progress;
}
public long getRid() {
return rid;
}
public float getProgress() {
return progress;
}
}
public static class UploadTaskComplete {
private long rid;
private FileReference location;
private FileSystemReference reference;
public UploadTaskComplete(long rid, FileReference location, FileSystemReference reference) {
this.rid = rid;
this.location = location;
this.reference = reference;
}
public long getRid() {
return rid;
}
public FileSystemReference getReference() {
return reference;
}
public FileReference getLocation() {
return location;
}
}
public static class UploadCompleted {
private long rid;
private FileReference fileReference;
public UploadCompleted(long rid, FileReference fileReference) {
this.rid = rid;
this.fileReference = fileReference;
}
public long getRid() {
return rid;
}
public FileReference getFileReference() {
return fileReference;
}
}
public static class UploadError {
private long rid;
public UploadError(long rid) {
this.rid = rid;
}
public long getRid() {
return rid;
}
}
public static class RequestState {
private long rid;
private UploadFileCallback callback;
public RequestState(long rid, UploadFileCallback callback) {
this.rid = rid;
this.callback = callback;
}
public long getRid() {
return rid;
}
public UploadFileCallback getCallback() {
return callback;
}
}
public static class PauseUpload {
private long rid;
public PauseUpload(long rid) {
this.rid = rid;
}
public long getRid() {
return rid;
}
}
public static class ResumeUpload {
private long rid;
public ResumeUpload(long rid) {
this.rid = rid;
}
public long getRid() {
return rid;
}
}
//endregion
}