diff --git a/app/build.gradle b/app/build.gradle
index 817c268..6a95bd3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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"
}
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
index 9b4b650..e0d4987 100644
--- a/app/release/output-metadata.json
+++ b/app/release/output-metadata.json
@@ -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"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0d88970..10b6c4d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,6 +17,12 @@
+
+
+
+
+
+
diff --git a/app/src/main/java/oppen/Extensions.kt b/app/src/main/java/oppen/Extensions.kt
index 6d7f068..b043141 100644
--- a/app/src/main/java/oppen/Extensions.kt
+++ b/app/src/main/java/oppen/Extensions.kt
@@ -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()
+
}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/io/RuntimeCache.kt b/app/src/main/java/oppen/tva/io/RuntimeCache.kt
index 12368b5..8489429 100644
--- a/app/src/main/java/oppen/tva/io/RuntimeCache.kt
+++ b/app/src/main/java/oppen/tva/io/RuntimeCache.kt
@@ -15,4 +15,8 @@ object RuntimeCache {
fun get(uri: URI): Pair>? = lruCache[uri.toString()]
fun clear() = lruCache.evictAll()
+
+ fun remove(address: String) {
+ lruCache.remove(address)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/io/history/BasicCache.kt b/app/src/main/java/oppen/tva/io/history/tabs/BasicTabHistoryCache.kt
similarity index 95%
rename from app/src/main/java/oppen/tva/io/history/BasicCache.kt
rename to app/src/main/java/oppen/tva/io/history/tabs/BasicTabHistoryCache.kt
index ac730cc..acb137e 100644
--- a/app/src/main/java/oppen/tva/io/history/BasicCache.kt
+++ b/app/src/main/java/oppen/tva/io/history/tabs/BasicTabHistoryCache.kt
@@ -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"
diff --git a/app/src/main/java/oppen/tva/io/history/Tab.kt b/app/src/main/java/oppen/tva/io/history/tabs/Tab.kt
similarity index 92%
rename from app/src/main/java/oppen/tva/io/history/Tab.kt
rename to app/src/main/java/oppen/tva/io/history/tabs/Tab.kt
index a3f4691..b0484f8 100644
--- a/app/src/main/java/oppen/tva/io/history/Tab.kt
+++ b/app/src/main/java/oppen/tva/io/history/tabs/Tab.kt
@@ -1,4 +1,4 @@
-package oppen.tva.io.history
+package oppen.tva.io.history.tabs
import java.net.URI
diff --git a/app/src/main/java/oppen/tva/io/history/CacheInterface.kt b/app/src/main/java/oppen/tva/io/history/tabs/TabHistoryInterface.kt
similarity index 52%
rename from app/src/main/java/oppen/tva/io/history/CacheInterface.kt
rename to app/src/main/java/oppen/tva/io/history/tabs/TabHistoryInterface.kt
index 0323e21..408d2c1 100644
--- a/app/src/main/java/oppen/tva/io/history/CacheInterface.kt
+++ b/app/src/main/java/oppen/tva/io/history/tabs/TabHistoryInterface.kt
@@ -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, activeIndex: Int)-> Unit)
fun update(tabs: List, activeIndex: Int)
companion object{
- fun default(context: Context): CacheInterface{
- return BasicCache(context)
+ fun default(context: Context): TabHistoryInterface {
+ return BasicTabHistoryCache(context)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/io/history/uris/BasicURIHistory.kt b/app/src/main/java/oppen/tva/io/history/uris/BasicURIHistory.kt
new file mode 100644
index 0000000..b79b80e
--- /dev/null
+++ b/app/src/main/java/oppen/tva/io/history/uris/BasicURIHistory.kt
@@ -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 {
+ return when (val raw = prefs.getString(prefsHistoryKey, null)) {
+ null -> arrayListOf()
+ else -> ArrayList(raw.split(DELIM))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/io/history/uris/HistoryInterface.kt b/app/src/main/java/oppen/tva/io/history/uris/HistoryInterface.kt
new file mode 100644
index 0000000..6825526
--- /dev/null
+++ b/app/src/main/java/oppen/tva/io/history/uris/HistoryInterface.kt
@@ -0,0 +1,15 @@
+package oppen.tva.io.history.uris
+
+import android.content.Context
+
+interface HistoryInterface {
+ fun add(address: String)
+ fun get(): List
+ fun clear()
+
+ companion object{
+ fun default(context: Context): HistoryInterface {
+ return BasicURIHistory(context)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/ui/TvaActivity.kt b/app/src/main/java/oppen/tva/ui/TvaActivity.kt
index da7e91a..02dffd0 100644
--- a/app/src/main/java/oppen/tva/ui/TvaActivity.kt
+++ b/app/src/main/java/oppen/tva/ui/TvaActivity.kt
@@ -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()
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 {
diff --git a/app/src/main/java/oppen/tva/ui/TvaViewModel.kt b/app/src/main/java/oppen/tva/ui/TvaViewModel.kt
index 56a2a0c..ef2af0f 100644
--- a/app/src/main/java/oppen/tva/ui/TvaViewModel.kt
+++ b/app/src/main/java/oppen/tva/ui/TvaViewModel.kt
@@ -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()
- 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
diff --git a/app/src/main/java/oppen/tva/ui/history/HistoryAdapter.kt b/app/src/main/java/oppen/tva/ui/history/HistoryAdapter.kt
new file mode 100644
index 0000000..d59da0d
--- /dev/null
+++ b/app/src/main/java/oppen/tva/ui/history/HistoryAdapter.kt
@@ -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, val onClick:(address: String) -> Unit): RecyclerView.Adapter() {
+
+ 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])
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/ui/history/HistoryDialog.kt b/app/src/main/java/oppen/tva/ui/history/HistoryDialog.kt
new file mode 100644
index 0000000..d67f452
--- /dev/null
+++ b/app/src/main/java/oppen/tva/ui/history/HistoryDialog.kt
@@ -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()
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/oppen/tva/ui/tabs/TabsAdapter.kt b/app/src/main/java/oppen/tva/ui/tabs/TabsAdapter.kt
index ef2547f..7531cbe 100644
--- a/app/src/main/java/oppen/tva/ui/tabs/TabsAdapter.kt
+++ b/app/src/main/java/oppen/tva/ui/tabs/TabsAdapter.kt
@@ -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,
diff --git a/app/src/main/res/layout/dialog_history.xml b/app/src/main/res/layout/dialog_history.xml
new file mode 100644
index 0000000..a22b3da
--- /dev/null
+++ b/app/src/main/res/layout/dialog_history.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/row_history.xml b/app/src/main/res/layout/row_history.xml
new file mode 100644
index 0000000..bd53d11
--- /dev/null
+++ b/app/src/main/res/layout/row_history.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/history_overflow_menu.xml b/app/src/main/res/menu/history_overflow_menu.xml
new file mode 100644
index 0000000..861457d
--- /dev/null
+++ b/app/src/main/res/menu/history_overflow_menu.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/overflow_menu.xml b/app/src/main/res/menu/overflow_menu.xml
index 90bbf77..25bcec9 100644
--- a/app/src/main/res/menu/overflow_menu.xml
+++ b/app/src/main/res/menu/overflow_menu.xml
@@ -1,17 +1,27 @@
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 307d7b8..0bf5f10 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -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
- Clear cache
+ Clear runtime cache
+ History
+ Clear history
+ Reload
\ No newline at end of file