package oppen.tva.ui import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.inputmethod.EditorInfo import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar import oppen.tva.R import oppen.tva.Tva import oppen.tva.databinding.ActivityTvaBinding import oppen.tva.io.GeminiResponse import oppen.tva.io.RuntimeCache import oppen.tva.io.TvaState import oppen.tva.io.history.tabs.TabHistoryInterface import oppen.tva.io.history.uris.HistoryInterface import oppen.tva.ui.modals_menus.about.AboutDialog import oppen.tva.ui.modals_menus.history.HistoryDialog import oppen.tva.ui.modals_menus.input.InputDialog import oppen.tva.ui.modals_menus.overflow.OverflowPopup import oppen.tva.ui.modals_menus.set_home.SetHomeDialog import oppen.tva.ui.modals_menus.tabs.NewTabPopup import oppen.tva.ui.modals_menus.tabs.TabsDialog import oppen.visibleRetainingSpace class TvaActivity : AppCompatActivity() { private val model by viewModels() private lateinit var binding: ActivityTvaBinding private lateinit var history: HistoryInterface private val adapter = GemtextAdapter { uri, longTap, view -> if(longTap){ NewTabPopup.show(view){ menuId -> when (menuId) { R.id.link_menu_open_in_new_tab -> { model.newTab(uri) } } } }else{ model.request(uri) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_tva) binding.viewmodel = model binding.lifecycleOwner = this binding.gemtextRecycler.layoutManager = LinearLayoutManager(this) binding.gemtextRecycler.adapter = adapter history = HistoryInterface.default(this) model.initialise(TabHistoryInterface.default(this)){ state -> when(state){ is TvaState.AppQuery -> runOnUiThread{ showAlert("App backdoor/query not implemented yet") } is TvaState.ResponseInput -> runOnUiThread { InputDialog.show(this, state){ queryAddress -> model.request(queryAddress) } } is TvaState.Requesting -> loadingView(true) is TvaState.NotGeminiRequest -> externalProtocol(state) is TvaState.ResponseError -> showAlert("${GeminiResponse.getCodeString(state.header.code)}: ${state.header.meta}") is TvaState.ResponseGemtext -> renderGemtext(state) is TvaState.ResponseText -> runOnUiThread{ showAlert("Plain text display not implemented") } is TvaState.TabChange -> binding.tabCount.text = "${state.count}" is TvaState.Blank -> { binding.addressEdit.setText("") adapter.render(arrayListOf()) } } } binding.addressEdit.setOnEditorActionListener { _, actionId, _ -> when (actionId) { EditorInfo.IME_ACTION_GO -> { model.request(binding.addressEdit.text.toString()) return@setOnEditorActionListener true } else -> return@setOnEditorActionListener false } } binding.more.setOnClickListener { OverflowPopup.show(binding.more){menuId -> when (menuId) { R.id.overflow_menu_share -> { Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, binding.addressEdit.text.toString()) type = "text/plain" startActivity(Intent.createChooser(this, null)) } } R.id.overflow_menu_reload -> { val address = binding.addressEdit.text.toString() RuntimeCache.remove(address) model.request(address) } R.id.overflow_menu_history -> HistoryDialog.show(this){ historyAddress -> model.request(historyAddress) } R.id.overflow_menu_about -> AboutDialog.show(this) R.id.overflow_menu_set_home -> { SetHomeDialog.show(this, binding.addressEdit.text.toString()){ showAlert("Home capsule updated") } } R.id.overflow_menu_copy -> { val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(getString(R.string.gemini_address), binding.addressEdit.text.toString()) clipboard.setPrimaryClip(clip) Snackbar.make(binding.root, getString(R.string.address_copied_to_clipboard), Snackbar.LENGTH_SHORT).setAction(R.string.share) { Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, binding.addressEdit.text.toString()) type = "text/plain" startActivity(Intent.createChooser(this, null)) } }.show() } } } } binding.home.setOnClickListener { val prefs = getSharedPreferences("oppen.tva.ui.dialogs.set_home", Context.MODE_PRIVATE) val home = prefs.getString("home", Tva.DEFAULT_HOME_CAPSULE) model.request(home!!) } binding.tabs.setOnClickListener { TabsDialog().show(this, model) } } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) intent?.data.toString().let{model.request(it)} } private fun showAlert(message: String) = runOnUiThread{ loadingView(false) if(message.length > 40){ AlertDialog.Builder(this) .setMessage(message) .show() }else { Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show() } } private fun externalProtocol(state: TvaState.NotGeminiRequest) = runOnUiThread { loadingView(false) val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(state.uri.toString())) startActivity(browserIntent) } private fun renderGemtext(state: TvaState.ResponseGemtext) = runOnUiThread { loadingView(false) binding.addressEdit.setText(state.uri.toString()) adapter.render(state.lines) history.add(state.uri.toString()) } private fun loadingView(visible: Boolean) = runOnUiThread { binding.progressBar.visibleRetainingSpace(visible) if(visible) binding.appBar.setExpanded(true) } @ExperimentalStdlibApi override fun onBackPressed() { if(model.canGoBack()){ model.goBack() }else{ super.onBackPressed() } } override fun onDestroy() { super.onDestroy() model.persistTabState() } }