mirror of
https://git.sr.ht/~oppen/ariane
synced 2025-03-15 16:25:45 +00:00
new homepage, history changes
This commit is contained in:
parent
37d05f2c91
commit
11a4b6d8d2
20 changed files with 295 additions and 30 deletions
app
|
@ -10,8 +10,8 @@ android {
|
|||
applicationId "oppen.tva"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 3
|
||||
versionName "0.0.3 Alpha"
|
||||
versionCode 5
|
||||
versionName "0.2.0 Beta"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"properties": [],
|
||||
"versionCode": 3,
|
||||
"versionName": "0.0.3 Alpha",
|
||||
"versionCode": 5,
|
||||
"versionName": "0.2.0 Beta",
|
||||
"enabled": true,
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
|
|
|
@ -17,6 +17,12 @@
|
|||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="gemini" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package oppen
|
||||
|
||||
import android.os.CountDownTimer
|
||||
import android.view.View
|
||||
|
||||
|
||||
fun View.visible(visible: Boolean) = when {
|
||||
visible -> this.visibility = View.VISIBLE
|
||||
else -> this.visibility = View.GONE
|
||||
|
@ -10,4 +12,15 @@ fun View.visible(visible: Boolean) = when {
|
|||
fun View.visibleRetainingSpace(visible: Boolean) = when {
|
||||
visible -> this.visibility = View.VISIBLE
|
||||
else -> this.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
fun delay(ms: Long, action: () -> Unit){
|
||||
object : CountDownTimer(ms, ms/2) {
|
||||
override fun onTick(millisUntilFinished: Long) {}
|
||||
|
||||
override fun onFinish() {
|
||||
action.invoke()
|
||||
}
|
||||
}.start()
|
||||
|
||||
}
|
|
@ -15,4 +15,8 @@ object RuntimeCache {
|
|||
fun get(uri: URI): Pair<GeminiResponse.Header, List<String>>? = lruCache[uri.toString()]
|
||||
|
||||
fun clear() = lruCache.evictAll()
|
||||
|
||||
fun remove(address: String) {
|
||||
lruCache.remove(address)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package oppen.tva.io.history
|
||||
package oppen.tva.io.history.tabs
|
||||
|
||||
import android.content.Context
|
||||
import java.net.URI
|
||||
|
@ -9,7 +9,7 @@ import kotlin.text.StringBuilder
|
|||
* This is slow, unsafe, awful, and synchronous but I don't want get bogged down implementing Room just yet
|
||||
*
|
||||
*/
|
||||
class BasicCache(context: Context): CacheInterface {
|
||||
class BasicTabHistoryCache(context: Context): TabHistoryInterface {
|
||||
|
||||
private val DELIM = "||"
|
||||
private val prefsKey = "oppen.tva.io.history.BasicCache.PREFS_KEY"
|
|
@ -1,4 +1,4 @@
|
|||
package oppen.tva.io.history
|
||||
package oppen.tva.io.history.tabs
|
||||
|
||||
import java.net.URI
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package oppen.tva.io.history
|
||||
package oppen.tva.io.history.tabs
|
||||
|
||||
import android.content.Context
|
||||
|
||||
interface CacheInterface {
|
||||
interface TabHistoryInterface {
|
||||
fun getTabs(onTabs: (tabs: MutableList<Tab>, activeIndex: Int)-> Unit)
|
||||
fun update(tabs: List<Tab>, activeIndex: Int)
|
||||
|
||||
companion object{
|
||||
fun default(context: Context): CacheInterface{
|
||||
return BasicCache(context)
|
||||
fun default(context: Context): TabHistoryInterface {
|
||||
return BasicTabHistoryCache(context)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package oppen.tva.io.history.uris
|
||||
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
*
|
||||
* Another shared prefs implementation so I don't get slowed down by a Room implementation at this point
|
||||
*
|
||||
*/
|
||||
class BasicURIHistory(context: Context): HistoryInterface {
|
||||
|
||||
private val DELIM = "||"
|
||||
private val prefsKey = "oppen.tva.io.history.BasicURIHistory.prefsKey"
|
||||
private val prefsHistoryKey = "oppen.tva.io.history.BasicURIHistory.prefsHistoryKey"
|
||||
private val prefs = context.getSharedPreferences(prefsKey, Context.MODE_PRIVATE)
|
||||
|
||||
override fun add(address: String) {
|
||||
|
||||
val history = get()
|
||||
|
||||
when {
|
||||
history.size >= 50 -> history.removeAt(0)
|
||||
}
|
||||
|
||||
if(history.isNotEmpty() && history.size > 10){
|
||||
if(history.subList(history.size - 10, history.size).contains(address)) return
|
||||
}
|
||||
|
||||
history.add(address)
|
||||
val raw = history.joinToString(DELIM)
|
||||
prefs.edit().putString(prefsHistoryKey, raw).apply()
|
||||
}
|
||||
|
||||
override fun clear(){
|
||||
prefs.edit().clear().apply()
|
||||
}
|
||||
|
||||
override fun get(): ArrayList<String> {
|
||||
return when (val raw = prefs.getString(prefsHistoryKey, null)) {
|
||||
null -> arrayListOf()
|
||||
else -> ArrayList(raw.split(DELIM))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package oppen.tva.io.history.uris
|
||||
|
||||
import android.content.Context
|
||||
|
||||
interface HistoryInterface {
|
||||
fun add(address: String)
|
||||
fun get(): List<String>
|
||||
fun clear()
|
||||
|
||||
companion object{
|
||||
fun default(context: Context): HistoryInterface {
|
||||
return BasicURIHistory(context)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,8 +18,10 @@ 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.CacheInterface
|
||||
import oppen.tva.io.history.tabs.TabHistoryInterface
|
||||
import oppen.tva.io.history.uris.HistoryInterface
|
||||
import oppen.tva.ui.about.AboutDialog
|
||||
import oppen.tva.ui.history.HistoryDialog
|
||||
import oppen.tva.ui.overflow.OverflowPopup
|
||||
import oppen.tva.ui.set_home.SetHomeDialog
|
||||
import oppen.tva.ui.tabs.NewTabPopup
|
||||
|
@ -31,6 +33,7 @@ class TvaActivity : AppCompatActivity() {
|
|||
|
||||
private val model by viewModels<TvaViewModel>()
|
||||
private lateinit var binding: ActivityTvaBinding
|
||||
private lateinit var history: HistoryInterface
|
||||
private val adapter = GemtextAdapter { uri, longTap, view ->
|
||||
if(longTap){
|
||||
NewTabPopup.show(view){ menuId ->
|
||||
|
@ -43,7 +46,6 @@ class TvaActivity : AppCompatActivity() {
|
|||
}else{
|
||||
model.request(uri)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -55,7 +57,9 @@ class TvaActivity : AppCompatActivity() {
|
|||
binding.gemtextRecycler.layoutManager = LinearLayoutManager(this)
|
||||
binding.gemtextRecycler.adapter = adapter
|
||||
|
||||
model.initialise(CacheInterface.default(this)){ state ->
|
||||
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.Requesting -> loadingView(true)
|
||||
|
@ -92,9 +96,13 @@ class TvaActivity : AppCompatActivity() {
|
|||
startActivity(Intent.createChooser(this, null))
|
||||
}
|
||||
}
|
||||
R.id.overflow_menu_clear_cache -> {
|
||||
RuntimeCache.clear()
|
||||
showAlert("Runtime cache cleared")
|
||||
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 -> {
|
||||
|
@ -128,6 +136,11 @@ class TvaActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
intent?.data.toString().let{model.request(it)}
|
||||
}
|
||||
|
||||
private fun showAlert(message: String) = runOnUiThread{
|
||||
loadingView(false)
|
||||
|
||||
|
@ -150,6 +163,8 @@ class TvaActivity : AppCompatActivity() {
|
|||
loadingView(false)
|
||||
binding.addressEdit.setText(state.uri.toString())
|
||||
adapter.render(state.lines)
|
||||
|
||||
history.add(state.uri.toString())
|
||||
}
|
||||
|
||||
private fun loadingView(visible: Boolean) = runOnUiThread {
|
||||
|
|
|
@ -3,8 +3,8 @@ package oppen.tva.ui
|
|||
import androidx.lifecycle.ViewModel
|
||||
import oppen.tva.io.Datasource
|
||||
import oppen.tva.io.TvaState
|
||||
import oppen.tva.io.history.CacheInterface
|
||||
import oppen.tva.io.history.Tab
|
||||
import oppen.tva.io.history.tabs.TabHistoryInterface
|
||||
import oppen.tva.io.history.tabs.Tab
|
||||
import java.net.URI
|
||||
|
||||
class TvaViewModel: ViewModel() {
|
||||
|
@ -13,10 +13,10 @@ class TvaViewModel: ViewModel() {
|
|||
private var onState: (state: TvaState) -> Unit = {}
|
||||
private var activeTab = 0
|
||||
|
||||
private lateinit var cache: CacheInterface
|
||||
private lateinit var cache: TabHistoryInterface
|
||||
var tabs = mutableListOf<Tab>()
|
||||
|
||||
fun initialise(cache: CacheInterface, onState: (state: TvaState) -> Unit){
|
||||
fun initialise(cache: TabHistoryInterface, onState: (state: TvaState) -> Unit){
|
||||
this.cache = cache
|
||||
this.onState = onState
|
||||
|
||||
|
@ -25,7 +25,7 @@ class TvaViewModel: ViewModel() {
|
|||
if(tabs.isEmpty()){
|
||||
this.tabs.add(Tab(0))
|
||||
activeTab = 0
|
||||
request(URI.create("gemini://gemini.circumlunar.space/"))
|
||||
request(URI.create("gemini://gemini.circumlunar.space/~oppen/tva/index.gmi"))
|
||||
onState(TvaState.TabChange(1))
|
||||
}else{
|
||||
activeTab = activeIndex
|
||||
|
|
29
app/src/main/java/oppen/tva/ui/history/HistoryAdapter.kt
Normal file
29
app/src/main/java/oppen/tva/ui/history/HistoryAdapter.kt
Normal file
|
@ -0,0 +1,29 @@
|
|||
package oppen.tva.ui.history
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.row_history.view.*
|
||||
import oppen.delay
|
||||
import oppen.tva.R
|
||||
|
||||
class HistoryAdapter(val history: List<String>, val onClick:(address: String) -> Unit): RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {
|
||||
|
||||
class ViewHolder(view: View): RecyclerView.ViewHolder(view)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_history, parent, false))
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = history.size
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.itemView.history_address.text = history[position]
|
||||
holder.itemView.history_row.setOnClickListener {
|
||||
delay(500){
|
||||
onClick(history[holder.adapterPosition])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
app/src/main/java/oppen/tva/ui/history/HistoryDialog.kt
Normal file
60
app/src/main/java/oppen/tva/ui/history/HistoryDialog.kt
Normal file
|
@ -0,0 +1,60 @@
|
|||
package oppen.tva.ui.history
|
||||
|
||||
import android.content.Context
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.MenuCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.synthetic.main.dialog_about.view.close_tab_dialog
|
||||
import kotlinx.android.synthetic.main.dialog_history.view.*
|
||||
import oppen.tva.R
|
||||
import oppen.tva.io.RuntimeCache
|
||||
import oppen.tva.io.history.uris.HistoryInterface
|
||||
|
||||
object HistoryDialog {
|
||||
fun show(context: Context, onHistoryItem: (address: String) -> Unit){
|
||||
val historyCache = HistoryInterface.default(context)
|
||||
val history = historyCache.get()
|
||||
|
||||
val dialog = AppCompatDialog(context, R.style.AppTheme)
|
||||
|
||||
val view = View.inflate(context, R.layout.dialog_history, null)
|
||||
dialog.setContentView(view)
|
||||
|
||||
view.close_tab_dialog.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
view.history_overflow.setOnClickListener {
|
||||
val popup = PopupMenu(view.context, view.history_overflow)
|
||||
val inflater: MenuInflater = popup.menuInflater
|
||||
inflater.inflate(R.menu.history_overflow_menu, popup.menu)
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
if(menuItem.itemId == R.id.history_overflow_clear_history){
|
||||
historyCache.clear()
|
||||
dialog.dismiss()
|
||||
Toast.makeText(context, "History cleared", Toast.LENGTH_SHORT).show()
|
||||
}else if(menuItem.itemId == R.id.history_overflow_clear_runtime_cache){
|
||||
RuntimeCache.clear()
|
||||
dialog.dismiss()
|
||||
Toast.makeText(context, "Runtime cache cleared", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
true
|
||||
}
|
||||
MenuCompat.setGroupDividerEnabled(popup.menu, true)
|
||||
popup.show()
|
||||
}
|
||||
|
||||
view.history_recycler.layoutManager = LinearLayoutManager(context)
|
||||
view.history_recycler.adapter = HistoryAdapter(history.asReversed()){ address ->
|
||||
onHistoryItem(address)
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.grid_cell_tab.view.*
|
||||
import oppen.tva.R
|
||||
import oppen.tva.io.history.Tab
|
||||
import oppen.tva.io.history.tabs.Tab
|
||||
|
||||
class TabsAdapter(
|
||||
private val tabs: List<Tab>,
|
||||
|
|
40
app/src/main/res/layout/dialog_history.xml
Normal file
40
app/src/main/res/layout/dialog_history.xml
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?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="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/default_margin">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/close_tab_dialog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_margin="@dimen/button_margin"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/vector_close" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/history_overflow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_margin="@dimen/button_margin"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/vector_overflow" />
|
||||
|
||||
</RelativeLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/history_recycler"
|
||||
android:layout_below="@+id/header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</RelativeLayout>
|
19
app/src/main/res/layout/row_history.xml
Normal file
19
app/src/main/res/layout/row_history.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/history_row"
|
||||
android:minHeight="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/history_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingStart="@dimen/default_margin" />
|
||||
|
||||
</RelativeLayout>
|
7
app/src/main/res/menu/history_overflow_menu.xml
Normal file
7
app/src/main/res/menu/history_overflow_menu.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/history_overflow_clear_history"
|
||||
android:title="@string/clear_history" />
|
||||
<item android:id="@+id/history_overflow_clear_runtime_cache"
|
||||
android:title="@string/clear_cache" />
|
||||
</menu>
|
|
@ -1,17 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group android:id="@+id/app" >
|
||||
<item android:id="@+id/overflow_menu_share"
|
||||
<item
|
||||
android:id="@+id/overflow_menu_share"
|
||||
android:title="@string/share" />
|
||||
<item android:id="@+id/overflow_menu_copy"
|
||||
<item
|
||||
android:id="@+id/overflow_menu_copy"
|
||||
android:title="@string/copy_address" />
|
||||
<item android:id="@+id/overflow_menu_set_home"
|
||||
<item
|
||||
android:id="@+id/overflow_menu_reload"
|
||||
android:title="@string/reload"/>
|
||||
<item
|
||||
android:id="@+id/overflow_menu_set_home"
|
||||
android:title="@string/set_home" />
|
||||
</group>
|
||||
<group android:id="@+id/cache" >
|
||||
<item
|
||||
android:id="@+id/overflow_menu_history"
|
||||
android:title="@string/history" />
|
||||
</group>
|
||||
<group android:id="@+id/other" >
|
||||
<item android:id="@+id/overflow_menu_clear_cache"
|
||||
android:title="@string/clear_cache" />
|
||||
<item android:id="@+id/overflow_menu_about"
|
||||
<item
|
||||
android:id="@+id/overflow_menu_about"
|
||||
android:title="@string/about" />
|
||||
</group>
|
||||
</menu>
|
|
@ -16,5 +16,8 @@
|
|||
\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or any later version.\n
|
||||
\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n
|
||||
\nYou should have received a copy of the GNU General Public License along with this program. If not, see www.gnu.org/licenses</string>
|
||||
<string name="clear_cache">Clear cache</string>
|
||||
<string name="clear_cache">Clear runtime cache</string>
|
||||
<string name="history">History</string>
|
||||
<string name="clear_history">Clear history</string>
|
||||
<string name="reload">Reload</string>
|
||||
</resources>
|
Loading…
Add table
Reference in a new issue