diff --git a/README.md b/README.md index b82cffcf..ef8ef0c2 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ final taskId = await FlutterDownloader.enqueue( savedDir: 'the path of directory where you want to save downloaded files', showNotification: true, // show download progress in status bar (for Android) openFileFromNotification: true, // click on notification to open downloaded file (for Android) + displayName: 'Readable download name', // For now is used for notification title (for Android) ); ``` @@ -376,6 +377,7 @@ CREATE TABLE `task` ( `status` INTEGER DEFAULT 0, `progress` INTEGER DEFAULT 0, `file_name` TEXT, + `display_name` TEXT, `saved_dir` TEXT, `resumable` TINYINT DEFAULT 0, `headers` TEXT, diff --git a/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadTask.kt b/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadTask.kt index ed3da7f4..6132f4b7 100644 --- a/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadTask.kt +++ b/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadTask.kt @@ -7,6 +7,7 @@ data class DownloadTask( var progress: Int, var url: String, var filename: String?, + var displayName: String?, var savedDir: String, var headers: String, var mimeType: String, diff --git a/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadWorker.kt b/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadWorker.kt index 1b869589..a01249c4 100644 --- a/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadWorker.kt +++ b/android/src/main/kotlin/vn/hunghd/flutterdownloader/DownloadWorker.kt @@ -136,10 +136,10 @@ class DownloadWorker(context: Context, params: WorkerParameters) : dbHelper = TaskDbHelper.getInstance(context) taskDao = TaskDao(dbHelper!!) val url: String? = inputData.getString(ARG_URL) - val filename: String? = inputData.getString(ARG_FILE_NAME) val task = taskDao?.loadTask(id.toString()) + var displayName: String? = inputData.getString(ARG_DISPLAY_NAME) ?: inputData.getString(ARG_FILE_NAME) if (task != null && task.status == DownloadStatus.ENQUEUED) { - updateNotification(context, filename ?: url, DownloadStatus.CANCELED, -1, null, true) + updateNotification(context, displayName ?: url, DownloadStatus.CANCELED, -1, null, true) taskDao?.updateTask(id.toString(), DownloadStatus.CANCELED, lastProgress) } } @@ -149,8 +149,8 @@ class DownloadWorker(context: Context, params: WorkerParameters) : taskDao = TaskDao(dbHelper!!) val url: String = inputData.getString(ARG_URL) ?: throw IllegalArgumentException("Argument '$ARG_URL' should not be null") - val filename: String? = - inputData.getString(ARG_FILE_NAME) // ?: throw IllegalArgumentException("Argument '$ARG_FILE_NAME' should not be null") + val filename: String? = inputData.getString(ARG_FILE_NAME) + var displayName: String? = inputData.getString(ARG_DISPLAY_NAME) ?: filename // ?: throw IllegalArgumentException("Argument '$ARG_FILE_NAME' should not be null") val savedDir: String = inputData.getString(ARG_SAVED_DIR) ?: throw IllegalArgumentException("Argument '$ARG_SAVED_DIR' should not be null") val headers: String = inputData.getString(ARG_HEADERS) @@ -187,7 +187,7 @@ class DownloadWorker(context: Context, params: WorkerParameters) : setupNotification(applicationContext) updateNotification( applicationContext, - filename ?: url, + displayName ?: url, DownloadStatus.RUNNING, task.progress, null, @@ -203,13 +203,13 @@ class DownloadWorker(context: Context, params: WorkerParameters) : log("exists file for " + filename + "automatic resuming...") } return try { - downloadFile(applicationContext, url, savedDir, filename, headers, isResume, timeout) + downloadFile(applicationContext, url, savedDir, filename, displayName, headers, isResume, timeout) cleanUp() dbHelper = null taskDao = null Result.success() } catch (e: Exception) { - updateNotification(applicationContext, filename ?: url, DownloadStatus.FAILED, -1, null, true) + updateNotification(applicationContext, displayName ?: url, DownloadStatus.FAILED, -1, null, true) taskDao?.updateTask(id.toString(), DownloadStatus.FAILED, lastProgress) e.printStackTrace() dbHelper = null @@ -255,11 +255,13 @@ class DownloadWorker(context: Context, params: WorkerParameters) : fileURL: String, savedDir: String, filename: String?, + displayName: String?, headers: String, isResume: Boolean, timeout: Int, ) { var actualFilename = filename + var displayName = displayName var url = fileURL var resourceUrl: URL var base: URL? @@ -410,7 +412,7 @@ class DownloadWorker(context: Context, params: WorkerParameters) : taskDao!!.updateTask(id.toString(), DownloadStatus.RUNNING, progress) updateNotification( context, - actualFilename, + displayName ?: actualFilename, DownloadStatus.RUNNING, progress, null, @@ -456,19 +458,21 @@ class DownloadWorker(context: Context, params: WorkerParameters) : } } taskDao!!.updateTask(id.toString(), status, progress) - updateNotification(context, actualFilename, status, progress, pendingIntent, true) + updateNotification(context, displayName ?: actualFilename, status, progress, pendingIntent, true) log(if (isStopped) "Download canceled" else "File downloaded") } else { val loadedTask = taskDao!!.loadTask(id.toString()) val status = if (isStopped) if (loadedTask!!.resumable) DownloadStatus.PAUSED else DownloadStatus.CANCELED else DownloadStatus.FAILED + val notificationTitle: String? = displayName ?: actualFilename taskDao!!.updateTask(id.toString(), status, lastProgress) - updateNotification(context, actualFilename ?: fileURL, status, -1, null, true) + updateNotification(context, notificationTitle ?: fileURL, status, -1, null, true) log(if (isStopped) "Download canceled" else "Server replied HTTP code: $responseCode") } } catch (e: IOException) { taskDao!!.updateTask(id.toString(), DownloadStatus.FAILED, lastProgress) - updateNotification(context, actualFilename ?: fileURL, DownloadStatus.FAILED, -1, null, true) + val notificationTitle: String? = displayName ?: actualFilename + updateNotification(context, notificationTitle ?: fileURL, DownloadStatus.FAILED, -1, null, true) e.printStackTrace() } finally { if (outputStream != null) { @@ -827,6 +831,7 @@ class DownloadWorker(context: Context, params: WorkerParameters) : companion object { const val ARG_URL = "url" const val ARG_FILE_NAME = "file_name" + const val ARG_DISPLAY_NAME = "display_name" const val ARG_SAVED_DIR = "saved_file" const val ARG_HEADERS = "headers" const val ARG_IS_RESUME = "is_resume" diff --git a/android/src/main/kotlin/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.kt b/android/src/main/kotlin/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.kt index b6f527b2..0aad906e 100644 --- a/android/src/main/kotlin/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.kt +++ b/android/src/main/kotlin/vn/hunghd/flutterdownloader/FlutterDownloaderPlugin.kt @@ -84,6 +84,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { url: String?, savedDir: String?, filename: String?, + displayName: String?, headers: String?, showNotification: Boolean, openFileFromNotification: Boolean, @@ -107,6 +108,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { .putString(DownloadWorker.ARG_URL, url) .putString(DownloadWorker.ARG_SAVED_DIR, savedDir) .putString(DownloadWorker.ARG_FILE_NAME, filename) + .putString(DownloadWorker.ARG_DISPLAY_NAME, displayName) .putString(DownloadWorker.ARG_HEADERS, headers) .putBoolean(DownloadWorker.ARG_SHOW_NOTIFICATION, showNotification) .putBoolean( @@ -162,6 +164,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { val url: String = call.requireArgument("url") val savedDir: String = call.requireArgument("saved_dir") val filename: String? = call.argument("file_name") + val displayName: String? = call.argument("display_name") val headers: String = call.requireArgument("headers") val timeout: Int = call.requireArgument("timeout") val showNotification: Boolean = call.requireArgument("show_notification") @@ -173,6 +176,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { url, savedDir, filename, + displayName, headers, showNotification, openFileFromNotification, @@ -192,6 +196,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { DownloadStatus.ENQUEUED, 0, filename, + displayName, savedDir, headers, showNotification, @@ -277,6 +282,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { task.url, task.savedDir, task.filename, + task.displayName, task.headers, task.showNotification, task.openFileFromNotification, @@ -321,7 +327,7 @@ class FlutterDownloaderPlugin : MethodChannel.MethodCallHandler, FlutterPlugin { if (task != null) { if (task.status == DownloadStatus.FAILED || task.status == DownloadStatus.CANCELED) { val request: WorkRequest = buildRequest( - task.url, task.savedDir, task.filename, + task.url, task.savedDir, task.filename, task.displayName, task.headers, task.showNotification, task.openFileFromNotification, false, requiresStorageNotLow, task.saveInPublicStorage, timeout, allowCellular = task.allowCellular ) diff --git a/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDao.kt b/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDao.kt index 6bb7a8a9..146c0522 100644 --- a/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDao.kt +++ b/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDao.kt @@ -13,6 +13,7 @@ class TaskDao(private val dbHelper: TaskDbHelper) { TaskEntry.COLUMN_NAME_STATUS, TaskEntry.COLUMN_NAME_URL, TaskEntry.COLUMN_NAME_FILE_NAME, + TaskEntry.COLUMN_NAME_DISPLAY_NAME, TaskEntry.COLUMN_NAME_SAVED_DIR, TaskEntry.COLUMN_NAME_HEADERS, TaskEntry.COLUMN_NAME_MIME_TYPE, @@ -30,6 +31,7 @@ class TaskDao(private val dbHelper: TaskDbHelper) { status: DownloadStatus, progress: Int, fileName: String?, + displayName: String?, savedDir: String?, headers: String?, showNotification: Boolean, @@ -48,6 +50,7 @@ class TaskDao(private val dbHelper: TaskDbHelper) { values.put(TaskEntry.COLUMN_NAME_HEADERS, headers) values.put(TaskEntry.COLUMN_NAME_MIME_TYPE, "unknown") values.put(TaskEntry.COLUMN_NAME_SHOW_NOTIFICATION, if (showNotification) 1 else 0) + values.put(TaskEntry.COLUMN_NAME_DISPLAY_NAME, displayName) values.put( TaskEntry.COLUMN_NAME_OPEN_FILE_FROM_NOTIFICATION, if (openFileFromNotification) 1 else 0 @@ -238,6 +241,7 @@ class TaskDao(private val dbHelper: TaskDbHelper) { val progress = cursor.getInt(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_PROGRESS)) val url = cursor.getString(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_URL)) val filename = cursor.getString(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_FILE_NAME)) + val displayName = cursor.getString(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_DISPLAY_NAME)) val savedDir = cursor.getString(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_SAVED_DIR)) val headers = cursor.getString(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_HEADERS)) val mimeType = cursor.getString(cursor.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_MIME_TYPE)) @@ -254,6 +258,7 @@ class TaskDao(private val dbHelper: TaskDbHelper) { progress, url, filename, + displayName, savedDir, headers, mimeType, diff --git a/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDbHelper.kt b/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDbHelper.kt index 51849631..1e3f2862 100644 --- a/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDbHelper.kt +++ b/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskDbHelper.kt @@ -12,10 +12,12 @@ class TaskDbHelper private constructor(context: Context) : } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { - if (newVersion == 4) { + if (newVersion == 5) { + db.execSQL("ALTER TABLE ${TaskEntry.TABLE_NAME} ADD COLUMN ${TaskEntry.COLUMN_NAME_DISPLAY_NAME} TEXT") + } else if (oldVersion == 3 && newVersion == 4) { db.execSQL("ALTER TABLE ${TaskEntry.TABLE_NAME} ADD COLUMN ${TaskEntry.COLUMN_ALLOW_CELLULAR} TINYINT DEFAULT 1") } else if (oldVersion == 2 && newVersion == 3) { - db.execSQL("ALTER TABLE " + TaskEntry.TABLE_NAME + " ADD COLUMN " + TaskEntry.COLUMN_SAVE_IN_PUBLIC_STORAGE + " TINYINT DEFAULT 0") + db.execSQL("ALTER TABLE ${TaskEntry.TABLE_NAME} ADD COLUMN ${TaskEntry.COLUMN_SAVE_IN_PUBLIC_STORAGE} TINYINT DEFAULT 0") } else { db.execSQL(SQL_DELETE_ENTRIES) onCreate(db) @@ -27,7 +29,7 @@ class TaskDbHelper private constructor(context: Context) : } companion object { - const val DATABASE_VERSION = 4 + const val DATABASE_VERSION = 5 const val DATABASE_NAME = "download_tasks.db" private var instance: TaskDbHelper? = null private const val SQL_CREATE_ENTRIES = ( @@ -38,6 +40,7 @@ class TaskDbHelper private constructor(context: Context) : TaskEntry.COLUMN_NAME_STATUS + " INTEGER DEFAULT 0, " + TaskEntry.COLUMN_NAME_PROGRESS + " INTEGER DEFAULT 0, " + TaskEntry.COLUMN_NAME_FILE_NAME + " TEXT, " + + TaskEntry.COLUMN_NAME_DISPLAY_NAME + " TEXT, " + TaskEntry.COLUMN_NAME_SAVED_DIR + " TEXT, " + TaskEntry.COLUMN_NAME_HEADERS + " TEXT, " + TaskEntry.COLUMN_NAME_MIME_TYPE + " VARCHAR(128), " + diff --git a/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskEntry.kt b/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskEntry.kt index 78cececf..434ded23 100644 --- a/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskEntry.kt +++ b/android/src/main/kotlin/vn/hunghd/flutterdownloader/TaskEntry.kt @@ -10,6 +10,7 @@ object TaskEntry : BaseColumns { const val COLUMN_NAME_URL = "url" const val COLUMN_NAME_SAVED_DIR = "saved_dir" const val COLUMN_NAME_FILE_NAME = "file_name" + const val COLUMN_NAME_DISPLAY_NAME = "display_name" const val COLUMN_NAME_MIME_TYPE = "mime_type" const val COLUMN_NAME_RESUMABLE = "resumable" const val COLUMN_NAME_HEADERS = "headers" diff --git a/example/lib/home_page.dart b/example/lib/home_page.dart index 6b2e0b0b..4f5eace2 100644 --- a/example/lib/home_page.dart +++ b/example/lib/home_page.dart @@ -216,6 +216,7 @@ class _MyHomePageState extends State { url: task.link!, headers: {'auth': 'test_for_sql_encoding'}, savedDir: _localPath, + displayName: task.name, saveInPublicStorage: _saveInPublicStorage, ); } diff --git a/lib/src/downloader.dart b/lib/src/downloader.dart index 998f28ce..3a68141e 100644 --- a/lib/src/downloader.dart +++ b/lib/src/downloader.dart @@ -78,6 +78,9 @@ class FlutterDownloader { /// If [showNotification] is true, a notification with the current download /// progress will be shown. /// + /// If [displayName] is set and if [showNotification] is true, sets title of notification. + /// If this parameter is not set, the plugin will apply value of [fileName] or [url] parameter + /// /// If [requiresStorageNotLow] is true, the download won't run unless the /// device's available storage is at an acceptable level. /// @@ -96,6 +99,7 @@ class FlutterDownloader { required String url, required String savedDir, String? fileName, + String? displayName, Map headers = const {}, bool showNotification = true, bool openFileFromNotification = true, @@ -112,6 +116,7 @@ class FlutterDownloader { 'url': url, 'saved_dir': savedDir, 'file_name': fileName, + 'display_name': displayName, 'headers': jsonEncode(headers), 'show_notification': showNotification, 'open_file_from_notification': openFileFromNotification,