package me.devsaki.hentoid.services;
import android.content.pm.PackageManager;
import android.webkit.CookieManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketTimeoutException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import me.devsaki.hentoid.HentoidApp;
import me.devsaki.hentoid.R;
import me.devsaki.hentoid.util.Consts;
import me.devsaki.hentoid.util.FileHelper;
import me.devsaki.hentoid.util.Helper;
import me.devsaki.hentoid.util.LogHelper;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* Created by Shiro on 3/28/2016.
* Handles image download tasks and batch operations
* Intended to have default access level for use with DownloadService class only
*/
final class ImageDownloadBatch {
private static final String TAG = LogHelper.makeLogTag(ImageDownloadBatch.class);
private static final int BUFFER_SIZE = 10 * 1024;
private static final CookieManager cookieManager = CookieManager.getInstance();
private static OkHttpClient client = new OkHttpClient();
private final Semaphore semaphore = new Semaphore(0);
private boolean hasError = false;
private short errorCount = 0;
void newTask(final File dir, final String filename, final String url) {
String cookie = cookieManager.getCookie(url);
if (cookie == null || cookie.isEmpty()) {
cookie = Helper.getSessionCookie();
}
String userAgent;
try {
userAgent = Helper.getAppUserAgent(HentoidApp.getAppContext());
} catch (PackageManager.NameNotFoundException e) {
userAgent = Consts.USER_AGENT;
}
Request request = new Request.Builder()
.url(url)
.addHeader("User-Agent", userAgent)
.addHeader("Cookie", cookie)
.build();
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
client.newCall(request)
.enqueue(new Callback(dir, filename));
}
void waitForOneCompletedTask() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
LogHelper.e(TAG, e, "Interrupt while waiting on download task completion");
}
}
void cancelAllTasks() {
client.dispatcher().cancelAll();
}
boolean hasError() {
return hasError;
}
short getErrorCount() {
return errorCount;
}
private void downloadHandler(File file, Response response) throws IOException {
boolean npeError = false;
OutputStream output = null;
final InputStream input = response.body().byteStream();
final byte[] buffer = new byte[BUFFER_SIZE];
try {
output = FileHelper.getOutputStream(file);
int dataLength;
while ((dataLength = input.read(buffer, 0, BUFFER_SIZE)) != -1) {
output.write(buffer, 0, dataLength);
}
FileHelper.sync(output);
output.flush();
} catch (NullPointerException npe) {
Helper.toast(R.string.sd_access_error);
npeError = true;
} catch (IOException e) {
if (!file.delete()) {
LogHelper.e(TAG, e, "Failed to delete file: " + file.getAbsolutePath());
}
throw e;
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
// Ignore
}
}
try {
input.close();
response.close();
} catch (IOException e) {
// Ignore
}
if (npeError) {
LogHelper.d(TAG, "NPE on file: " + file.getAbsolutePath());
Helper.toast(R.string.sd_access_error);
}
}
}
private class Callback implements okhttp3.Callback {
private final File dir;
private final String filename;
private Callback(final File dir, final String filename) {
this.dir = dir;
this.filename = filename;
}
@Override
public void onFailure(Call call, IOException e) {
LogHelper.e(TAG, e, "Error downloading image: " + call.request().url());
if (e instanceof SocketTimeoutException) {
LogHelper.w(TAG, e, "Socket Timeout Exception!");
// TODO: Handle this somehow
semaphore.release(); // <-- Requires testing~
}
hasError = true;
synchronized (ImageDownloadBatch.this) {
errorCount++;
}
}
@Override
public void onResponse(Call call, Response response) throws IOException {
LogHelper.d(TAG, "Start downloading image: " + call.request().url());
if (!response.isSuccessful()) {
LogHelper.w(TAG, "Unexpected http status code: " + response.code());
}
final File file;
switch (response.header("Content-Type")) {
case "image/png":
file = new File(dir, filename + ".png");
break;
case "image/gif":
file = new File(dir, filename + ".gif");
break;
default:
file = new File(dir, filename + ".jpg");
break;
}
long fileSize = file.length();
if (file.exists()) {
if (fileSize == 0) {
// Carry on~
LogHelper.d(TAG, "Empty File!!");
} else {
LogHelper.d(TAG, "Existing file size: " + fileSize);
// TODO: Compare file sizes (without eating the response body)
}
}
downloadHandler(file, response);
semaphore.release();
LogHelper.d(TAG, "Done downloading image: " + call.request().url());
}
}
}