ShareActivity.kt 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package io.github.zadam.triliumsender
  2. import android.content.Intent
  3. import android.graphics.Bitmap
  4. import android.graphics.BitmapFactory
  5. import android.net.Uri
  6. import android.os.AsyncTask
  7. import android.os.Bundle
  8. import android.support.v7.app.AppCompatActivity
  9. import android.util.Log
  10. import android.widget.Toast
  11. import okhttp3.*
  12. import okhttp3.internal.Util
  13. import okio.BufferedSink
  14. import okio.Okio
  15. import okio.Source
  16. import java.io.ByteArrayInputStream
  17. import java.io.ByteArrayOutputStream
  18. import java.io.IOException
  19. import java.io.InputStream
  20. import java.text.SimpleDateFormat
  21. import java.util.*
  22. class ShareActivity : AppCompatActivity() {
  23. override fun onCreate(savedInstanceState: Bundle?) {
  24. super.onCreate(savedInstanceState)
  25. setContentView(R.layout.activity_share)
  26. val settings = TriliumSettings(this)
  27. if (!settings.isConfigured()) {
  28. Toast.makeText(this, "Trilium Sender is not configured. Can't sent the image.", Toast.LENGTH_LONG).show()
  29. finish()
  30. return
  31. }
  32. val imageUri = intent.extras!!.get(Intent.EXTRA_STREAM) as Uri
  33. val mimeType = contentResolver.getType(imageUri)
  34. val sendImageTask = SendImageTask(imageUri, mimeType, settings.triliumAddress, settings.apiToken)
  35. sendImageTask.execute(null as Void?)
  36. }
  37. inner class SendImageResult (val success: Boolean, val contentLength: Long? = null)
  38. inner class SendImageTask internal constructor(private val imageUri: Uri,
  39. private val mimeType: String,
  40. private val triliumAddress: String,
  41. private val apiToken: String) : AsyncTask<Void, Void, SendImageResult>() {
  42. val TAG : String = "SendImageTask"
  43. override fun doInBackground(vararg params: Void): SendImageResult {
  44. val imageStream = contentResolver.openInputStream(imageUri);
  45. val imageBody = RequestBodyUtil.create(MediaType.parse(mimeType)!!, scaleImage(imageStream, mimeType))
  46. val contentLength = imageBody.contentLength()
  47. val requestBody = MultipartBody.Builder()
  48. .setType(MultipartBody.FORM)
  49. .addFormDataPart("upload", "image", imageBody)
  50. .build()
  51. val client = OkHttpClient()
  52. val request = Request.Builder()
  53. .url(triliumAddress + "/api/sender/image")
  54. .addHeader("Authorization", apiToken)
  55. .addHeader("X-Local-Date", now())
  56. .post(requestBody)
  57. .build()
  58. try {
  59. val response = client.newCall(request).execute()
  60. return SendImageResult(response.code() == 200, contentLength)
  61. }
  62. catch (e: Exception) {
  63. Log.e(TAG, "Sending to Trilium failed", e)
  64. return SendImageResult(false)
  65. }
  66. }
  67. override fun onPostExecute(result: SendImageResult) {
  68. if (result.success) {
  69. Toast.makeText(this@ShareActivity, "Image sent to Trilium (" + (result.contentLength!! / 1000) + " KB)", Toast.LENGTH_LONG).show()
  70. }
  71. else {
  72. Toast.makeText(this@ShareActivity, "Sending to Trilium failed", Toast.LENGTH_LONG).show()
  73. }
  74. finish()
  75. }
  76. override fun onCancelled() {
  77. }
  78. }
  79. private fun now(): String {
  80. val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
  81. val date = dateFormat.format(Calendar.getInstance().getTime())
  82. return date!!
  83. }
  84. private fun scaleImage(inputStream: InputStream, mimeType: String): InputStream {
  85. // we won't do anything with GIFs, PNGs etc. This is minority use case anyway
  86. if (mimeType != "image/jpeg") {
  87. return inputStream;
  88. }
  89. val options = BitmapFactory.Options()
  90. val bitmap = BitmapFactory.decodeStream(inputStream, null, options)
  91. val maxWidth = 2000
  92. val maxHeight = 2000
  93. val scale = Math.min(maxHeight.toFloat() / bitmap.width, maxWidth.toFloat() / bitmap.height)
  94. val newWidth : Int = if (scale < 1) (bitmap.width * scale).toInt() else bitmap.width;
  95. val newHeight : Int = if (scale < 1) (bitmap.height * scale).toInt() else bitmap.height;
  96. val scaledBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
  97. val baos = ByteArrayOutputStream()
  98. scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 75, baos)
  99. val bitmapdata = baos.toByteArray()
  100. return ByteArrayInputStream(bitmapdata)
  101. }
  102. object RequestBodyUtil {
  103. fun create(mediaType: MediaType, inputStream: InputStream): RequestBody {
  104. return object : RequestBody() {
  105. override fun contentType(): MediaType? {
  106. return mediaType
  107. }
  108. override fun contentLength(): Long {
  109. try {
  110. return inputStream.available().toLong()
  111. } catch (e: IOException) {
  112. return 0
  113. }
  114. }
  115. @Throws(IOException::class)
  116. override fun writeTo(sink: BufferedSink) {
  117. var source: Source? = null
  118. try {
  119. source = Okio.source(inputStream)
  120. sink.writeAll(source!!)
  121. } finally {
  122. Util.closeQuietly(source)
  123. }
  124. }
  125. }
  126. }
  127. }
  128. }