package `in`.caresoft.sahi import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import `in`.caresoft.sahi.databinding.ActivityProcessTextBinding import kotlinx.coroutines.launch /** * The path that needs no keyboard. * * Select text anywhere on the phone โ€” WhatsApp, Gmail, Chrome, the HIS web app โ€” * and tap Sahi in the selection toolbar. Android hands over the selection and * takes back the corrected version. It cannot see anything else you type, * which is the whole reason to prefer it. * * Read-only callers (some apps offer the action on text you cannot edit) get * the corrected version to copy instead. */ class ProcessTextActivity : AppCompatActivity() { private lateinit var ui: ActivityProcessTextBinding private var original: String = "" private var corrected: String = "" private var issues: List = emptyList() private var readOnly = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ui = ActivityProcessTextBinding.inflate(layoutInflater) setContentView(ui.root) original = extractText().orEmpty() readOnly = intent.getBooleanExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false) || intent.action == Intent.ACTION_SEND if (original.isBlank()) { finishWith("Select some text first.") return } if (!Prefs.isConfigured) { startActivity(Intent(this, SettingsActivity::class.java)) finishWith("Enter your server address and key to get started.") return } ui.originalText.text = original ui.applyButton.setOnClickListener { applyAndReturn() } ui.copyButton.setOnClickListener { copyToClipboard() } ui.rewriteButton.setOnClickListener { showRewriteMenu() } check() } private fun extractText(): CharSequence? = when (intent.action) { Intent.ACTION_PROCESS_TEXT -> intent.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT) Intent.ACTION_SEND -> intent.getCharSequenceExtra(Intent.EXTRA_TEXT) else -> null } private fun check() { setBusy(true, getString(R.string.checking)) lifecycleScope.launch { try { issues = SahiApi.check(original) corrected = SahiApi.applyAll(original, issues) render() } catch (e: Exception) { setBusy(false, e.message ?: getString(R.string.something_went_wrong)) } } } private fun render() { setBusy(false, null) if (issues.isEmpty()) { ui.status.text = getString(R.string.nothing_to_change) ui.correctedText.visibility = View.GONE ui.issueList.visibility = View.GONE ui.applyButton.visibility = View.GONE return } ui.status.text = resources.getQuantityString(R.plurals.corrections, issues.size, issues.size) ui.correctedText.text = corrected ui.correctedText.visibility = View.VISIBLE ui.applyButton.visibility = if (readOnly) View.GONE else View.VISIBLE // A plain list of what changed. On a phone this reads better than trying // to paint underlines onto text the user cannot edit here anyway. ui.issueList.text = issues.joinToString("\n") { issue -> "ยท ${issue.original} โ†’ ${issue.replacement}" } ui.issueList.visibility = View.VISIBLE } private fun showRewriteMenu() { val labels = resources.getStringArray(R.array.rewrite_labels) val modes = resources.getStringArray(R.array.rewrite_modes) androidx.appcompat.app.AlertDialog.Builder(this) .setTitle(R.string.rewrite) .setItems(labels) { _, which -> rewrite(modes[which]) } .show() } private fun rewrite(mode: String) { setBusy(true, getString(R.string.rewriting)) lifecycleScope.launch { try { corrected = SahiApi.rewrite(original, mode) issues = emptyList() setBusy(false, getString(R.string.rewritten)) ui.correctedText.text = corrected ui.correctedText.visibility = View.VISIBLE ui.issueList.visibility = View.GONE ui.applyButton.visibility = if (readOnly) View.GONE else View.VISIBLE } catch (e: Exception) { setBusy(false, e.message ?: getString(R.string.something_went_wrong)) } } } private fun applyAndReturn() { if (corrected.isEmpty()) return lifecycleScope.launch { issues.forEach { SahiApi.report("accepted", it) } } // Returning the text is what makes the host app replace the selection. setResult( Activity.RESULT_OK, Intent().putExtra(Intent.EXTRA_PROCESS_TEXT, corrected) ) finish() } private fun copyToClipboard() { val text = corrected.ifEmpty { original } val clipboard = getSystemService(CLIPBOARD_SERVICE) as android.content.ClipboardManager clipboard.setPrimaryClip(android.content.ClipData.newPlainText("Sahi", text)) Toast.makeText(this, R.string.copied, Toast.LENGTH_SHORT).show() } private fun setBusy(busy: Boolean, message: String?) { ui.progress.visibility = if (busy) View.VISIBLE else View.GONE ui.rewriteButton.isEnabled = !busy ui.applyButton.isEnabled = !busy message?.let { ui.status.text = it } } private fun finishWith(message: String) { Toast.makeText(this, message, Toast.LENGTH_LONG).show() finish() } }