Android studio image saving to external storage - android

So I've created a function to save photos to gallery with the name of the application, everything was working just fine, I worked on other stuff in the app (which are not related to this function) then when I wanted to use this function again it stopped working, here is my code
fun saveImage(itemImage: View, activity: Activity) {
var fileName: String
val imageFromView = getBitmapFromView(itemImage)
ByteArrayOutputStream().apply {
Bitmap.createBitmap(imageFromView).compress(Bitmap.CompressFormat.JPEG, 100, this)
fileName = UUID.nameUUIDFromBytes(this.toByteArray()).toString().replace("-", "")
}
val imageFile = File("${Environment.getExternalStorageDirectory().absolutePath}/ChatOut/$fileName.jpg/")
val direct = File("${Environment.getExternalStorageDirectory().absolutePath}/ChatOut/").apply {
if (!exists())
mkdirs()
}
if (!imageFile.exists()) {
File(direct, "$fileName.jpg").apply {
FileOutputStream(this).apply {
Bitmap.createBitmap(imageFromView).compress(Bitmap.CompressFormat.JPEG, 100, this)
flush()
close()
}
}.let {
activity.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
ContentValues().apply {
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATA, it.absolutePath)
}
)
}
Toast.makeText(activity, "saved", Toast.LENGTH_SHORT).show()
} else
Toast.makeText(activity, "Already saved", Toast.LENGTH_SHORT).show()
}
this function checks if an image is already saved or not, if not then it saves it, I am getting a
/storage/emulated/0/ChatOut/0fe4706621ce318fb4e7292e16bcfb17.jpg: open failed: ENOENT (No such file or directory)
I have usage permission in my manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I made sure that the permission is granted as well.
for some reason no matter what I do the mkdirs() returns false, it never succeeds in creating the file or the directory...
Please help, and thank you for your time.

I've solved my problem by applying the following code, in case someone else is having the same problem
fun saveImage(itemImage: View, activity: Activity) {
val fileName: String
val imageFromView = getBitmapFromView(itemImage)
ByteArrayOutputStream().apply {
imageFromView.compress(Bitmap.CompressFormat.JPEG, 100, this)
fileName = UUID.nameUUIDFromBytes(this.toByteArray()).toString().replace("-", "")
}
val imageFile = File("${activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)}/ChatOut/$fileName.jpg/")
if (!imageFile.exists()) {
val contentResolver = ContentValues().apply {
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATA, imageFile.absolutePath)
}
activity.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentResolver).apply {
imageFromView.compress(Bitmap.CompressFormat.JPEG, 100, activity.contentResolver.openOutputStream(this!!))
}
Toast.makeText(activity, "saved", Toast.LENGTH_SHORT).show()
} else
Toast.makeText(activity, "Already saved", Toast.LENGTH_SHORT).show()
}
fun getBitmapFromView(view: View): Bitmap {
return Bitmap.createBitmap(view.width, view.height,Bitmap.Config.ARGB_8888).apply {
Canvas(this).apply {
view.draw(this)
}
}
}

Related

Install apk update from firbase | Android Kotlin

I have an android app that not on Google play and I want it to get updates using APK on the server.
This code is working and checks for updates by searching for APK on Firebase storage with greater version than the current build and ending it to download
private fun checkUpdates(){
val storage = FirebaseStorage.getInstance()
val storageRef = storage.reference
val rootRef = storageRef.root
rootRef.listAll().addOnSuccessListener { listResult ->
for (item in listResult.items) {
// item is a StorageReference for a file
val fileName = item.name
val version = fileName.substring(fileName.indexOf("app_v") + 5, fileName.indexOf(".apk"))
if (version.replace(".", "") > BuildConfig.VERSION_CODE.toString().replace(".", "")) {
val builder = AlertDialog.Builder(this)
builder.setMessage(R.string.new_up_av)
builder.setPositiveButton(R.string.install) { _, _ ->
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_STORAGE
)
download(version)
} else {
download(version)
}
}
val dialog = builder.create()
dialog.show()
}
else{
Toast.makeText(this, R.string.no_updates, Toast.LENGTH_LONG).show()
}
}
} .addOnFailureListener{
Toast.makeText(this, "ERROR", Toast.LENGTH_SHORT).show()
}
}
The download function is this (notice the comment on line 4):
private fun download(version: String) {
Toast.makeText(this, R.string.download_updates, Toast.LENGTH_SHORT).show()
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadUri = Uri.parse(//url for apk here.)
val request = DownloadManager.Request(downloadUri)
.setTitle(getString(R.string.download_updates))
.setDescription(getString(R.string.app_name))
val downloadId = downloadManager.enqueue(request)
// Check if the download was successful
if (downloadId == -1L) {
// Download failed, show an error message
Toast.makeText(this, "download failed", Toast.LENGTH_SHORT).show()
return
}
// Listen for the download to complete and install the update
val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
if (downloadId == id) {
installApk(context, "app_v$version.apk")
}
}
}
registerReceiver(downloadReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
on line 4 i verified it works manually and the problems is showing also with sure-working APKs like WhatsApp.
the installApk function:
fun installApk(context: Context, fileName: String) {
val file = File(Environment.getExternalStorageDirectory().toString() + "/Download/" + filename)
val fileUri = FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}
provider in Manifest:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android: resource="#xml/provider_paths" />
</provider>
provider_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="downloads" path="Download/" />
</paths>
The problem: seems like its downloading, starting to install, and than shows an error occurred while parsing the package message. no logs.
(It works manually)
I have tried many other ways buts I couldn't figure what cussing the problem.
If someone fammiliar with intalling apk programmatically I looking for help.

Getting 'Unresolved reference' for 'runOnUiThread'

While trying to save an image I am getting the error message 'Unresolved reference:runOnUiThread' and also on the context terms inside the toast messages. The relevant code snippets are as below. I have tried all possible fixes, but failed. Please help!
private suspend fun saveDrawnView(myBitmap: Bitmap): String {
var result = ""
withContext(Dispatchers.IO) {
if (myBitmap != null) {
try {
val bytes = ByteArrayOutputStream()
myBitmap.compress(Bitmap.CompressFormat.PNG, 90, bytes)
// val externalCacheDir = null
val f = File(
getExternalStorageDirectory()?.absolutePath.toString() +
File.separator + "com.example.drawingboardforkids" + System.currentTimeMillis() / 1000 + ".PNG"
)
val fo = FileOutputStream(f)
fo.write(bytes.toByteArray())
fo.close()
result = f.absolutePath
runOnUiThread{
if (!result.isEmpty()) {
Toast.makeText(
this#MainActivity,
"File saved successfully :$result",
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
this#MainActivity,
"Something went wrong while saving the file.",
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
result = ""
e.printStackTrace()
}
}
}
return result
}
}
I've checked the coroutine dependencies inside build gradle, went throught clean, rebuild, invalidate caches, restart the pc etc., but the problem persists and hence the query. Please help!
The problem resolved. It occurred because the function was out of the scope of the MainActivity. Such a silly mistake..sorry for taking up the space and time.

Delete a image from InternalStorage using ImagePath

I am working with Android version 10.
I have enabled Permissions to Read & Write Storage
Device Name : Poco F1
Scenario: I have to capture a screenshot of the current layout and save it to internalStorage and preview that image to the user. Here users have an option to delete the image.
Here are the codes I am using to save & delete
Saving a screenshot:
//I will pass the bitmap here
fun saveBitmapToInternalStorage(bitmap: Bitmap?) {
bitmap?.let {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
saveBitmapToOlderDevice(it)
} else {
saveBitmapToNewerDevice(it)
}
}
}
//This method is to save image to newerdevice >= Q
#RequiresApi(Build.VERSION_CODES.Q)
private fun saveBitmapToNewerDevice(bitmap: Bitmap) {
val uri = generateUri()
context.contentResolver.openOutputStream(uri ?: return).use { outputStream ->
outputStream?.let {
writeBitmapToJpeg(bitmap, outputStream, uri.toString())
}
}
}
//This is to generate the URI.
#RequiresApi(Build.VERSION_CODES.Q)
private fun generateUri(): Uri? {
val dateFormat = getDateFormat()
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "${dateFormat}.jpg")
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
put(MediaStore.MediaColumns.RELATIVE_PATH, "Pictures/${context.resources.getString(R.string.app_name)}")
}
return context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
}
// To save images to olderDevice
private fun saveBitmapToOlderDevice(bmp: Bitmap) {
val filename = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath +
"/${context.resources.getString(R.string.app_name)}/${getDateFormat()}.jpg"
createDirectory(filename)
val outputStream = FileOutputStream(filename)
writeBitmapToJpeg(bmp, outputStream, filename)
}
//This method is to save the image to InternalStorage
private fun writeBitmapToJpeg(bmp: Bitmap, outputStream: OutputStream, imagePath: String) {
try {
val outputData = ByteArrayOutputStream()
bmp.compress(CompressFormat.JPEG, 100, outputData)
outputData.writeTo(outputStream)
outputStream.flush()
outputStream.close()
} catch (e: IOException) {
showBitmapWriteErrorMessage()
}
}
I save the path while storing the image in internalStorgae
the path looks like
/storage/emulated/0/Pictures/TGP AR/20211011142001.jpg
and i pass this path into below method
To delete the image :
private fun deleteImage(imagePath: String) {
val file = File(imagePath)
file.delete()
}
file.exists() is returning true.
file.delete() is returning false.
I think, there might be two different ways to delete ( > & < Q ).
Please help me
You can delete the image by modifying your method to the following:
private fun deleteImage(imagePath: Uri) {
getContentResolver().delete(imagePath, null, null)
}
Then pass the Uri created in generateUri() to delete the file.

Kotlin saving video to gallery

I tried saving video to gallery like i found on example:
private fun saveVideoToInternalStorage3(filePath: String) {
val newfile: File
try {
val currentFile = File(filePath)
val fileName = currentFile.name
val cw = ContextWrapper(applicationContext)
val directory = cw.getDir("videoDir", Context.MODE_PRIVATE)
newfile = File(directory.absolutePath, fileName)
if(currentFile.exists()) {
val `in`: InputStream = FileInputStream(currentFile)
val out: OutputStream = FileOutputStream(newfile)
// Copy the bits from instream to outstream
val buf = ByteArray(1024)
var len: Int
while(`in`.read(buf).also {len = it} > 0) {
out.write(buf, 0, len)
}
scanner(filePath)
`in`.close()
out.close()
Log.d("", "Video file saved successfully.")
Toast.makeText(this, "Push Video Saved TO Gallery", Toast.LENGTH_SHORT).show()
} else {
Log.d("", "Video saving failed. Source file missing.")
}
} catch(e: java.lang.Exception) {
Log.d("", "EXS " + e.message)
e.printStackTrace()
}
And it works fine it shows message "Video file saved successfully." But there is no video in my gallery.
After searching for problem i found post that says you need a scanner and i tried:
private fun scanner(path: String) {
MediaScannerConnection.scanFile(this, arrayOf(path), null, object: OnScanCompletedListener {
override fun onScanCompleted(path: String, uri: Uri?) {
Log.i("", "Finished scanning $path")
}
})
But still no luck. Does any1 know what is going on? Saving images in gallery works fine btw.

Android Change the Gallery Album name of stored Images

I am trying to save images to an album with the app name inside the gallery, so far I managed to save the image into the gallery but the problem is that the album name is always "Pictures", I've checked all of the other posts out there and nothing worked for me...
here is my code
val fileName = "abc"
val ImageToSave /*the image that I save, I send it value through method*/
val imageDir = File(activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES),"appName")
val image = File(imageDir,fileName)
if (!imageDir.exists())
imageDir.mkdirs()
val contentValues = ContentValues()
contentValues.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
contentValues.put(MediaStore.Images.Media.DATA, image.absolutePath)
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
contentValues.put(MediaStore.Images.Media.BUCKET_DISPLAY_NAME, "appName")
val url = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
val out = contentResolver.openOutputStream(url)
imageFromView.compress(Bitmap.CompressFormat.JPEG, 100, out)
Toast.makeText(activity, "saved", Toast.LENGTH_SHORT).show()
I am saving the image without any problem I just want to change the album name. thank you in advance...
UPDATE
I tried to just create the image file without the contentValue but it appears that something is wrong with the file I keep getting an error that says "File does not exist" here is my code now
val imageDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "appName")
if (!imageDir.exists())
imageDir.mkdirs()
val image = File(imageDir, fileName)
if (!image.exists()) {
val out = FileOutputStream(image)
ImageToSave.compress(Bitmap.CompressFormat.JPEG, 100, out)
Toast.makeText(activity, "saved", Toast.LENGTH_SHORT).show()
} else
Toast.makeText(activity, "Already saved", Toast.LENGTH_SHORT).show()
also, I made sure that I have the ask permission in my manifest file, and I am asking for permission when the user presses on the button... I made sure that my application has permission to read and write to files....
After a lot of research, I was able to find a solution for my problem, all I had to do is add
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/AppName/")
this will create a folder with your app name inside the pictures folder, in the gallery, it will appear with the app name you entered... for extra detail read this link
I found out that a huge chunk of the code that I wrote was not needed,I ended up removing all the extra stuff, this is the end result
UPDATE
I've updated the code so it works with the old versions of android as well
fun saveImage(itemImage: View, context: Context) {
val imageName: String
val imageToSave = getBitmapFromView(itemImage)
val exists: Boolean
ByteArrayOutputStream().apply {
imageToSave.compress(Bitmap.CompressFormat.JPEG, 100, this)
imageName = "ChatOut_" + UUID.nameUUIDFromBytes(this.toByteArray()).toString().replace("-", "") + ".jpg"
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,arrayOf(MediaStore.Images.Media.DISPLAY_NAME), "${MediaStore.Images.Media.DISPLAY_NAME} = '$imageName' ", null, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC").let {
exists = it?.count ?: 0 >= 1
it?.close()
}
if (!exists) {
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DISPLAY_NAME, imageName)
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/ChatOut/")
}
val url = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
val out = context.contentResolver.openOutputStream(url)
imageToSave.compress(Bitmap.CompressFormat.JPEG, 100, out)
Toast.makeText(context, "saved", Toast.LENGTH_SHORT).show()
} else
Toast.makeText(context, "Already saved", Toast.LENGTH_SHORT).show()
}else{
val imageDir = File("${Environment.getExternalStorageDirectory()}/ChatOut/")
if (!imageDir.exists())
imageDir.mkdirs()
val image = File(imageDir,imageName)
if (!image.exists()){
val outputStream = FileOutputStream(image)
imageToSave.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
outputStream.close()
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DISPLAY_NAME, imageName)
put(MediaStore.Images.Media.DATA, image.absolutePath)
}
context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
Toast.makeText(context, "saved", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(context, "Already saved", Toast.LENGTH_SHORT).show()
}
}
}
fun getBitmapFromView(view: View): Bitmap {
return Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888).apply {
Canvas(this).apply {
view.draw(this)
}
}
}
I hope this helps others :) have a good time!
It is because you are using Environment.DIRECTORY_PICTURES
You should create a custom directory instead, here is an example:
String path = Environment.getExternalStorageDirectory().toString();
File appDirectory = new File(path + "/" + "FolderName");
appDirectory.mkdirs();

Categories

Resources