From: Soren Stoutner Date: Fri, 28 Jul 2023 00:48:04 +0000 (-0700) Subject: Refactor page switching code. https://redmine.stoutner.com/issues/1020 X-Git-Tag: v3.16~18 X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff_plain;h=6f53fabebc2ce78292a268e6ad0712dec8b6f3d9 Refactor page switching code. https://redmine.stoutner.com/issues/1020 --- diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt index ccaa58fa..1553146f 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.kt @@ -380,6 +380,7 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook private var orbotStatusBroadcastReceiver: BroadcastReceiver? = null private var reapplyAppSettingsOnRestart = false private var reapplyDomainSettingsOnRestart = false + private var restartTime = Date(0) private var sanitizeAmpRedirects = false private var sanitizeTrackingQueries = false private var savedProxyMode: String? = null @@ -1012,12 +1013,15 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Toggle the pages if there is more than one so that the view pager will recalculate their size. if (currentPage > 0) { - // Switch to the previous page. - webViewViewPager2.currentItem = (currentPage - 1) + // Switch to the previous page after 25 milliseconds. + webViewViewPager2.postDelayed ({ webViewViewPager2.currentItem = (currentPage - 1) }, 25) - // Switch back to the current page after the view pager has quiesced. - webViewViewPager2.post { webViewViewPager2.currentItem = currentPage } + // Switch back to the current page after the view pager has quiesced (which we are deciding should be 25 milliseconds). + webViewViewPager2.postDelayed ({ webViewViewPager2.currentItem = currentPage }, 25) } + + // Scroll to the current tab position after 25 milliseconds. + tabLayout.postDelayed ({ tabLayout.setScrollPosition(currentPage, 0F, false, false) }, 25) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -2792,8 +2796,11 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Set a custom view on the new tab. newTab.setCustomView(R.layout.tab_custom_view) + // Scroll to the new tab position. + tabLayout.post { tabLayout.setScrollPosition(newTabNumber, 0F, false, false) } + // Add the new WebView page. - webViewStateAdapter!!.addPage(newTabNumber, webViewViewPager2, urlString, moveToTab) + webViewStateAdapter!!.addPage(newTabNumber, newTab, urlString, moveToTab) // Show the app bar if it is at the bottom of the screen and the new tab is taking focus. if (bottomAppBar && moveToTab && appBarLayout.translationY != 0f) { @@ -3992,7 +3999,10 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook if ((savedStateArrayList == null) || (savedStateArrayList!!.size == 0)) { // The activity has not been restarted or it was restarted on start to change the theme. // Add the first tab. addNewTab("", false) - } else { // The activity has been restarted. + } else { // The activity has been restarted with a saved state. + // Set the current restart time. + restartTime = Date() + // Restore each tab. for (i in savedStateArrayList!!.indices) { // Add a new tab. @@ -4017,12 +4027,14 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Set the first page as the current WebView. setCurrentWebView(0) } else { // The first tab is not selected. - // Switch to the page before the saved tab position. - webViewViewPager2.post { webViewViewPager2.currentItem = (savedTabPosition - 1) } + // Select the tab when the layout has finished populating. + tabLayout.post { + // Get a handle for the tab. + val tab = tabLayout.getTabAt(savedTabPosition)!! - // Switch to the saved tab position. - // This has to be done twice because, for some reason, if the above step is skipped there is some race condition where nothing happens and the first page is displayed. - webViewViewPager2.post { webViewViewPager2.currentItem = savedTabPosition } + // Select the tab. + tab.select() + } } // Get the intent that started the app. @@ -4182,46 +4194,37 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook val createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab) val createBookmarkFab = findViewById(R.id.create_bookmark_fab) - // Update the WebView pager every time a tab is modified. - webViewViewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - // Close the find on page bar if it is open. - closeFindOnPage(null) - - // Set the current WebView. - setCurrentWebView(position) - - // Wait until the new tab has been created. - tabLayout.post { - // Select the corresponding tab if it does not match the currently selected page. This will happen if the page was scrolled by creating a new tab. - // The checking of the position was moved inside the post block to prevent a race condition that caused the tab to be selected twice and the encryption dialog to be displayed. - // - if (tabLayout.selectedTabPosition != position) { - // Get a handle for the tab. - val tab = tabLayout.getTabAt(position)!! - - // Select the tab. - tab.select() - } - } - } - }) - // Handle tab selections. tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { + // Close the find on page bar if it is open. + closeFindOnPage(null) + // Select the same page in the view pager. webViewViewPager2.currentItem = tab.position + + // Set the current WebView. + setCurrentWebView(tab.position) } override fun onTabUnselected(tab: TabLayout.Tab) {} override fun onTabReselected(tab: TabLayout.Tab) { - // Instantiate the View SSL Certificate dialog. - val viewSslCertificateDialogFragment: DialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView!!.webViewFragmentId, currentWebView!!.getFavoriteIcon()) - - // Display the View SSL Certificate dialog. - viewSslCertificateDialogFragment.show(supportFragmentManager, getString(R.string.view_ssl_certificate)) + // Only display the view SSL certificate dialog if the current WebView is not null. + // This can happen if the tab is programmatically reselected while the app is being restarted and is not yet populated. + if (currentWebView != null) { + // Calculate the milliseconds since the last restart. This can be replaced by the simpler LocalDateTime once the minimum API >= 26. + val millisecondsSinceLastRestart = Date().time - restartTime.time + + // Only display the SSL certificate dialog if it has been at least 1 second since the last restart as deep restarts sometimes end up selecting a tab twice. + if (millisecondsSinceLastRestart > 1000) { + // Instantiate the View SSL Certificate dialog. + val viewSslCertificateDialogFragment: DialogFragment = ViewSslCertificateDialog.displayDialog(currentWebView!!.webViewFragmentId, currentWebView!!.getFavoriteIcon()) + + // Display the View SSL Certificate dialog. + viewSslCertificateDialogFragment.show(supportFragmentManager, getString(R.string.view_ssl_certificate)) + } + } } }) @@ -5948,16 +5951,16 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Stop the swipe to refresh indicator if it is running swipeRefreshLayout.isRefreshing = false - // Get the WebView tab fragment. - val webViewTabFragment = webViewStateAdapter!!.getPageFragment(pageNumber) + // Try to set the current WebView. This will fail if the WebView has not yet been populated. + try { + // Get the WebView tab fragment. + val webViewTabFragment = webViewStateAdapter!!.getPageFragment(pageNumber) - // Get the fragment view. - val webViewFragmentView = webViewTabFragment.view + // Get the fragment view. + val webViewFragmentView = webViewTabFragment.view - // Set the current WebView if the fragment view is not null. - if (webViewFragmentView != null) { // The fragment has been populated. // Store the current WebView. - currentWebView = webViewFragmentView.findViewById(R.id.nestedscroll_webview) + currentWebView = webViewFragmentView!!.findViewById(R.id.nestedscroll_webview) // Update the status of swipe to refresh. if (currentWebView!!.swipeToRefresh) { // Swipe to refresh is enabled. @@ -6017,8 +6020,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook // Remove any background on the URL relative layout. urlRelativeLayout.background = AppCompatResources.getDrawable(this, R.color.transparent) } - } else if ((pageNumber == savedTabPosition) || (pageNumber >= (webViewStateAdapter!!.itemCount - 1))) { // The tab has not been populated yet. - // Try again in 100 milliseconds if the app is being restored or the a new tab has been added (the last tab). + } catch (exception: Exception) { + // Try again in 100 milliseconds if the WebView has not yet been populated. // Create a handler to set the current WebView. val setCurrentWebViewHandler = Handler(Looper.getMainLooper()) @@ -6028,8 +6031,8 @@ class MainWebViewActivity : AppCompatActivity(), CreateBookmarkDialog.CreateBook setCurrentWebView(pageNumber) } - // Try setting the current WebView again after 100 milliseconds. - setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 100) + // Try setting the current WebView again after 50 milliseconds. + setCurrentWebViewHandler.postDelayed(setCurrentWebWebRunnable, 50) } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/adapters/WebViewStateAdapter.kt b/app/src/main/java/com/stoutner/privacybrowser/adapters/WebViewStateAdapter.kt index 0d0fdef2..e67a99a3 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/adapters/WebViewStateAdapter.kt +++ b/app/src/main/java/com/stoutner/privacybrowser/adapters/WebViewStateAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2019-2023 Soren Stoutner . + * Copyright 2019-2023 Soren Stoutner . * * This file is part of Privacy Browser Android . * @@ -28,6 +28,8 @@ import androidx.recyclerview.widget.RecyclerView.NO_ID import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.tabs.TabLayout + import com.stoutner.privacybrowser.R import com.stoutner.privacybrowser.fragments.WebViewTabFragment import com.stoutner.privacybrowser.views.NestedScrollWebView @@ -82,7 +84,7 @@ class WebViewStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAda NO_ID } - fun addPage(pageNumber: Int, webViewViewPager2: ViewPager2, url: String, moveToNewPage: Boolean) { + fun addPage(pageNumber: Int, newTab: TabLayout.Tab, url: String, moveToNewPage: Boolean) { // Add a new page. webViewFragmentsList.add(WebViewTabFragment.createPage(pageNumber, url)) @@ -90,8 +92,10 @@ class WebViewStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAda notifyItemInserted(pageNumber) // Move to the new page if indicated. - if (moveToNewPage) - webViewViewPager2.currentItem = pageNumber + if (moveToNewPage) { + // Select the newTab. + newTab.select() + } } fun deletePage(pageNumber: Int, webViewPager2: ViewPager2): Boolean {