From f436a49e5fab147f0bf7f00af53bb2f6db9de751 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Tue, 14 Feb 2023 20:33:01 +0200 Subject: [PATCH] Add support for the predictive back gesture --- app/src/main/AndroidManifest.xml | 1 + .../kotatsu/base/ui/AlertDialogFragment.kt | 3 ++ .../koitharu/kotatsu/base/ui/BaseActivity.kt | 4 +- .../kotatsu/base/ui/BaseBottomSheet.kt | 4 ++ .../ui/util/CollapseActionViewCallback.kt | 24 ++++++++++ .../kotatsu/browser/BrowserActivity.kt | 16 +++---- .../kotatsu/browser/BrowserCallback.kt | 4 +- .../koitharu/kotatsu/browser/BrowserClient.kt | 9 +++- .../browser/OnHistoryChangedListener.kt | 6 +++ .../browser/WebViewBackPressedCallback.kt | 21 +++++++++ .../browser/cloudflare/CloudFlareCallback.kt | 10 ++++- .../browser/cloudflare/CloudFlareClient.kt | 12 ++--- .../browser/cloudflare/CloudFlareDialog.kt | 16 +++++++ .../list/ui/filter/FilterBottomSheet.kt | 44 ++++++++----------- .../koitharu/kotatsu/main/ui/MainActivity.kt | 41 ++++++++++------- .../selector/ScrobblingSelectorBottomSheet.kt | 36 ++++++--------- .../sources/auth/SourceAuthActivity.kt | 16 +++---- .../kotatsu/sync/ui/SyncAuthActivity.kt | 33 ++++++++++---- 18 files changed, 197 insertions(+), 103 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/base/ui/util/CollapseActionViewCallback.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/browser/OnHistoryChangedListener.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/browser/WebViewBackPressedCallback.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5483b2063..58e59c8ff 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ android:allowBackup="true" android:backupAgent="org.koitharu.kotatsu.settings.backup.AppBackupAgent" android:dataExtractionRules="@xml/backup_rules" + android:enableOnBackInvokedCallback="true" android:fullBackupContent="@xml/backup_content" android:fullBackupOnly="true" android:icon="@mipmap/ic_launcher" diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt index 8866cc068..a667fec32 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/AlertDialogFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.annotation.CallSuper +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import androidx.viewbinding.ViewBinding import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -39,6 +40,8 @@ abstract class AlertDialogFragment : DialogFragment() { open fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder = builder + open fun onDialogCreated(dialog: AlertDialog) = Unit + protected fun bindingOrNull(): B? = viewBinding protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt index 1d67f55aa..68eb5701a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseActivity.kt @@ -82,6 +82,7 @@ abstract class BaseActivity : } override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) { + @Suppress("DEPRECATION") onBackPressed() true } else super.onOptionsItemSelected(item) @@ -130,7 +131,8 @@ abstract class BaseActivity : window.statusBarColor = getThemeColor(android.R.attr.statusBarColor) } - @Suppress("OVERRIDE_DEPRECATION", "DEPRECATION") + @Suppress("DEPRECATION", "DeprecatedCallableAddReplaceWith") + @Deprecated("Should not be used") override fun onBackPressed() { if ( // https://issuetracker.google.com/issues/139738913 Build.VERSION.SDK_INT == Build.VERSION_CODES.Q && diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt index 947b80e8c..a7252ecb9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseBottomSheet.kt @@ -7,6 +7,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams +import androidx.activity.OnBackPressedDispatcher import androidx.core.view.updateLayoutParams import androidx.viewbinding.ViewBinding import com.google.android.material.bottomsheet.BottomSheetBehavior @@ -30,6 +31,9 @@ abstract class BaseBottomSheet : BottomSheetDialogFragment() { val isExpanded: Boolean get() = behavior?.state == BottomSheetBehavior.STATE_EXPANDED + val onBackPressedDispatcher: OnBackPressedDispatcher + get() = (requireDialog() as AppBottomSheetDialog).onBackPressedDispatcher + final override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CollapseActionViewCallback.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CollapseActionViewCallback.kt new file mode 100644 index 000000000..5d9058de1 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CollapseActionViewCallback.kt @@ -0,0 +1,24 @@ +package org.koitharu.kotatsu.base.ui.util + +import android.view.MenuItem +import android.view.MenuItem.OnActionExpandListener +import androidx.activity.OnBackPressedCallback + +class CollapseActionViewCallback( + private val menuItem: MenuItem +) : OnBackPressedCallback(menuItem.isActionViewExpanded), OnActionExpandListener { + + override fun handleOnBackPressed() { + menuItem.collapseActionView() + } + + override fun onMenuItemActionExpand(item: MenuItem): Boolean { + isEnabled = true + return true + } + + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + isEnabled = false + return true + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt index 27c25f2a4..991a86d8c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserActivity.kt @@ -20,6 +20,8 @@ import com.google.android.material.R as materialR @SuppressLint("SetJavaScriptEnabled") class BrowserActivity : BaseActivity(), BrowserCallback { + private lateinit var onBackPressedCallback: WebViewBackPressedCallback + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityBrowserBinding.inflate(layoutInflater)) @@ -33,6 +35,8 @@ class BrowserActivity : BaseActivity(), BrowserCallback } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) + onBackPressedCallback = WebViewBackPressedCallback(binding.webView) + onBackPressedDispatcher.addCallback(onBackPressedCallback) if (savedInstanceState != null) { return } @@ -84,14 +88,6 @@ class BrowserActivity : BaseActivity(), BrowserCallback else -> super.onOptionsItemSelected(item) } - override fun onBackPressed() { - if (binding.webView.canGoBack()) { - binding.webView.goBack() - } else { - super.onBackPressed() - } - } - override fun onPause() { binding.webView.onPause() super.onPause() @@ -116,6 +112,10 @@ class BrowserActivity : BaseActivity(), BrowserCallback supportActionBar?.subtitle = subtitle } + override fun onHistoryChanged() { + onBackPressedCallback.onHistoryChanged() + } + override fun onWindowInsetsChanged(insets: Insets) { binding.appbar.updatePadding( top = insets.top, diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserCallback.kt b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserCallback.kt index e6f3fae84..b85fdaa39 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserCallback.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserCallback.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.browser -interface BrowserCallback { +interface BrowserCallback : OnHistoryChangedListener { fun onLoadingStateChanged(isLoading: Boolean) fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserClient.kt b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserClient.kt index 0beaa7566..e6906014e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/BrowserClient.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/BrowserClient.kt @@ -4,7 +4,7 @@ import android.graphics.Bitmap import android.webkit.WebView import android.webkit.WebViewClient -class BrowserClient(private val callback: BrowserCallback) : WebViewClient() { +open class BrowserClient(private val callback: BrowserCallback) : WebViewClient() { override fun onPageFinished(webView: WebView, url: String) { super.onPageFinished(webView, url) @@ -20,4 +20,9 @@ class BrowserClient(private val callback: BrowserCallback) : WebViewClient() { super.onPageCommitVisible(view, url) callback.onTitleChanged(view.title.orEmpty(), url) } -} \ No newline at end of file + + override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) { + super.doUpdateVisitedHistory(view, url, isReload) + callback.onHistoryChanged() + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/OnHistoryChangedListener.kt b/app/src/main/java/org/koitharu/kotatsu/browser/OnHistoryChangedListener.kt new file mode 100644 index 000000000..da23f6931 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/browser/OnHistoryChangedListener.kt @@ -0,0 +1,6 @@ +package org.koitharu.kotatsu.browser + +fun interface OnHistoryChangedListener { + + fun onHistoryChanged() +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/WebViewBackPressedCallback.kt b/app/src/main/java/org/koitharu/kotatsu/browser/WebViewBackPressedCallback.kt new file mode 100644 index 000000000..dfe049040 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/browser/WebViewBackPressedCallback.kt @@ -0,0 +1,21 @@ +package org.koitharu.kotatsu.browser + +import android.webkit.WebView +import androidx.activity.OnBackPressedCallback + +class WebViewBackPressedCallback( + private val webView: WebView, +) : OnBackPressedCallback(false), OnHistoryChangedListener { + + init { + onHistoryChanged() + } + + override fun handleOnBackPressed() { + webView.goBack() + } + + override fun onHistoryChanged() { + isEnabled = webView.canGoBack() + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareCallback.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareCallback.kt index aedd22605..978858604 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareCallback.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareCallback.kt @@ -1,8 +1,14 @@ package org.koitharu.kotatsu.browser.cloudflare -interface CloudFlareCallback { +import org.koitharu.kotatsu.browser.BrowserCallback + +interface CloudFlareCallback : BrowserCallback { + + override fun onLoadingStateChanged(isLoading: Boolean) = Unit + + override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) = Unit fun onPageLoaded() fun onCheckPassed() -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt index c6ad65f5f..9bdaed17d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareClient.kt @@ -2,8 +2,8 @@ package org.koitharu.kotatsu.browser.cloudflare import android.graphics.Bitmap import android.webkit.WebView -import android.webkit.WebViewClient import okhttp3.HttpUrl.Companion.toHttpUrl +import org.koitharu.kotatsu.browser.BrowserClient import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar private const val CF_CLEARANCE = "cf_clearance" @@ -12,22 +12,22 @@ class CloudFlareClient( private val cookieJar: MutableCookieJar, private val callback: CloudFlareCallback, private val targetUrl: String, -) : WebViewClient() { +) : BrowserClient(callback) { private val oldClearance = getClearance() - override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) checkClearance() } - override fun onPageCommitVisible(view: WebView?, url: String?) { + override fun onPageCommitVisible(view: WebView, url: String?) { super.onPageCommitVisible(view, url) callback.onPageLoaded() } - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) + override fun onPageFinished(webView: WebView, url: String) { + super.onPageFinished(webView, url) callback.onPageLoaded() } diff --git a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt index 994e52c95..1a70db008 100644 --- a/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/browser/cloudflare/CloudFlareDialog.kt @@ -8,12 +8,14 @@ import android.view.View import android.view.ViewGroup import android.webkit.CookieManager import android.webkit.WebSettings +import androidx.appcompat.app.AlertDialog import androidx.core.view.isInvisible import androidx.fragment.app.setFragmentResult import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import okhttp3.Headers import org.koitharu.kotatsu.base.ui.AlertDialogFragment +import org.koitharu.kotatsu.browser.WebViewBackPressedCallback import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.CommonHeadersInterceptor import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar @@ -31,6 +33,8 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud @Inject lateinit var cookieJar: MutableCookieJar + private var onBackPressedCallback: WebViewBackPressedCallback? = null + override fun onInflateView( inflater: LayoutInflater, container: ViewGroup?, @@ -58,6 +62,7 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud override fun onDestroyView() { binding.webView.stopLoading() binding.webView.destroy() + onBackPressedCallback = null super.onDestroyView() } @@ -65,6 +70,13 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud return super.onBuildDialog(builder).setNegativeButton(android.R.string.cancel, null) } + override fun onDialogCreated(dialog: AlertDialog) { + super.onDialogCreated(dialog) + onBackPressedCallback = WebViewBackPressedCallback(binding.webView).also { + dialog.onBackPressedDispatcher.addCallback(it) + } + } + override fun onResume() { super.onResume() binding.webView.onResume() @@ -89,6 +101,10 @@ class CloudFlareDialog : AlertDialogFragment(), Cloud dismissAllowingStateLoss() } + override fun onHistoryChanged() { + onBackPressedCallback?.onHistoryChanged() + } + companion object { const val TAG = "CloudFlareDialog" diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt index e7be8c8b6..226d73ed6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterBottomSheet.kt @@ -1,34 +1,29 @@ package org.koitharu.kotatsu.list.ui.filter -import android.app.Dialog -import android.content.DialogInterface import android.os.Bundle -import android.view.* +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.appcompat.widget.SearchView import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.LinearLayoutManager import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseBottomSheet +import org.koitharu.kotatsu.base.ui.util.CollapseActionViewCallback import org.koitharu.kotatsu.databinding.SheetFilterBinding import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel -import org.koitharu.kotatsu.utils.ext.isScrolledToTop import org.koitharu.kotatsu.utils.ext.parentFragmentViewModels class FilterBottomSheet : BaseBottomSheet(), MenuItem.OnActionExpandListener, SearchView.OnQueryTextListener, - DialogInterface.OnKeyListener, AsyncListDiffer.ListListener { private val viewModel by parentFragmentViewModels() - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return super.onCreateDialog(savedInstanceState).also { - it.setOnKeyListener(this) - } - } + private var collapsibleActionViewCallback: CollapseActionViewCallback? = null override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetFilterBinding { return SheetFilterBinding.inflate(inflater, container, false) @@ -42,8 +37,14 @@ class FilterBottomSheet : initOptionsMenu() } + override fun onDestroyView() { + super.onDestroyView() + collapsibleActionViewCallback = null + } + override fun onMenuItemActionExpand(item: MenuItem): Boolean { setExpanded(isExpanded = true, isLocked = true) + collapsibleActionViewCallback?.onMenuItemActionExpand(item) return true } @@ -51,6 +52,7 @@ class FilterBottomSheet : val searchView = (item.actionView as? SearchView) ?: return false searchView.setQuery("", false) searchView.post { setExpanded(isExpanded = false, isLocked = false) } + collapsibleActionViewCallback?.onMenuItemActionCollapse(item) return true } @@ -61,19 +63,6 @@ class FilterBottomSheet : return true } - override fun onKey(dialog: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean { - if (keyCode == KeyEvent.KEYCODE_BACK) { - val menuItem = binding.headerBar.toolbar.menu.findItem(R.id.action_search) ?: return false - if (menuItem.isActionViewExpanded) { - if (event?.action == KeyEvent.ACTION_UP) { - menuItem.collapseActionView() - } - return true - } - } - return false - } - override fun onCurrentListChanged(previousList: MutableList, currentList: MutableList) { if (currentList.size > previousList.size && view != null) { (binding.recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0) @@ -81,13 +70,16 @@ class FilterBottomSheet : } private fun initOptionsMenu() { - binding.headerBar.toolbar.inflateMenu(R.menu.opt_filter) - val searchMenuItem = binding.headerBar.toolbar.menu.findItem(R.id.action_search) + binding.headerBar.inflateMenu(R.menu.opt_filter) + val searchMenuItem = binding.headerBar.menu.findItem(R.id.action_search) searchMenuItem.setOnActionExpandListener(this) val searchView = searchMenuItem.actionView as SearchView searchView.setOnQueryTextListener(this) searchView.setIconifiedByDefault(false) searchView.queryHint = searchMenuItem.title + collapsibleActionViewCallback = CollapseActionViewCallback(searchMenuItem).also { + onBackPressedDispatcher.addCallback(it) + } } companion object { diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt index 06160ea3b..15a3b303f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.util.SparseIntArray import android.view.MenuItem import android.view.View +import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResultCallback import androidx.activity.viewModels import androidx.appcompat.view.ActionMode @@ -84,6 +85,7 @@ class MainActivity : private val viewModel by viewModels() private val searchSuggestionViewModel by viewModels() private val voiceInputLauncher = registerForActivityResult(VoiceInputContract(), VoiceInputCallback()) + private val closeSearchCallback = CloseSearchCallback() private lateinit var navigationDelegate: MainNavigationDelegate override val appBar: AppBarLayout @@ -121,8 +123,9 @@ class MainActivity : navigationDelegate.addOnFragmentChangedListener(this) navigationDelegate.onCreate(savedInstanceState) - onBackPressedDispatcher.addCallback(navigationDelegate) onBackPressedDispatcher.addCallback(ExitCallback(this, binding.container)) + onBackPressedDispatcher.addCallback(navigationDelegate) + onBackPressedDispatcher.addCallback(closeSearchCallback) if (savedInstanceState == null) { onFirstStart() @@ -142,21 +145,6 @@ class MainActivity : adjustSearchUI(isSearchOpened(), animate = false) } - override fun onBackPressed() { - val fragment = supportFragmentManager.findFragmentByTag(TAG_SEARCH) - binding.searchView.clearFocus() - when { - fragment != null -> supportFragmentManager.commit { - setReorderingAllowed(true) - remove(fragment) - setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) - runOnCommit { onSearchClosed() } - } - - else -> super.onBackPressed() - } - } - override fun onFragmentChanged(fragment: Fragment, fromUser: Boolean) { adjustFabVisibility(topFragment = fragment) if (fromUser) { @@ -300,11 +288,13 @@ class MainActivity : private fun onSearchOpened() { adjustSearchUI(isOpened = true, animate = true) + closeSearchCallback.isEnabled = true } private fun onSearchClosed() { binding.searchView.hideKeyboard() adjustSearchUI(isOpened = false, animate = true) + closeSearchCallback.isEnabled = false } private fun showNav(visible: Boolean) { @@ -406,4 +396,23 @@ class MainActivity : } } } + + private inner class CloseSearchCallback : OnBackPressedCallback(false) { + + override fun handleOnBackPressed() { + val fragment = supportFragmentManager.findFragmentByTag(TAG_SEARCH) + binding.searchView.clearFocus() + if (fragment == null) { + // this should not happen but who knows + isEnabled = false + return + } + supportFragmentManager.commit { + setReorderingAllowed(true) + remove(fragment) + setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) + runOnCommit { onSearchClosed() } + } + } + } } diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt index 488bdd57e..a000c8b8a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/common/ui/selector/ScrobblingSelectorBottomSheet.kt @@ -1,9 +1,6 @@ package org.koitharu.kotatsu.scrobbling.common.ui.selector -import android.app.Dialog -import android.content.DialogInterface import android.os.Bundle -import android.view.KeyEvent import android.view.LayoutInflater import android.view.MenuItem import android.view.View @@ -20,6 +17,7 @@ import org.koitharu.kotatsu.base.domain.MangaIntent import org.koitharu.kotatsu.base.ui.BaseBottomSheet import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.base.ui.list.PaginationScrollListener +import org.koitharu.kotatsu.base.ui.util.CollapseActionViewCallback import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.databinding.SheetScrobblingSelectorBinding import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener @@ -43,7 +41,6 @@ class ScrobblingSelectorBottomSheet : View.OnClickListener, MenuItem.OnActionExpandListener, SearchView.OnQueryTextListener, - DialogInterface.OnKeyListener, TabLayout.OnTabSelectedListener, ListStateHolderListener { @@ -53,6 +50,8 @@ class ScrobblingSelectorBottomSheet : @Inject lateinit var coil: ImageLoader + private var collapsibleActionViewCallback: CollapseActionViewCallback? = null + private val viewModel by assistedViewModels { viewModelFactory.create( requireArguments().requireParcelable(MangaIntent.KEY_MANGA).manga, @@ -63,12 +62,6 @@ class ScrobblingSelectorBottomSheet : return SheetScrobblingSelectorBinding.inflate(inflater, container, false) } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return super.onCreateDialog(savedInstanceState).also { - it.setOnKeyListener(this) - } - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val listAdapter = ScrobblerSelectorAdapter(viewLifecycleOwner, coil, this, this) @@ -102,6 +95,11 @@ class ScrobblingSelectorBottomSheet : } } + override fun onDestroyView() { + super.onDestroyView() + collapsibleActionViewCallback = null + } + override fun onClick(v: View) { when (v.id) { R.id.button_done -> viewModel.onDoneClick() @@ -126,6 +124,7 @@ class ScrobblingSelectorBottomSheet : override fun onMenuItemActionExpand(item: MenuItem): Boolean { setExpanded(isExpanded = true, isLocked = true) + collapsibleActionViewCallback?.onMenuItemActionExpand(item) return true } @@ -133,6 +132,7 @@ class ScrobblingSelectorBottomSheet : val searchView = (item.actionView as? SearchView) ?: return false searchView.setQuery("", false) searchView.post { setExpanded(isExpanded = false, isLocked = false) } + collapsibleActionViewCallback?.onMenuItemActionCollapse(item) return true } @@ -147,19 +147,6 @@ class ScrobblingSelectorBottomSheet : override fun onQueryTextChange(newText: String?): Boolean = false - override fun onKey(dialog: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean { - if (keyCode == KeyEvent.KEYCODE_BACK) { - val menuItem = binding.headerBar.menu.findItem(R.id.action_search) ?: return false - if (menuItem.isActionViewExpanded) { - if (event?.action == KeyEvent.ACTION_UP) { - menuItem.collapseActionView() - } - return true - } - } - return false - } - override fun onTabSelected(tab: TabLayout.Tab) { viewModel.setScrobblerIndex(tab.position) } @@ -193,6 +180,9 @@ class ScrobblingSelectorBottomSheet : searchView.setOnQueryTextListener(this) searchView.setIconifiedByDefault(false) searchView.queryHint = searchMenuItem.title + collapsibleActionViewCallback = CollapseActionViewCallback(searchMenuItem).also { + onBackPressedDispatcher.addCallback(it) + } } private fun initTabs() { diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index b70ba4497..8862948d5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.browser.BrowserCallback import org.koitharu.kotatsu.browser.BrowserClient import org.koitharu.kotatsu.browser.ProgressChromeClient +import org.koitharu.kotatsu.browser.WebViewBackPressedCallback import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.CommonHeadersInterceptor import org.koitharu.kotatsu.core.parser.MangaRepository @@ -34,6 +35,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba @Inject lateinit var mangaRepositoryFactory: MangaRepository.Factory + private lateinit var onBackPressedCallback: WebViewBackPressedCallback private lateinit var authProvider: MangaParserAuthProvider @SuppressLint("SetJavaScriptEnabled") @@ -66,6 +68,8 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba } binding.webView.webViewClient = BrowserClient(this) binding.webView.webChromeClient = ProgressChromeClient(binding.progressBar) + onBackPressedCallback = WebViewBackPressedCallback(binding.webView) + onBackPressedDispatcher.addCallback(onBackPressedCallback) if (savedInstanceState != null) { return } @@ -103,14 +107,6 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba else -> super.onOptionsItemSelected(item) } - override fun onBackPressed() { - if (binding.webView.canGoBack()) { - binding.webView.goBack() - } else { - super.onBackPressed() - } - } - override fun onPause() { binding.webView.onPause() super.onPause() @@ -135,6 +131,10 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba supportActionBar?.subtitle = subtitle } + override fun onHistoryChanged() { + onBackPressedCallback.onHistoryChanged() + } + override fun onWindowInsetsChanged(insets: Insets) { binding.appbar.updatePadding(top = insets.top) binding.webView.updatePadding(bottom = insets.bottom) diff --git a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt index f4236fae1..f08a41404 100644 --- a/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/sync/ui/SyncAuthActivity.kt @@ -8,6 +8,7 @@ import android.text.Editable import android.text.TextWatcher import android.view.View import android.widget.Button +import androidx.activity.OnBackPressedCallback import androidx.activity.viewModels import androidx.core.graphics.Insets import androidx.core.view.isGone @@ -28,6 +29,7 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi private var accountAuthenticatorResponse: AccountAuthenticatorResponse? = null private var resultBundle: Bundle? = null + private val pageBackCallback = PageBackCallback() private val viewModel by viewModels() @@ -44,9 +46,13 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi binding.editEmail.addTextChangedListener(EmailTextWatcher(binding.buttonNext)) binding.editPassword.addTextChangedListener(PasswordTextWatcher(binding.buttonDone)) + onBackPressedDispatcher.addCallback(pageBackCallback) + viewModel.onTokenObtained.observe(this, ::onTokenReceived) viewModel.onError.observe(this, ::onError) viewModel.isLoading.observe(this, ::onLoadingStateChanged) + + pageBackCallback.update() } override fun onWindowInsetsChanged(insets: Insets) { @@ -59,27 +65,23 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi ) } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") - override fun onBackPressed() { - if (binding.switcher.isVisible && binding.switcher.displayedChild > 0) { - binding.switcher.showPrevious() - } else { - super.onBackPressed() - } - } - override fun onClick(v: View) { when (v.id) { R.id.button_cancel -> { setResult(RESULT_CANCELED) finish() } + R.id.button_next -> { binding.switcher.showNext() + pageBackCallback.update() } + R.id.button_back -> { binding.switcher.showPrevious() + pageBackCallback.update() } + R.id.button_done -> { viewModel.obtainToken( email = binding.editEmail.text.toString(), @@ -105,6 +107,7 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi TransitionManager.beginDelayedTransition(binding.root, Fade()) binding.switcher.isGone = isLoading binding.layoutProgress.isVisible = isLoading + pageBackCallback.update() } private fun onError(error: Throwable) { @@ -161,4 +164,16 @@ class SyncAuthActivity : BaseActivity(), View.OnClickLi button.isEnabled = text != null && text.length >= 4 } } + + private inner class PageBackCallback : OnBackPressedCallback(false) { + + override fun handleOnBackPressed() { + binding.switcher.showPrevious() + update() + } + + fun update() { + isEnabled = binding.switcher.isVisible && binding.switcher.displayedChild > 0 + } + } }