I am making a specimen inventorying android app in Kotlin using Google Firebase. I use the firebase Realtime Datebase to store the specimen details & use firebase storage to store a picture of the specimen.
Eventually I will want to pull all of this data into the app to browse.
My Question: What is the best way to link the specimen's details in Realtime db to it's associated picture in Storage?
my code
RockEntry.kt
package com.inven.rock_stock
import android.util.Log
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import java.util.*
class RockEntry {
var name = ""
var purchDate = ""
var local = ""
var mine = ""
var weight = ""
var paid = ""
var asking = ""
var description = ""
var dimensions = ""
var specimenNumber = ""
var Uid = ""
var database = FirebaseDatabase.getInstance()
var ref = database.getReference("Rocks")
constructor(name:String,purchDate:String,local:String,mine:String,
weight:String,dimensions:String,paid:String,asking:String,
description:String,Uid:String){
this.name = name
this.purchDate = purchDate.toString()
this.local = local
this.mine = mine
this.weight = weight
this.dimensions = dimensions
this.paid = paid
this.asking = asking
this.description = description
this.Uid = UUID.randomUUID().toString()
}
MainActivity.kt
package com.inven.rock_stock
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.tasks.Continuation
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import kotlinx.android.synthetic.main.activity_main.*
import java.io.ByteArrayOutputStream
import java.io.File
import java.net.URI
var CAMERA_REQUEST_CODE = 0
var database = FirebaseDatabase.getInstance()
var ref = database.getReference("Rocks")
private var mStorageRef: StorageReference? = null
class MainActivity : AppCompatActivity() {
private val TAG = "MyActivity"
override fun onCreate(savedInstanceState: Bundle?) {
mStorageRef = FirebaseStorage.getInstance().getReference("ImagesBB")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
makeQuery()
}
imageBtn.setOnClickListener {
takePicture()
}
}
private fun makeQuery(){
var name = name.text.toString()
var purchDate = purchDate.toString()
var local = locality.text.toString()
var mine = mine.text.toString()
var weight = weight.text.toString()
var dimensions = dimensions.text.toString()
var paid = paid.text.toString()
var asking = asking.text.toString()
var description = description.text.toString()
if (!name.isBlank()) {
ref.child(name.toLowerCase()).setValue(
RockEntry(
name,
purchDate,
local,
mine,
weight,
paid,
asking,
dimensions,
description
)
)
}
else {
Toast.makeText(applicationContext, "Type in a name", Toast.LENGTH_LONG).show()
}
}
private fun takePicture() {
CAMERA_REQUEST_CODE = 222
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, CAMERA_REQUEST_CODE)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
CAMERA_REQUEST_CODE -> {
if (resultCode == Activity.RESULT_OK && data != null) {
val imageBitmap = data.extras?.get("data") as Bitmap
val baos = ByteArrayOutputStream()
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos)
val datar = baos.toByteArray()
mStorageRef!!.putBytes(datar)
}
}
}
}
}
'Linking' a FireBase Storage file to your Realtime Database can be accomplished by either
getting the location where a file lives in FireBase storage, via
toString() on the storage reference
or
getting a URL where you can download the file, via the .downloadUrl function.
Either of these options can then be stored as text into a Realtime Database 'entry'.
code example:
yourStorageBase?.putFile(element)?.addOnSuccessListener {
yourStorageBase?.downloadUrl?.addOnSuccessListener { downloadUri ->
// #1
var filepath = yourStorageBase.toString()
// #2
var downloadLink = downloadUri.toString()
}
}
Note:
If the following code is used within a function, you may 'prematurely' return to the caller before the picture has finished uploading, meaning you can't use the 'links' immediately. You can write a coroutine to ensure the IO has time to complete.
User RecyclerView to load all data in your app using live model class with data adapter.
User DataSnapShot in firebase to get all of the child nodes from a parent.
In Firebase DB, You have specimen which holds a number of Specimen, then you need to have one picture node as a child of each specimen node. When you upload the picture to Firebase Storage fetch its URL and insert it to the picture node under that particular Specimen.
Related
I want to parse an excel file in "external storage" with apache POI on an android phone. I put the file in the downloads folder when I downloaded it. I can always expect it to be there, not on a cloud, not being edited by other apps, not ephemeral in any way. The Apache POI workbookfactory needs a FileInputStream. After trying many different things I am burnt out.
Other similar questions on here involve huge swaths of code that deal in images and fanciful sources. All the examples I find use startActivityForResult which is DEPRECATED. So I tried some registerForActivityResult using contracts. No dice. Anyone know why the mime type "application/vnd.ms-excel.sheet.macroEnabled.12" doesn't work for xlsm files but images/png does? I also tried copying the file from content URI to local/app/scoped/cachedir storage and I wasn't successful either.
I was expecting the ability to point to a file and say "into the Apache POI excel file wood chipper with you!" and like in Fargo the Apache POI would spit little red cell values all over Log.d and eventually I'll do stuff with that information.
import android.content.ContentResolver
import android.net.Uri
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.provider.OpenableColumns
import android.util.Log
import android.widget.Button
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import org.apache.commons.compress.utils.IOUtils
import org.apache.poi.ss.usermodel.WorkbookFactory
import java.io.*
import java.nio.channels.FileChannel
fun readingxl(input: InputStream?) {
//Workbook wb = Workbookfactory.create(new File(bob.getPath()))
//Log.d("wtf",bob.toString())
//val input = FileInputStream("./text.xlsm")
//val xlWb = WorkbookFactory.create(input)
//val input = FileInputStream(bob.getPath())
val xlWb = WorkbookFactory.create(input)
Log.d("wtf","b")
//val xlWb = WorkbookFactory.create(input)
Log.d("wtf","c")
val xlWs = xlWb.getSheet("Daily Recording")
Log.d("wtf","d")
for (j in 44..50) {
for (i in 0..11) {
//Log.d("wtf",((("${xlWs.getRow(j).getCell(i)}, ")).toString()))
Log.d("wtf", "${xlWs.getRow(j).getCell(i)}, ")
}
}
}
fun ContentResolver.getFileName(fileUri: Uri): String {
var name = ""
val returnCursor = this.query(fileUri, null, null, null, null)
if (returnCursor != null) {
val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
name = returnCursor.getString(nameIndex)
returnCursor.close()
}
return name
}
class MainActivity : AppCompatActivity() {
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// 'ActivityResultCallback': Handle the returned Uri
if (uri != null) {
Log.d("wtf",uri.getPath().toString())
val inputStream = applicationContext.contentResolver.openInputStream(uri)
readingxl(inputStream)
/*
val parcelFileDescriptor = applicationContext.contentResolver.openFileDescriptor(uri, "r", null)
parcelFileDescriptor?.let {
val inputStream = FileInputStream(parcelFileDescriptor.fileDescriptor)
val file = File(applicationContext.cacheDir, "text.xlsm")//applicationContext.contentResolver.getFileName(uri))
val outputStream = FileOutputStream(file)
IOUtils.copy(inputStream, outputStream)
}*/
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
///storage/emulated/0/Download
var button = findViewById<Button>(R.id.button)
button.setOnClickListener{
//getContent.launch("*/*")
getContent.launch("*/*")
}
}
}
I want to save images taken from my app directly to a ssd drive (removable storage) plugged in my device.
The issue I have now, is that with Android 11, I didn't manage to get the path of this storage, and so I can't write the files...
I tried use Storage Access Framework to ask the user to specify the path directly for each images but I can't use this solution as I need to write 30 images per seconds and it kept asking the user select an action on the screen.
This application is only for internal use, so I can grant all the permission without any Google deployment politics issues.
Can anybody help me, i'm so desperate...
So here's my code, I can write on a folder the user choose with SAF. Still have speed issue using DocumentFile.createFile function.
package com.example.ssdwriter
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.*
import android.util.Log
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
class MainActivity : AppCompatActivity() {
private val TAG = "SSDActivity"
private val CONTENT = ByteArray(2 * 1024 * 1024)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
grantDirectoryAccess()
}
private fun grantDirectoryAccess() {
val treeUri = contentResolver.persistedUriPermissions
if (treeUri.size > 0) {
Log.e(TAG, treeUri.size.toString())
startWriting(treeUri[0].uri)
} else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
var resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data: Intent? = result.data
result.data?.data?.let {
contentResolver.takePersistableUriPermission(
it,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
}
startWriting(result.data?.data!!)
}
}
resultLauncher.launch(intent)
}
}
private fun startWriting(uri: Uri) {
var handlerThread = HandlerThread("writer")
handlerThread.start()
var counter = 0
val handler = Handler(handlerThread.looper)
val runnableCode: Runnable = object : Runnable {
override fun run() {
Log.e(TAG, "Writing File $counter")
createFile(uri, counter++)
Log.e(TAG, "File $counter written ")
if(counter <= 150){
handler.postDelayed(this, 33)
}
}
}
handler.post(runnableCode)
}
private fun createFile(treeUri: Uri, counter: Int) {
val dir = DocumentFile.fromTreeUri(this, treeUri)
val file = dir!!.createFile("*/bmp", "Test$counter.bmp")
if (file != null) {
var outputStream = contentResolver.openOutputStream(file.uri)
if (outputStream != null) {
outputStream.write(CONTENT)
outputStream.flush()
outputStream.close()
}
}
}
}
If anyone got some clues to make this faster, it would be great !
i'm making an app on AndroidStudio and I need to verify credentials when they log in to the app. The app works with an API and to verifiy credentials i created this function in the database to check someones email and password:
(postgresql)
create or replace function login (emailf text, passwordf text)
returns boolean
language plpgsql
as
$$
declare pp text;
begin
pp = (select pass_w from utilizador where utilizador.email = emailf);
if (pp = passwordf) then return true;
else return false;
end if; end
$$
I'm parsing the data through this CheckLoginas function:
var bola: Boolean? = null
fun CheckLoginas(c: Context?, email: String, pass: String): Boolean? {
var mQueue: RequestQueue
mQueue = Volley.newRequestQueue(c);
var url = "https://myurl.com" + "/utilizador/login/" + email + "/" + pass
val request = JsonArrayRequest(Request.Method.GET, url, null, Response.Listener {
response ->try {
var jsonArray = JSONArray()
jsonArray = response.getJSONArray(0)
for (i in 0 until jsonArray.length())
{
val jsonObject : JSONObject? = jsonArray.getJSONObject(i)
//val user = jsonArray.getJSONObject(i)
//val bool = jsonObject.getBoolean("login")
val boo : Boolean = jsonObject!!.getBoolean("login")
println("im inside CheckLoginas boo $boo\n\n")
bola = boo
}
} catch (e: JSONException) {
e.printStackTrace()
}
}, Response.ErrorListener { error -> error.printStackTrace() })
mQueue?.add(request)
return bola
}
'bola' variable is a global variable because I needed to return a boolean from the function so I can know if the credentials check (or not) in another activity.
The Problem:
To login when the credentials are correct, I have to press twice in the login button. If the email and password are correct, the first time I press it gives me the "Wrong credentials" error and in the second time it logs in. I already tried to do it with a while(), I checked it step by step and it seems fine, nothing seems to work to fix this error... The function works, the API too, and the app itself kinda works too, it just has this bug of clicking twice on the button... This is the activity code:
package com.example.crowdzero
import CheckLoginas
import Database
import android.content.Intent
import android.os.Bundle
import android.view.View.OnFocusChangeListener
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.textfield.TextInputLayout
import java.lang.Thread.sleep
class Login : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val log_in_btn_log_in = findViewById<Button>(R.id.log_in_btn_log_in)
val log_in_btn_registar = findViewById<Button>(R.id.log_in_btn_registar)
log_in_btn_log_in.setOnClickListener {
verificacao()
}
log_in_btn_registar.setOnClickListener {
val intent = Intent(this, Registo::class.java)
startActivity(intent)
}
}
private fun verificacao() {
val log_in_input_text_email = findViewById<TextInputLayout>(R.id.log_in_input_text_email)
val log_in_input_text_password = findViewById<TextInputLayout>(R.id.log_in_input_text_password)
val string_email = log_in_input_text_email?.getEditText()?.getText().toString()?.trim()
val string_password = log_in_input_text_password?.getEditText()?.getText().toString()?.trim()
if (string_email.isNullOrEmpty())
{
log_in_input_text_email.setError(" ")
}
else if (string_password.isNullOrEmpty())
{
log_in_input_text_password.setError(" ")
}
else
{
val email = log_in_input_text_email.editText?.text.toString()
val password = log_in_input_text_password.editText?.text.toString()
//var baca = CheckLoginas(this,email,password)
println(email)
println(password)
var baca: Boolean? = null
baca = CheckLoginas(this, email, password)
//baca = CheckLoginas(this,email,password)
if (baca == false) {
//Toast.makeText(this, "Esta conta não está registada", Toast.LENGTH_SHORT).show();
println("Im inside if in login baca $baca")
} else if (baca == true) {
Toast.makeText(this, email, Toast.LENGTH_SHORT).show();
Toast.makeText(this, password, Toast.LENGTH_SHORT).show();
val intent = Intent(this, Home::class.java)
startActivity(intent)
finish()
}
}
}
}
When I test this with an actual email and password from the database, baca variable stays false when it should be true, since CheckLoginas boo var is true. This is what is causing the problem.
image that shows it
I'm fairly new to the Database-API-App thing, so please forgive me if its a trivial thing
You are calling baca = CheckLoginas(this, email, password)
baca will not update immedietly, the next line if (baca == false) will be executed before you API response arrives, so after you got some response baca becomes true. This is why you need to click twice.
SOLVED:
I pretty much inserted the CheckLoginas function inside the login.kt file. It works now! It looks like this now:
package com.example.crowdzero
import Database
import android.content.Intent
import android.os.Bundle
import android.view.View.OnFocusChangeListener
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.JsonArrayRequest
import com.android.volley.toolbox.Volley
import com.google.android.material.textfield.TextInputLayout
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.lang.Thread.sleep
class Login : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val log_in_btn_log_in = findViewById<Button>(R.id.log_in_btn_log_in)
val log_in_btn_registar = findViewById<Button>(R.id.log_in_btn_registar)
log_in_btn_log_in.setOnClickListener {
verificacao()
}
log_in_btn_registar.setOnClickListener {
val intent = Intent(this, Registo::class.java)
startActivity(intent)
}
}
private fun verificacao() {
val log_in_input_text_email = findViewById<TextInputLayout>(R.id.log_in_input_text_email)
val log_in_input_text_password = findViewById<TextInputLayout>(R.id.log_in_input_text_password)
val string_email = log_in_input_text_email?.getEditText()?.getText().toString()?.trim()
val string_password = log_in_input_text_password?.getEditText()?.getText().toString()?.trim()
if (string_email.isNullOrEmpty())
{
log_in_input_text_email.setError(" ")
}
else if (string_password.isNullOrEmpty())
{
log_in_input_text_password.setError(" ")
}
else
{
val email = log_in_input_text_email.editText?.text.toString()
val password = log_in_input_text_password.editText?.text.toString()
var mQueue: RequestQueue
mQueue = Volley.newRequestQueue(this);
var url = "https://myurl.com" + "/utilizador/login/" + email + "/" + password
val request = JsonArrayRequest(Request.Method.GET, url, null, Response.Listener {
response ->try {
var jsonArray = JSONArray()
jsonArray = response.getJSONArray(0)
for (i in 0 until jsonArray.length())
{
val jsonObject : JSONObject? = jsonArray.getJSONObject(i)
//val user = jsonArray.getJSONObject(i)
//val bool = jsonObject.getBoolean("login")
val boo : Boolean = jsonObject!!.getBoolean("login")
println("im inside CheckLoginas boo $boo\n\n")
if (boo == false) {
Toast.makeText(this, "Esta conta não está registada", Toast.LENGTH_SHORT).show();
} else if (boo == true) {
Toast.makeText(this, email, Toast.LENGTH_SHORT).show();
Toast.makeText(this, password, Toast.LENGTH_SHORT).show();
val intent = Intent(this, Home::class.java)
startActivity(intent)
finish()
}
}
} catch (e: JSONException) {
e.printStackTrace()
}
}, Response.ErrorListener { error -> error.printStackTrace() })
mQueue?.add(request)
}
}
}
kotlin code for android app
This is the code for reading the data from the firebase firestore database.. but unable to get data from firestore database where is the falt in the code
these two Error show in logcat:
2021-05-09 02:12:26.315 19481-19481/? E/raunak.alison_: Unknown bits set in runtime_flags: 0x8000
2021-05-09 02:12:26.828 19481-19481/com.raunak.alison_4 E/libc: Access denied finding property "ro.vendor.df.effect.conflict"
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.auth.User
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.comment_list_view.*
import java.util.*
class home : AppCompatActivity() {
var selectCategery = Funny
lateinit var commentAdopter: CommentAdopter
val comment_detail = arrayListOf<data_constructer>()
val commentClollectionRef = FirebaseFirestore.getInstance().collection(Reference_data)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
commentAdopter = CommentAdopter(comment_detail)
recycler_list_view.adapter = commentAdopter
val layoutManager = LinearLayoutManager(this)
recycler_list_view.layoutManager = layoutManager
commentClollectionRef.get()
.addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
val data = document.data
val name = data!![USERNAME] as String
val timestamp = data[TIMESTAMP] as Date
val comment = data[COMMENT] as String
val numLikes = data[NUM_LIKES] as Long
val numComments = data[NUM_COMMENTS] as Long
val documentId = document.id
val newThoughts = data_constructer(
name,
timestamp,
comment,
numLikes.toInt(),
numComments.toInt(),
documentId
)
comment_detail.add(newThoughts)
}
commentAdopter.notifyDataSetChanged()
}.addOnFailureListener { exception ->
Log.e("Exception", "Could not add post:$exception")
}
}
fun mainFunnyClicked(view: View) {
if (selectCategery == Funny) {
main_funnyBtn.isChecked = true
return
}
main_seriousBtn.isChecked = false
main_crazyBtn.isChecked = false
main_populerBtn.isChecked = false
selectCategery = Funny
}
fun mainSeriousClicked(view: View?) {
if (selectCategery == SERIOUS) {
main_seriousBtn.isChecked = true
return
}
main_funnyBtn.isChecked = false
main_crazyBtn.isChecked = false
main_populerBtn.isChecked = false
selectCategery = SERIOUS
}
fun mainCrazyClicked(view: View?) {
if (selectCategery == CRAZY) {
main_crazyBtn.isChecked = true
return
}
main_seriousBtn.isChecked = false
main_funnyBtn.isChecked = false
main_populerBtn.isChecked = false
selectCategery = Funny
}
fun mainPopulerClicked(view: View?) {
if (selectCategery == POPULAR) {
main_populerBtn.isChecked = true
return
}
main_seriousBtn.isChecked = false
main_funnyBtn.isChecked = false
main_crazyBtn.isChecked = false
selectCategery = Funny
}
}
when i comment down the code below: application run fine..
commentClollectionRef.get()
.addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
val data = document.data
val name = data!![USERNAME] as String
val timestamp = data[TIMESTAMP] as Date
val comment = data[COMMENT] as String
val numLikes = data[NUM_LIKES] as Long
val numComments = data[NUM_COMMENTS] as Long
val documentId = document.id
val newThoughts = data_constructer(
name,
timestamp,
comment,
numLikes.toInt(),
numComments.toInt(),
documentId
)
comment_detail.add(newThoughts)
}
commentAdopter.notifyDataSetChanged()
}.addOnFailureListener { exception ->
Log.e("Exception", "Could not add post:$exception")
}
how can i read the data from the firestore database give me the simple
code example for kotlin if i want to upload and read the data ... as
well as images on firebase.. help me out
I am working on a music player and want to use Storage access framework to access the files in the storage. For that I used Intent.ACTION_OPEN_DOCUMENT_TREE and contentResolver.takePersistableUriPermission(...). But once I've got the permissions, I have to store the allowed path so I am using SharedPreferences for that.
When I convert the URI I got from the Intent.ACTION_OPEN_DOCUMENT_TREE to string to reuse it, it gives me a nullPointerException(It says the URI I got from converting the string from preferences is null).
While if I use the same URI without going through saving to the SharedPreferences, It works just fine.
Here's the code:
package com.gaurav712.music
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.documentfile.provider.DocumentFile
class MainActivity : AppCompatActivity() {
private val openDocumentTreeRequestCode: Int = 40
private lateinit var setPreferences: SharedPreferences
private lateinit var rootUri: Uri
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setPreferences = getPreferences(Context.MODE_PRIVATE)
// Check if access to storage is permitted
checkAccess()
// Now that we have access to storage, set the root Uri
rootUri = Uri.parse(setPreferences.getString("allowed_path", ""))
Log.i("rootUri", rootUri.toString())
// listFiles()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == openDocumentTreeRequestCode && resultCode == RESULT_OK) {
val treeUri = data?.data // get data
if (treeUri != null) {
contentResolver.takePersistableUriPermission(treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
val editor = setPreferences.edit()
editor.putBoolean("got_permissions", true)
editor.putString("allowed_path", treeUri?.toString())
editor.apply()
Log.i("treeUri", treeUri.toString())
val documentFile = treeUri?.let { DocumentFile.fromTreeUri(this, it) }
for (file in documentFile?.listFiles()!!) {
file.name?.let { Log.i("file: ", it) }
}
}
}
private fun checkAccess() {
val gotPermission: Boolean = setPreferences.getBoolean("got_permissions", false)
if (!gotPermission)
getAccess()
else
Log.i("got_permissions",
gotPermission.toString()
+ " : "
+ setPreferences.getString("allowed_path", ""))
}
private fun getAccess() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, openDocumentTreeRequestCode)
}
private fun listFiles() {
val documentFile: DocumentFile = DocumentFile.fromTreeUri(this, rootUri)!!
for (file in documentFile.listFiles()) {
Log.i("file: ", file.name.toString())
}
}
}
In onCreate() if I uncomment listFiles(), It gives nullPointerException but using the same chunk of code as you can see above in onActivityResult(), it all works fine. The chunk I am talking about:
val documentFile = treeUri?.let { DocumentFile.fromTreeUri(this, it) }
for (file in documentFile?.listFiles()!!) {
file.name?.let { Log.i("file: ", it) }
}
I can't figure out why it says rootUri is null.
I looked at all the similar questions asked like this one. I am using the same functions suggested (toString() and Uri.parse) for conversion but it doesn't seem to work with Storage access framework.
I solved it! the function getAccess() should be:
private fun getAccess() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
}
startActivityForResult(intent, openDocumentTreeRequestCode)
}
I was missing Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION flag in the intent.