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.widget.FrameLayout
25 import androidx.fragment.app.Fragment
26 import androidx.fragment.app.FragmentActivity
27 import androidx.recyclerview.widget.RecyclerView.NO_ID
28 import androidx.viewpager2.adapter.FragmentStateAdapter
29 import androidx.viewpager2.widget.ViewPager2
31 import com.google.android.material.tabs.TabLayout
33 import com.stoutner.privacybrowser.R
34 import com.stoutner.privacybrowser.fragments.WebViewTabFragment
35 import com.stoutner.privacybrowser.views.NestedScrollWebView
37 import java.util.LinkedList
39 class WebViewStateAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
40 // Define the class variables.
41 private val webViewFragmentsList = LinkedList<WebViewTabFragment>()
43 // Get a page fragment.
44 override fun createFragment(pageNumber: Int): Fragment {
45 // Get the fragment for a particular page. Page numbers are 0 indexed.
46 return webViewFragmentsList[pageNumber]
49 override fun containsItem(itemId: Long): Boolean {
50 // Initialize the position variable.
53 // Initialize the while counter.
56 // Find the current position of the WebView fragment with the given ID.
57 while ((position < 0) && (i < webViewFragmentsList.size)) {
58 // Check to see if the tab ID of this WebView matches the page ID.
59 if (webViewFragmentsList[i].fragmentId == itemId) {
60 // Store the position if they are a match.
64 // Increment the counter.
68 // Return true if the item was found in the WebView fragments list.
69 return (position != -1)
72 // Get the number of tabs.
73 override fun getItemCount(): Int {
74 // Return the number of pages.
75 return webViewFragmentsList.size
78 // Get the unique ID for the item.
79 override fun getItemId(position: Int): Long {
80 // Return the unique ID for this page.
81 return if ((position >= 0) && (position < webViewFragmentsList.size)) // The position is 0 based, so it is contained in the WebView fragment list.
82 webViewFragmentsList[position].fragmentId
83 else // The item does not exist.
87 fun addPage(pageNumber: Int, newTab: TabLayout.Tab, url: String, moveToNewPage: Boolean) {
89 webViewFragmentsList.add(WebViewTabFragment.createPage(pageNumber, url))
91 // Update the view pager.
92 notifyItemInserted(pageNumber)
94 // Move to the new page if indicated.
101 fun deletePage(pageNumber: Int, webViewPager2: ViewPager2): Boolean {
102 // Get the WebView tab fragment.
103 val webViewTabFragment = webViewFragmentsList[pageNumber]
105 // Get the WebView frame layout.
106 val webViewFrameLayout = (webViewTabFragment.view as FrameLayout)
108 // Get a handle for the nested scroll WebView.
109 val nestedScrollWebView = webViewFrameLayout.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)
111 // Pause the current WebView.
112 nestedScrollWebView.onPause()
114 // Remove all the views from the frame layout.
115 webViewFrameLayout.removeAllViews()
117 // Destroy the current WebView.
118 nestedScrollWebView.destroy()
121 webViewFragmentsList.removeAt(pageNumber)
123 // Update the view pager.
124 notifyItemRemoved(pageNumber)
126 // 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).
127 // This will cause the calling method to reset the current WebView to the new contents of this page number.
128 return (webViewPager2.currentItem == pageNumber)
131 fun getPageFragment(pageNumber: Int): WebViewTabFragment {
132 // Return the page fragment.
133 return webViewFragmentsList[pageNumber]
136 fun getPositionForId(fragmentId: Long): Int {
137 // Initialize the position variable.
140 // Initialize the while counter.
143 // Find the current position of the WebView fragment with the given ID.
144 while ((position < 0) && (i < webViewFragmentsList.size)) {
145 // Check to see if the tab ID of this WebView matches the page ID.
146 if (webViewFragmentsList[i].fragmentId == fragmentId) {
147 // Store the position if they are a match.
151 // Increment the counter.
155 // Set the position to be the last tab if it is not found.
156 // 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.
157 // In that case, the last tab should be the one it is looking for, which is one less than the size because it is zero based.
159 position = (webViewFragmentsList.size - 1)
161 // Return the position.
165 fun restorePage(savedState: Bundle, savedNestedScrollWebViewState: Bundle) {
167 webViewFragmentsList.add(WebViewTabFragment.restorePage(savedState, savedNestedScrollWebViewState))
169 // Update the view pager. The position is zero indexed.
170 notifyItemInserted(webViewFragmentsList.size - 1)