1
0

SendNoteActivity.kt 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package io.github.zadam.triliumsender
  2. import android.content.Intent
  3. import android.content.pm.PackageManager
  4. import android.os.Bundle
  5. import android.util.Log
  6. import android.widget.TextView
  7. import android.widget.Toast
  8. import androidx.appcompat.app.AppCompatActivity
  9. import io.github.zadam.triliumsender.services.HtmlConverter
  10. import io.github.zadam.triliumsender.services.TriliumSettings
  11. import io.github.zadam.triliumsender.services.Utils
  12. import kotlinx.android.synthetic.main.activity_send_note.*
  13. import kotlinx.coroutines.CoroutineScope
  14. import kotlinx.coroutines.Dispatchers
  15. import kotlinx.coroutines.launch
  16. import kotlinx.coroutines.withContext
  17. import okhttp3.OkHttpClient
  18. import okhttp3.Request
  19. import okhttp3.RequestBody.Companion.toRequestBody
  20. import org.json.JSONObject
  21. class SendNoteActivity : AppCompatActivity() {
  22. override fun onCreate(savedInstanceState: Bundle?) {
  23. val tag = "SendNoteOnCreateHandler"
  24. super.onCreate(savedInstanceState)
  25. setContentView(R.layout.activity_send_note)
  26. val settings = TriliumSettings(this)
  27. if (!settings.isConfigured()) {
  28. // We can't do anything useful if we're not configured. Abort out.
  29. Toast.makeText(this, getString(R.string.sender_not_configured_note), Toast.LENGTH_LONG).show()
  30. finish()
  31. return
  32. }
  33. // If we're a share-intent, pre-populate the note.
  34. when (intent?.action) {
  35. Intent.ACTION_SEND -> {
  36. if ("text/plain" == intent.type) {
  37. handleSendText(intent)
  38. } else {
  39. // We don't yet have text/html support.
  40. Log.e(tag, "Don't have a handler for share type ${intent.type}.")
  41. }
  42. }
  43. }
  44. sendNoteButton.setOnClickListener {
  45. // Kick off a coroutine to handle the actual send attempt without blocking the UI.
  46. // Since we want to be able to fire Toasts, we should use the Main (UI) scope.
  47. val uiScope = CoroutineScope(Dispatchers.Main)
  48. uiScope.launch {
  49. val success = sendNote(noteTitleEditText.text.toString(), noteContentEditText.text.toString(), settings.triliumAddress, settings.apiToken)
  50. if (success) {
  51. // Announce our success and end the activity.
  52. Toast.makeText(this@SendNoteActivity, getString(R.string.sending_note_complete), Toast.LENGTH_LONG).show()
  53. finish()
  54. } else {
  55. // Announce our failure.
  56. // Do not end the activity, so the user may decide to copy/store their note's contents without it disappearing.
  57. Toast.makeText(this@SendNoteActivity, getString(R.string.sending_note_failed), Toast.LENGTH_LONG).show()
  58. }
  59. }
  60. }
  61. }
  62. /**
  63. * If we're the target of a SEND intent, pre-fill the note's contents.
  64. *
  65. * @param intent, the Intent object, already verified to be of type text/plain.
  66. */
  67. private fun handleSendText(intent: Intent) {
  68. val tag = "SendNoteHandleSendText"
  69. // Many apps will suggest a "Text Subject", as in an email.
  70. val suggestedSubject = intent.getStringExtra((Intent.EXTRA_SUBJECT))
  71. if (suggestedSubject != null && suggestedSubject.isNotEmpty()) {
  72. // Use suggested subject.
  73. noteTitleEditText.setText(suggestedSubject, TextView.BufferType.EDITABLE)
  74. } else {
  75. // Try to craft a sane default title.
  76. var referrerName = "Android"
  77. // SDKs greater than 23 can access the referrer's package URI...
  78. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
  79. val referrer = this.referrer
  80. if (referrer != null && referrer.host != null) {
  81. // Which we can use to get an AppInfo handle...
  82. try {
  83. // Which we can use to get an app label!
  84. val referrerInfo = packageManager.getApplicationInfo(referrer.host.toString(), 0)
  85. val potentialReferrerName = referrerInfo.loadLabel(packageManager).toString()
  86. // Sanity check: is it an empty string?
  87. if (potentialReferrerName.isNotEmpty()) {
  88. referrerName = potentialReferrerName
  89. }
  90. } catch (e: PackageManager.NameNotFoundException) {
  91. // Oh well, we have the default name to fall back on.
  92. Log.w(tag, "Could not find package label for package name ${referrer.host}")
  93. }
  94. }
  95. }
  96. // Ultimately, set the note title.
  97. noteTitleEditText.setText(getString(R.string.share_note_title, referrerName), TextView.BufferType.EDITABLE)
  98. }
  99. // And populate the note body!
  100. noteContentEditText.setText(intent.getStringExtra(Intent.EXTRA_TEXT), TextView.BufferType.EDITABLE)
  101. }
  102. /**
  103. * Attempts to send a note to the Trilium server.
  104. *
  105. * Runs in the IO thread, to avoid blocking the UI thread.
  106. *
  107. * @param noteTitle, the title of the proposed note.
  108. * @param noteText, the body of the proposed note.
  109. * @param triliumAddress, the address of the Trilium server to send this note to.
  110. * @param apiToken, the security token for communicating with the Trilium server.
  111. *
  112. * @return Success or failure, as a boolean.
  113. */
  114. private suspend fun sendNote(noteTitle: String, noteText: String, triliumAddress: String, apiToken: String): Boolean {
  115. val tag = "SendNoteCoroutine"
  116. return withContext(Dispatchers.IO) {
  117. val client = OkHttpClient()
  118. val json = JSONObject()
  119. if (noteTitle.isEmpty()) {
  120. // API does not allow empty note titles, so use a default value if the user doesn't set one.
  121. json.put("title", "Note from Android")
  122. } else {
  123. json.put("title", noteTitle)
  124. }
  125. json.put("content", HtmlConverter().convertPlainTextToHtml(noteText))
  126. val body = json.toString().toRequestBody(Utils.JSON)
  127. val request = Request.Builder()
  128. .url("$triliumAddress/api/sender/note")
  129. .addHeader("Authorization", apiToken)
  130. .addHeader("X-Local-Date", Utils.localDateStr())
  131. .post(body)
  132. .build()
  133. return@withContext try {
  134. // In the Dispatchers.IO context, blocking http requests are allowed.
  135. @Suppress("BlockingMethodInNonBlockingContext")
  136. val response = client.newCall(request).execute()
  137. response.code == 200
  138. } catch (e: Exception) {
  139. Log.e(tag, getString(R.string.sending_failed), e)
  140. false
  141. }
  142. }
  143. }
  144. }