package com.tomclaw.mandarin.im.icq;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import com.tomclaw.mandarin.R;
import com.tomclaw.mandarin.core.BitmapCache;
import com.tomclaw.mandarin.core.GlobalProvider;
import com.tomclaw.mandarin.core.NotifiableDownloadRequest;
import com.tomclaw.mandarin.core.PreferenceHelper;
import com.tomclaw.mandarin.core.QueryHelper;
import com.tomclaw.mandarin.core.exceptions.DownloadCancelledException;
import com.tomclaw.mandarin.core.exceptions.DownloadException;
import com.tomclaw.mandarin.core.exceptions.MessageNotFoundException;
import com.tomclaw.mandarin.main.ChatActivity;
import com.tomclaw.mandarin.main.MainActivity;
import com.tomclaw.mandarin.util.FileHelper;
import com.tomclaw.mandarin.util.HttpUtil;
import com.tomclaw.mandarin.util.Logger;
import com.tomclaw.mandarin.util.StringUtil;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* Created by solkin on 30.10.14.
*/
public class IcqFileDownloadRequest extends NotifiableDownloadRequest<IcqAccountRoot> {
private static final transient int MAX_META_TRY_COUNT = 5;
private final String buddyId;
private final String cookie;
private final long time;
private final String fileId;
private final String fileUrl;
private final String originalMessage;
private String tag;
private boolean isFirstAttempt = false;
private String previewHash = "";
private String cachedFileName;
private transient long fileSize;
private transient File storeFile;
private transient int metaTryCount;
public IcqFileDownloadRequest(String buddyId, String cookie, long time, String fileId,
String fileUrl, String originalMessage, String tag) {
this.buddyId = buddyId;
this.cookie = cookie;
this.time = time;
this.fileId = fileId;
this.fileUrl = fileUrl;
this.originalMessage = originalMessage;
this.tag = tag;
this.isFirstAttempt = true;
}
@Override
public String getUrl() throws Throwable {
String fileInfoUrl = "http://files.icq.net/get/" + fileId + "?json=1&meta=1";
HttpURLConnection connection = (HttpURLConnection) new URL(fileInfoUrl).openConnection();
String response = HttpUtil.streamToString(HttpUtil.executeGet(connection));
Logger.log(response);
JSONObject rootObject = new JSONObject(response);
int fileCount = rootObject.getInt("file_count");
JSONArray fileList = rootObject.getJSONArray("file_list");
if (fileCount > 0 && fileList.length() > 0) {
// No multi-file support. It's really
// rare case and a lot of strange logic.
JSONObject file = fileList.getJSONObject(0);
fileSize = file.getLong("filesize");
String fileName = file.getString("filename");
String downloadLink = file.getString("dlink");
int isPreviewable = file.getInt("is_previewable");
String previewUrl = file.optString("static600");
String mimeType = file.getString("mime");
// Checking for download link is empty because file is not ready yet.
if (TextUtils.isEmpty(downloadLink)) {
Thread.sleep(1000);
return getUrl();
}
// Downloading preview.
if (isPreviewable == 1) {
// Check for preview is ready now or try again after short delay.
if (TextUtils.isEmpty(previewUrl)) {
if (metaTryCount < MAX_META_TRY_COUNT) {
// No preview this time. Sleep a while and try again.
metaTryCount++;
Thread.sleep(1000);
return getUrl();
}
} else {
// For the first attempt we must download and store preview.
if (isFirstAttempt) {
// Preview Url is ready - let's download it and cache.
Bitmap previewBitmap = getPreviewBitmap(previewUrl);
if (previewBitmap != null) {
previewHash = HttpUtil.getUrlHash(previewUrl);
saveBitmap(previewBitmap, previewHash);
}
}
}
}
// Saving obtained data to the history table.
if (TextUtils.isEmpty(cachedFileName)) {
// This is probable first attempt to download file.
// Create new file.
storeFile = getUniqueFile(mimeType, fileName);
cachedFileName = storeFile.getName();
} else {
// Continue downloading to existing file.
storeFile = new File(getStoragePublicFolder(mimeType), cachedFileName);
}
// Make directories for this path.
storeFile.getParentFile().mkdirs();
int buddyDbId = QueryHelper.getBuddyDbId(getAccountRoot().getContentResolver(),
getAccountRoot().getAccountDbId(), buddyId);
int contentType = getContentType(mimeType);
Uri uri = Uri.fromFile(storeFile);
QueryHelper.insertIncomingFileMessage(getAccountRoot().getContentResolver(), buddyDbId, cookie,
time, getUrlMessage(), uri, fileName, contentType, fileSize, previewHash, tag);
// Check to download file now.
if (!isStartDownload(isFirstAttempt, fileSize)) {
// All other attempts will be manual.
isFirstAttempt = false;
throw new DownloadCancelledException();
}
return downloadLink;
} else {
QueryHelper.insertMessage(getAccountRoot().getContentResolver(),
PreferenceHelper.isCollapseMessages(getAccountRoot().getContext()),
getAccountRoot().getAccountDbId(), buddyId, GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING,
GlobalProvider.HISTORY_MESSAGE_STATE_UNDETERMINED, cookie, time, originalMessage);
throw new DownloadException();
}
}
private File getUniqueFile(String mimeType, String fileName) {
final String base = FileHelper.getFileBaseFromName(fileName);
final String extension = FileHelper.getFileExtensionFromPath(fileName);
File directory = getStoragePublicFolder(mimeType);
if (directory.exists()) {
File[] files = directory.listFiles(new FilenameFilter() {
public boolean accept(File file, String name) {
return FileHelper.getFileBaseFromName(name).toLowerCase().startsWith(base.toLowerCase()) &&
FileHelper.getFileExtensionFromPath(name).toLowerCase().equals(extension.toLowerCase());
}
});
if (files.length > 0) {
fileName = base + "-" + files.length + "." + extension;
}
}
return new File(directory, fileName);
}
private File getStoragePublicFolder(String mimeType) {
return Environment.getExternalStoragePublicDirectory(getStorageFolder(mimeType));
}
private Bitmap getPreviewBitmap(String url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
return BitmapFactory.decodeStream(HttpUtil.executeGet(urlConnection));
}
private boolean saveBitmap(Bitmap bitmap, String hash) {
return BitmapCache.getInstance().saveBitmapSync(hash, bitmap);
}
private String getStorageFolder(String mimeType) {
if (mimeType.startsWith("image")) {
return Environment.DIRECTORY_PICTURES;
} else if (mimeType.startsWith("video")) {
return Environment.DIRECTORY_MOVIES;
} else if (mimeType.startsWith("audio")) {
return Environment.DIRECTORY_MUSIC;
} else {
return Environment.DIRECTORY_DOWNLOADS;
}
}
public int getContentType(String mimeType) {
if (mimeType.startsWith("image")) {
return GlobalProvider.HISTORY_CONTENT_TYPE_PICTURE;
} else if (mimeType.startsWith("video")) {
return GlobalProvider.HISTORY_CONTENT_TYPE_VIDEO;
} else {
return GlobalProvider.HISTORY_CONTENT_TYPE_FILE;
}
}
@Override
public long getSize() {
return fileSize;
}
@Override
public FileOutputStream getOutputStream() throws FileNotFoundException {
return new FileOutputStream(storeFile);
}
@Override
protected PendingIntent getIntent() {
// Show chat activity with concrete buddy.
Context context = getAccountRoot().getContext();
try {
int buddyDbId = QueryHelper.getBuddyDbId(context.getContentResolver(),
getAccountRoot().getAccountDbId(), buddyId);
return PendingIntent.getActivity(context, 0,
new Intent(context, ChatActivity.class)
.putExtra(GlobalProvider.HISTORY_BUDDY_DB_ID, buddyDbId)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP),
PendingIntent.FLAG_CANCEL_CURRENT);
} catch (Throwable ignored) {
// No such buddy?!
// Okay, open chats at least.
return PendingIntent.getActivity(context, 0,
new Intent(context, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP),
PendingIntent.FLAG_CANCEL_CURRENT);
}
}
@Override
protected String getDescription() {
String buddyNick = "";
Context context = getAccountRoot().getContext();
try {
int buddyDbId = QueryHelper.getBuddyDbId(context.getContentResolver(),
getAccountRoot().getAccountDbId(), buddyId);
buddyNick = QueryHelper.getBuddyNick(context.getContentResolver(), buddyDbId);
} catch (Throwable ignored) {
}
if (TextUtils.isEmpty(buddyNick)) {
buddyNick = buddyId;
}
return context.getString(R.string.file_from, buddyNick);
}
@Override
protected void onStartedDelegate() throws DownloadCancelledException, DownloadException {
try {
int contentState = QueryHelper.getFileState(getAccountRoot().getContentResolver(),
GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING, cookie);
if (contentState == GlobalProvider.HISTORY_CONTENT_STATE_INTERRUPT) {
throw new DownloadCancelledException();
}
} catch (MessageNotFoundException ignored) {
// This may be if this is first request call and no message inserted yet.
}
}
@Override
protected void onDownload() {
QueryHelper.updateFileState(getAccountRoot().getContentResolver(),
GlobalProvider.HISTORY_CONTENT_STATE_RUNNING, GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING, cookie);
}
@Override
protected long getProgressStepDelay() {
return 1000;
}
@Override
protected void onProgressUpdated(int progress) {
QueryHelper.updateFileProgress(getAccountRoot().getContentResolver(), progress,
GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING, cookie);
}
@Override
protected void onSuccessDelegate() {
QueryHelper.updateFileStateAndText(getAccountRoot().getContentResolver(),
GlobalProvider.HISTORY_CONTENT_STATE_STABLE, getUrlMessage(),
GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING, cookie);
// Update file in system gallery.
MediaScannerConnection.scanFile(getAccountRoot().getContext(), new String[]{storeFile.getPath()}, null, null);
}
@Override
protected void onFailDelegate() {
QueryHelper.revertFileToMessage(getAccountRoot().getContentResolver(),
GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING, originalMessage, cookie);
// Checking for file is being created.
if (storeFile != null) {
// Remove file fragment.
storeFile.delete();
}
}
@Override
protected void onCancelDelegate() {
// Update message to be in waiting state.
QueryHelper.updateFileState(getAccountRoot().getContentResolver(),
GlobalProvider.HISTORY_CONTENT_STATE_STOPPED, GlobalProvider.HISTORY_MESSAGE_TYPE_INCOMING, cookie);
}
@Override
protected void onPendingDelegate() {
}
private String getUrlMessage() {
return storeFile.getName() + " (" + StringUtil.formatBytes(getAccountRoot().getResources(), fileSize) + ")"
+ "\n" + fileUrl;
}
}