ui: migrate to OnBackPressedDispatcher

This is compatible with Android 13's prediction-based back gesture
animation.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2023-03-23 15:45:01 +01:00
parent b7295cd56f
commit cf943b7119
3 changed files with 28 additions and 10 deletions

View File

@ -35,6 +35,7 @@
android:name=".Application" android:name=".Application"
android:allowBackup="false" android:allowBackup="false"
android:banner="@mipmap/banner" android:banner="@mipmap/banner"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"

View File

@ -9,6 +9,8 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.activity.addCallback
import androidx.appcompat.app.ActionBar import androidx.appcompat.app.ActionBar
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
@ -26,27 +28,29 @@ import com.wireguard.android.model.ObservableTunnel
class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener { class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener {
private var actionBar: ActionBar? = null private var actionBar: ActionBar? = null
private var isTwoPaneLayout = false private var isTwoPaneLayout = false
private var backPressedCallback: OnBackPressedCallback? = null
override fun onBackPressed() { private fun handleBackPressed() {
val backStackEntries = supportFragmentManager.backStackEntryCount val backStackEntries = supportFragmentManager.backStackEntryCount
// If the two-pane layout does not have an editor open, going back should exit the app. // If the two-pane layout does not have an editor open, going back should exit the app.
if (isTwoPaneLayout && backStackEntries <= 1) { if (isTwoPaneLayout && backStackEntries <= 1) {
finish() finish()
return return
} }
// Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
if (!isTwoPaneLayout && backStackEntries == 1) { if (backStackEntries >= 1)
supportFragmentManager.popBackStack() supportFragmentManager.popBackStack()
// Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
if (backStackEntries == 1)
selectedTunnel = null selectedTunnel = null
return
}
super.onBackPressed()
} }
override fun onBackStackChanged() { override fun onBackStackChanged() {
val backStackEntries = supportFragmentManager.backStackEntryCount
backPressedCallback?.isEnabled = backStackEntries >= 1
if (actionBar == null) return if (actionBar == null) return
// Do not show the home menu when the two-pane layout is at the detail view (see above). // Do not show the home menu when the two-pane layout is at the detail view (see above).
val backStackEntries = supportFragmentManager.backStackEntryCount
val minBackStackEntries = if (isTwoPaneLayout) 2 else 1 val minBackStackEntries = if (isTwoPaneLayout) 2 else 1
actionBar!!.setDisplayHomeAsUpEnabled(backStackEntries >= minBackStackEntries) actionBar!!.setDisplayHomeAsUpEnabled(backStackEntries >= minBackStackEntries)
} }
@ -57,6 +61,7 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
actionBar = supportActionBar actionBar = supportActionBar
isTwoPaneLayout = findViewById<View?>(R.id.master_detail_wrapper) != null isTwoPaneLayout = findViewById<View?>(R.id.master_detail_wrapper) != null
supportFragmentManager.addOnBackStackChangedListener(this) supportFragmentManager.addOnBackStackChangedListener(this)
backPressedCallback = onBackPressedDispatcher.addCallback(this) { handleBackPressed() }
onBackStackChanged() onBackStackChanged()
} }
@ -69,7 +74,7 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
return when (item.itemId) { return when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
// The back arrow in the action bar should act the same as the back button. // The back arrow in the action bar should act the same as the back button.
onBackPressed() onBackPressedDispatcher.onBackPressed()
true true
} }
R.id.menu_action_edit -> { R.id.menu_action_edit -> {

View File

@ -16,12 +16,14 @@ import android.os.storage.StorageVolume
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.databinding.Observable
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField import androidx.databinding.ObservableField
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -185,6 +187,17 @@ class TvMainActivity : AppCompatActivity() {
binding.tunnelList.requestFocus() binding.tunnelList.requestFocus()
} }
} }
val backPressedCallback = onBackPressedDispatcher.addCallback(this) { handleBackPressed() }
val updateBackPressedCallback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
backPressedCallback.isEnabled = isDeleting.get() || filesRoot.get()?.isNotEmpty() == true
}
}
isDeleting.addOnPropertyChangedCallback(updateBackPressedCallback)
filesRoot.addOnPropertyChangedCallback(updateBackPressedCallback)
backPressedCallback.isEnabled = false
binding.executePendingBindings() binding.executePendingBindings()
setContentView(binding.root) setContentView(binding.root)
@ -298,7 +311,7 @@ class TvMainActivity : AppCompatActivity() {
} }
} }
override fun onBackPressed() { private fun handleBackPressed() {
when { when {
isDeleting.get() -> { isDeleting.get() -> {
isDeleting.set(false) isDeleting.set(false)
@ -313,7 +326,6 @@ class TvMainActivity : AppCompatActivity() {
binding.tunnelList.requestFocus() binding.tunnelList.requestFocus()
} }
} }
else -> super.onBackPressed()
} }
} }