I want to download an image in svg that is in the FirebaseStore, using data method, but the bitmap conversion returns null.
I need returns the svg image and put on the screen
private lateinit var auth: FirebaseAuth
private lateinit var storage: FirebaseStorage
private lateinit var storageRef: StorageReference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
auth = Firebase.auth
storage = Firebase.storage
storageRef = storage.reference
val fileRef = storageRef.child("CFM/cfm2.back.svg")
downloadImgData(fileRef)
}
private fun downloadImgData(fileRef: StorageReference?){
if (fileRef != null) {
val ONE_MEGABYTE = (1024 * 1024).toLong()
fileRef.getBytes(ONE_MEGABYTE)
.addOnSuccessListener { bytes ->
val bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
imageView.setImageBitmap(bmp)
textview.text = fileRef.name
}
.addOnFailureListener { exception ->
Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
}
}
You can use Pixplicity's Sharp for loading SVG images into ImageView.
It provides method to load svg image from InputStream into ImageView.
ByteArray obtained from Firebase Storage can be converted to InputStream by calling bytes.inputStream().
Add the library dependency
dependencies {
implementation 'com.pixplicity.sharp:library:[VERSION_HERE]#aar'
}
This loads the SVG image to imageView
val stream = bytes.inputStream()
Sharp.loadInputStream(stream).into(imageView)
stream.close()
Finally the function looks like,
private fun downloadImgData(fileRef: StorageReference?) {
if (fileRef != null) {
val ONE_MEGABYTE = (1024 * 1024).toLong()
fileRef.getBytes(ONE_MEGABYTE)
.addOnSuccessListener { bytes ->
val stream = bytes.inputStream()
Sharp.loadInputStream(stream).into(imageView)
stream.close()
textview.text = fileRef.name
}
.addOnFailureListener { exception ->
Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
}
}
}
Related
I get the following error when trying to iterate over the uploadTasks inside an addOnSuccessListener method.
java.lang.ClassCastException: com.google.firebase.storage.UploadTask$TaskSnapshot cannot be cast to com.google.firebase.storage.UploadTask
So how can i get the Download String of each Img inside addOnSuccessListener?
val baos = ByteArrayOutputStream()
val tasks = mutableListOf<UploadTask>()
listImg.forEach {
if(bitmap!!.byteCount != it.byteCount) {
val bitmap = it
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val data = baos.toByteArray()
var uploadTask = spaceRef.putBytes(data)
tasks.add(uploadTask)
}
}
Tasks.whenAllSuccess<UploadTask>(tasks).addOnSuccessListener { uploadTasks ->
//uploadTasks has size of 2
val urls = mutableListOf<Uri>()
lifecycleScope.launch
{
//Error throws here
uploadTasks.forEach{
urls.add(it.await().storage.downloadUrl.await())
}
}
}
The type of whenAllSuccess is <TResult>, so you should use the result type of UploadTask (UploadTask.TaskSnapshot) instead:
Tasks.whenAllSuccess<UploadTask.TaskSnapshot>(tasks).addOnSuccessListener { uploadTasks ->
And then you can drop the the first await() on that last line:
urls.add(it.storage.downloadUrl.await())
Bonus: Don't block the main thread
Note that Tasks.whenAllSuccess() will block the main thread until all uploads succeed, meaning your UI might freeze while uploading files.
To avoid that, consider uploading your files with Coroutines:
val baos = ByteArrayOutputStream()
val urls = mutableListOf<Uri>()
lifecycleScope.launch {
listImg.forEach {
if(bitmap!!.byteCount != it.byteCount) {
val bitmap = it
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val data = baos.toByteArray()
// Upload the image first
val taskSnapshot = spaceRef.putBytes(data).await()
// Get the download Url
val downloadUri = taskSnapshot.storage.downloadUrl.await()
// Add it to the list of Uris
urls.add(downloadUri)
}
}
}
I need to upload image to my Graphql server from android application. The details in the documentation is not working. I need an example.
Came up with the solution. 1st I needed to create an upload scalar type. in Fragment class:
requireContext().contentResolver.openFileDescriptor(
selectedImageUri!!,
"r",
null
) ?: return
val file = File(
requireContext().cacheDir, requireContext().contentResolver.getFileName(
selectedImageUri
)
)
val body = UploadRequestBody(file, "image")
val upload = DefaultUpload.Builder()
.content(file)
.fileName(file.name)
.contentType(body.contentType().toString())
.build()
In case what the UploadRequestBody class does:
class UploadRequestBody(
private val file: File,
private val contentType: String
) : RequestBody() {
override fun contentType() = "$contentType/*".toMediaTypeOrNull()
override fun contentLength() = file.length()
override fun writeTo(sink: BufferedSink) {
val length = file.length()
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
val fileInputStream = FileInputStream(file)
var uploaded = 0L
fileInputStream.use { inputStream ->
var read: Int
while (inputStream.read(buffer).also { read = it } != -1) {
uploaded += read
sink.write(buffer, 0, read)
}
}
}
companion object {
private const val DEFAULT_BUFFER_SIZE = 2048
}
}
The doWork() method returns the Result before completing the task, resulting in blocking the main thread.
Some of the answers to this question are: To make the thread wait until the task is completed.
I feel there could be a better way to handle it.
class CloudWorker(context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
val fireStore = FirebaseFirestore.getInstance()
#SuppressLint("LogNotTimber")
override fun doWork(): Result {
Log.i("thred_dowork"," :"+Thread.currentThread().name)
val appContext = applicationContext
val dataString = inputData.getString(KEY_NOTE_DATA)
val data = Gson().fromJson(dataString, NoteData::class.java)
makeStatusNotification("Uploading Notes", appContext)
Log.i("uri:"," ${data.uri}")
val resolver = appContext.contentResolver
appContext.grantUriPermission(appContext.packageName,
Uri.parse(data.uri),
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val f = File(data.uri, "profile.jpg")
val picture = BitmapFactory.decodeStream(FileInputStream(f))
// Create a storage reference from our app
val storage = Firebase.storage
// Create a storage reference from our app
val storageRef = storage.reference
val baos = ByteArrayOutputStream()
picture.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val imageDataX: ByteArray = baos.toByteArray()
var pathX = "images/"+System.currentTimeMillis().toString()+".jpg"
val mountainImagesRef = storageRef.child(pathX)
val uploadTask = mountainImagesRef.putBytes(imageDataX)
uploadTask.addOnSuccessListener {
Log.i("sync_","Image uploaded"+ it.metadata!!.path+" : "+it.metadata!!.name)
data.uri= it.metadata!!.path
fireStore.collection("notes")
.add(data)
.addOnSuccessListener {
Log.i("sync_","Sync Succes")
if(isStopped)
Log.i("sync_","Worker Stopped")
else
saveUrl()
}.addOnFailureListener {
Log.i("sync_","Sync Succes")
}
}.addOnFailureListener{
Log.e("sync_","Image not uploaded"+it.toString())
}
Timber.i("sync_data: $dataString")
Log.i("sync_","Called before firestore"+dataString)
return Result.success()
}
private fun saveUrl() {
Log.i("sync_","sleeping for 10 sec. thread_ "+Thread.currentThread().name)
sleep(15000)
if(isStopped)
Log.i("sync_","Worker Stopped")
else
Log.i("sync_","Worker not stopped")
Log.i("sync_","Wake up after 10 sec")
fireStore.collection("employee")
.get()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
for (document in task.result!!) {
Log.d("sync_employ", document.id + " => " + document.data)
}
} else {
Log.w("sync_", "Error getting documents.", task.exception)
}
}
}
}
Here I am doing firebase operation.
uploading image to firebase file storage
After successful upload, uploading content to the database.
On successful upload, retrieving some data from the database.
Your suggestions are most welcome here.
I'm creating a photo making app using Camera2 API.
I would want to set additional information to the photo (date, location), but I'm getting "Uknown URL" exception.
When i comment out contentResolver, photo is saved, but is lacking any additional information, and I need to have access to the location - I will be filtering gallery to only those which are in close proximity.
internal class ImageSaver(
private val image: Image,
private val file: File,
private val watermark: Bitmap,
private val mContext: Context
) : Runnable {
private val saveImageExecutor: Executor = Executors.newSingleThreadExecutor()
override fun run() {
val jpegByteBuffer = image.planes[0].buffer
val jpegByteArray = ByteArray(jpegByteBuffer.remaining())
jpegByteBuffer.get(jpegByteArray)
val width = image.width
val height = image.height
saveImageExecutor.execute {
val date = System.currentTimeMillis()
val location = getLocation(mContext)
val longitude = location?.longitude ?: 0.0
val latitude = location?.latitude ?: 0.0
// watermark
val options = BitmapFactory.Options()
options.inMutable = true
val original =
BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.size, options)
val overlayed = overlay(original, watermark)
val watermarkedByteArrayOS = ByteArrayOutputStream()
overlayed!!.compress(Bitmap.CompressFormat.JPEG, 100, watermarkedByteArrayOS)
val watermarkedByteArray = watermarkedByteArrayOS.toByteArray()
Log.d(TAG, "saving pic meta-data")
val values = ContentValues()
values.put(MediaStore.Images.ImageColumns.TITLE, file.name)
values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, file.name)
values.put(MediaStore.Images.ImageColumns.DATA, file.path)
values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, date)
values.put(MediaStore.Images.ImageColumns.WIDTH, width)
values.put(MediaStore.Images.ImageColumns.HEIGHT, height)
values.put(MediaStore.Images.ImageColumns.LONGITUDE, longitude)
values.put(MediaStore.Images.ImageColumns.LATITUDE, latitude)
Log.d(TAG, "LON: ${values.get(MediaStore.Images.ImageColumns.LATITUDE)}")
Log.d(TAG, "LAT: ${values.get(MediaStore.Images.ImageColumns.LONGITUDE)}")
var output: FileOutputStream? = null
try {
output = FileOutputStream(file).apply {
write(watermarkedByteArray)
}
} catch (e: IOException) {
Log.e(TAG, e.toString())
} finally {
image.close()
output?.let {
try {
it.close()
} catch (e: IOException) {
Log.e(TAG, e.toString())
}
}
}
mContext.contentResolver.insert(Uri.fromFile(file), values)
}
}
Output:
java.lang.IllegalArgumentException: Unknown URL file:///storage/emulated/0/Android/data/(...)/DCIM/20200610165428492.jpg
at android.content.ContentResolver.insert(ContentResolver.java:1831)
at ...ImageSaver$run$1.run(ImageSaver.kt:86)
What should be the URI? Is there any better way to store location of a photo?
I am new to Android Studio and especially to Kotlin. I need to load image from internet and then save it to phone. I tried to load image with Glide as Bitmap and then save it. But it doesn't work. This code is best thing i found but it doesn't work.
try {
var bitmap = Glide.with(this)
.asBitmap()
.load("https://s3.amazonaws.com/appsdeveloperblog/Micky.jpg")
.apply(RequestOptions().override(100).downsample(DownsampleStrategy.CENTER_INSIDE).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE))
.submit().get()
val wrapper = ContextWrapper(applicationContext)
var file = wrapper.getDir("Images", Context.MODE_PRIVATE)
file = File(file, "img.jpg")
val out = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out)
out.flush()
out.close()
}
catch (e: Exception) {
println(e)
}
How I understood problem is in this ".submit().get()" part of Glide. But if I take it away then compress doesn't work.
submit(): Returns a future that can be used to do a blocking get on a background thread.
get(): Waits if necessary for the computation to complete, and then retrieves its result.
In your case to download an image from a url and save it to internal storage, you should use a background thread to do that. If you calling on main thread, your app might be throws ANR dialog.
Here I will demo how to download and save the image by using AsyncTask API
First write a class to download and save the image.
class DownloadAndSaveImageTask(context: Context) : AsyncTask<String, Unit, Unit>() {
private var mContext: WeakReference<Context> = WeakReference(context)
override fun doInBackground(vararg params: String?) {
val url = params[0]
val requestOptions = RequestOptions().override(100)
.downsample(DownsampleStrategy.CENTER_INSIDE)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
mContext.get()?.let {
val bitmap = Glide.with(it)
.asBitmap()
.load(url)
.apply(requestOptions)
.submit()
.get()
try {
var file = it.getDir("Images", Context.MODE_PRIVATE)
file = File(file, "img.jpg")
val out = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out)
out.flush()
out.close()
Log.i("Seiggailion", "Image saved.")
} catch (e: Exception) {
Log.i("Seiggailion", "Failed to save image.")
}
}
}
}
Then in activity just call
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DownloadAndSaveImageTask(this).execute("https://s3.amazonaws.com/appsdeveloperblog/Micky.jpg")
}
}
If you want to save to internal storage
var file = File(it.filesDir, "Images")
if (!file.exists()) {
file.mkdir()
}
file = File(file, "img.jpg")
It will save the image in path data/data/yourapppackagename/files/Images/img.jpg
Just use this method
fun DownloadImageFromPath(path: String?) {
var `in`: InputStream? = null
var bmp: Bitmap? = null
val iv = findViewById<View>(R.id.imagFullImage) as ImageView
var responseCode = -1
try {
val url = URL(path) //"http://192.xx.xx.xx/mypath/img1.jpg
val con: HttpURLConnection = url.openConnection() as HttpURLConnection
con.setDoInput(true)
con.connect()
responseCode = con.getResponseCode()
if (responseCode == HttpURLConnection.HTTP_OK) {
//download
`in` = con.getInputStream()
bmp = BitmapFactory.decodeStream(`in`)
`in`.close()
iv.setImageBitmap(bmp)
}
} catch (ex: Exception) {
Log.e("Exception", ex.toString())
}
}