I try to compress images with a large size >3MB to 200BK with my code. But It does not work. Although I put any quality, the output always with the same result. I don't know where I was wrong.
Please show me my mistake.
I want to convert PNG only.
MAX_SIZE_UPLOAD = 200KB. Ex:
input: 4900000, quality: 90 output = 32000000
input: 4900000, quality: 80 output = 32000000
override fun compress(file: File): Single<File> {
val result: SingleSubject<File> = SingleSubject.create()
val options = RequestOptions()
val optionsBitmap = BitmapFactory.Options()
val originSize = file.length()
optionsBitmap.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, optionsBitmap)
Glide.with(context)
.asBitmap().load(file)
.into(object : SimpleTarget<Bitmap>() {
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
}
override fun onResourceReady(
resource: Bitmap,
transition: com.bumptech.glide.request.transition.Transition<in Bitmap>?
) {
thread {
try {
val stream = ByteArrayOutputStream()
val quality = ((100 * MAX_SIZE_UPLOAD) / file.length())
resource.compress(Bitmap.CompressFormat.PNG, quality.toInt(), stream)
saveFileToCacheDir(stream.toByteArray())
.observeOnUiThread()
.subscribe({
result.onSuccess(it)
}, {
result.onError(Throwable())
})
} catch (e: Exception) {
result.onError(Throwable())
}
}
}
})
return result
}
override fun saveFileToCacheDir(data: ByteArray): Single<File> {
val result: SingleSubject<File> = SingleSubject.create()
try {
val file = File(context.cacheDir, "$FILE_NAME${System.currentTimeMillis()}")
file.createNewFile()
val fos = FileOutputStream(file)
fos.write(data)
fos.flush()
fos.close()
result.onSuccess(file)
} catch (e: IOException) {
result.onError(e)
}
return result
}
Here is the solution to compress image bitmap to file.
fun bitmapToFile(bitmap: Bitmap, context: Context): String {
// Get the context wrapper
val wrapper = ContextWrapper(context)
// Initialize a new file instance to save bitmap object
var file = wrapper.getDir("Images", Context.MODE_PRIVATE)
file = File(file, "${UUID.randomUUID()}.jpg")
try {
val stream: OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG,15,stream)
stream.flush()
stream.close()
} catch (e: IOException) {
e.printStackTrace()
}
// Return the saved bitmap uri
return file.absolutePath
}
Related
I implemented share image to other apps using intent, I followed the recommended approach by saving the image first then getting its LocalBitmapUri. but when i run the app and share image i get the Toast message [The file format is not supported]. and i cant seem to figure out what I did wrong.. thanks.
fun shareItem(url: String?,context: Context,scope: CoroutineScope) {
scope.launch {
withContext(Dispatchers.IO){
val i = Intent(Intent.ACTION_SEND)
i.type = "image/*"
i.putExtra(Intent.EXTRA_STREAM,
getBitmap("d",context,"https://cdn2.thecatapi.com/images/3o8.jpg")?.let {
getLocalBitmapUri(
it,
context
)
})
startActivity(context,Intent.createChooser( i, "Share Image"),null)
}
}
}
fun getLocalBitmapUri(bmp: Bitmap,context: Context): Uri? {
val builder = VmPolicy.Builder()
StrictMode.setVmPolicy(builder.build())
var bmpUri: Uri? = null
try {
val file = File(
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
"share_image_jj" + System.currentTimeMillis() + ".png"
)
val out = FileOutputStream(file)
bmp.compress(Bitmap.CompressFormat.PNG, 90, out)
out.close()
bmpUri = Uri.fromFile(file)
} catch (e: IOException) {
e.printStackTrace()
}
return bmpUri
}
private suspend fun getBitmap(tag: String, context: Context, imageUrl: String): Bitmap? {
var bitmap: Bitmap? = null
val imageRequest = ImageRequest.Builder(context)
.data(imageUrl)
.target(
...//,
onSuccess = { result ->
Log.e(tag, "Coil loader success.")
bitmap = result.toBitmap()
}
)
.build()
context.imageLoader.execute(imageRequest)
return bitmap
}
I'm using below code for downloading the image and it download successfully but not showing in galary please help!!!
I have tried couple of examples none of them are working..
I have also tried the MediaScanner to scan the file and update the gallery
If anyone have links related this question please share
class DownloadFileFromURL : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void): Void? {
var count: Int
try {
val url = URL(getUrl())
val conection: URLConnection = url.openConnection()
conection.connect()
// input stream to read file - with 8k buffer
val input: InputStream = BufferedInputStream(url.openStream(), 8192)
val output: OutputStream?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver: ContentResolver = activity?.contentResolver!!
val contentValues = ContentValues()
val name = System.currentTimeMillis()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "$name.jpg")
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
output = imageUri?.let { resolver.openOutputStream(it) }!!
} else {
val imgFile = createImageFile()
if (!imgFile.exists()) {
imgFile.createNewFile()
}
output = FileOutputStream(imgFile)
}
// Output stream to write file
val data = ByteArray(1024)
var total: Long = 0
while (input.read(data).also { count = it } != -1) {
total += count.toLong()
// writing data to file
output.write(data, 0, count)
}
// flushing output
output.flush()
// closing streams
output.close()
input.close()
} catch (e: Exception) {
Log.e("Error: ", e.message)
}
return null
}
override fun onPostExecute(result: Void?) {
downloadComplete()
}
#Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val storageDir: File = activity?.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
return File(
storageDir, /* directory */
"JPEG_${timeStamp}"+ /* prefix */
".jpg" /* suffix */
).apply {
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = absolutePath
}
}
}
I have created a function to upload images to drive in kotlin. It is as follow.
DriveServiceHelper class
class DriveServiceHelper(private val mDriveService: Drive) {
private val mExecutor: Executor =
Executors.newSingleThreadExecutor()
private val TAG = "DRIVE_TAG"
fun uploadFile(
localFile: java.io.File,
mimeType: String?, folderId: String?
): Any? {
return Tasks.call(mExecutor, Callable<Any> { // Retrieve the metadata as a File object.
val root: List<String>
root = folderId?.let { listOf(it) } ?: listOf("root")
val metadata =
File()
.setParents(root)
.setMimeType(mimeType)
.setName(localFile.name)
val fileContent = FileContent(mimeType, localFile)
val fileMeta =
mDriveService.files().create(
metadata,
fileContent
).execute()
val googleDriveFileHolder = GoogleDriveFileHolder()
googleDriveFileHolder.id=(fileMeta.id)
googleDriveFileHolder.name=(fileMeta.name)
googleDriveFileHolder
})
}
In my activity i call it as follows.
var mDriveServiceHelper: DriveServiceHelper? = null
private fun driveSetUp() {
val mAccount =
GoogleSignIn.getLastSignedInAccount(this)
val credential = GoogleAccountCredential.usingOAuth2(
applicationContext, setOf(Scopes.DRIVE_FILE)
)
credential.selectedAccount = mAccount!!.account
googleDriveService = Drive.Builder(
AndroidHttp.newCompatibleTransport(),
GsonFactory(),
credential
)
.setApplicationName("GoogleDriveIntegration 3")
.build()
mDriveServiceHelper = DriveServiceHelper(googleDriveService)
}
private fun uploadImageIntoDrive() {
driveSetUp()
val TAG = "image upload"
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, arrayList[0].uri)
try {
if (bitmap == null) {
Log.i(TAG, "Bitmap is null")
return
}
val file =
File(applicationContext.filesDir, "FirstFile")
val bos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos)
val bitmapdata = bos.toByteArray()
//write the bytes in file
val fos = FileOutputStream(file)
fos.write(bitmapdata)
fos.flush()
fos.close()
mDriveServiceHelper!!.uploadFile(file, "image/jpeg", null)
.addOnSuccessListener(OnSuccessListener<GoogleDriveFileHolder> { googleDriveFileHolder ->
Log.i(
TAG,
"Successfully Uploaded. File Id :" + googleDriveFileHolder.id)
})
.addOnFailureListener(OnFailureListener { e ->
Log.i(
TAG,
"Failed to Upload. File Id :" + e.message
)
})
} catch (e: Exception) {
Log.i(TAG, "Exception : " + e.message)
}
}
But the problem is in the uploadImageTodrive() function addOnSuccessListener is displayed in red color and says Unresolved Reference: addOnSuccessListener.
Please help
Your helper class returns type Any?:
fun uploadFile(
localFile: java.io.File,
mimeType: String?, folderId: String?
): Any? { ... // Look at the return type
But it actually should return Task<GoogleDriveFileHolder>:
fun uploadFile(
localFile: java.io.File,
mimeType: String?, folderId: String?
): Task<GoogleDriveFileHolder> { ... // Non-optional Task<GoogleDriveFileHolder>
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())
}
}