app: use real Navigation component flow
This fixes a lot of oddities in navigation, but also introduces UI changes, like moving the address bar to a specific sub-toolbar in the PageFragment. Unsure if this is the classiest choice but will do for now. Probably introduces more issues.
This commit is contained in:
parent
5fe56880cb
commit
0059b7ff4f
|
@ -2,6 +2,7 @@ plugins {
|
|||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
id 'androidx.navigation.safeargs.kotlin'
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
|
@ -46,8 +45,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.Listener {
|
|||
}
|
||||
|
||||
override fun onItemClick(url: String) {
|
||||
val bundle = bundleOf("url" to url)
|
||||
findNavController().navigate(R.id.action_global_pageFragment, bundle)
|
||||
findNavController().navigate(PageFragmentDirections.actionGlobalPageFragment(url))
|
||||
}
|
||||
|
||||
class HistoryViewModel(
|
||||
|
|
|
@ -3,39 +3,51 @@ package dev.lowrespalmtree.comet
|
|||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import dev.lowrespalmtree.comet.databinding.ActivityMainBinding
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private var nhf: NavHostFragment? = null
|
||||
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
|
||||
private val nhf by lazy { supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment }
|
||||
private val navController by lazy { nhf.navController }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.i(TAG, "onCreate")
|
||||
Log.d(TAG, "onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
||||
Database.init(applicationContext) // TODO move to App Startup?
|
||||
|
||||
nhf = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
|
||||
nhf?.also { binding.drawerNavigation.setupWithNavController(it.navController) }
|
||||
val toggle = ActionBarDrawerToggle(
|
||||
this,
|
||||
binding.drawerLayout,
|
||||
binding.toolbar,
|
||||
R.string.navigation_drawer_open,
|
||||
R.string.navigation_drawer_close
|
||||
)
|
||||
binding.drawerLayout.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
setupActionBarWithNavController(navController, binding.drawerLayout)
|
||||
binding.drawerNavigation.setupWithNavController(nhf.navController)
|
||||
}
|
||||
|
||||
/** Navigate to the PageViewFragment; this will automatically use the home URL if any. */
|
||||
fun goHome(@Suppress("unused_parameter") item: MenuItem) {
|
||||
val bundle = bundleOf()
|
||||
Preferences.getHomeUrl(this)?.let { bundle.putString("url", it) }
|
||||
nhf?.navController?.navigate(R.id.action_global_pageFragment, bundle)
|
||||
binding.drawerLayout.closeDrawers()
|
||||
}
|
||||
// /** Navigate to the PageViewFragment; this will automatically use the home URL if any. */
|
||||
// fun goHome(@Suppress("unused_parameter") item: MenuItem) {
|
||||
// val bundle = bundleOf()
|
||||
// Preferences.getHomeUrl(this)?.let { bundle.putString("url", it) }
|
||||
// }
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@ import androidx.activity.addCallback
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dev.lowrespalmtree.comet.databinding.FragmentPageViewBinding
|
||||
|
@ -26,6 +27,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
@ExperimentalCoroutinesApi
|
||||
class PageFragment : Fragment(), PageAdapter.Listener {
|
||||
private val vm: PageViewModel by viewModels()
|
||||
private val args: PageFragmentArgs by navArgs()
|
||||
private lateinit var binding: FragmentPageViewBinding
|
||||
private lateinit var adapter: PageAdapter
|
||||
|
||||
|
@ -35,12 +37,12 @@ class PageFragment : Fragment(), PageAdapter.Listener {
|
|||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
Log.d(TAG, "onCreateView")
|
||||
binding = FragmentPageViewBinding.inflate(layoutInflater)
|
||||
binding = FragmentPageViewBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
Log.d(TAG, "onViewCreated")
|
||||
Log.d(TAG, "onViewCreated (args: ${args})")
|
||||
binding.contentRecycler.layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = PageAdapter(this)
|
||||
binding.contentRecycler.adapter = adapter
|
||||
|
@ -53,14 +55,15 @@ class PageFragment : Fragment(), PageAdapter.Listener {
|
|||
vm.lines.observe(viewLifecycleOwner) { updateLines(it.second, it.first) }
|
||||
vm.event.observe(viewLifecycleOwner) { handleEvent(it) }
|
||||
|
||||
activity?.onBackPressedDispatcher?.addCallback(viewLifecycleOwner) { onBackPressed() }
|
||||
(activity as MainActivity?)?.let {
|
||||
it.onBackPressedDispatcher.addCallback(viewLifecycleOwner) { onBackPressed() }
|
||||
it.supportActionBar?.title = vm.currentTitle
|
||||
}
|
||||
|
||||
val url = arguments?.getString("url")
|
||||
if (!url.isNullOrEmpty()) {
|
||||
val url = args.url
|
||||
if (vm.currentUrl.isEmpty() && url.isNotEmpty()) {
|
||||
Log.d(TAG, "onViewCreated: open \"$url\"")
|
||||
openUrl(url)
|
||||
} else if (vm.currentUrl.isNotEmpty()) {
|
||||
Log.d(TAG, "onViewCreated: reuse current URL, probably fragment recreation")
|
||||
} else if (vm.visitedUrls.isEmpty()) {
|
||||
Log.d(TAG, "onViewCreated: no current URL, open home if configured")
|
||||
Preferences.getHomeUrl(requireContext())?.let { if (it.isNotBlank()) openUrl(it) }
|
||||
|
@ -72,6 +75,7 @@ class PageFragment : Fragment(), PageAdapter.Listener {
|
|||
}
|
||||
|
||||
private fun onBackPressed() {
|
||||
Log.d(TAG, "onBackPressed")
|
||||
if (vm.visitedUrls.size >= 2) {
|
||||
vm.visitedUrls.removeLastOrNull() // Always remove current page first.
|
||||
vm.visitedUrls.removeLastOrNull()?.also { openUrl(it) }
|
||||
|
@ -157,9 +161,11 @@ class PageFragment : Fragment(), PageAdapter.Listener {
|
|||
}
|
||||
is PageViewModel.SuccessEvent -> {
|
||||
vm.currentUrl = event.uri
|
||||
vm.currentTitle = event.mainTitle ?: ""
|
||||
if (vm.visitedUrls.isEmpty() || vm.visitedUrls.last() != event.uri)
|
||||
vm.visitedUrls.add(event.uri)
|
||||
binding.addressBar.setText(event.uri)
|
||||
(activity as MainActivity?)?.supportActionBar?.title = vm.currentTitle
|
||||
}
|
||||
is PageViewModel.BinaryEvent -> {
|
||||
// TODO this should present the user with options on what to do according to the
|
||||
|
|
|
@ -25,6 +25,9 @@ class PageViewModel(
|
|||
/** Currently viewed page URL. */
|
||||
var currentUrl: String = ""
|
||||
|
||||
/** The first level 1 header of the current page (default is an empty string). */
|
||||
var currentTitle: String = ""
|
||||
|
||||
/** Latest Uri requested using `sendGeminiRequest`. */
|
||||
var loadingUrl: Uri? = null
|
||||
|
||||
|
@ -58,7 +61,7 @@ class PageViewModel(
|
|||
data class InputEvent(val uri: Uri, val prompt: String) : Event()
|
||||
|
||||
/** The server responded with a success code and *has finished* its response. */
|
||||
data class SuccessEvent(val uri: String) : Event()
|
||||
data class SuccessEvent(val uri: String, val mainTitle: String?) : Event()
|
||||
|
||||
/** The server responded with a success code and a binary MIME type (not delivered yet). */
|
||||
data class BinaryEvent(
|
||||
|
@ -243,7 +246,7 @@ class PageViewModel(
|
|||
// We record the history entry here: it's nice because we have the main title available
|
||||
// and we're already in a coroutine for database access.
|
||||
History.record(uriString, mainTitle)
|
||||
event.postValue(SuccessEvent(uriString))
|
||||
event.postValue(SuccessEvent(uriString, mainTitle))
|
||||
state.postValue(State.IDLE)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
android:fitsSystemWindows="true"
|
||||
tools:openDrawer="start"
|
||||
tools:context="dev.lowrespalmtree.comet.MainActivity">
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/background_emph" />
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:navGraph="@navigation/main"
|
||||
app:defaultNavHost="true" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/drawer_navigation"
|
||||
|
@ -21,6 +35,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:fitsSystemWindows="true"
|
||||
app:headerLayout="@layout/activity_main_nav_header"
|
||||
app:menu="@menu/drawer" />
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
18
app/src/main/res/layout/activity_main_nav_header.xml
Normal file
18
app/src/main/res/layout/activity_main_nav_header.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="176dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/logo"
|
||||
android:src="@mipmap/ic_logo"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.5" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -4,13 +4,12 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/app_bar_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp">
|
||||
android:layout_height="48dp">
|
||||
|
||||
<Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/homeItem"
|
||||
android:icon="@android:drawable/ic_menu_myplaces"
|
||||
android:onClick="goHome"
|
||||
android:title="@string/home" />
|
||||
<item
|
||||
android:id="@+id/historyFragment"
|
||||
android:icon="@android:drawable/ic_menu_recent_history"
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
<fragment
|
||||
android:id="@+id/pageFragment"
|
||||
android:name="dev.lowrespalmtree.comet.PageFragment"
|
||||
android:label="PageViewFragment" />
|
||||
android:label="">
|
||||
<argument
|
||||
android:name="url"
|
||||
app:argType="string"
|
||||
android:defaultValue="" />
|
||||
</fragment>
|
||||
<action
|
||||
android:id="@+id/action_global_pageFragment"
|
||||
app:destination="@id/pageFragment"
|
||||
|
@ -19,7 +24,15 @@
|
|||
<fragment
|
||||
android:id="@+id/historyFragment"
|
||||
android:name="dev.lowrespalmtree.comet.HistoryFragment"
|
||||
android:label="HistoryFragment" />
|
||||
android:label="@string/history" >
|
||||
<action
|
||||
android:id="@+id/action_historyFragment_to_pageFragment"
|
||||
app:destination="@id/pageFragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
<action
|
||||
android:id="@+id/action_global_historyFragment"
|
||||
app:destination="@id/historyFragment"
|
||||
|
@ -31,12 +44,19 @@
|
|||
<fragment
|
||||
android:id="@+id/identitiesFragment"
|
||||
android:name="dev.lowrespalmtree.comet.IdentitiesFragment"
|
||||
android:label="IdentitiesFragment" />
|
||||
android:label="@string/identities" />
|
||||
<action
|
||||
android:id="@+id/action_global_identitiesFragment"
|
||||
app:destination="@id/identitiesFragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/settingsFragment"
|
||||
android:name="dev.lowrespalmtree.comet.SettingsFragment"
|
||||
android:label="SettingsFragment" />
|
||||
android:label="@string/settings" />
|
||||
<action
|
||||
android:id="@+id/action_global_settingsFragment"
|
||||
app:destination="@id/settingsFragment"
|
||||
|
|
|
@ -50,5 +50,8 @@
|
|||
<string name="open">Open</string>
|
||||
<string name="download_completed">File downloaded.</string>
|
||||
<string name="image_download_completed">Image downloaded.</string>
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="logo">Logo</string>
|
||||
|
||||
</resources>
|
Reference in a new issue