How do I append text to existing text file? - kotlin

When saving the text file it is being saved for example in the Documents/ folder. I'm aware of that I need to use append to add new line of text to that existing file but I am not entirely sure how and where to add this line of code and check that file exists.
This is the current code that is handling saving the file.
...
save.setOnClickListener {
saveFile()
}
}
private fun saveFile() {
val fileIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, "measurements.txt")
// Optionally, specify a URI for the directory that should be opened in
// the system file picker before your app creates the document.
//putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
saveLauncher.launch(fileIntent)
}
private var saveLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val uri = result.data?.data
try {
val outputStream = uri?.let { contentResolver.openOutputStream(it) }
outputStream?.write(getMeasurement?.toByteArray())
outputStream?.close()
} catch (e: Exception) {
Toast.makeText(this, "Error is ${e.localizedMessage}", Toast.LENGTH_SHORT).show()
}
}
}

You can use a Writer to append text. Also, you should not put your close call inside the try block, or it could get skipped and you won't release the file. close should go in a finally block, but it's easier to use use { } instead.
When you open an output stream from the content resolver, you can specify write and append mode by passing "wa" as the second argument. See here and here in the documentation.
openOutputStream throws an exception if the file doesn't already exist. If you want to create a file if it doesn't exist yet, you'll need to add that logic.
I like to exit early from a function rather than nest everything in an if-statement, so I rearranged it that way in my example, but you don't have to do that.
private var saveLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val uri = result.data?.data
val content = getMeasurement
if (result.resultCode != Activity.RESULT_OK || uri == null || content == null) {
return#registerForActivityResult
}
try {
contentResolver.openOutputStream(uri, "wa")
.writer().use { it.write(content) }
} catch (e: Exception) {
Toast.makeText(this, "Error is ${e.localizedMessage}", Toast.LENGTH_SHORT).show()
}
}

Related

Kotlin : My app creates and saves a PDF, but i want it to open the PDF in the default viewer instead of closing

I am trying to have my app open the PDF instead of just saving it to the downloads. I was hoping it could open it using the android default PDF viewer?
private fun savePDF() {
pdfDocument.finishPage(myPage)
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"$Number $fileName.pdf"
)
try {
pdfDocument.writeTo(FileOutputStream(file))
Toast.makeText(context, "PDF file generated successfully.", Toast.LENGTH_SHORT).show()
previewPDF(Uri.fromFile(file))
} catch (e: IOException) {
Log.d(tag, "Error: $e")
e.printStackTrace()
}
pdfDocument.close()
After save you pdf file, Call this below method for default pdf viewer
public static void openFile(Context context,File url) throws IOException {
// Create URI
File file=url;
Uri uri = null;
if(Build.VERSION.SDK_INT>=24)
{
uri= FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID+".provider",file);
}
else
{
uri=Uri.fromFile(file);
}
Intent intent = new Intent(Intent.ACTION_VIEW);
if(url.toString().contains(".pdf")) {
// PDF file
intent.setDataAndType(uri, "application/pdf");
} else {
intent.setDataAndType(uri, "*/*");
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(intent,"Open with"));
}

Ktor responding outputStream causing io.netty.handler.timeout.WriteTimeoutException

I have a Ktor application that return multiple files as a stream but when the client has a slow internet connection, apparently, the stream gets full and blows launching an io.netty.handler.timeout.WriteTimeoutException.
kotlinx.coroutines.JobCancellationException: Parent job is Cancelling
Caused by: io.netty.handler.timeout.WriteTimeoutException: null
Is there a way to prevent that ?
Here's my method:
suspend fun ApplicationCall.returnMultiFiles(files: List<StreamFile>) {
log.debug("Returning Multiple Files: ${files.size}")
val call = this // Just to make it more readable
val bufferSize = 16 * 1024
call.response.headers.append(
HttpHeaders.ContentDisposition,
ContentDisposition.Attachment.withParameter(ContentDisposition.Parameters.FileName, "${UUID.randomUUID()}.zip").toString())
call.respondOutputStream(ContentType.parse("application/octet-stream")) {
val returnOutputStream = this.buffered(bufferSize) // Just to make it more readable
ZipOutputStream(returnOutputStream).use { zipout ->
files.forEach{ record ->
try {
zipout.putNextEntry(ZipEntry(record.getZipEntry()))
log.debug("Adding: ${record.getZipEntry()}")
record.getInputStream().use { fileInput ->
fileInput.copyTo(zipout, bufferSize)
}
} catch (e: Exception) {
log.error("Failed to add ${record.getZipEntry()}", e)
}
}
}
}
}

How to receive and distinguish between regular attachments and inline attachments in Apache Commons Email 1.4

Currently we receive an email which gets parsed by
MimeMessageParser mimeMessageParser = parse(message);
and later pull out the attachments with
if (mimeMessageParser.hasAttachments()) {
List<DataSource> attachments = mimeMessageParser.getAttachmentList();
for (DataSource dataSource : attachments) {
saveAttachment(dataSource, subjectLineProperties, documentToUpload, firstHeaders);
}
}
The issue is that getAttachmentList is also returning inline images like in the signature line the business logo, and we do not want to pull out the inline images as attachments. We just want the actual email attachments. ATTACHMENT versus INLINE, but we also have no access to java.mail disposition via the Apache Commons Email 1.4 version, and can't find a solution. I checked their documentation https://commons.apache.org/proper/commons-email/javadocs/api-1.4/index.html
No luck. It seems that the attachments DataSource only allows me to get content and content type and name, but not if it is an inline attachment/image or a regular attachment like Mime Parts can.
I am under impression that there is a workaround without going into lower level... But I haven't checked it with all attachment types yet - only with images. Something like this:
for(DataSource ds : mimeMessageParser.getAttachmentList()) {
for(String id : mimeMessageParser.getContentIds()) {
if(ds == mimeMessageParser.findAttachmentByCid(id)) {
// It is inline attachment with Content ID
break;
}
}
// If not found - it is file attachment without content ID
}
The answer is that Apache Commons Email cannot do such a thing. You have to go lower level and code the old fashioned MimeMessage and MultiPart classes within the JDK in order to make these distinctions.
So from mimeMessageParser.getAttachmentList(); call we now have
if (mimeMessageParser.hasAttachments()) {
final Multipart mp = (Multipart) message.getContent();
if (mp != null) {
List<DataSource> attachments = extractAttachment(mp);
for (DataSource dataSource : attachments) {
saveAttachment(dataSource, subjectLineProperties, documentToUpload, firstHeaders);
}
}
}
private static List<DataSource> extractAttachment(Multipart multipart) {
List<DataSource> attachments = new ArrayList<>();
try {
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (bodyPart.getContent() instanceof Multipart) {
// part-within-a-part, do some recursion...
extractAttachment((Multipart) bodyPart.getContent());
}
System.out.println("bodyPart.getDisposition(): " + bodyPart.getDisposition());
if (!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
continue; // dealing with attachments only
}
InputStream is = bodyPart.getInputStream();
String fileName = bodyPart.getFileName();
String contentType = bodyPart.getContentType();
ByteArrayDataSource dataSource = new ByteArrayDataSource(is, contentType);
dataSource.setName(fileName);
attachments.add(dataSource);
}
} catch (IOException | MessagingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return attachments;
}

Error: file doesn't exist

Now am working on a project where I need to create a folder in sdcard which am able to do. Also I need to hide/unhide it according to need. The code is working fine on emulator but not in device this is my code what went wrong ?
public class FolderCreate extends MIDlet {
private Form form;
private Display display;
FileConnection fc;
String path;
public void startApp() {
form = new Form("Hello World");
String msg = "Hello World!!!!!!!";
form.append(msg);
display = Display.getDisplay(this);
display.setCurrent(form);
System.out.println("WWWW");
try {
path = System.getProperty("fileconn.dir.memorycard");
System.out.println("Path : "+path+"/sample");
fc = (FileConnection)Connector.open(path+"/ABCD/");
if(!fc.exists())
{
fc.mkdir();
System.out.println("directory created");
}
} catch (IOException e) {
// TODO Auto-generated catch block
//System.out.println("ERROR "+e.getMessage());
Alert alert = new Alert("Alert");
alert.setString(e.getMessage());
display.setCurrent(alert);
}
try
{
//fc = (FileConnection)Connector.open(path+"/sample/");
if(fc.isHidden())
{
fc.setHidden(false);
}
else{
fc.setHidden(true);
}
fc.close();
}
catch (Exception e)
{
Alert alert = new Alert("Alert2");
alert.setString(e.toString());
display.setCurrent(alert);
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
System.out.println("Destroyed");
notifyDestroyed();
}
}
The error am getting is: java.io.IOException: file does not exist
Check if path starts with "file://". If not, add the suffix.
path = System.getProperty("fileconn.dir.memorycard");
if (path != null && !path.startsWith("file://")) {
path = "file://" + path;
}
I think you are doing mistake at following line,
path = System.getProperty("fileconn.dir.memorycard");
When you are working with phone and SD-Card you should use e: drive for referring to SD Card as follows,
path = file:///e:/<folder-name>/

JavaMail - Pop3Folder accidentally gets closed

After successfully opening Pop3Folder, and retrieving messages from it, I then sometimes get to the point, when folder.isOpen returns false. At the same time, when looking at the Pop3Folder's fields in debug mode, I see that the field opened set to true.
Could somebody give me a hint, what might go wrong here?
Here is the code:
public void popMail(MessageProcessor messageProcessor) throws MessagingException {
Folder inboxFolder = null;
Store store = null;
try {
store = mailSession.getStore();
store.connect(mailSession.getProperty("mail.user"),
mailSession.getProperty("mail.password"));
// OK. Connected to POP3 Store.
inboxFolder = store.getFolder("inbox");
inboxFolder.open(Folder.READ_WRITE);
// The folder is successfully opened.
Message[] msgs = inboxFolder.getMessages();
// Messages are successfully retrieved.
if (msgs != null && msgs.length > 0) {
for (Message msg : msgs) {
if (messageProcessor != null) {
// Calling custom listener to process message
messageProcessor.processMessage(msg);
}
msg.setFlag(Flag.DELETED, true);
}
}
} finally {
// Oops, inboxFolder.isOpen returns false.
// Meanwhile I see in debug mode that inboxFolder#opened is set to true
if (inboxFolder != null && inboxFolder.isOpen()) {
try {
inboxFolder.close(true);
} catch (MessagingException e) {
log.warn("Error while closing folder");
}
} if (store != null) {
try {
store.close();
} catch (MessagingException e) {
log.warn("Error while closing store");
}
}
}
}
The server may be timing out the connection if your processMessage method takes too long. Turn on Session debugging and examine the protocol trace for clues.