2 * Copyright © 2019-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.adapters
22 import android.os.Bundle
23 import android.os.Handler
24 import android.os.Looper
25 import android.widget.FrameLayout
27 import androidx.fragment.app.Fragment
28 import androidx.fragment.app.FragmentManager
29 import androidx.fragment.app.FragmentPagerAdapter
30 import androidx.viewpager.widget.ViewPager
32 import com.stoutner.privacybrowser.R
33 import com.stoutner.privacybrowser.fragments.WebViewTabFragment
34 import com.stoutner.privacybrowser.fragments.WebViewTabFragment.Companion.createPage
35 import com.stoutner.privacybrowser.views.NestedScrollWebView
37 import java.util.LinkedList
39 class WebViewPagerAdapter(fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
40 // Define the class values.
41 private val webViewFragmentsList = LinkedList<WebViewTabFragment>()
43 override fun getCount(): Int {
44 // Return the number of pages.
45 return webViewFragmentsList.size
48 override fun getItem(pageNumber: Int): Fragment {
49 // Get the fragment for a particular page. Page numbers are 0 indexed.
50 return webViewFragmentsList[pageNumber]
53 override fun getItemId(position: Int): Long {
54 // Return the unique ID for this page.
55 return webViewFragmentsList[position].fragmentId
58 override fun getItemPosition(`object`: Any): Int {
59 return if (webViewFragmentsList.contains(`object`)) {
60 // Return the current page position.
61 webViewFragmentsList.indexOf(`object`)
63 // The tab has been deleted.
68 fun addPage(pageNumber: Int, webViewPager: ViewPager, url: String, moveToNewPage: Boolean) {
70 webViewFragmentsList.add(createPage(pageNumber, url))
72 // Update the view pager.
73 notifyDataSetChanged()
75 // Move to the new page if indicated.
77 moveToNewPage(pageNumber, webViewPager)
81 fun deletePage(pageNumber: Int, webViewPager: ViewPager): Boolean {
82 // Get the WebView tab fragment.
83 val webViewTabFragment = webViewFragmentsList[pageNumber]
85 // Get the WebView frame layout.
86 val webViewFrameLayout = (webViewTabFragment.view as FrameLayout?)!!
88 // Get a handle for the nested scroll WebView.
89 val nestedScrollWebView = webViewFrameLayout.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)
91 // Pause the current WebView.
92 nestedScrollWebView.onPause()
94 // Remove all the views from the frame layout.
95 webViewFrameLayout.removeAllViews()
97 // Destroy the current WebView.
98 nestedScrollWebView.destroy()
101 webViewFragmentsList.removeAt(pageNumber)
103 // Update the view pager.
104 notifyDataSetChanged()
106 // Return true if the selected page number did not change after the delete (because the newly selected tab has has same number as the previously deleted tab).
107 // This will cause the calling method to reset the current WebView to the new contents of this page number.
108 return webViewPager.currentItem == pageNumber
111 fun getPageFragment(pageNumber: Int): WebViewTabFragment {
112 // Return the page fragment.
113 return webViewFragmentsList[pageNumber]
116 fun getPositionForId(fragmentId: Long): Int {
117 // Initialize the position variable.
120 // Initialize the while counter.
123 // Find the current position of the WebView fragment with the given ID.
124 while (position < 0 && i < webViewFragmentsList.size) {
125 // Check to see if the tab ID of this WebView matches the page ID.
126 if (webViewFragmentsList[i].fragmentId == fragmentId) {
127 // Store the position if they are a match.
131 // Increment the counter.
135 // Set the position to be the last tab if it is not found.
136 // Sometimes there is a race condition in populating the webView fragments list when resuming Privacy Browser and displaying an SSL certificate error while loading a new intent.
137 // In that case, the last tab should be the one it is looking for.
138 if (position == -1) {
139 position = webViewFragmentsList.size - 1
142 // Return the position.
146 fun restorePage(savedState: Bundle, savedNestedScrollWebViewState: Bundle) {
148 webViewFragmentsList.add(WebViewTabFragment.restorePage(savedState, savedNestedScrollWebViewState))
150 // Update the view pager.
151 notifyDataSetChanged()
154 private fun moveToNewPage(pageNumber: Int, webViewPager: ViewPager) {
155 // Check to see if the new page has been populated.
156 if (webViewPager.childCount >= pageNumber) { // The new page is ready.
157 // Move to the new page.
158 webViewPager.currentItem = pageNumber
159 } else { // The new page is not yet ready.
161 val moveToNewPageHandler = Handler(Looper.getMainLooper())
163 // Create a runnable.
164 val moveToNewPageRunnable = Runnable {
165 // Move to the new page.
166 webViewPager.currentItem = pageNumber
169 // Try again to move to the new page after 50 milliseconds.
170 moveToNewPageHandler.postDelayed(moveToNewPageRunnable, 50)