e-ntfy-android/app/src/main/java/io/heckel/ntfy/ui/ShareActivity.kt

237 lines
8.7 KiB
Kotlin
Raw Normal View History

2022-02-11 16:46:55 +01:00
package io.heckel.ntfy.ui
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.text.Editable
import android.text.TextWatcher
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
2022-02-12 02:34:08 +01:00
import android.widget.Toast
2022-02-11 16:46:55 +01:00
import androidx.appcompat.app.AppCompatActivity
2022-02-11 21:55:08 +01:00
import androidx.lifecycle.lifecycleScope
2022-02-11 16:46:55 +01:00
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
import io.heckel.ntfy.msg.ApiService
2022-02-12 02:34:08 +01:00
import io.heckel.ntfy.util.*
2022-02-11 21:55:08 +01:00
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
2022-02-11 16:46:55 +01:00
class ShareActivity : AppCompatActivity() {
private val repository by lazy { (application as Application).repository }
private val api = ApiService()
2022-02-11 21:55:08 +01:00
// File to share
private var fileUri: Uri? = null
2022-02-11 16:46:55 +01:00
// UI elements
private lateinit var menu: Menu
private lateinit var sendItem: MenuItem
private lateinit var contentImage: ImageView
2022-02-12 02:34:08 +01:00
private lateinit var contentFileBox: View
private lateinit var contentFileInfo: TextView
private lateinit var contentFileIcon: ImageView
2022-02-11 16:46:55 +01:00
private lateinit var contentText: TextView
private lateinit var topicText: TextView
private lateinit var progress: ProgressBar
2022-02-11 21:55:08 +01:00
private lateinit var errorText: TextView
private lateinit var errorImage: ImageView
2022-02-11 16:46:55 +01:00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_share)
Log.init(this) // Init logs in all entry points
Log.d(TAG, "Create $this")
// Action bar
title = getString(R.string.share_title)
// Show 'Back' button
supportActionBar?.setDisplayHomeAsUpEnabled(true)
// UI elements
contentText = findViewById(R.id.share_content_text)
contentImage = findViewById(R.id.share_content_image)
2022-02-12 02:34:08 +01:00
contentFileBox = findViewById(R.id.share_content_file_box)
contentFileInfo = findViewById(R.id.share_content_file_info)
contentFileIcon = findViewById(R.id.share_content_file_icon)
2022-02-11 16:46:55 +01:00
topicText = findViewById(R.id.share_topic_text)
progress = findViewById(R.id.share_progress)
progress.visibility = View.GONE
2022-02-11 21:55:08 +01:00
errorText = findViewById(R.id.share_error_text)
errorText.visibility = View.GONE
errorImage = findViewById(R.id.share_error_image)
errorImage.visibility = View.GONE
2022-02-11 16:46:55 +01:00
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
validateInput()
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// Nothing
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// Nothing
}
}
2022-02-11 21:55:08 +01:00
contentText.addTextChangedListener(textWatcher)
2022-02-11 16:46:55 +01:00
topicText.addTextChangedListener(textWatcher)
// Incoming intent
val intent = intent ?: return
if (intent.action != Intent.ACTION_SEND) return
if ("text/plain" == intent.type) {
handleSendText(intent)
2022-02-11 21:55:08 +01:00
} else if (supportedImage(intent.type)) {
2022-02-11 16:46:55 +01:00
handleSendImage(intent)
2022-02-11 21:55:08 +01:00
} else {
handleSendFile(intent)
2022-02-11 16:46:55 +01:00
}
}
private fun handleSendText(intent: Intent) {
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { text ->
contentText.text = text
2022-02-12 02:34:08 +01:00
show()
2022-02-11 16:46:55 +01:00
}
}
private fun handleSendImage(intent: Intent) {
2022-02-11 21:55:08 +01:00
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
2022-02-11 16:46:55 +01:00
try {
val resolver = applicationContext.contentResolver
2022-02-11 21:55:08 +01:00
val bitmapStream = resolver.openInputStream(fileUri!!)
2022-02-11 16:46:55 +01:00
val bitmap = BitmapFactory.decodeStream(bitmapStream)
contentImage.setImageBitmap(bitmap)
contentText.text = getString(R.string.share_content_image_text)
2022-02-12 02:34:08 +01:00
show(image = true)
} catch (e: Exception) {
2022-02-11 21:55:08 +01:00
fileUri = null
2022-02-12 02:34:08 +01:00
contentText.text = ""
errorText.text = getString(R.string.share_content_image_error, e.message)
show(error = true)
2022-02-11 16:46:55 +01:00
}
}
2022-02-11 21:55:08 +01:00
private fun handleSendFile(intent: Intent) {
fileUri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri ?: return
2022-02-12 02:34:08 +01:00
try {
val resolver = applicationContext.contentResolver
val info = fileStat(this, fileUri)
val mimeType = resolver.getType(fileUri!!)
contentText.text = getString(R.string.share_content_file_text)
contentFileInfo.text = "${info.filename}\n${formatBytes(info.size)}"
contentFileIcon.setImageResource(mimeTypeToIconResource(mimeType))
show(file = true)
} catch (e: Exception) {
fileUri = null
contentText.text = ""
errorText.text = getString(R.string.share_content_file_error, e.message)
show(error = true)
}
2022-02-11 21:55:08 +01:00
}
2022-02-11 16:46:55 +01:00
override fun onSupportNavigateUp(): Boolean {
finish()
return true
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_share_action_bar, menu)
this.menu = menu
sendItem = menu.findItem(R.id.share_menu_send)
validateInput() // Disable icon
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.share_menu_send -> {
onShareClick()
true
}
else -> super.onOptionsItemSelected(item)
}
}
2022-02-12 02:34:08 +01:00
private fun show(image: Boolean = false, file: Boolean = false, error: Boolean = false) {
contentImage.visibility = if (image) View.VISIBLE else View.GONE
contentFileBox.visibility = if (file) View.VISIBLE else View.GONE
errorImage.visibility = if (error) View.VISIBLE else View.GONE
errorText.visibility = if (error) View.VISIBLE else View.GONE
}
2022-02-11 16:46:55 +01:00
private fun onShareClick() {
2022-02-11 21:55:08 +01:00
val baseUrl = "https://ntfy.sh" // FIXME
val topic = topicText.text.toString()
val message = contentText.text.toString()
progress.visibility = View.VISIBLE
lifecycleScope.launch(Dispatchers.IO) {
val user = repository.getUser(baseUrl)
try {
2022-02-12 19:51:41 +01:00
val (filename, body) = if (fileUri != null) {
val stat = fileStat(this@ShareActivity, fileUri)
val body = ContentUriRequestBody(applicationContext.contentResolver, fileUri!!, stat.size)
Pair(stat.filename, body)
2022-02-12 02:34:08 +01:00
} else {
2022-02-12 19:51:41 +01:00
Pair("", null)
2022-02-11 21:55:08 +01:00
}
api.publish(
baseUrl = baseUrl,
topic = topic,
user = user,
message = message,
title = "",
priority = 3,
tags = emptyList(),
delay = "",
2022-02-12 02:34:08 +01:00
body = body, // May be null
2022-02-12 19:51:41 +01:00
filename = filename, // May be empty
2022-02-11 21:55:08 +01:00
)
runOnUiThread {
finish()
2022-02-12 02:34:08 +01:00
Toast
.makeText(this@ShareActivity, getString(R.string.share_successful), Toast.LENGTH_LONG)
.show()
2022-02-11 21:55:08 +01:00
}
} catch (e: Exception) {
2022-02-12 19:51:41 +01:00
val message = if (e is ApiService.UnauthorizedException) {
if (e.user != null) {
getString(R.string.detail_test_message_error_unauthorized_user, e.user.username)
} else {
getString(R.string.detail_test_message_error_unauthorized_anon)
}
} else if (e is ApiService.EntityTooLargeException) {
getString(R.string.detail_test_message_error_too_large)
} else {
getString(R.string.detail_test_message_error, e.message)
}
2022-02-11 21:55:08 +01:00
runOnUiThread {
progress.visibility = View.GONE
2022-02-12 19:51:41 +01:00
errorText.text = message
2022-02-11 21:55:08 +01:00
errorImage.visibility = View.VISIBLE
errorText.visibility = View.VISIBLE
}
}
}
2022-02-11 16:46:55 +01:00
}
private fun validateInput() {
if (!this::sendItem.isInitialized) return // Initialized late in onCreateOptionsMenu
sendItem.isEnabled = contentText.text.isNotEmpty() && topicText.text.isNotEmpty()
sendItem.icon.alpha = if (sendItem.isEnabled) 255 else 130
}
companion object {
const val TAG = "NtfyShareActivity"
}
}