Skip to content

Commit 20718d8

Browse files
author
chimnayajith
committed
lightbox: Add download button to bottom app bar.
Fixes: #42
1 parent 2ec8b9d commit 20718d8

20 files changed

+554
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Autogenerated from Pigeon (v22.7.2), do not edit directly.
2+
// See also: https://pub.dev/packages/pigeon
3+
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
4+
5+
package com.zulip.flutter
6+
7+
import android.util.Log
8+
import io.flutter.plugin.common.BasicMessageChannel
9+
import io.flutter.plugin.common.BinaryMessenger
10+
import io.flutter.plugin.common.EventChannel
11+
import io.flutter.plugin.common.MessageCodec
12+
import io.flutter.plugin.common.StandardMethodCodec
13+
import io.flutter.plugin.common.StandardMessageCodec
14+
import java.io.ByteArrayOutputStream
15+
import java.nio.ByteBuffer
16+
17+
private fun wrapResult(result: Any?): List<Any?> {
18+
return listOf(result)
19+
}
20+
21+
private fun wrapError(exception: Throwable): List<Any?> {
22+
return if (exception is DownloadManagerError) {
23+
listOf(
24+
exception.code,
25+
exception.message,
26+
exception.details
27+
)
28+
} else {
29+
listOf(
30+
exception.javaClass.simpleName,
31+
exception.toString(),
32+
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
33+
)
34+
}
35+
}
36+
37+
/**
38+
* Error class for passing custom error details to Flutter via a thrown PlatformException.
39+
* @property code The error code.
40+
* @property message The error message.
41+
* @property details The error details. Must be a datatype supported by the api codec.
42+
*/
43+
class DownloadManagerError (
44+
val code: String,
45+
override val message: String? = null,
46+
val details: Any? = null
47+
) : Throwable()
48+
private open class DownloadManagerPigeonCodec : StandardMessageCodec() {
49+
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
50+
return super.readValueOfType(type, buffer)
51+
}
52+
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
53+
super.writeValue(stream, value)
54+
}
55+
}
56+
57+
58+
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
59+
interface DownloadManagerHostApi {
60+
/**
61+
* Downloads a file using the given URL and saves it with the specified file name in the Downloads directory.
62+
* Returns a success message or an error message.
63+
*/
64+
fun downloadFile(fileUrl: String, fileName: String, header: String, callback: (Result<String>) -> Unit)
65+
66+
companion object {
67+
/** The codec used by DownloadManagerHostApi. */
68+
val codec: MessageCodec<Any?> by lazy {
69+
DownloadManagerPigeonCodec()
70+
}
71+
/** Sets up an instance of `DownloadManagerHostApi` to handle messages through the `binaryMessenger`. */
72+
@JvmOverloads
73+
fun setUp(binaryMessenger: BinaryMessenger, api: DownloadManagerHostApi?, messageChannelSuffix: String = "") {
74+
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
75+
run {
76+
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.zulip.DownloadManagerHostApi.downloadFile$separatedMessageChannelSuffix", codec)
77+
if (api != null) {
78+
channel.setMessageHandler { message, reply ->
79+
val args = message as List<Any?>
80+
val fileUrlArg = args[0] as String
81+
val fileNameArg = args[1] as String
82+
val headerArg = args[2] as String
83+
api.downloadFile(fileUrlArg, fileNameArg, headerArg) { result: Result<String> ->
84+
val error = result.exceptionOrNull()
85+
if (error != null) {
86+
reply.reply(wrapError(error))
87+
} else {
88+
val data = result.getOrNull()
89+
reply.reply(wrapResult(data))
90+
}
91+
}
92+
}
93+
} else {
94+
channel.setMessageHandler(null)
95+
}
96+
}
97+
}
98+
}
99+
}

android/app/src/main/kotlin/com/zulip/flutter/ZulipPlugin.kt

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.zulip.flutter
22

33
import android.annotation.SuppressLint
4+
import android.app.DownloadManager
45
import android.content.ContentUris
56
import android.content.ContentValues
67
import android.content.Context
78
import android.content.Intent
89
import android.media.AudioAttributes
10+
import android.media.MediaScannerConnection
911
import android.net.Uri
1012
import android.os.Build
1113
import android.os.Bundle
@@ -19,7 +21,7 @@ import androidx.core.app.NotificationCompat
1921
import androidx.core.app.NotificationManagerCompat
2022
import androidx.core.graphics.drawable.IconCompat
2123
import io.flutter.embedding.engine.plugins.FlutterPlugin
22-
24+
import org.json.JSONObject
2325
private const val TAG = "ZulipPlugin"
2426

2527
fun toAndroidPerson(person: Person): androidx.core.app.Person {
@@ -283,17 +285,50 @@ private class AndroidNotificationHost(val context: Context)
283285
}
284286
}
285287

288+
/** Host class for handling downloads via DownloadManager */
289+
class DownloadHost(private val context: Context) : DownloadManagerHostApi {
290+
291+
/** Downloads a file from the given URL and saves it to the specified filename in the Downloads folder. */
292+
override fun downloadFile(url: String, fileName: String, header: String, callback: (Result<String>) -> Unit) {
293+
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
294+
val headersJsonObject = JSONObject(header)
295+
val headersMap = mutableMapOf<String, String>()
296+
for (key in headersJsonObject.keys()) {
297+
headersMap[key] = headersJsonObject.getString(key)
298+
}
299+
val uri = Uri.parse(url)
300+
301+
val request = DownloadManager.Request(uri).apply {
302+
setTitle("Downloading $fileName")
303+
setDescription("File is being downloaded...")
304+
setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
305+
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
306+
307+
for ((key, value) in headersMap) {
308+
addRequestHeader(key, value)
309+
}
310+
}
311+
// Queue the download
312+
downloadManager.enqueue(request)
313+
callback(Result.success("Download started successfully for: $fileName"))
314+
}
315+
}
316+
286317
/** A Flutter plugin for the Zulip app's ad-hoc needs. */
287318
// @Keep is needed because this class is used only
288319
// from ZulipShimPlugin, via reflection.
289320
@Keep
290321
class ZulipPlugin : FlutterPlugin { // TODO ActivityAware too?
291322
private var notificationHost: AndroidNotificationHost? = null
323+
private var downloadHost: DownloadHost? = null
292324

293325
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
294326
Log.d(TAG, "Attaching to Flutter engine.")
295327
notificationHost = AndroidNotificationHost(binding.applicationContext)
328+
downloadHost = DownloadHost(binding.applicationContext)
329+
296330
AndroidNotificationHostApi.setUp(binding.binaryMessenger, notificationHost)
331+
DownloadManagerHostApi.setUp(binding.binaryMessenger, downloadHost)
297332
}
298333

299334
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
@@ -302,6 +337,9 @@ class ZulipPlugin : FlutterPlugin { // TODO ActivityAware too?
302337
return
303338
}
304339
AndroidNotificationHostApi.setUp(binding.binaryMessenger, null)
340+
DownloadManagerHostApi.setUp(binding.binaryMessenger, null)
341+
305342
notificationHost = null
343+
downloadHost = null
306344
}
307-
}
345+
}

assets/l10n/app_en.arb

+16
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,22 @@
395395
"@lightboxCopyLinkTooltip": {
396396
"description": "Tooltip in lightbox for the copy link action."
397397
},
398+
"lightboxDownloadImageTooltip": "Download image",
399+
"@lightboxDownloadImageTooltip": {
400+
"description": "Tooltip in lightbox for the download image action."
401+
},
402+
"lightboxDownloadImageSuccess": "Image downloaded successfully!",
403+
"@lightboxDownloadImageSuccess": {
404+
"description": "Message shown when the image downloads successfully."
405+
},
406+
"lightboxDownloadImageFailed": "Failed to download the image.",
407+
"@lightboxDownloadImageFailed": {
408+
"description": "Message shown when the image download fails."
409+
},
410+
"lightboxDownloadImageError": "An error occurred while downloading the image.",
411+
"@lightboxDownloadImageError": {
412+
"description": "Message shown when an unexpected error occurs during image download."
413+
},
398414
"loginPageTitle": "Log in",
399415
"@loginPageTitle": {
400416
"description": "Title for login page."

lib/generated/l10n/zulip_localizations.dart

+24
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,30 @@ abstract class ZulipLocalizations {
633633
/// **'Copy link'**
634634
String get lightboxCopyLinkTooltip;
635635

636+
/// Tooltip in lightbox for the download image action.
637+
///
638+
/// In en, this message translates to:
639+
/// **'Download image'**
640+
String get lightboxDownloadImageTooltip;
641+
642+
/// Message shown when the image downloads successfully.
643+
///
644+
/// In en, this message translates to:
645+
/// **'Image downloaded successfully!'**
646+
String get lightboxDownloadImageSuccess;
647+
648+
/// Message shown when the image download fails.
649+
///
650+
/// In en, this message translates to:
651+
/// **'Failed to download the image.'**
652+
String get lightboxDownloadImageFailed;
653+
654+
/// Message shown when an unexpected error occurs during image download.
655+
///
656+
/// In en, this message translates to:
657+
/// **'An error occurred while downloading the image.'**
658+
String get lightboxDownloadImageError;
659+
636660
/// Title for login page.
637661
///
638662
/// In en, this message translates to:

lib/generated/l10n/zulip_localizations_ar.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Copy link';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Log in';
315327

lib/generated/l10n/zulip_localizations_en.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Copy link';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Log in';
315327

lib/generated/l10n/zulip_localizations_ja.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Copy link';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Log in';
315327

lib/generated/l10n/zulip_localizations_nb.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Copy link';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Log in';
315327

lib/generated/l10n/zulip_localizations_pl.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Skopiuj odnośnik';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Zaloguj';
315327

lib/generated/l10n/zulip_localizations_ru.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Скопировать ссылку';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Вход в систему';
315327

lib/generated/l10n/zulip_localizations_sk.dart

+12
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
310310
@override
311311
String get lightboxCopyLinkTooltip => 'Skopírovať odkaz';
312312

313+
@override
314+
String get lightboxDownloadImageTooltip => 'Download image';
315+
316+
@override
317+
String get lightboxDownloadImageSuccess => 'Image downloaded successfully!';
318+
319+
@override
320+
String get lightboxDownloadImageFailed => 'Failed to download the image.';
321+
322+
@override
323+
String get lightboxDownloadImageError => 'An error occurred while downloading the image.';
324+
313325
@override
314326
String get loginPageTitle => 'Prihlásiť sa';
315327

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'android_download_manager.g.dart';
2+
3+
// Wrapper class for Download functionality
4+
class AndroidDownloader {
5+
final DownloadManagerHostApi _api = DownloadManagerHostApi();
6+
7+
/// Downloads a file from the given URL and saves it with the specified file name.
8+
Future<void> downloadFile(String url, String fileName, String header) async {
9+
await _api.downloadFile(url, fileName, header);
10+
}
11+
}

0 commit comments

Comments
 (0)