/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.scratchconverter.protocol;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.util.Log;
import com.google.android.gms.common.images.WebImage;
import com.google.common.base.Preconditions;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.scratchconverter.Client;
import org.catrobat.catroid.scratchconverter.ClientException;
import org.catrobat.catroid.scratchconverter.protocol.Job.State;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobAlreadyRunningMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobFailedMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobFinishedMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobOutputMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobProgressMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobReadyMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.job.JobRunningMessage;
public class JobHandler implements Client.DownloadCallback {
private static final String TAG = JobHandler.class.getSimpleName();
private final Job job;
private Client.ConvertCallback callback;
public JobHandler(@NonNull final Job job, @NonNull final Client.ConvertCallback callback) {
Preconditions.checkArgument(job != null);
this.job = job;
this.callback = callback;
}
public boolean isInProgress() {
return job.isInProgress();
}
public void onJobScheduled() {
Log.d(TAG, "Setting job as scheduled (jobID: " + job.getJobID() + ")");
job.setState(State.SCHEDULED);
callback.onJobScheduled(job);
}
@Override
public void onDownloadStarted(final String url) {
Log.d(TAG, "Download started - Job ID is: " + job.getJobID());
job.setDownloadState(Job.DownloadState.DOWNLOADING);
job.setState(State.FINISHED);
}
@Override
public void onDownloadProgress(short progress, String url) {
}
@Override
public void onDownloadFinished(final String programName, final String url) {
Log.d(TAG, "Download finished - Resetting job with ID: " + job.getJobID());
job.setDownloadState(Job.DownloadState.DOWNLOADED);
job.setState(State.FINISHED);
}
@Override
public void onUserCanceledDownload(final String url) {
Log.d(TAG, "User canceled download - Resetting job with ID: " + job.getJobID());
job.setDownloadState(Job.DownloadState.CANCELED);
job.setState(State.FINISHED);
}
public void onUserCanceledConversion() {
Log.d(TAG, "User canceled conversion - Resetting job with ID: " + job.getJobID());
job.setState(State.FINISHED);
}
public Job getJob() {
return job;
}
public long getJobID() {
return job.getJobID();
}
public void setCallback(@NonNull Client.ConvertCallback callback) {
this.callback = callback;
}
public void onJobMessage(final JobMessage jobMessage) {
Preconditions.checkArgument(job.getJobID() == jobMessage.getJobID());
Preconditions.checkState(job.getState().isInProgress());
switch (job.getState()) {
case SCHEDULED:
if (jobMessage instanceof JobReadyMessage) {
handleJobReadyMessage((JobReadyMessage) jobMessage);
return;
} else if (jobMessage instanceof JobAlreadyRunningMessage) {
handleJobAlreadyRunningMessage((JobAlreadyRunningMessage) jobMessage);
return;
} else if (jobMessage instanceof JobFinishedMessage) {
handleJobFinishedMessage((JobFinishedMessage) jobMessage);
return;
} else if (jobMessage instanceof JobFailedMessage) {
handleJobFailedMessage((JobFailedMessage) jobMessage);
return;
}
break;
case READY:
if (jobMessage instanceof JobRunningMessage) {
handleJobRunningMessage((JobRunningMessage) jobMessage);
return;
}
break;
case RUNNING:
if (jobMessage instanceof JobProgressMessage) {
handleJobProgressMessage((JobProgressMessage) jobMessage);
return;
} else if (jobMessage instanceof JobOutputMessage) {
handleJobOutputMessage((JobOutputMessage) jobMessage);
return;
} else if (jobMessage instanceof JobFinishedMessage) {
handleJobFinishedMessage((JobFinishedMessage) jobMessage);
return;
} else if (jobMessage instanceof JobFailedMessage) {
handleJobFailedMessage((JobFailedMessage) jobMessage);
return;
}
break;
}
Log.w(TAG, "Unable to handle message of type in current state " + job.getState());
}
private void handleJobReadyMessage(@NonNull final JobReadyMessage jobReadyMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobReadyMessage.getJobID());
Preconditions.checkState(job.getState() == State.SCHEDULED);
job.setState(Job.State.READY);
callback.onConversionReady(job);
}
private void handleJobAlreadyRunningMessage(@NonNull final JobAlreadyRunningMessage jobAlreadyRunningMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobAlreadyRunningMessage.getJobID());
Preconditions.checkState(job.getState() == State.SCHEDULED);
job.setState(Job.State.READY);
final long jobID = jobAlreadyRunningMessage.getJobID();
final String jobTitle = jobAlreadyRunningMessage.getJobTitle();
final String jobImageURL = jobAlreadyRunningMessage.getJobImageURL();
handleJobRunningMessage(new JobRunningMessage(jobID, jobTitle, jobImageURL));
}
private void handleJobRunningMessage(@NonNull final JobRunningMessage jobRunningMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobRunningMessage.getJobID());
Preconditions.checkState(job.getState() == State.READY);
job.setTitle(jobRunningMessage.getJobTitle());
final String jobImageURL = jobRunningMessage.getJobImageURL();
if (jobImageURL != null) {
final int[] imageSize = new int[] { Constants.SCRATCH_IMAGE_DEFAULT_WIDTH, Constants.SCRATCH_IMAGE_DEFAULT_HEIGHT };
job.setImage(new WebImage(Uri.parse(jobImageURL), imageSize[0], imageSize[1]));
}
job.setState(Job.State.RUNNING);
callback.onConversionStart(job);
}
private void handleJobProgressMessage(@NonNull final JobProgressMessage jobProgressMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobProgressMessage.getJobID());
Preconditions.checkState(job.getState() == State.RUNNING);
job.setProgress(jobProgressMessage.getProgress());
callback.onJobProgress(job, jobProgressMessage.getProgress());
}
private void handleJobOutputMessage(@NonNull final JobOutputMessage jobOutputMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobOutputMessage.getJobID());
Preconditions.checkState(job.getState() == State.RUNNING);
final String[] lines = jobOutputMessage.getLines();
for (String line : lines) {
Log.d(TAG, line);
}
callback.onJobOutput(job, lines);
}
private void handleJobFinishedMessage(@NonNull final JobFinishedMessage jobFinishedMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobFinishedMessage.getJobID());
Preconditions.checkState(job.getState() == State.SCHEDULED || job.getState() == State.RUNNING);
job.setState(State.FINISHED);
job.setDownloadURL(jobFinishedMessage.getDownloadURL());
callback.onConversionFinished(job, this, jobFinishedMessage.getDownloadURL(),
jobFinishedMessage.getCachedDate());
}
private void handleJobFailedMessage(@NonNull final JobFailedMessage jobFailedMessage) {
Preconditions.checkArgument(getJob().getJobID() == jobFailedMessage.getJobID());
Preconditions.checkState(job.getState() == State.SCHEDULED || job.getState() == State.RUNNING);
job.setState(Job.State.FAILED);
callback.onConversionFailure(job, new ClientException("Job failed - Reason: " + jobFailedMessage.getMessage()));
}
}