mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 06:37:30 +00:00
Convert DownloadWorker to kotlin
This commit is contained in:
parent
9e65ee9d27
commit
5756f055d9
@ -172,7 +172,6 @@ dependencies {
|
|||||||
implementation "androidx.navigation:navigation-ui:$nav_version"
|
implementation "androidx.navigation:navigation-ui:$nav_version"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||||
implementation "androidx.preference:preference:1.1.1"
|
implementation "androidx.preference:preference:1.1.1"
|
||||||
implementation "androidx.work:work-runtime:2.5.0"
|
|
||||||
implementation 'androidx.palette:palette:1.0.0'
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
|
|
||||||
implementation 'com.google.guava:guava:27.0.1-android'
|
implementation 'com.google.guava:guava:27.0.1-android'
|
||||||
@ -204,6 +203,11 @@ dependencies {
|
|||||||
implementation "androidx.emoji:emoji:$emoji_compat_version"
|
implementation "androidx.emoji:emoji:$emoji_compat_version"
|
||||||
implementation "androidx.emoji:emoji-appcompat:$emoji_compat_version"
|
implementation "androidx.emoji:emoji-appcompat:$emoji_compat_version"
|
||||||
|
|
||||||
|
// Work
|
||||||
|
def work_version = '2.5.0'
|
||||||
|
implementation "androidx.work:work-runtime:$work_version"
|
||||||
|
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||||
|
|
||||||
implementation 'com.facebook.fresco:fresco:2.3.0'
|
implementation 'com.facebook.fresco:fresco:2.3.0'
|
||||||
implementation 'com.facebook.fresco:animated-webp:2.3.0'
|
implementation 'com.facebook.fresco:animated-webp:2.3.0'
|
||||||
implementation 'com.facebook.fresco:webpsupport:2.3.0'
|
implementation 'com.facebook.fresco:webpsupport:2.3.0'
|
||||||
|
@ -1,399 +1,383 @@
|
|||||||
package awais.instagrabber.workers;
|
package awais.instagrabber.workers
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver
|
||||||
import android.content.Context;
|
import android.content.Context
|
||||||
import android.content.Intent;
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap
|
||||||
import android.media.MediaMetadataRetriever;
|
import android.media.MediaMetadataRetriever
|
||||||
import android.media.MediaScannerConnection;
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri;
|
import android.net.Uri
|
||||||
import android.os.Build;
|
import android.os.Build
|
||||||
import android.os.Handler;
|
import android.os.Handler
|
||||||
import android.os.Looper;
|
import android.os.Looper
|
||||||
import android.util.Log;
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import awais.instagrabber.BuildConfig
|
||||||
|
import awais.instagrabber.R
|
||||||
|
import awais.instagrabber.services.DeleteImageIntentService
|
||||||
|
import awais.instagrabber.utils.BitmapUtils
|
||||||
|
import awais.instagrabber.utils.Constants.DOWNLOAD_CHANNEL_ID
|
||||||
|
import awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME
|
||||||
|
import awais.instagrabber.utils.DownloadUtils
|
||||||
|
import awais.instagrabber.utils.TextUtils.isEmpty
|
||||||
|
import awais.instagrabber.utils.Utils
|
||||||
|
import awais.instagrabber.utils.extensions.TAG
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.apache.commons.imaging.formats.jpeg.iptc.JpegIptcRewriter
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.ExecutionException
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
class DownloadWorker(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {
|
||||||
import androidx.annotation.Nullable;
|
private val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(context)
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
import androidx.work.Data;
|
|
||||||
import androidx.work.ForegroundInfo;
|
|
||||||
import androidx.work.Worker;
|
|
||||||
import androidx.work.WorkerParameters;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
override suspend fun doWork(): Result {
|
||||||
import com.google.gson.JsonSyntaxException;
|
val downloadRequestFilePath = inputData.getString(KEY_DOWNLOAD_REQUEST_JSON)
|
||||||
|
if (downloadRequestFilePath.isNullOrBlank()) {
|
||||||
import org.apache.commons.imaging.formats.jpeg.iptc.JpegIptcRewriter;
|
return Result.failure(Data.Builder()
|
||||||
|
.putString("error", "downloadRequest is empty or null")
|
||||||
import java.io.BufferedInputStream;
|
.build())
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import awais.instagrabber.BuildConfig;
|
|
||||||
import awais.instagrabber.R;
|
|
||||||
import awais.instagrabber.services.DeleteImageIntentService;
|
|
||||||
import awais.instagrabber.utils.BitmapUtils;
|
|
||||||
import awais.instagrabber.utils.Constants;
|
|
||||||
import awais.instagrabber.utils.DownloadUtils;
|
|
||||||
import awais.instagrabber.utils.TextUtils;
|
|
||||||
import awais.instagrabber.utils.Utils;
|
|
||||||
|
|
||||||
import static awais.instagrabber.utils.BitmapUtils.THUMBNAIL_SIZE;
|
|
||||||
import static awais.instagrabber.utils.Constants.DOWNLOAD_CHANNEL_ID;
|
|
||||||
import static awais.instagrabber.utils.Constants.NOTIF_GROUP_NAME;
|
|
||||||
|
|
||||||
public class DownloadWorker extends Worker {
|
|
||||||
private static final String TAG = "DownloadWorker";
|
|
||||||
private static final String DOWNLOAD_GROUP = "DOWNLOAD_GROUP";
|
|
||||||
|
|
||||||
public static final String PROGRESS = "PROGRESS";
|
|
||||||
public static final String URL = "URL";
|
|
||||||
public static final String KEY_DOWNLOAD_REQUEST_JSON = "download_request_json";
|
|
||||||
public static final int DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE = 2020;
|
|
||||||
public static final int DELETE_IMAGE_REQUEST_CODE = 2030;
|
|
||||||
|
|
||||||
private final NotificationManagerCompat notificationManager;
|
|
||||||
|
|
||||||
public DownloadWorker(@NonNull final Context context, @NonNull final WorkerParameters workerParams) {
|
|
||||||
super(context, workerParams);
|
|
||||||
notificationManager = NotificationManagerCompat.from(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Result doWork() {
|
|
||||||
final String downloadRequestFilePath = getInputData().getString(KEY_DOWNLOAD_REQUEST_JSON);
|
|
||||||
if (TextUtils.isEmpty(downloadRequestFilePath)) {
|
|
||||||
return Result.failure(new Data.Builder()
|
|
||||||
.putString("error", "downloadRequest is empty or null")
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
final String downloadRequestString;
|
val downloadRequestString: String
|
||||||
final File requestFile = new File(downloadRequestFilePath);
|
val requestFile = File(downloadRequestFilePath)
|
||||||
try (Scanner scanner = new Scanner(requestFile)) {
|
|
||||||
downloadRequestString = scanner.useDelimiter("\\A").next();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "doWork: ", e);
|
|
||||||
return Result.failure(new Data.Builder()
|
|
||||||
.putString("error", e.getLocalizedMessage())
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
if (TextUtils.isEmpty(downloadRequestString)) {
|
|
||||||
return Result.failure(new Data.Builder()
|
|
||||||
.putString("error", "downloadRequest is empty or null")
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
final DownloadRequest downloadRequest;
|
|
||||||
try {
|
try {
|
||||||
downloadRequest = new Gson().fromJson(downloadRequestString, DownloadRequest.class);
|
downloadRequestString = requestFile.bufferedReader().use { it.readText() }
|
||||||
} catch (JsonSyntaxException e) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "doWork", e);
|
Log.e(TAG, "doWork: ", e)
|
||||||
return Result.failure(new Data.Builder()
|
return Result.failure(Data.Builder()
|
||||||
.putString("error", e.getLocalizedMessage())
|
.putString("error", e.localizedMessage)
|
||||||
.build());
|
.build())
|
||||||
}
|
}
|
||||||
if (downloadRequest == null) {
|
if (downloadRequestString.isBlank()) {
|
||||||
return Result.failure(new Data.Builder()
|
return Result.failure(Data.Builder()
|
||||||
.putString("error", "downloadRequest is null")
|
.putString("error", "downloadRequest is empty")
|
||||||
.build());
|
.build())
|
||||||
}
|
}
|
||||||
final Map<String, String> urlToFilePathMap = downloadRequest.getUrlToFilePathMap();
|
val downloadRequest: DownloadRequest = try {
|
||||||
download(urlToFilePathMap);
|
Gson().fromJson(downloadRequestString, DownloadRequest::class.java)
|
||||||
new Handler(Looper.getMainLooper()).postDelayed(() -> showSummary(urlToFilePathMap), 500);
|
} catch (e: JsonSyntaxException) {
|
||||||
final boolean deleted = requestFile.delete();
|
Log.e(TAG, "doWork", e)
|
||||||
|
return Result.failure(Data.Builder()
|
||||||
|
.putString("error", e.localizedMessage)
|
||||||
|
.build())
|
||||||
|
} ?: return Result.failure(Data.Builder()
|
||||||
|
.putString("error", "downloadRequest is null")
|
||||||
|
.build())
|
||||||
|
val urlToFilePathMap = downloadRequest.urlToFilePathMap
|
||||||
|
download(urlToFilePathMap)
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({ showSummary(urlToFilePathMap) }, 500)
|
||||||
|
val deleted = requestFile.delete()
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
Log.w(TAG, "doWork: requestFile not deleted!");
|
Log.w(TAG, "doWork: requestFile not deleted!")
|
||||||
}
|
}
|
||||||
return Result.success();
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void download(final Map<String, String> urlToFilePathMap) {
|
private suspend fun download(urlToFilePathMap: Map<String, String>) {
|
||||||
final int notificationId = getNotificationId();
|
val notificationId = notificationId
|
||||||
final Set<Map.Entry<String, String>> entries = urlToFilePathMap.entrySet();
|
val entries = urlToFilePathMap.entries
|
||||||
int count = 1;
|
var count = 1
|
||||||
final int total = urlToFilePathMap.size();
|
val total = urlToFilePathMap.size
|
||||||
for (final Map.Entry<String, String> urlAndFilePath : entries) {
|
for ((url, value) in entries) {
|
||||||
final String url = urlAndFilePath.getKey();
|
updateDownloadProgress(notificationId, count, total, 0f)
|
||||||
updateDownloadProgress(notificationId, count, total, 0);
|
withContext(Dispatchers.IO) {
|
||||||
download(notificationId, count, total, url, urlAndFilePath.getValue());
|
download(notificationId, count, total, url, value)
|
||||||
count++;
|
}
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getNotificationId() {
|
private val notificationId: Int
|
||||||
return Math.abs(getId().hashCode());
|
get() = abs(id.hashCode())
|
||||||
}
|
|
||||||
|
|
||||||
private void download(final int notificationId,
|
private fun download(
|
||||||
final int position,
|
notificationId: Int,
|
||||||
final int total,
|
position: Int,
|
||||||
final String url,
|
total: Int,
|
||||||
final String filePath) {
|
url: String,
|
||||||
final boolean isJpg = filePath.endsWith("jpg");
|
filePath: String,
|
||||||
|
) {
|
||||||
|
val isJpg = filePath.endsWith("jpg")
|
||||||
// using temp file approach to remove IPTC so that download progress can be reported
|
// using temp file approach to remove IPTC so that download progress can be reported
|
||||||
final File outFile = isJpg ? DownloadUtils.getTempFile() : new File(filePath);
|
val outFile = if (isJpg) DownloadUtils.getTempFile() else File(filePath)
|
||||||
try {
|
try {
|
||||||
final URLConnection urlConnection = new URL(url).openConnection();
|
val urlConnection = URL(url).openConnection()
|
||||||
final long fileSize = Build.VERSION.SDK_INT >= 24 ? urlConnection.getContentLengthLong() :
|
val fileSize = if (Build.VERSION.SDK_INT >= 24) urlConnection.contentLengthLong else urlConnection.contentLength.toLong()
|
||||||
urlConnection.getContentLength();
|
var totalRead = 0f
|
||||||
float totalRead = 0;
|
try {
|
||||||
try (final BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
|
BufferedInputStream(urlConnection.getInputStream()).use { bis ->
|
||||||
final FileOutputStream fos = new FileOutputStream(outFile)) {
|
FileOutputStream(outFile).use { fos ->
|
||||||
final byte[] buffer = new byte[0x2000];
|
val buffer = ByteArray(0x2000)
|
||||||
int count;
|
var count: Int
|
||||||
while ((count = bis.read(buffer, 0, 0x2000)) != -1) {
|
while (bis.read(buffer, 0, 0x2000).also { count = it } != -1) {
|
||||||
totalRead = totalRead + count;
|
totalRead += count
|
||||||
fos.write(buffer, 0, count);
|
fos.write(buffer, 0, count)
|
||||||
setProgressAsync(new Data.Builder().putString(URL, url)
|
setProgressAsync(Data.Builder().putString(URL, url)
|
||||||
.putFloat(PROGRESS, totalRead * 100f / fileSize)
|
.putFloat(PROGRESS, totalRead * 100f / fileSize)
|
||||||
.build());
|
.build())
|
||||||
updateDownloadProgress(notificationId, position, total, totalRead * 100f / fileSize);
|
updateDownloadProgress(notificationId, position, total, totalRead * 100f / fileSize)
|
||||||
|
}
|
||||||
|
fos.flush()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fos.flush();
|
} catch (e: Exception) {
|
||||||
} catch (final Exception e) {
|
Log.e(TAG, "Error while writing data from url: " + url + " to file: " + outFile.absolutePath, e)
|
||||||
Log.e(TAG, "Error while writing data from url: " + url + " to file: " + outFile.getAbsolutePath(), e);
|
|
||||||
}
|
}
|
||||||
if (isJpg) {
|
if (isJpg) {
|
||||||
final File finalFile = new File(filePath);
|
val finalFile = File(filePath)
|
||||||
try (FileInputStream fis = new FileInputStream(outFile);
|
try {
|
||||||
FileOutputStream fos = new FileOutputStream(finalFile)) {
|
FileInputStream(outFile).use { fis ->
|
||||||
final JpegIptcRewriter jpegIptcRewriter = new JpegIptcRewriter();
|
FileOutputStream(finalFile).use { fos ->
|
||||||
jpegIptcRewriter.removeIPTC(fis, fos);
|
val jpegIptcRewriter = JpegIptcRewriter()
|
||||||
} catch (Exception e) {
|
jpegIptcRewriter.removeIPTC(fis, fos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error while removing iptc: url: " + url
|
Log.e(TAG, "Error while removing iptc: url: " + url
|
||||||
+ ", tempFile: " + outFile.getAbsolutePath()
|
+ ", tempFile: " + outFile.absolutePath
|
||||||
+ ", finalFile: " + finalFile.getAbsolutePath(), e);
|
+ ", finalFile: " + finalFile.absolutePath, e)
|
||||||
}
|
}
|
||||||
final boolean deleted = outFile.delete();
|
val deleted = outFile.delete()
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
Log.w(TAG, "download: tempFile not deleted!");
|
Log.w(TAG, "download: tempFile not deleted!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error while downloading: " + url, e);
|
Log.e(TAG, "Error while downloading: $url", e)
|
||||||
}
|
}
|
||||||
setProgressAsync(new Data.Builder().putString(URL, url)
|
setProgressAsync(Data.Builder().putString(URL, url)
|
||||||
.putFloat(PROGRESS, 100)
|
.putFloat(PROGRESS, 100f)
|
||||||
.build());
|
.build())
|
||||||
updateDownloadProgress(notificationId, position, total, 100);
|
updateDownloadProgress(notificationId, position, total, 100f)
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDownloadProgress(final int notificationId,
|
private fun updateDownloadProgress(
|
||||||
final int position,
|
notificationId: Int,
|
||||||
final int total,
|
position: Int,
|
||||||
final float percent) {
|
total: Int,
|
||||||
final Notification notification = createProgressNotification(position, total, percent);
|
percent: Float,
|
||||||
|
) {
|
||||||
|
val notification = createProgressNotification(position, total, percent)
|
||||||
try {
|
try {
|
||||||
if (notification == null) {
|
if (notification == null) {
|
||||||
notificationManager.cancel(notificationId);
|
notificationManager.cancel(notificationId)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
setForegroundAsync(new ForegroundInfo(notificationId, notification)).get();
|
setForegroundAsync(ForegroundInfo(notificationId, notification)).get()
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (e: ExecutionException) {
|
||||||
Log.e(TAG, "updateDownloadProgress", e);
|
Log.e(TAG, "updateDownloadProgress", e)
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
Log.e(TAG, "updateDownloadProgress", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification createProgressNotification(final int position, final int total, final float percent) {
|
private fun createProgressNotification(position: Int, total: Int, percent: Float): Notification? {
|
||||||
final Context context = getApplicationContext();
|
val context = applicationContext
|
||||||
boolean ongoing = true;
|
var ongoing = true
|
||||||
int totalPercent;
|
val totalPercent: Int
|
||||||
if (position == total && percent == 100) {
|
if (position == total && percent == 100f) {
|
||||||
ongoing = false;
|
ongoing = false
|
||||||
totalPercent = 100;
|
totalPercent = 100
|
||||||
} else {
|
} else {
|
||||||
totalPercent = (int) ((100f * (position - 1) / total) + (1f / total) * (percent));
|
totalPercent = (100f * (position - 1) / total + 1f / total * percent).toInt()
|
||||||
}
|
}
|
||||||
if (totalPercent == 100) {
|
if (totalPercent == 100) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
// Log.d(TAG, "createProgressNotification: position: " + position
|
// Log.d(TAG, "createProgressNotification: position: " + position
|
||||||
// + ", total: " + total
|
// + ", total: " + total
|
||||||
// + ", percent: " + percent
|
// + ", percent: " + percent
|
||||||
// + ", totalPercent: " + totalPercent);
|
// + ", totalPercent: " + totalPercent);
|
||||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, Constants.DOWNLOAD_CHANNEL_ID)
|
val builder = NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
|
||||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||||
.setSmallIcon(R.drawable.ic_download)
|
.setSmallIcon(R.drawable.ic_download)
|
||||||
.setOngoing(ongoing)
|
.setOngoing(ongoing)
|
||||||
.setProgress(100, totalPercent, totalPercent < 0)
|
.setProgress(100, totalPercent, totalPercent < 0)
|
||||||
.setAutoCancel(false)
|
.setAutoCancel(false)
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setContentTitle(context.getString(R.string.downloader_downloading_post));
|
.setContentTitle(context.getString(R.string.downloader_downloading_post))
|
||||||
if (total > 1) {
|
if (total > 1) {
|
||||||
builder.setContentText(context.getString(R.string.downloader_downloading_child, position, total));
|
builder.setContentText(context.getString(R.string.downloader_downloading_child, position, total))
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSummary(final Map<String, String> urlToFilePathMap) {
|
private fun showSummary(urlToFilePathMap: Map<String, String>?) {
|
||||||
final Context context = getApplicationContext();
|
val context = applicationContext
|
||||||
final Collection<String> filePaths = urlToFilePathMap.values();
|
val filePaths = urlToFilePathMap!!.values
|
||||||
final List<NotificationCompat.Builder> notifications = new LinkedList<>();
|
val notifications: MutableList<NotificationCompat.Builder> = LinkedList()
|
||||||
final List<Integer> notificationIds = new LinkedList<>();
|
val notificationIds: MutableList<Int> = LinkedList()
|
||||||
int count = 1;
|
var count = 1
|
||||||
for (final String filePath : filePaths) {
|
for (filePath in filePaths) {
|
||||||
final File file = new File(filePath);
|
val file = File(filePath)
|
||||||
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
|
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)))
|
||||||
MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
|
MediaScannerConnection.scanFile(context, arrayOf(file.absolutePath), null, null)
|
||||||
final Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
|
val uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
|
||||||
final ContentResolver contentResolver = context.getContentResolver();
|
val contentResolver = context.contentResolver
|
||||||
final Bitmap bitmap = getThumbnail(context, file, uri, contentResolver);
|
val bitmap = getThumbnail(context, file, uri, contentResolver)
|
||||||
final String downloadComplete = context.getString(R.string.downloader_complete);
|
val downloadComplete = context.getString(R.string.downloader_complete)
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
| Intent.FLAG_FROM_BACKGROUND
|
or Intent.FLAG_FROM_BACKGROUND
|
||||||
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
.putExtra(Intent.EXTRA_STREAM, uri);
|
.putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
final PendingIntent pendingIntent = PendingIntent.getActivity(
|
val pendingIntent = PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE,
|
DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE,
|
||||||
intent,
|
intent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_ONE_SHOT
|
||||||
);
|
)
|
||||||
final int notificationId = getNotificationId() + count;
|
val notificationId = notificationId + count
|
||||||
notificationIds.add(notificationId);
|
notificationIds.add(notificationId)
|
||||||
count++;
|
count++
|
||||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
|
val builder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
|
||||||
.setSmallIcon(R.drawable.ic_download)
|
.setSmallIcon(R.drawable.ic_download)
|
||||||
.setContentText(null)
|
.setContentText(null)
|
||||||
.setContentTitle(downloadComplete)
|
.setContentTitle(downloadComplete)
|
||||||
.setWhen(System.currentTimeMillis())
|
.setWhen(System.currentTimeMillis())
|
||||||
.setOnlyAlertOnce(true)
|
.setOnlyAlertOnce(true)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setGroup(NOTIF_GROUP_NAME + "_" + getId())
|
.setGroup(NOTIF_GROUP_NAME + "_" + id)
|
||||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.addAction(R.drawable.ic_delete,
|
.addAction(R.drawable.ic_delete,
|
||||||
context.getString(R.string.delete),
|
context.getString(R.string.delete),
|
||||||
DeleteImageIntentService.pendingIntent(context, filePath, notificationId));
|
DeleteImageIntentService.pendingIntent(context, filePath, notificationId))
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
builder.setLargeIcon(bitmap)
|
builder.setLargeIcon(bitmap)
|
||||||
.setStyle(new NotificationCompat.BigPictureStyle()
|
.setStyle(NotificationCompat.BigPictureStyle()
|
||||||
.bigPicture(bitmap)
|
.bigPicture(bitmap)
|
||||||
.bigLargeIcon(null))
|
.bigLargeIcon(null))
|
||||||
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL);
|
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
|
||||||
}
|
}
|
||||||
notifications.add(builder);
|
notifications.add(builder)
|
||||||
}
|
}
|
||||||
Notification summaryNotification = null;
|
var summaryNotification: Notification? = null
|
||||||
if (urlToFilePathMap.size() != 1) {
|
if (urlToFilePathMap.size != 1) {
|
||||||
final String text = "Downloaded " + urlToFilePathMap.size() + " items";
|
val text = "Downloaded " + urlToFilePathMap.size + " items"
|
||||||
summaryNotification = new NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
|
summaryNotification = NotificationCompat.Builder(context, DOWNLOAD_CHANNEL_ID)
|
||||||
.setContentTitle("Downloaded")
|
.setContentTitle("Downloaded")
|
||||||
.setContentText(text)
|
.setContentText(text)
|
||||||
.setSmallIcon(R.drawable.ic_download)
|
.setSmallIcon(R.drawable.ic_download)
|
||||||
.setStyle(new NotificationCompat.InboxStyle().setSummaryText(text))
|
.setStyle(NotificationCompat.InboxStyle().setSummaryText(text))
|
||||||
.setGroup(NOTIF_GROUP_NAME + "_" + getId())
|
.setGroup(NOTIF_GROUP_NAME + "_" + id)
|
||||||
.setGroupSummary(true)
|
.setGroupSummary(true)
|
||||||
.build();
|
.build()
|
||||||
}
|
}
|
||||||
for (int i = 0; i < notifications.size(); i++) {
|
for (i in notifications.indices) {
|
||||||
final NotificationCompat.Builder builder = notifications.get(i);
|
val builder = notifications[i]
|
||||||
// only make sound and vibrate for the last notification
|
// only make sound and vibrate for the last notification
|
||||||
if (i != notifications.size() - 1) {
|
if (i != notifications.size - 1) {
|
||||||
builder.setSound(null)
|
builder.setSound(null)
|
||||||
.setVibrate(null);
|
.setVibrate(null)
|
||||||
}
|
}
|
||||||
notificationManager.notify(notificationIds.get(i), builder.build());
|
notificationManager.notify(notificationIds[i], builder.build())
|
||||||
}
|
}
|
||||||
if (summaryNotification != null) {
|
if (summaryNotification != null) {
|
||||||
notificationManager.notify(getNotificationId() + count, summaryNotification);
|
notificationManager.notify(notificationId + count, summaryNotification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private fun getThumbnail(
|
||||||
private Bitmap getThumbnail(final Context context,
|
context: Context,
|
||||||
final File file,
|
file: File,
|
||||||
final Uri uri,
|
uri: Uri,
|
||||||
final ContentResolver contentResolver) {
|
contentResolver: ContentResolver,
|
||||||
final String mimeType = Utils.getMimeType(uri, contentResolver);
|
): Bitmap? {
|
||||||
if (TextUtils.isEmpty(mimeType)) return null;
|
val mimeType = Utils.getMimeType(uri, contentResolver)
|
||||||
Bitmap bitmap = null;
|
if (isEmpty(mimeType)) return null
|
||||||
|
var bitmap: Bitmap? = null
|
||||||
if (mimeType.startsWith("image")) {
|
if (mimeType.startsWith("image")) {
|
||||||
try {
|
try {
|
||||||
final BitmapUtils.BitmapResult bitmapResult = BitmapUtils
|
val bitmapResult = BitmapUtils.getBitmapResult(
|
||||||
.getBitmapResult(context.getContentResolver(), uri, THUMBNAIL_SIZE, THUMBNAIL_SIZE, -1, true);
|
context.contentResolver,
|
||||||
if (bitmapResult == null) return null;
|
uri,
|
||||||
bitmap = bitmapResult.bitmap;
|
BitmapUtils.THUMBNAIL_SIZE,
|
||||||
} catch (final Exception e) {
|
BitmapUtils.THUMBNAIL_SIZE,
|
||||||
Log.e(TAG, "", e);
|
-1f,
|
||||||
|
true
|
||||||
|
) ?: return null
|
||||||
|
bitmap = bitmapResult.bitmap
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "", e)
|
||||||
}
|
}
|
||||||
return bitmap;
|
return bitmap
|
||||||
}
|
}
|
||||||
if (mimeType.startsWith("video")) {
|
if (mimeType.startsWith("video")) {
|
||||||
try {
|
try {
|
||||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
val retriever = MediaMetadataRetriever()
|
||||||
try {
|
bitmap = try {
|
||||||
try {
|
try {
|
||||||
retriever.setDataSource(context, uri);
|
retriever.setDataSource(context, uri)
|
||||||
} catch (final Exception e) {
|
} catch (e: Exception) {
|
||||||
retriever.setDataSource(file.getAbsolutePath());
|
retriever.setDataSource(file.absolutePath)
|
||||||
}
|
}
|
||||||
bitmap = retriever.getFrameAtTime();
|
retriever.frameAtTime
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
retriever.release();
|
retriever.release()
|
||||||
} catch (Exception e) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "getThumbnail: ", e);
|
Log.e(TAG, "getThumbnail: ", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "", e);
|
Log.e(TAG, "", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bitmap;
|
return bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DownloadRequest {
|
class DownloadRequest private constructor(val urlToFilePathMap: Map<String, String>) {
|
||||||
private final Map<String, String> urlToFilePathMap;
|
|
||||||
|
|
||||||
public static class Builder {
|
class Builder {
|
||||||
private Map<String, String> urlToFilePathMap;
|
private var urlToFilePathMap: MutableMap<String, String> = mutableMapOf()
|
||||||
|
fun setUrlToFilePathMap(urlToFilePathMap: MutableMap<String, String>): Builder {
|
||||||
public Builder setUrlToFilePathMap(final Map<String, String> urlToFilePathMap) {
|
this.urlToFilePathMap = urlToFilePathMap
|
||||||
this.urlToFilePathMap = urlToFilePathMap;
|
return this
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addUrl(@NonNull final String url, @NonNull final String filePath) {
|
fun addUrl(url: String, filePath: String): Builder {
|
||||||
if (urlToFilePathMap == null) {
|
urlToFilePathMap[url] = filePath
|
||||||
urlToFilePathMap = new HashMap<>();
|
return this
|
||||||
}
|
|
||||||
urlToFilePathMap.put(url, filePath);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadRequest build() {
|
fun build(): DownloadRequest {
|
||||||
return new DownloadRequest(urlToFilePathMap);
|
return DownloadRequest(urlToFilePathMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder() {
|
companion object {
|
||||||
return new Builder();
|
@JvmStatic
|
||||||
}
|
fun builder(): Builder {
|
||||||
|
return Builder()
|
||||||
private DownloadRequest(final Map<String, String> urlToFilePathMap) {
|
}
|
||||||
this.urlToFilePathMap = urlToFilePathMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getUrlToFilePathMap() {
|
|
||||||
return urlToFilePathMap;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
companion object {
|
||||||
|
const val PROGRESS = "PROGRESS"
|
||||||
|
const val URL = "URL"
|
||||||
|
const val KEY_DOWNLOAD_REQUEST_JSON = "download_request_json"
|
||||||
|
private const val DOWNLOAD_GROUP = "DOWNLOAD_GROUP"
|
||||||
|
private const val DOWNLOAD_NOTIFICATION_INTENT_REQUEST_CODE = 2020
|
||||||
|
private const val DELETE_IMAGE_REQUEST_CODE = 2030
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user