mirror of https://git.sr.ht/~oppen/ariane
numerous fixes
This commit is contained in:
parent
4ea369610a
commit
e58deb28c3
|
@ -1,16 +1,7 @@
|
|||
package oppen
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
|
||||
fun String.alert(context: Context){
|
||||
AlertDialog.Builder(context)
|
||||
.setMessage(this)
|
||||
.setPositiveButton("OK"){_, _ ->}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun View.visible(visible: Boolean) = when {
|
||||
visible -> this.visibility = View.VISIBLE
|
||||
else -> this.visibility = View.GONE
|
||||
|
|
|
@ -22,39 +22,50 @@ class GeminiDatasource: Datasource{
|
|||
|
||||
when (uri.scheme) {
|
||||
GEMINI_SCHEME -> {
|
||||
//Indicate app should show progress indicator
|
||||
onUpdate(TvaState.Requesting(uri))
|
||||
|
||||
GlobalScope.launch {
|
||||
geminiRequest(uri, onUpdate)
|
||||
val cached = RuntimeCache.get(uri)
|
||||
if(cached != null){
|
||||
last = uri
|
||||
onUpdate(TvaState.ResponseGemtext(uri, cached.first, cached.second))
|
||||
return
|
||||
}else{
|
||||
onUpdate(TvaState.Requesting(uri))
|
||||
|
||||
GlobalScope.launch {
|
||||
geminiRequest(uri, onUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else -> {
|
||||
val address = uri.toString()
|
||||
when {
|
||||
val parsedUri = when {
|
||||
address.startsWith("//") -> {
|
||||
//just missing protocol
|
||||
onUpdate(TvaState.Requesting(uri))
|
||||
request(URI.create("gemini:$address"), onUpdate)
|
||||
return
|
||||
URI.create("gemini:$address")
|
||||
}
|
||||
address.startsWith("/") -> {
|
||||
//internal navigation
|
||||
val internalNav = "gemini://${last?.host}$address"
|
||||
onUpdate(TvaState.Requesting(uri))
|
||||
request(URI.create(internalNav), onUpdate)
|
||||
return
|
||||
URI.create(internalNav)
|
||||
}
|
||||
!address.contains("://") -> {
|
||||
//looks like a relative link
|
||||
val lastAddress = last.toString()
|
||||
val relAddress = "${lastAddress.substring(0, lastAddress.lastIndexOf("/") + 1)}$address"
|
||||
onUpdate(TvaState.Requesting(uri))
|
||||
request(URI.create(relAddress), onUpdate)
|
||||
URI.create(relAddress)
|
||||
}
|
||||
else -> {
|
||||
onUpdate(TvaState.NotGeminiRequest(uri))
|
||||
return
|
||||
}
|
||||
else -> onUpdate(TvaState.NotGeminiRequest(uri))
|
||||
}
|
||||
|
||||
val cached = RuntimeCache.get(parsedUri)
|
||||
if(cached != null){
|
||||
last = parsedUri
|
||||
onUpdate(TvaState.ResponseGemtext(parsedUri, cached.first, cached.second))
|
||||
}else{
|
||||
request(parsedUri, onUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,31 +98,56 @@ class GeminiDatasource: Datasource{
|
|||
outWriter.flush()
|
||||
|
||||
if (outWriter.checkError()) {
|
||||
onUpdate(TvaState.GeminiPrintWriterError)
|
||||
onUpdate(TvaState.ResponseError(GeminiResponse.Header(-1, "Print Writer Error")))
|
||||
outWriter.close()
|
||||
return
|
||||
}
|
||||
|
||||
outputStreamWriter.close()
|
||||
bufferedWriter.close()
|
||||
outWriter.close()
|
||||
|
||||
// IN <<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
var headerLine = ""
|
||||
InputStreamReader(socket.inputStream).use{ streamReader ->
|
||||
BufferedReader(streamReader).use{ bufferedReader ->
|
||||
headerLine = bufferedReader.readLine()
|
||||
}
|
||||
}
|
||||
|
||||
println("Tva: header: $headerLine")
|
||||
|
||||
val header = GeminiResponse.parseHeader(headerLine)
|
||||
|
||||
when {
|
||||
header.code != GeminiResponse.SUCCESS -> onUpdate(TvaState.ResponseError(header))
|
||||
header.meta == "text/gemini" -> getGemtext(socket, uri, header, onUpdate)
|
||||
header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate)
|
||||
else -> onUpdate(TvaState.ResponseError(header))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGemtext(socket: SSLSocket, uri: URI, header: GeminiResponse.Header, onUpdate: (state: TvaState) -> Unit){
|
||||
|
||||
val lines = mutableListOf<String>()
|
||||
|
||||
socket.inputStream.reader().use { inputStreamReader ->
|
||||
BufferedReader(inputStreamReader).use { reader ->
|
||||
lines.addAll(reader.readLines())
|
||||
}
|
||||
}
|
||||
|
||||
val header = lines.firstOrNull() ?: ""
|
||||
lines.removeAt(0)
|
||||
socket.close()
|
||||
|
||||
println("Tva: header: $header")
|
||||
val processed = GemtextHelper.findCodeBlocks(lines)
|
||||
RuntimeCache.put(uri, header, processed)
|
||||
onUpdate(TvaState.ResponseGemtext(uri, header, processed))
|
||||
}
|
||||
|
||||
outputStreamWriter.close()
|
||||
bufferedWriter.close()
|
||||
outWriter.close()
|
||||
private fun getString(socket: SSLSocket, uri: URI, header: GeminiResponse.Header, onUpdate: (state: TvaState) -> Unit){
|
||||
val content = socket.inputStream.bufferedReader().use { reader -> reader.readText() }
|
||||
|
||||
|
||||
|
||||
|
||||
onUpdate(TvaState.GeminiResponse(uri, header, GemtextHelper.findCodeBlocks(lines)))
|
||||
socket.close()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package oppen.tva.io
|
||||
|
||||
object GeminiResponse {
|
||||
|
||||
const val INPUT = 1
|
||||
const val SUCCESS = 2
|
||||
const val REDIRECT = 3
|
||||
const val TEMPORARY_FAILURE = 4
|
||||
const val PERMANENT_FAILURE = 5
|
||||
const val CLIENT_CERTIFICATE_REQUIRED = 6
|
||||
const val UNKNOWN = -1
|
||||
|
||||
fun parseHeader(header: String): Header{
|
||||
val segments = header.trim().split(" ")
|
||||
val meta = if(segments.size > 1){
|
||||
segments[1]
|
||||
}else{
|
||||
"No meta/mime type"
|
||||
}
|
||||
return when {
|
||||
segments.first().startsWith("1") -> Header(INPUT, meta)
|
||||
segments.first().startsWith("2") -> Header(SUCCESS, meta)
|
||||
segments.first().startsWith("3") -> Header(REDIRECT, meta)
|
||||
segments.first().startsWith("4") -> Header(TEMPORARY_FAILURE, meta)
|
||||
segments.first().startsWith("5") -> Header(PERMANENT_FAILURE, meta)
|
||||
segments.first().startsWith("6") -> Header(CLIENT_CERTIFICATE_REQUIRED, meta)
|
||||
else -> Header(UNKNOWN, meta)
|
||||
}
|
||||
}
|
||||
|
||||
class Header(val code: Int, val meta: String)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package oppen.tva.io
|
||||
|
||||
import androidx.collection.LruCache
|
||||
import java.net.URI
|
||||
|
||||
object RuntimeCache {
|
||||
|
||||
private const val CACHE_SIZE = 4 * 1024 * 1024
|
||||
private val lruCache = LruCache<String, Pair<GeminiResponse.Header, List<String>>>(CACHE_SIZE)
|
||||
|
||||
fun put(uri: URI, header: GeminiResponse.Header, lines: List<String>){
|
||||
lruCache.put(uri.toString(), Pair(header, lines))
|
||||
}
|
||||
|
||||
fun get(uri: URI): Pair<GeminiResponse.Header, List<String>>? = lruCache[uri.toString()]
|
||||
}
|
|
@ -6,8 +6,11 @@ sealed class TvaState {
|
|||
data class AppQuery(val uri: URI): TvaState()
|
||||
data class Requesting(val uri: URI): TvaState()
|
||||
data class NotGeminiRequest(val uri: URI) : TvaState()
|
||||
data class GeminiResponse(val uri: URI, val header: String, val lines: List<String>) : TvaState()
|
||||
|
||||
data class ResponseGemtext(val uri: URI, val header: GeminiResponse.Header, val lines: List<String>) : TvaState()
|
||||
data class ResponseText(val uri: URI, val header: GeminiResponse.Header, val content: String) : TvaState()
|
||||
data class ResponseError(val header: GeminiResponse.Header): TvaState()
|
||||
|
||||
data class TabChange(val count: Int) : TvaState()
|
||||
object Blank: TvaState()
|
||||
object GeminiPrintWriterError : TvaState()
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package oppen.tva.ui
|
||||
|
||||
import android.R.attr.label
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
|
@ -12,11 +12,12 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import oppen.alert
|
||||
import oppen.tva.R
|
||||
import oppen.tva.databinding.ActivityTvaBinding
|
||||
import oppen.tva.io.TvaState
|
||||
import oppen.tva.io.history.CacheInterface
|
||||
import oppen.tva.ui.overflow.OverflowPopup
|
||||
import oppen.tva.ui.set_home.SetHome
|
||||
import oppen.tva.ui.tabs.NewTabPopup
|
||||
import oppen.tva.ui.tabs.TabsDialog
|
||||
import oppen.visibleRetainingSpace
|
||||
|
@ -33,19 +34,6 @@ class TvaActivity : AppCompatActivity() {
|
|||
R.id.link_menu_open_in_new_tab -> {
|
||||
model.newTab(uri)
|
||||
}
|
||||
R.id.link_menu_copy -> {
|
||||
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.gemini_address), uri.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, uri.toString())
|
||||
type = "text/plain"
|
||||
startActivity(Intent.createChooser(this, null))
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
|
@ -65,16 +53,17 @@ class TvaActivity : AppCompatActivity() {
|
|||
|
||||
model.initialise(CacheInterface.default(this)){ state ->
|
||||
when(state){
|
||||
is TvaState.AppQuery -> TODO()
|
||||
is TvaState.AppQuery -> runOnUiThread{ showAlert("App backdoor/query not implemented yet") }
|
||||
is TvaState.Requesting -> loadingView(true)
|
||||
is TvaState.NotGeminiRequest -> externalProtocol(state)
|
||||
is TvaState.GeminiResponse -> renderGemtext(state)
|
||||
is TvaState.ResponseError -> showAlert("${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())
|
||||
}
|
||||
TvaState.GeminiPrintWriterError -> "Error with socket writer".alert(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,27 +77,66 @@ class TvaActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
binding.more.setOnClickListener { "Not implemented yet".alert(this) }
|
||||
binding.home.setOnClickListener { "Not implemented yet".alert(this) }
|
||||
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_about -> {
|
||||
|
||||
}
|
||||
R.id.overflow_menu_set_home -> {
|
||||
SetHome.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.set_home", Context.MODE_PRIVATE)
|
||||
val home = prefs.getString("home", "gemini://gemini.circumlunar.space/")
|
||||
model.request(home!!)
|
||||
}
|
||||
binding.tabs.setOnClickListener {
|
||||
TabsDialog().show(this, model)
|
||||
}
|
||||
}
|
||||
|
||||
private fun externalProtocol(state: TvaState.NotGeminiRequest) {
|
||||
private fun showAlert(message: String) = runOnUiThread{
|
||||
loadingView(false)
|
||||
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.GeminiResponse) = runOnUiThread {
|
||||
private fun renderGemtext(state: TvaState.ResponseGemtext) = runOnUiThread {
|
||||
loadingView(false)
|
||||
if(state.header.startsWith("2") && state.header.contains("text/gemini")) {
|
||||
binding.addressEdit.setText(state.uri.toString())
|
||||
adapter.render(state.lines)
|
||||
}else{
|
||||
"Server returned an error - or non gemtext mimetype not implemented yet: ${state.header}".alert(this)
|
||||
}
|
||||
binding.addressEdit.setText(state.uri.toString())
|
||||
adapter.render(state.lines)
|
||||
}
|
||||
|
||||
private fun loadingView(visible: Boolean) = runOnUiThread {
|
||||
|
|
|
@ -50,16 +50,17 @@ class TvaViewModel: ViewModel() {
|
|||
fun request(uri: URI){
|
||||
gemini.request(uri){ state ->
|
||||
when(state){
|
||||
is TvaState.Requesting -> onState(state)
|
||||
is TvaState.AppQuery -> {}
|
||||
is TvaState.ResponseGemtext -> renderGemini(state)
|
||||
is TvaState.Requesting -> onState(state)
|
||||
is TvaState.ResponseError -> onState(state)
|
||||
is TvaState.NotGeminiRequest -> onState(state)
|
||||
is TvaState.GeminiResponse -> renderGemini(state)
|
||||
TvaState.GeminiPrintWriterError -> onState(state)
|
||||
is TvaState.ResponseText -> onState(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderGemini(state: TvaState.GeminiResponse) {
|
||||
private fun renderGemini(state: TvaState.ResponseGemtext) {
|
||||
if(tabs[activeTab].history.isEmpty() || tabs[activeTab].history.last() != state.uri){
|
||||
tabs[activeTab].add(state.uri)
|
||||
}
|
||||
|
@ -100,11 +101,9 @@ class TvaViewModel: ViewModel() {
|
|||
|
||||
if(deleteIndex > activeTab){
|
||||
tabs.removeAt(deleteIndex)
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
}else if(deleteIndex < activeTab){
|
||||
tabs.removeAt(deleteIndex)
|
||||
activeTab--
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
}else if(deleteIndex == activeTab){
|
||||
if(tabs.size > 1){
|
||||
tabs.removeAt(deleteIndex)
|
||||
|
@ -120,5 +119,6 @@ class TvaViewModel: ViewModel() {
|
|||
onState(TvaState.Blank)
|
||||
}
|
||||
}
|
||||
onState(TvaState.TabChange(tabs.size))
|
||||
}
|
||||
}
|
|
@ -3,9 +3,10 @@ package oppen.tva.ui.overflow
|
|||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.MenuCompat
|
||||
import oppen.tva.R
|
||||
|
||||
class OverflowPopup {
|
||||
object OverflowPopup {
|
||||
|
||||
fun show(view: View?, onMenuOption: (menuId: Int) -> Unit){
|
||||
if(view != null) {
|
||||
|
@ -16,6 +17,7 @@ class OverflowPopup {
|
|||
onMenuOption(menuItem.itemId)
|
||||
true
|
||||
}
|
||||
MenuCompat.setGroupDividerEnabled(popup.menu, true)
|
||||
popup.show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package oppen.tva.ui.set_home
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDialog
|
||||
import kotlinx.android.synthetic.main.dialog_set_home.view.*
|
||||
import oppen.tva.R
|
||||
|
||||
object SetHome {
|
||||
|
||||
fun show(context: Context, currentAddress: String, onUpdate: () -> Unit){
|
||||
val prefs = context.getSharedPreferences("oppen.tva.ui.set_home", Context.MODE_PRIVATE)
|
||||
|
||||
val home = prefs.getString("home", "")
|
||||
|
||||
val dialog = AppCompatDialog(context, R.style.DayNightDialog)
|
||||
|
||||
val view = View.inflate(context, R.layout.dialog_set_home, null)
|
||||
|
||||
view.home_edit_text.setText(home)
|
||||
|
||||
view.set_home_button.setOnClickListener {
|
||||
prefs.edit().putString("home", view.home_edit_text.text.toString()).apply()
|
||||
onUpdate()
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
view.use_current_button.setOnClickListener {
|
||||
view.home_edit_text.setText(currentAddress)
|
||||
}
|
||||
|
||||
dialog.setTitle("Set home capsule")
|
||||
|
||||
dialog.setContentView(view)
|
||||
dialog.show()
|
||||
}
|
||||
}
|
|
@ -1,10 +1,25 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:height="24.5dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="49"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
|
||||
android:pathData="M47.723,19.3306L24.481,0.1701C24.3393,0.0567 24.1692,0 24.0086,0C23.8385,0 23.6685,0.0567 23.5362,0.1701L11.5845,10.0054V0.7464C11.5845,0.3307 11.2444,0 10.8381,0C10.4318,0 10.0917,0.3401 10.0917,0.7464V11.2431L0.2753,19.3306C-0.046,19.5951 -0.0932,20.0675 0.1713,20.3887C0.4359,20.71 0.9083,20.7572 1.2295,20.4927L4.2056,18.0456V45.0007C4.2056,46.6541 5.5472,48.0052 7.2101,48.0052H40.8071C42.4605,48.0052 43.8115,46.6635 43.8115,45.0007V18.0362L46.7877,20.4832C46.9294,20.5966 47.0994,20.6533 47.2601,20.6533C47.4774,20.6533 47.6947,20.5588 47.8364,20.3793C48.0915,20.0675 48.0442,19.5951 47.723,19.3306ZM35.2422,46.4935H25.2652V34.0977H35.2422V46.4935ZM42.2999,45.0007C42.2999,45.8321 41.6291,46.5029 40.7976,46.5029H36.7445V33.3419C36.7445,32.9262 36.4043,32.5955 35.9981,32.5955H24.5188C24.1031,32.5955 23.7724,32.9356 23.7724,33.3419V46.4935H7.2006C6.3692,46.4935 5.6984,45.8227 5.6984,44.9913V16.7985L23.9991,1.7195L42.2999,16.7985V45.0007Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M10.8381,34.6646C10.4224,34.6646 10.0917,35.0047 10.0917,35.411V36.6581C10.0917,37.0738 10.4319,37.4045 10.8381,37.4045C11.2444,37.4045 11.5845,37.0644 11.5845,36.6581V35.411C11.5845,34.9953 11.2538,34.6646 10.8381,34.6646Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M10.8381,38.8029C10.4224,38.8029 10.0917,39.143 10.0917,39.5492V40.7964C10.0917,41.2121 10.4319,41.5428 10.8381,41.5428C11.2444,41.5428 11.5845,41.2026 11.5845,40.7964V39.5492C11.5845,39.143 11.2538,38.8029 10.8381,38.8029Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M17.2249,34.6646C16.8092,34.6646 16.4785,35.0047 16.4785,35.411V36.6581C16.4785,37.0738 16.8187,37.4045 17.2249,37.4045C17.6312,37.4045 17.9713,37.0644 17.9713,36.6581V35.411C17.9808,34.9953 17.6406,34.6646 17.2249,34.6646Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M17.2249,38.8029C16.8092,38.8029 16.4785,39.143 16.4785,39.5492V40.7964C16.4785,41.2121 16.8187,41.5428 17.2249,41.5428C17.6312,41.5428 17.9713,41.2026 17.9713,40.7964V39.5492C17.9808,39.143 17.6406,38.8029 17.2249,38.8029Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M17.2722,16.3072C17.2722,20.0203 20.2861,23.0342 23.9991,23.0342C27.7122,23.0342 30.7261,20.0203 30.7261,16.3072C30.7261,12.5942 27.7122,9.5803 23.9991,9.5803C20.2861,9.5803 17.2722,12.5942 17.2722,16.3072ZM29.2239,16.3072C29.2239,19.1889 26.8808,21.5319 23.9991,21.5319C21.1175,21.5319 18.7744,19.1889 18.7744,16.3072C18.7744,13.4256 21.1175,11.0825 23.9991,11.0825C26.8808,11.0825 29.2239,13.4256 29.2239,16.3072Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent" android:layout_height="wrap_content"
|
||||
android:padding="@dimen/default_margin">
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatEditText
|
||||
android:id="@+id/home_edit_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/set_home_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_alignParentRight="true"
|
||||
android:text="Update"
|
||||
android:layout_below="@+id/home_edit_text"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/use_current_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toLeftOf="@+id/set_home_button"
|
||||
android:text="Use current"
|
||||
android:layout_below="@+id/home_edit_text"/>
|
||||
</RelativeLayout>
|
|
@ -11,5 +11,6 @@
|
|||
android:paddingBottom="@dimen/default_margin"
|
||||
android:fontFamily="monospace"
|
||||
android:background="@color/code_background"
|
||||
android:textIsSelectable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
|
@ -5,4 +5,5 @@
|
|||
android:textSize="@dimen/h1_text_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
android:layout_margin="@dimen/default_margin" />
|
|
@ -5,4 +5,5 @@
|
|||
android:textSize="@dimen/h2_text_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
android:layout_margin="@dimen/default_margin" />
|
|
@ -5,4 +5,5 @@
|
|||
android:textSize="@dimen/h3_text_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
android:layout_margin="@dimen/default_margin" />
|
|
@ -6,7 +6,7 @@
|
|||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingLeft="@dimen/default_margin"
|
||||
android:paddingRight="@dimen/default_margin"
|
||||
android:layout_marginLeft="@dimen/default_margin"
|
||||
android:layout_marginRight="@dimen/default_margin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
|
@ -5,5 +5,6 @@
|
|||
android:textSize="@dimen/default_text_size"
|
||||
android:paddingLeft="@dimen/default_margin"
|
||||
android:paddingRight="@dimen/default_margin"
|
||||
android:textIsSelectable="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
|
@ -2,6 +2,4 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/link_menu_open_in_new_tab"
|
||||
android:title="@string/open_in_new_tab" />
|
||||
<item android:id="@+id/link_menu_copy"
|
||||
android:title="@string/copy_address" />
|
||||
</menu>
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/overflow_menu_about"
|
||||
android:title="@string/about" />
|
||||
<group android:id="@+id/app" >
|
||||
<item android:id="@+id/overflow_menu_share"
|
||||
android:title="@string/share" />
|
||||
<item android:id="@+id/overflow_menu_copy"
|
||||
android:title="@string/copy_address" />
|
||||
<item android:id="@+id/overflow_menu_set_home"
|
||||
android:title="@string/set_home" />
|
||||
</group>
|
||||
<group android:id="@+id/other" >
|
||||
<item android:id="@+id/overflow_menu_about"
|
||||
android:title="@string/about" />
|
||||
</group>
|
||||
</menu>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#FFFFFF</color>
|
||||
<color name="colorPrimary">#ffffff</color>
|
||||
<color name="colorPrimaryDark">#B8B8B8</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
<color name="code_background">#000000</color>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#FFFFFF</color>
|
||||
<color name="colorPrimaryDark">#B8B8B8</color>
|
||||
<color name="colorPrimary">#ffffff</color>
|
||||
<color name="colorPrimaryDark">#1d1d1d</color>
|
||||
<color name="colorAccent">#03DAC5</color>
|
||||
<color name="code_background">#efefef</color>
|
||||
</resources>
|
|
@ -4,7 +4,8 @@
|
|||
<string name="open_in_new_tab">Open in new tab</string>
|
||||
<string name="copy_address">Copy address</string>
|
||||
<string name="about">About</string>
|
||||
<string name="address_copied_to_clipboard">Link copied to clipboard</string>
|
||||
<string name="address_copied_to_clipboard">Address copied to clipboard</string>
|
||||
<string name="gemini_address">Gemini address</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="set_home">Set home</string>
|
||||
</resources>
|
|
@ -14,4 +14,6 @@
|
|||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<style name="DayNightDialog" parent="Theme.AppCompat.DayNight.Dialog.Alert"/>
|
||||
|
||||
</resources>
|
Loading…
Reference in New Issue