From 11a4b6d8d2ba9d56b337aa25ca7c8dd79feca720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96ppen?= Date: Thu, 20 Aug 2020 14:52:24 +0100 Subject: [PATCH] new homepage, history changes --- app/build.gradle | 4 +- app/release/output-metadata.json | 4 +- app/src/main/AndroidManifest.xml | 6 ++ app/src/main/java/oppen/Extensions.kt | 13 ++++ .../main/java/oppen/tva/io/RuntimeCache.kt | 4 ++ .../BasicTabHistoryCache.kt} | 4 +- .../oppen/tva/io/history/{ => tabs}/Tab.kt | 2 +- .../TabHistoryInterface.kt} | 8 +-- .../tva/io/history/uris/BasicURIHistory.kt | 44 ++++++++++++++ .../tva/io/history/uris/HistoryInterface.kt | 15 +++++ app/src/main/java/oppen/tva/ui/TvaActivity.kt | 27 +++++++-- .../main/java/oppen/tva/ui/TvaViewModel.kt | 10 ++-- .../oppen/tva/ui/history/HistoryAdapter.kt | 29 +++++++++ .../oppen/tva/ui/history/HistoryDialog.kt | 60 +++++++++++++++++++ .../java/oppen/tva/ui/tabs/TabsAdapter.kt | 2 +- app/src/main/res/layout/dialog_history.xml | 40 +++++++++++++ app/src/main/res/layout/row_history.xml | 19 ++++++ .../main/res/menu/history_overflow_menu.xml | 7 +++ app/src/main/res/menu/overflow_menu.xml | 22 +++++-- app/src/main/res/values/strings.xml | 5 +- 20 files changed, 295 insertions(+), 30 deletions(-) rename app/src/main/java/oppen/tva/io/history/{BasicCache.kt => tabs/BasicTabHistoryCache.kt} (95%) rename app/src/main/java/oppen/tva/io/history/{ => tabs}/Tab.kt (92%) rename app/src/main/java/oppen/tva/io/history/{CacheInterface.kt => tabs/TabHistoryInterface.kt} (52%) create mode 100644 app/src/main/java/oppen/tva/io/history/uris/BasicURIHistory.kt create mode 100644 app/src/main/java/oppen/tva/io/history/uris/HistoryInterface.kt create mode 100644 app/src/main/java/oppen/tva/ui/history/HistoryAdapter.kt create mode 100644 app/src/main/java/oppen/tva/ui/history/HistoryDialog.kt create mode 100644 app/src/main/res/layout/dialog_history.xml create mode 100644 app/src/main/res/layout/row_history.xml create mode 100644 app/src/main/res/menu/history_overflow_menu.xml 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