ShareActivity.kt 5.7 KB

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