I'm working with SAF (Storage access framework) to write down video files onto SD-CARD.
I've successfully create files, read them and moving.
But i have found the issue with deleting files.
When i delete files using SAF. Files are gone, but space isn't recover.
For example: when 1 have 2GB left out of 10GB.
And i delete file (1GB total) space left is 2GB, not 3GB.
I've used: enter code here
val file: DocumentFile = DocumentFile.fromTreeUri(ctx, treeUri)
file?.delete()
Even:
DocumentsContract.deleteDocument(
activity.contentResolver,
uri
)
and even this:
val path = "/storage/${external?.name}/root.img"
val f = File(path)
if (f.exists()) {
val deleteCmd = "rm -r $path"
val runtime = Runtime.getRuntime()
try {
runtime.exec(deleteCmd)
} catch (e: IOException) {
e.printStackTrace()
}
}
app.applicationContext.deleteFile(f.name)
f.absoluteFile.delete()
f.exists()
Related
I am currently working with one of my app updates and I am looking for a way to save some files at the root of shared internal storage.
Don't confuse with the word shared here. I just meant here with the phone's internal/external storage which holds a large amount of data.
Now coming to the main point, I have an app that uses the FFmpeg library for android and it records the live streams and saves it into the phone's storage.
Now here's the problem I don't want to save this file in my app package folder.
e.g: /storage/emulated/0/Android/data/com.app.package/files/...
I want to save this video file in the root of the storage folder where I can create a special folder for my app just like WhatsApp then save all required data in it only.
e.g: /storage/emulated/0/MyApp/...
To be more concise I want my app just like WhatsApp which has a separate folder in internal storage for storing its app-related data.
Now, so far I tried these.
getExternalFilesDir(null)?.absolutePath -> /storage/emulated/0/Android/data/com.app.package/files
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath -> /storage/emulated/0/Android/data/com.app.package/files/Download
externalCacheDir?.absolutePath -> /storage/emulated/0/Android/data/com.app.package/cache
filesDir?.absolutePath -> /data/user/0/com.app.package/files
Environment.getRootDirectory().absolutePath -> /system
Environment.getExternalStorageDirectory().absolutePath -> /storage/emulated/0
Environment.getExternalStoragePublicDirectory("").absolutePath -> /storage/emulated/0
Now these to methods can work for me but they are deprecated getExternalStorageDirectory() & getExternalStoragePublicDirectory()
Just keep in mind my solution is related to the WhatsApp storage folder.
I searched a lot but not so much help as I don't want to go with just a copy-paste and hack solutions. I want to do it in a clean way.
I am trying to open a File Picker via intent for this I have set permission
....
....
and for intent.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
intent.flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
startActivityForResult(Intent.createChooser(intent, "Select .... file"), RC)
Now here on Android 10, a file picker is not opening and this works perfectly if I add the requestLegacyExternalStorage flag in the manifest.
Or
if I set android:maxSdkVersion="29" also worked but here google wants to use Some SAF or Media Store API but I don't get it I just want to pick a file just a simple txt nothing else.
You can save video file to external storage using this:
#RequiresApi(api = Build.VERSION_CODES.Q)
#Throws(FileNotFoundException::class)
fun addToApiAbove29Gallery(
context: Context,
file: File?,
fileName: String?,
destinationPath: String?,
action: () -> Unit
) {
val valuesvideos: ContentValues
valuesvideos = ContentValues()
valuesvideos.put(
MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM
+ File.separator + context.resources.getString(R.string.app_name)
+ File.separator + File(destinationPath).name
)
valuesvideos.put(MediaStore.Video.Media.TITLE, "${fileName}.mp4")
valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, "${fileName}.mp4")
valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")
valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis())
valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 1)
val resolver = context.contentResolver
val collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uriSavedVideo = resolver.insert(collection, valuesvideos)
val pfd = context.contentResolver.openFileDescriptor(uriSavedVideo!!, "w")
val executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
val handler = Handler(Looper.getMainLooper())
executor.execute {
if (pfd != null) {
try {
val out =
FileOutputStream(pfd.fileDescriptor)
val inputStream = FileInputStream(file)
val buf = ByteArray(8192)
var len: Int
while (inputStream.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
out.close()
inputStream.close()
pfd.close()
valuesvideos.clear()
valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 0)
try {
setExifFromUri(
context,
uriSavedVideo,
File(destinationPath)
)
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
}
handler.post {
context.contentResolver.update(uriSavedVideo, valuesvideos, null, null)
action()
}
}
}
}
Note that this code is only for android 10 and above, because you can easily save the video file to ecternal storage below android 10.
I am working on an android application where i have used FileObserver to get data changes on a specific folder. The folder is WhatsApp Images and whenever there is a new file added to that folder i perform my further work when onEvent of FileObserver is triggered.
Everything works fine until i set targetSdkVersion=29 but as i am migrating my project to targetSdkVersion=30 FileObserver stopped working.
Below is my code for FileObserver
import android.os.Environment
import android.os.FileObserver
import android.util.Log
import java.io.File
class WhatsAppImageObserver11(var notify:(msg:String)->Unit) : FileObserver(
File(
Environment.getExternalStorageDirectory().toString(),
Constants.whatsapp_images_path11).toString(), ALL_EVENTS
) {
init {
Log.d("WhatsAppImageObserver11", "start")
}
override fun onEvent(i: Int, str: String?) {
val str2 = "WhatsAppImageObserver11"
if (i == CREATE || i == MOVED_TO && str != ".probe") {
val sb = StringBuilder()
sb.append("create File path--> ")
sb.append(str)
Log.d(str2, sb.toString())
try {
val whatsDeleted = File(
Environment.getExternalStorageDirectory().path,
Constants.whatsapp_reserved_media
)
if(!whatsDeleted.exists()) {
whatsDeleted.mkdirs()
}
val srcFile = File(
Environment.getExternalStorageDirectory(),
Constants.whatsapp_images_path11+str)
val destFile = File(Environment.getExternalStorageDirectory(), Constants.whatsapp_reserved_media+str)
if (srcFile.exists()){
srcFile.copyTo(target = destFile, overwrite = false, bufferSize = DEFAULT_BUFFER_SIZE)
}
} catch (e: Exception) {
val sb2 = StringBuilder()
sb2.append("create error: ")
sb2.append(e.toString())
Log.d(str2, sb2.toString())
}
}
if (i and 512 != 0 || i and 1024 != 0) {
val sb3 = StringBuilder()
sb3.append("dlete File path--> ")
sb3.append(str)
Log.d(str2, sb3.toString())
try {
val whatsDeleted = File(
Environment.getExternalStorageDirectory().path,
Constants.new_whatsapp_deleted_media
)
if(!whatsDeleted.exists()) {
whatsDeleted.mkdirs()
}
val srcFile = File(Environment.getExternalStorageDirectory().path, Constants.whatsapp_reserved_media+str)
val destFile = File(Environment.getExternalStorageDirectory().path, Constants.new_whatsapp_deleted_media+str)
if (srcFile.exists()){
srcFile.copyTo(target = destFile, overwrite = false, bufferSize = DEFAULT_BUFFER_SIZE)
srcFile.delete()
notify(destFile.absolutePath)
}
} catch (e2: Exception) {
val sb4 = StringBuilder()
sb4.append("del error: ")
sb4.append(e2.toString())
Log.d(str2, sb4.toString())
}
}
}
}
Here in this FileObserver i am copying media from WhatsApp Reserved Media to My own WhatsAppDeleted folder for media recovery feature.
What i have tried?
1- As i know Environment.getExternalStorageDirectory() is deprecated i tried replacing it with mContext.getExternalFilesDir(null).getAbsolutePath()
2- Also checked using android:preserveLegacyExternalStorage="true" in Manifest.
3- Tried added ignore attribute in
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage"/>
What is Required?
1- FileObserver on path "/Android/media/" + WHATSAPP_PKG_NAME + "/WhatsApp/Media/WhatsApp Images/" should trigger onEvent when i set targetSdkVersion-30
Can somebody please help me out with this? Any help will be appreciated.
Thank you
As written here:
Apps that run on Android 11 but target Android 10 (API level 29) can still request the requestLegacyExternalStorage attribute. This flag allows apps to temporarily opt out of the changes associated with scoped storage, such as granting access to different directories and different types of media files. After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag.
Basically, you don't have permission to "listen" to external directories in the device, but only to your own internal/external directories.
You should use the MediaStore API in order to access media files which have been scanned by the OS (Including WhatsApp's files).
You can use components like JobService to register for the event of a new media been scanned.
Enjoy
Simple to do in every system but Android, but I have and app that needs to create a csv file in SHARED EXTERNAL Download folder. Shutdown/restart phone/app and the file persist. Then I need to delete the file (persistent URI somewhere?) before I recreate it with new data. (if exists() delete)
I see all kinds of doc pointing to SAF for this process, but can find no examples anywhere of how to perform this simple process of create/delete. If is can do this on API 28 and it work on 29+ that would be great.
Without SAF I've done this:
fun writeCSV() {
val CSV_HEADER = "Last,First\n"
val mockData = "Tester,Joe\n"
val contentValues = ContentValues()
contentValues.put(MediaStore.Downloads.DISPLAY_NAME, "aTest.csv")
contentValues.put(MediaStore.Downloads.MIME_TYPE, "text/csv")
contentValues.put(MediaStore.Downloads.IS_PENDING, true)
val uri = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val fileUri = contentResolver.insert(uri, contentValues)
if (fileUri != null) {
try {
val outputStream = contentResolver.openOutputStream(fileUri)
outputStream!!.write(CSV_HEADER.toByteArray())
outputStream!!.write(mockData.toByteArray())
outputStream!!.close()
contentValues.put(MediaStore.Images.Media.IS_PENDING, false)
contentResolver.update(fileUri, contentValues, null, null)
} catch (e: IOException) {
e.printStackTrace()
}
}
}
The problem is when fun is called again I need to delete this file and recreate it with new data because Android creates "aTest (1).csv" instead of overwriting it.
I want to copy my local SQLite database out of data/data folder and see its content in some db browser for debug purposes.
Is there any way how to copy it out to root folder?
I used this function 2 months ago, but as I updated my Android Studio from 3.5 to 4.1, it is not working anymore. I cant even see content of databases folder using adb shell. It says Permission denied on both debug and release build version.
fun backupDatabase(){
try {
val sd: File = Environment.getExternalStorageDirectory()
if (sd.canWrite()) {
val currentDBPath = "/data/data/${app.packageName}/databases/$DB_NAME"
val backupDBPath = "backupDB.db"
val currentDB = File(currentDBPath)
val backupDB = File(sd, backupDBPath)
if (currentDB.exists()) {
val src: FileChannel = FileInputStream(currentDB).channel
val dst: FileChannel = FileOutputStream(backupDB).channel
dst.transferFrom(src, 0, src.size())
src.close()
dst.close()
}
}
} catch (e: java.lang.Exception) {
App.log("BackupEx: ${e.localizedMessage}")
}
}
Follow this directory pattern in your Device Explorer on Android Studio.
data -> data -> com.example.appname -> databases.
Then you copy three files:
One with your database name, the other with a "-shm" suffix and another with a "-wal" suffix.
Open all of these files in your SQLite browser.
As per Android 10 update, Reading and writing from external storage is restricted even we have
WRITE_EXTERNAL_STORAGE
I want to move my pdf file from internal storage (private of my android app) to DOWNLOADS folder. I tried so many approaches but nothing worked and there is no any help available on internet related to accessing external storage in android 10.
Privacy changes in Android 10 | Android Developers
https://developer.android.com
hope below code will help you to resolve your issue and copy any file from internal to external storage.
fun copyPdfFrom(context: Context, sourceFile: File, destinationURIString: String) {
val destinationURI = Uri.parse(destinationURIString)
try {
val bufferedInputStream = BufferedInputStream(FileInputStream(sourceFile.absoluteFile))
val outPutStream = context.contentResolver.openOutputStream(destinationURI)!!
var len = 0
val brr = ByteArray(1024)
while ((bufferedInputStream.read(brr, 0, brr.size).also { len = it }) != -1) {
outPutStream.write(brr, 0, len)
}
outPutStream.flush()
outPutStream.close()
bufferedInputStream.close()
}
catch (e: Exception) {
e.printStackTrace()
}
}