I am currently trying to save a text file inside a fragment, but I can't get it to work:
Here is the method called when the user clicks the save button
private fun saveText(){
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_TITLE, "text.txt")
}
startActivityForResult(intent, 1)
}
Here is the onActivityResult method:
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
try {
val path = resultData?.data?.path
Log.wtf("Path", filePath)
val writer: Writer = BufferedWriter(FileWriter(path))
writer.write("Example Text")
writer.close()
} catch (e: Exception){
e.printStackTrace()
}
}
}
I also have the permissions set in the manifest and the file itself is created, but nothing is written. Perhaps I'm writing to the file wrong?
The error thrown is FileNotFoundException, because its trying to use a file from /document when I'm selecting one from /downloads
Suggested solution which unfortunately doesn't work:
resultData?.data?.let {
requireActivity().contentResolver.openOutputStream(it).use { stream ->
stream!!.bufferedWriter().write("Example Text")
}
}
A Uri is not a file.
Replace:
val path = resultData?.data?.path
Log.wtf("Path", filePath)
val writer: Writer = BufferedWriter(FileWriter(path))
writer.write("Example Text")
writer.close()
with:
resultData?.data?.let { contentResolver.openOutputStream(it).use { stream ->
stream.writer().write("Example Text")
}
}
Related
I'm new to android development and I've been building an app for studies purposes that is supposed to display one of the images selected from the gallery previously in a recyclerview.
private fun configDocImageDialog(pathGallery: Int, pathCamera: Int) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.camera_or_gallery))
.setMessage(getString(R.string.image_path))
.setPositiveButton(getString(R.string.gallery)) { _, _ ->
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select: "), pathGallery)
}.setNegativeButton(getString(R.string.camera)) { _, _ ->
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(Intent.createChooser(intent, "Take: "), pathCamera)
}.setNeutralButton(getString(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}.show()
}
Not I'm not worried about the camera result.
Then receiving the result as:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == RESULT_OK) {
val selectedImage = data!!.data
val pathImage = selectedImage.toString()
try {
when (requestCode) {
GALLERY_1 -> {
binding.imageDoc1.setImageURI(selectedImage)
}
CAMERA_1 -> {
binding.imageDoc1.setImageURI(selectedImage)
}
GALLERY_2 -> {
binding.imageDoc2.setImageURI(selectedImage)
}
CAMERA_2 -> {
binding.imageDoc2.setImageURI(selectedImage)
}
GALLERY_3 -> {
binding.imageDoc3.setImageURI(selectedImage)
}
CAMERA_3 -> {
binding.imageDoc3.setImageURI(selectedImage)
}
}
imageList.add(pathImage)
} catch (e: Exception){
e.printStackTrace()
}
}
}
I received as a result from the intent a list with this kind of content:
content://com.android.providers.media.documents/document/image%3A20
And those are saved into the database, the list of paths. /
Is it possible to use this path to my exhibit the image in my adapter? I've been trying different treatments but it always blank.
I've trying to use Picasso as:
override fun onBindViewHolder(holder: DocViewHolder, position: Int) {
val doc = docs[position]
holder.binding.apply {
Picasso.get()
.load(doc.docImageList[0])
.into(imageDoc)
textDocName.text = doc.title
textValidity.text = doc.validity
}
holder.itemView.setOnClickListener {
onItemClickListener?.let {
it(doc)
}
}
}
I want to display an image from internal storage in a recyclerview but the image is always blank.
Any ideas how to do it properly? Thanks
I try to make an Export Function for my App, that can export(and later Import) data to a txt-file in the shared storage. I use the ACTION_OPEN_DOCUMENT_TREE to choose a Folder and get "resultdata: Intent?" back.
For explanation in the Downloads-folder/Documents-Folder there is a "myApp"-folder. The user gave permission for that folder. So i get an Intent? with the path to this place back
How can i use that to create a "Spells.txt" in said folder without ACTION_CREATE_DOCUMENT
Edit: Thanks to blackapps, I've found DocumentFile, which helped create and fill the File. Here are the relevant parts of my Code so far:
lateinit var permissedFolder: Intent
in onCreate
permissiontest_btn_Choose_Permission.setOnClickListener(){chooseFolder()}
permissiontest_btn_createFile.setOnClickListener(){createFile()}
fun chooseFolder(){
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
}
startActivityForResult(intent, 100)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
if(requestCode==100 && resultCode== RESULT_OK){
if (resultData != null) {
permissedFolder = resultData
}
fun createFile() {
if(permissedFolder.data != null) {
val folderUri:Uri = permissedFolder.data!!
val enabledDirectory:DocumentFile= DocumentFile.fromTreeUri(this, folderUri)!!
val spellsExportDF =enabledDirectory.createFile(".txt","Spells.txt")
val spellsExportURI = spellsExportDF?.uri
if(spellsExportURI != null) {
val fileOutputStream = getContentResolver().openOutputStream(spellsExportURI)
//FileOutputStream(spellsExportURI, true)
fileOutputStream.use { fileout ->
fileout?.writer(Charsets.UTF_8)?.use {
it.write(test)
it.flush()
it.close()
}
}
val readfile = spellsExportDF.canRead()
val writefile =spellsExportDF.canWrite()
Toast.makeText(this, "can Read: $readfile", Toast.LENGTH_SHORT).show()
Toast.makeText(this, "can Write: $writefile", Toast.LENGTH_SHORT).show()
}
I try to make an storage where i can save my image in firebase storage but everytime i upload a new image it will replace the old one,i want evertime a new image will be save on storage,i am on android studio using kotlin,Is my code is wrong?here is my code
class Activity2 : AppCompatActivity() {
var curFile: Uri? = null
val imageRef = Firebase.storage.reference
private val userCollection = Firebase.firestore.collection("persons")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity2)
//here where a button to get an image from gallery
tvpilihfoto.setOnClickListener {
Intent(Intent.ACTION_GET_CONTENT).also {
it.type = "image/*"
val REQUEST_CODE_IMAGE_PICK = 0
startActivityForResult(it, REQUEST_CODE_IMAGE_PICK)
}
//this button for an upload activity to send the image to database firebase
btnupload.setOnClickListener {
uploadImageToStorage("my image")
}
}
private fun uploadImageToStorage(filename : String) = CoroutineScope(Dispatchers.IO).launch {
try {
curFile?.let {
imageRef.child("images/$filename").putFile(it).await()
withContext(Dispatchers.Main) {
Toast.makeText(this#Activity2,"Foto anda telah dipilih",
Toast.LENGTH_LONG).show()
}
}
} catch (e : Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(this#Activity2,e.message,Toast.LENGTH_LONG).show()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val REQUEST_CODE_IMAGE_PICK = 0
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_IMAGE_PICK) {
data?.data?.let {
curFile = it
ivfoto.setImageURI(it)
//this where my image get or display in app
}
}
}
}
Since you're always calling uploadImageToStorage("my image"), the image will alway be called my image. So each time you make that call, it will overwrite the previous my image in storage.
To always add a new image, generate a unique filename in your code. For example:
uploadImageToStorage(UUID.randomUUID().toString())
Context: Android 10, API 29.
I print a PDF file generated from a WebView, but now I'd like to save it to a file. So I tried the Intent.ACTION_CREATE_DOCUMENT to pick the file and save it via the printAdapter's onWrite method.
The problem is that the file is always empty - 0 bytes - and no errors are raised. It justs calls onWriteFailed, but with an empty error message.
choosenFileUri has a value like content://com.android.providers.downloads.documents/document/37
The method I use to start the intent to pick a new file. Note that the result of this activity is a Uri:
fun startIntentToCreatePdfFile(fragment: Fragment, filename : String) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_TITLE, filename)
}
fragment.startActivityForResult(intent, IntentCreatePdfDocument)
}
The method I use to "print" the PDF to a file. The fileUri comes from the Intent.ACTION_CREATE_DOCUMENT:
fun printPdfToFile(
context: Context,
webView: WebView,
fileUri: Uri
) {
(context.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let {
val jobName = "Print PDF to save it"
val printAdapter = webView.createPrintDocumentAdapter(jobName)
val printAttributes = PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(PrintAttributes.Resolution("pdf", "pdf", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS).build()
printAdapter.onLayout(null, printAttributes, null, object : LayoutResultCallback() {
override fun onLayoutFinished(info: PrintDocumentInfo, changed: Boolean) {
context.contentResolver.openFileDescriptor(fileUri, "w")?.use {
printAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
it,
CancellationSignal(),
object : WriteResultCallback() {
})
}
}
}, null)
}
}
What I do pick file onActivityResult:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != Activity.RESULT_OK) {
return null
}
if (requestCode != IntentCreatePdfDocument) {
throw Exception("RequestCode not implemented: $requestCode")
}
val choosenFileUri = data?.data
// If it is null, nothing to do
if (choosenFileUri == null) {
return
}
try {
HtmlHelpers.savePdfFromHtml(
requireContext(),
"html document to be represented in the WebView",
choosenFileUri)
} catch (exception: Exception) {
_logger.error(exception)
Helpers.showError(requireActivity(), getString(R.string.generic_error))
}
dismiss()
}
...where HtmlHelpers.savePdfFromHtml is:
fun savePdfFromHtml(
context: Context,
htmlContent: String,
fileUri: Uri
) {
generatePdfFromHtml(
context,
htmlContent
) { webView ->
PrintHelpers.printPdfToFile(
context,
webView,
fileUri)
}
}
...and generatePdfFromHtml is:
private fun generatePdfFromHtml(
context: Context,
htmlContent: String,
onPdfCreated: (webView: WebView) -> Unit
) {
val webView = WebView(context)
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(webView: WebView, url: String) {
onPdfCreated(webView)
}
}
webView.loadDataWithBaseURL(
null,
htmlContent,
"text/html; charset=utf-8",
"UTF-8",
null);
}
I checked all the other answer about this topic, but everyone creates manually the ParcelFileDescriptor instead of it in the onWrite method. Everyone does something like this:
fun getOutputFile(path: File, fileName: String): ParcelFileDescriptor? {
val file = File(path, fileName)
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)
}
But I cannot do this since I have only the Uri.
Edit: as suggested by #blackapps, I tried to open the output stream after I got the FileDescriptor, but I still got the same result:
context.contentResolver.openFileDescriptor(fileUri, "w")?.use {
val fileDescriptor = it
FileOutputStream(it.fileDescriptor).use {
printAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
fileDescriptor,
CancellationSignal(),
object : WriteResultCallback() {
})
}
}
For generating the PDF file from the HTML content I've used this Library
I'm storing the pdf file inside the download folder of shared storage(External). Use the below method to retrieve the location.
//fileName is the name of the file that you want to save.
fun getSavedFile(context: Context, fileName: String): File {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!,
"$fileName.pdf"
)
}
return File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"$fileName.pdf"
)
}
Then use the library inbuilt method to generate the PDF from the HTML content loading inside the WebView
//webView is the ID of WebView where we are loading the html content
// fileName : Pass whatever name you want.
PDFUtil.generatePDFFromWebView(pdfDownloadUtil.getSavedFile(getApplicationContext(), fileName), webView, new PDFPrint.OnPDFPrintListener() {
#Override
public void onSuccess(File file) {
savedPdfFile = file;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createFile(Uri.fromFile(file));
}
}
#Override
public void onError(Exception exception) {
}
});
}
Now, We need to fire the intent after getting the pdf of passed html content.
private void createFile(Uri pickerInitialUri) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, fileName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
createFileResult.launch(intent);
}
ActivityResultLauncher<Intent> createFileResult = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == AppCompatActivity.RESULT_OK) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
pdfDownloadUtil.writeFileContent(this, savedPdfFile, result.getData().getData(), () -> {
showSnackToOpenPdf();
return null;
});
}
}
}
);
In oreo or above device, We are using the above method i.e writeFileContent
#RequiresApi(Build.VERSION_CODES.O)
fun writeFileContent(
context: #NotNull Context,
savedPdfFile: #NotNull File,
uri: #NotNull Uri,
listener: () -> Unit
) {
try {
val file = uri.let { context.contentResolver.openFileDescriptor(it, "w") }
file?.let {
val fileOutputStream = FileOutputStream(it.fileDescriptor)
fileOutputStream.write(Files.readAllBytes(savedPdfFile.toPath()))
fileOutputStream.close()
it.close()
}
listener()
} catch (e: FileNotFoundException) {
//print logs
e.printStackTrace()
} catch (e: IOException) {
//print logs
e.printStackTrace()
}
}
Note: If the number of pages is large i.e more than 200 pages. Then it won't work as internally it's cache the pages in the WebView and then load it. So alternative way is to get the link to the PDF file from the API.
I checked all the other answer about this topic, but everyone creates manually the ParcelFileDescriptor instead of it in the onWrite method. Everyone does something like this:
fun getOutputFile(path: File, fileName: String): ParcelFileDescriptor? {
val file = File(path, fileName)
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)
}
But I cannot do this since I have only the Uri.
-> Uri contains the getPath() method. You can use this to create a file object,
val file = File(uri.path)
and use in ParcelFileDescriptor. Null-check the getPath() method.
I am creating a simple Android app where when a user clicks on a button, a file picker appears in which a user selects a file, and the app reads the contents of the file. However, in my code below, I get a FileNotFound following error after selecting a file (at the line where the File object is instantiated):
EXCEPTION: java.io.FileNotFoundException: /document/6471 (No such file
or directory)
Below is my code:
// File picker implementation
private fun chooseFile(view:View) {
println("chooseFile activated!");
var selectFile = Intent(Intent.ACTION_GET_CONTENT)
selectFile.type = "*/*"
selectFile = Intent.createChooser(selectFile, "Choose a file")
startActivityForResult(selectFile, READ_IN_FILE)
}
/* After startActivityForResult is executed, when the selectFile Intent is completed, onActivityResult is executed with
the result code READ_IN_FILE.*/
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == READ_IN_FILE) { // Step 1: When a result has been received, check if it is the result for READ_IN_FILE
if (resultCode == Activity.RESULT_OK) { // Step 2: Check if the operation to retrieve thea ctivity's result is successful
// Attempt to retrieve the file
try {
// Retrieve the true file path of the file
var uri: Uri? = data?.getData();
// Instantiate a File object from the file name
var file:File = File(uri?.getPath());
// Read the file, line by line
file.forEachLine { println(it) }
} catch (e: Exception) { // If the app failed to attempt to retrieve the error file, throw an error alert
println("EXCEPTION: " + e.toString());
Toast.makeText(this, "Sorry, but there was an error reading in the file", Toast.LENGTH_SHORT).show()
}
}
}
}
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var file1:Button = findViewById(R.id.file1);
file1.setOnClickListener(::chooseFile)
}
Below is my XML code (activity_main.xml):
<Button
android:id="#+id/file1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:text="File 1"
android:textAllCaps="false"
android:textSize="16sp" />
// File picker implementation
private fun chooseFile(view:View) {
//only the below specified mime type is allowed in the picker
val mimeTypes = arrayOf(
"application/msword",
"application/vnd.ms-powerpoint",
"application/vnd.ms-excel",
"text/plain",
"application/pdf"
)
println("chooseFile activated!");
var selectFile = Intent(Intent.ACTION_GET_CONTENT)
selectFile.type = if (mimeTypes.size == 1) mimeTypes[0] else "*/*"
if (mimeTypes.isNotEmpty()) {
selectFile.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
}
selectFile = Intent.createChooser(selectFile, "Choose a file")
startActivityForResult(selectFile, READ_IN_FILE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 200) { // Step 1: When a result has been received, check if it is the result for READ_IN_FILE
if (resultCode == Activity.RESULT_OK) { // Step 2: Check if the operation to retrieve thea ctivity's result is successful
// Attempt to retrieve the file
try {
data?.data?.let {
contentResolver.openInputStream(it)
}?.let {
val r = BufferedReader(InputStreamReader(it))
while (true) {
val line: String? = r.readLine() ?: break
println(line)
}
}
} catch (e: Exception) { // If the app failed to attempt to retrieve the error file, throw an error alert
Toast.makeText(
this,
"Sorry, but there was an error reading in the file",
Toast.LENGTH_SHORT
).show()
}
}
}
}