package com.kenny.openimgur.services;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import com.kenny.openimgur.R;
import com.kenny.openimgur.classes.ImgurPhoto;
import com.kenny.openimgur.classes.OpengurApp;
import com.kenny.openimgur.classes.VideoCache;
import com.kenny.openimgur.ui.BaseNotification;
import com.kenny.openimgur.util.FileUtil;
import com.kenny.openimgur.util.ImageUtil;
import com.kenny.openimgur.util.LinkUtils;
import com.kenny.openimgur.util.LogUtil;
import com.kenny.openimgur.util.NetworkUtils;
import com.kenny.openimgur.util.RequestCodes;
import java.io.File;
import java.util.ArrayList;
/**
* Created by kcampagna on 6/30/14.
*/
public class DownloaderService extends IntentService {
private static final String FOLDER_NAME = "Opengur";
private static final String TAG = DownloaderService.class.getSimpleName();
private static final String KEY_IMAGE_URLS = "image_urls";
public static Intent createIntent(@NonNull Context context, @NonNull String url) {
ArrayList<String> urls = new ArrayList<>(1);
urls.add(url);
return createIntent(context, urls);
}
public static Intent createIntent(@NonNull Context context, ArrayList<String> urls) {
return new Intent(context, DownloaderService.class).putExtra(DownloaderService.KEY_IMAGE_URLS, urls);
}
public DownloaderService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
// Get a wake lock so any long uploads will not be interrupted
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
wakeLock.acquire();
try {
Context context = getApplicationContext();
ArrayList<String> photoUrls = intent.getStringArrayListExtra(KEY_IMAGE_URLS);
if (photoUrls == null || photoUrls.isEmpty()) {
LogUtil.e(TAG, "Nothing was passed in to be downloaded");
return;
}
boolean isMultiUpload = photoUrls.size() > 1;
DownloadNotification notification = new DownloadNotification(context, photoUrls.size());
int count = 1;
int totalDownloaded = 0;
int totalPhotos = photoUrls.size();
// Make any needed folders
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), FOLDER_NAME);
file.mkdirs();
String directoryPath = file.getAbsolutePath();
LogUtil.v(TAG, "Downloading " + totalPhotos + " photos");
for (String url : photoUrls) {
File savedFile = saveUrl(url, directoryPath);
notification.updateMessage(count);
if (FileUtil.isFileValid(savedFile)) {
totalDownloaded++;
LogUtil.v(TAG, "Image download completed for URL " + url);
Uri fileUri = Uri.fromFile(savedFile);
// Let the system know we have a new file
FileUtil.scanFile(fileUri, context);
// Single image downloads will show multiple options and a preview in the notification
if (!isMultiUpload) {
Uri shareUri = FileProvider.getUriForFile(context, OpengurApp.AUTHORITY, savedFile);
String photoType = LinkUtils.getImageType(url);
boolean isVideoLink = savedFile.getAbsolutePath().endsWith(FileUtil.EXTENSION_MP4);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType(photoType);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
shareIntent.putExtra(Intent.EXTRA_STREAM, shareUri);
PendingIntent shareP = PendingIntent.getActivity(context, RequestCodes.DOWNLOAD_SHARE, Intent.createChooser(shareIntent, getString(R.string.share)), PendingIntent.FLAG_UPDATE_CURRENT);
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
viewIntent.setDataAndType(shareUri, isVideoLink ? "video/mp4" : photoType);
PendingIntent viewP = PendingIntent.getActivity(context, RequestCodes.DOWNLOAD_VIEW, viewIntent, PendingIntent.FLAG_ONE_SHOT);
Intent deleteIntent = NotificationReceiver.createDeleteIntent(context, notification.getNotificationId(), savedFile.getAbsolutePath());
PendingIntent deleteP = PendingIntent.getBroadcast(context, RequestCodes.DOWNLOAD_DELETE, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
// Get the correct preview image for the notification based on if it is a video or not
Bitmap bm = isVideoLink ? ImageUtil.toGrayScale(ThumbnailUtils.createVideoThumbnail(savedFile.getAbsolutePath(), MediaStore.Video.Thumbnails.MINI_KIND)) :
ImageUtil.toGrayScale(ImageUtil.decodeSampledBitmapFromResource(savedFile, 256, 256));
notification.onSingleImageDownloadComplete(bm, viewP, shareP, deleteP);
}
} else if (!isMultiUpload) {
notification.onError();
}
count++;
}
if (isMultiUpload) {
if (totalDownloaded == totalPhotos) {
LogUtil.v(TAG, "All photos downloaded successfully");
notification.onMultiImageDownloadComplete(totalDownloaded);
} else {
LogUtil.w(TAG, totalDownloaded + " of " + totalPhotos + " downloaded successfully");
notification.onMultiImageDownloadError(totalDownloaded, totalPhotos);
}
}
} catch (Exception e) {
LogUtil.e(TAG, "Exception while downloading image", e);
} finally {
NetworkUtils.releaseWakeLock(wakeLock);
}
}
@Nullable
private String getPhotoFileName(@Nullable String url) {
if (TextUtils.isEmpty(url)) return null;
String photoFileName;
String photoType = LinkUtils.getImageType(url);
String photoId = LinkUtils.getId(url);
if (TextUtils.isEmpty(photoId)) photoId = String.valueOf(System.currentTimeMillis());
if (ImgurPhoto.IMAGE_TYPE_JPEG.equals(photoType)) {
photoFileName = photoId + FileUtil.EXTENSION_JPEG;
} else if (ImgurPhoto.IMAGE_TYPE_GIF.equals(photoType)) {
photoFileName = photoId + FileUtil.EXTENSION_GIF;
} else if (LinkUtils.isVideoLink(url)) {
photoFileName = photoId + FileUtil.EXTENSION_MP4;
} else {
photoFileName = photoId + FileUtil.EXTENSION_PNG;
}
return photoFileName;
}
/**
* Attempts to save the url to the device
*
* @param url URL of the photo
* @param directoryPath The parent directory where the photo will be saved
* @return The {@link File} representing the photo. NULL may be returned if unsuccessful
*/
@Nullable
private File saveUrl(@NonNull String url, @NonNull String directoryPath) {
File photoFile = null;
String fileName = getPhotoFileName(url);
if (!TextUtils.isEmpty(fileName)) {
boolean isVideoLink = fileName.endsWith(FileUtil.EXTENSION_MP4);
photoFile = new File(directoryPath, fileName);
File cachedFile;
// Check if we already downloaded the image before
if (isVideoLink) {
cachedFile = VideoCache.getInstance().getVideoFile(url);
} else {
cachedFile = ImageUtil.getImageLoader(getApplicationContext()).getDiskCache().get(url);
}
if (FileUtil.isFileValid(cachedFile)) {
LogUtil.v(TAG, "Image present in cache, copying");
FileUtil.copyFile(cachedFile, photoFile);
} else {
LogUtil.v(TAG, "Downloading image to " + photoFile.getAbsolutePath());
FileUtil.saveUrl(url, photoFile);
}
}
return photoFile;
}
private static class DownloadNotification extends BaseNotification {
private int mId;
private int mNumPhotos;
public DownloadNotification(Context context, int numPhotos) {
super(context);
mNumPhotos = numPhotos;
mId = (int) System.currentTimeMillis();
if (numPhotos == 1) {
builder.setProgress(0, 0, false);
postNotification();
}
}
@NonNull
@Override
protected String getTitle() {
return resources.getQuantityString(R.plurals.download_notif_title, mNumPhotos);
}
@Override
protected int getNotificationId() {
return mId;
}
/**
* Updates the message for the notification. This should only be called when multiple images are being downloaded
*
* @param currentPhotoNumber The current photo number that is being downloaded
*/
public void updateMessage(int currentPhotoNumber) {
String message = resources.getString(R.string.download_notif_message, currentPhotoNumber, mNumPhotos);
builder.setProgress(mNumPhotos, currentPhotoNumber, false);
builder.setContentText(message);
postNotification();
}
/**
* Called when a single image has been downloaded. A single image will have a view and share action
*
* @param bitmap The {@link Bitmap} for display in the notification
* @param viewIntent The {@link PendingIntent} for viewing the image
* @param shareIntent The {@link PendingIntent} for sharing the image
*/
public void onSingleImageDownloadComplete(Bitmap bitmap, PendingIntent viewIntent, PendingIntent shareIntent, PendingIntent deleteIntent) {
String title = resources.getString(R.string.download_notif_complete);
if (bitmap != null) {
NotificationCompat.BigPictureStyle bigPicStyle = new NotificationCompat.BigPictureStyle();
bigPicStyle.setBigContentTitle(title);
bigPicStyle.setSummaryText(resources.getString(R.string.tap_to_view));
bigPicStyle.bigPicture(bitmap);
builder.setStyle(bigPicStyle)
.setLargeIcon(bitmap);
}
builder.setProgress(0, 0, false)
.setContentIntent(viewIntent)
.addAction(R.drawable.ic_share_24dp, resources.getString(R.string.share), shareIntent)
.addAction(R.drawable.ic_delete_24dp, resources.getString(R.string.delete), deleteIntent)
.setContentTitle(title)
.setContentText(resources.getString(R.string.tap_to_view));
postNotification();
}
/**
* Called when all images have successfully downloaded
*
* @param totalImages The total number of images downloaded
*/
public void onMultiImageDownloadComplete(int totalImages) {
builder.setProgress(0, 0, false)
.setContentTitle(resources.getString(R.string.download_notif_complete))
.setContentText(resources.getString(R.string.download_notif_multi_message, totalImages));
postNotification();
}
/**
* Called when not all images are downloaded successfully
*
* @param totalDownloaded The total number of images that were downloaded
* @param totalPhotos The total number of photos that were supposed to be downloaded
*/
public void onMultiImageDownloadError(int totalDownloaded, int totalPhotos) {
builder.setProgress(0, 0, false)
.setContentTitle(resources.getString(R.string.download_notif_error))
.setContentText(resources.getString(R.string.download_notif_multi_error_msg, totalDownloaded, totalPhotos));
postNotification();
}
public void onError() {
builder.setProgress(0, 0, false)
.setContentTitle(resources.getString(R.string.download_notif_error))
.setContentText(resources.getString(R.string.download_error));
postNotification();
}
}
}