- // Close the streams.
- temporaryMhtFileOutputStream.close()
- mhtFileInputStream.close()
-
- // Load the temporary MHT file.
- currentWebView!!.loadUrl(temporaryMhtFile.toString())
- } catch (exception: Exception) {
- // Display a snackbar.
- Snackbar.make(currentWebView!!, getString(R.string.error, exception), Snackbar.LENGTH_INDEFINITE).show()
- }
- } else { // Let the WebView handle opening of the file.
- // Open the file.
- currentWebView!!.loadUrl(openFilePath)
- }
- }
-
- private fun downloadUrlWithExternalApp(url: String) {
- // Create a download intent. Not specifying the action type will display the maximum number of options.
- val downloadIntent = Intent()
-
- // Set the URI and the mime type.
- downloadIntent.setDataAndType(Uri.parse(url), "text/html")
-
- // Flag the intent to open in a new task.
- downloadIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
-
- // Show the chooser.
- startActivity(Intent.createChooser(downloadIntent, getString(R.string.download_with_external_app)))
- }
-
- override fun onSaveUrl(originalUrlString: String, fileNameString: String, dialogFragment: DialogFragment) {
- // Store the URL. This will be used in the save URL activity result launcher.
- saveUrlString = if (originalUrlString.startsWith("data:")) {
- // Save the original URL.
- originalUrlString
- } else {
- // Get the dialog.
- val dialog = dialogFragment.dialog!!
-
- // Get a handle for the dialog URL edit text.
- val dialogUrlEditText = dialog.findViewById<EditText>(R.id.url_edittext)
-
- // Get the URL from the edit text, which may have been modified.
- dialogUrlEditText.text.toString()
- }
-
- // Open the file picker.
- saveUrlActivityResultLauncher.launch(fileNameString)
- }
-
- // Remove the warning that `OnTouchListener()` needs to override `performClick()`, as the only purpose of setting the `OnTouchListener()` is to make it do nothing.
- @SuppressLint("ClickableViewAccessibility")
- private fun initializeApp() {
- // Get a handle for the input method.
- val inputMethodManager = (getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
-
- // Initialize the color spans for highlighting the URLs.
- initialGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500))
- finalGrayColorSpan = ForegroundColorSpan(getColor(R.color.gray_500))
- redColorSpan = ForegroundColorSpan(getColor(R.color.red_text))
-
- // Remove the formatting from the URL edit text when the user is editing the text.
- urlEditText.onFocusChangeListener = View.OnFocusChangeListener { _: View?, hasFocus: Boolean ->
- if (hasFocus) { // The user is editing the URL text box.
- // Remove the syntax highlighting.
- urlEditText.text.removeSpan(redColorSpan)
- urlEditText.text.removeSpan(initialGrayColorSpan)
- urlEditText.text.removeSpan(finalGrayColorSpan)
- } else { // The user has stopped editing the URL text box.
- // Move to the beginning of the string.
- urlEditText.setSelection(0)
-
- // Reapply the syntax highlighting.
- UrlHelper.highlightSyntax(urlEditText, initialGrayColorSpan, finalGrayColorSpan, redColorSpan)
- }
- }
-
- // Set the go button on the keyboard to load the URL in url text box.
- urlEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
- // If the event is a key-down event on the `enter` button, load the URL.
- if ((keyEvent.action == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { // The enter key was pressed.
- // Load the URL.
- loadUrlFromTextBox()
-
- // Consume the event.
- return@setOnKeyListener true
- } else { // Some other key was pressed.
- // Do not consume the event.
- return@setOnKeyListener false
- }
- }
-
- // Create an Orbot status broadcast receiver.
- orbotStatusBroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- // Get the content of the status message.
- orbotStatus = intent.getStringExtra("org.torproject.android.intent.extra.STATUS")!!
-
- // If Privacy Browser is waiting on the proxy, load the website now that Orbot is connected.
- if ((orbotStatus == ProxyHelper.ORBOT_STATUS_ON) && waitingForProxy) {
- // Reset the waiting for proxy status.
- waitingForProxy = false
-
- // Get a list of the current fragments.
- val fragmentList = supportFragmentManager.fragments
-
- // Check each fragment to see if it is a waiting for proxy dialog. Sometimes more than one is displayed.
- for (i in fragmentList.indices) {
- // Get the fragment tag.
- val fragmentTag = fragmentList[i].tag
-
- // Check to see if it is the waiting for proxy dialog.
- if (fragmentTag != null && fragmentTag == getString(R.string.waiting_for_proxy_dialog)) {
- // Dismiss the waiting for proxy dialog.
- (fragmentList[i] as DialogFragment).dismiss()
- }
- }
-
- // Reload existing URLs and load any URLs that are waiting for the proxy.
- for (i in 0 until webViewPagerAdapter!!.count) {
- // Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
-
- // Get the fragment view.
- val fragmentView = webViewTabFragment.view
-
- // Only process the WebViews if they exist.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- val nestedScrollWebView = fragmentView.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)
-
- // Get the waiting for proxy URL string.
- val waitingForProxyUrlString = nestedScrollWebView.waitingForProxyUrlString
-
- // Load the pending URL if it exists.
- if (waitingForProxyUrlString.isNotEmpty()) { // A URL is waiting to be loaded.
- // Load the URL.
- loadUrl(nestedScrollWebView, waitingForProxyUrlString)
-
- // Reset the waiting for proxy URL string.
- nestedScrollWebView.waitingForProxyUrlString = ""
- } else { // No URL is waiting to be loaded.
- // Reload the existing URL.
- nestedScrollWebView.reload()
- }
- }
- }
- }
- }
- }
-
- // Register the Orbot status broadcast receiver.
- registerReceiver(orbotStatusBroadcastReceiver, IntentFilter("org.torproject.android.intent.action.STATUS"))
-
- // Get handles for views that need to be modified.
- val bookmarksHeaderLinearLayout = findViewById<LinearLayout>(R.id.bookmarks_header_linearlayout)
- val launchBookmarksActivityFab = findViewById<FloatingActionButton>(R.id.launch_bookmarks_activity_fab)
- val createBookmarkFolderFab = findViewById<FloatingActionButton>(R.id.create_bookmark_folder_fab)
- val createBookmarkFab = findViewById<FloatingActionButton>(R.id.create_bookmark_fab)
-
- // Update the WebView pager every time a tab is modified.
- webViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
- override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
-
- override fun onPageSelected(position: Int) {
- // Close the find on page bar if it is open.
- closeFindOnPage(null)
-
- // Set the current WebView.
- setCurrentWebView(position)
-
- // 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.
- if (tabLayout.selectedTabPosition != position) {
- // Wait until the new tab has been created.
- tabLayout.post {
- // Get a handle for the tab.
- val tab = tabLayout.getTabAt(position)!!
-
- // Select the tab.
- tab.select()
- }
- }
- }
-
- override fun onPageScrollStateChanged(state: Int) {}
- })
-
- // Handle tab selections.
- tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
- override fun onTabSelected(tab: TabLayout.Tab) {
- // Select the same page in the view pager.
- webViewPager.currentItem = 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))
- }
- })
-
- // Set a touch listener on the bookmarks header linear layout so that touches don't pass through to the button underneath.
- bookmarksHeaderLinearLayout.setOnTouchListener { _: View?, _: MotionEvent? -> true }
-
- // Set the launch bookmarks activity floating action button to launch the bookmarks activity.
- launchBookmarksActivityFab.setOnClickListener {
- // Get a copy of the favorite icon bitmap.
- val currentFavoriteIconBitmap = currentWebView!!.getFavoriteIcon()
-
- // Create a favorite icon byte array output stream.
- val currentFavoriteIconByteArrayOutputStream = ByteArrayOutputStream()
-
- // Convert the favorite icon bitmap to a byte array. `0` is for lossless compression (the only option for a PNG).
- currentFavoriteIconBitmap.compress(Bitmap.CompressFormat.PNG, 0, currentFavoriteIconByteArrayOutputStream)
-
- // Convert the favorite icon byte array stream to a byte array.
- val currentFavoriteIconByteArray = currentFavoriteIconByteArrayOutputStream.toByteArray()
-
- // Create an intent to launch the bookmarks activity.
- val bookmarksIntent = Intent(applicationContext, BookmarksActivity::class.java)
-
- // Add the extra information to the intent.
- bookmarksIntent.putExtra(CURRENT_FOLDER, currentBookmarksFolder)
- bookmarksIntent.putExtra(CURRENT_TITLE, currentWebView!!.title)
- bookmarksIntent.putExtra(CURRENT_URL, currentWebView!!.url)
- bookmarksIntent.putExtra(CURRENT_FAVORITE_ICON_BYTE_ARRAY, currentFavoriteIconByteArray)
-
- // Make it so.
- startActivity(bookmarksIntent)
- }
-
- // Set the create new bookmark folder floating action button to display an alert dialog.
- createBookmarkFolderFab.setOnClickListener {
- // Create a create bookmark folder dialog.
- val createBookmarkFolderDialog: DialogFragment = CreateBookmarkFolderDialog.createBookmarkFolder(currentWebView!!.getFavoriteIcon())
-
- // Show the create bookmark folder dialog.
- createBookmarkFolderDialog.show(supportFragmentManager, getString(R.string.create_folder))
- }
-
- // Set the create new bookmark floating action button to display an alert dialog.
- createBookmarkFab.setOnClickListener {
- // Instantiate the create bookmark dialog.
- val createBookmarkDialog: DialogFragment = CreateBookmarkDialog.createBookmark(currentWebView!!.url!!, currentWebView!!.title!!, currentWebView!!.getFavoriteIcon())
-
- // Display the create bookmark dialog.
- createBookmarkDialog.show(supportFragmentManager, getString(R.string.create_bookmark))
- }
-
- // Search for the string on the page whenever a character changes in the find on page edit text.
- findOnPageEditText.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
-
- override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
-
- override fun afterTextChanged(s: Editable) {
- // Search for the text in the WebView if it is not null. Sometimes on resume after a period of non-use the WebView will be null.
- currentWebView?.findAllAsync(findOnPageEditText.text.toString())
- }
- })
-
- // Set the `check mark` button for the find on page edit text keyboard to close the soft keyboard.
- findOnPageEditText.setOnKeyListener { _: View?, keyCode: Int, keyEvent: KeyEvent ->
- if ((keyEvent.action == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { // The `enter` key was pressed.
- // Hide the soft keyboard.
- inputMethodManager.hideSoftInputFromWindow(currentWebView!!.windowToken, 0)
-
- // Consume the event.
- return@setOnKeyListener true
- } else { // A different key was pressed.
- // Do not consume the event.
- return@setOnKeyListener false
- }
- }
-
- // Implement swipe to refresh.
- swipeRefreshLayout.setOnRefreshListener {
- // Reload the website.
- currentWebView!!.reload()
- }
-
- // Store the default progress view offsets.
- defaultProgressViewStartOffset = swipeRefreshLayout.progressViewStartOffset
- defaultProgressViewEndOffset = swipeRefreshLayout.progressViewEndOffset
-
- // Set the refresh color scheme according to the theme.
- swipeRefreshLayout.setColorSchemeResources(R.color.blue_text)
-
- // Initialize a color background typed value.
- val colorBackgroundTypedValue = TypedValue()
-
- // Get the color background from the theme.
- theme.resolveAttribute(android.R.attr.colorBackground, colorBackgroundTypedValue, true)
-
- // Get the color background int from the typed value.
- val colorBackgroundInt = colorBackgroundTypedValue.data
-
- // Set the swipe refresh background color.
- swipeRefreshLayout.setProgressBackgroundColorSchemeColor(colorBackgroundInt)
-
- // Set the drawer titles, which identify the drawer layouts in accessibility mode.
- drawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.navigation_drawer))
- drawerLayout.setDrawerTitle(GravityCompat.END, getString(R.string.bookmarks))
-
- // Load the bookmarks folder.
- loadBookmarksFolder()
-
- // Handle clicks on bookmarks.
- bookmarksListView.onItemClickListener = AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
- // Convert the id from long to int to match the format of the bookmarks database.
- val databaseId = id.toInt()
-
- // Get the bookmark cursor for this ID.
- val bookmarkCursor = bookmarksDatabaseHelper!!.getBookmark(databaseId)
-
- // Move the bookmark cursor to the first row.
- bookmarkCursor.moveToFirst()
-
- // Act upon the bookmark according to the type.
- if (bookmarkCursor.getInt(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.IS_FOLDER)) == 1) { // The selected bookmark is a folder.
- // Store the folder name.
- currentBookmarksFolder = bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_NAME))
-
- // Load the new folder.
- loadBookmarksFolder()
- } else { // The selected bookmark is not a folder.
- // Load the bookmark URL.
- loadUrl(currentWebView!!, bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)))
-
- // Close the bookmarks drawer if it is not pinned.
- if (!bookmarksDrawerPinned)
- drawerLayout.closeDrawer(GravityCompat.END)
- }
-
- // Close the cursor.
- bookmarkCursor.close()
- }
-
- // Handle long-presses on bookmarks.
- bookmarksListView.onItemLongClickListener = AdapterView.OnItemLongClickListener { _: AdapterView<*>?, _: View?, _: Int, id: Long ->
- // Convert the database ID from `long` to `int`.
- val databaseId = id.toInt()
-
- // Run the commands associated with the type.
- if (bookmarksDatabaseHelper!!.isFolder(databaseId)) { // The bookmark is a folder.
- // Get a cursor of all the bookmarks in the folder.
- val bookmarksCursor = bookmarksDatabaseHelper!!.getFolderBookmarks(databaseId)
-
- // Move to the first entry in the cursor.
- bookmarksCursor.moveToFirst()
-
- // Open each bookmark
- for (i in 0 until bookmarksCursor.count) {
- // Load the bookmark in a new tab, moving to the tab for the first bookmark if the drawer is not pinned.
- addNewTab(bookmarksCursor.getString(bookmarksCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), !bookmarksDrawerPinned && (i == 0))
-
- // Move to the next bookmark.
- bookmarksCursor.moveToNext()
- }
-
- // Close the cursor.
- bookmarksCursor.close()
- } else { // The bookmark is not a folder.
- // Get the bookmark cursor for this ID.
- val bookmarkCursor = bookmarksDatabaseHelper!!.getBookmark(databaseId)
-
- // Move the bookmark cursor to the first row.
- bookmarkCursor.moveToFirst()
-
- // Load the bookmark in a new tab and move to the tab if the drawer is not pinned.
- addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndexOrThrow(BookmarksDatabaseHelper.BOOKMARK_URL)), !bookmarksDrawerPinned)
-
- // Close the cursor.
- bookmarkCursor.close()
- }
-
- // Close the bookmarks drawer if it is not pinned.
- if (!bookmarksDrawerPinned)
- drawerLayout.closeDrawer(GravityCompat.END)
-
- // Consume the event.
- true
- }
-
- // The drawer listener is used to update the navigation menu.
- drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
- override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}
-
- override fun onDrawerOpened(drawerView: View) {}
-
- override fun onDrawerClosed(drawerView: View) {
- // Reset the drawer icon when the drawer is closed. Otherwise, it remains an arrow if the drawer is open when the app is restarted.
- actionBarDrawerToggle!!.syncState()
- }
-
- override fun onDrawerStateChanged(newState: Int) {
- if (newState == DrawerLayout.STATE_SETTLING || newState == DrawerLayout.STATE_DRAGGING) { // A drawer is opening or closing.
- // Update the navigation menu items if the WebView is not null.
- if (currentWebView != null) {
- navigationBackMenuItem.isEnabled = currentWebView!!.canGoBack()
- navigationForwardMenuItem.isEnabled = currentWebView!!.canGoForward()
- navigationHistoryMenuItem.isEnabled = currentWebView!!.canGoBack() || currentWebView!!.canGoForward()
- navigationRequestsMenuItem.title = getString(R.string.requests) + " - " + currentWebView!!.getRequestsCount(BLOCKED_REQUESTS)
-
- // Hide the keyboard (if displayed).
- inputMethodManager.hideSoftInputFromWindow(currentWebView!!.windowToken, 0)
- }
-
- // Clear the focus from from the URL text box. This removes any text selection markers and context menus, which otherwise draw above the open drawers.
- urlEditText.clearFocus()
-
- // Clear the focus from from the WebView if it is not null, which can happen if a user opens a drawer while the browser is being resumed.
- // Clearing the focus from the WebView removes any text selection markers and context menus, which otherwise draw above the open drawers.
- currentWebView?.clearFocus()
- }
- }
- })
-
- // Inflate a bare WebView to get the default user agent. It is not used to render content on the screen.
- @SuppressLint("InflateParams") val webViewLayout = layoutInflater.inflate(R.layout.bare_webview, null, false)
-
- // Get a handle for the WebView.
- val bareWebView = webViewLayout.findViewById<WebView>(R.id.bare_webview)
-
- // Store the default user agent.
- webViewDefaultUserAgent = bareWebView.settings.userAgentString
-
- // Destroy the bare WebView.
- bareWebView.destroy()
-
- // Update the domains settings set.
- updateDomainsSettingsSet()
- }
-
- private fun applyAppSettings() {
- // Store the values from the shared preferences in variables.
- incognitoModeEnabled = sharedPreferences.getBoolean(getString(R.string.incognito_mode_key), false)
- sanitizeTrackingQueries = sharedPreferences.getBoolean(getString(R.string.tracking_queries_key), true)
- sanitizeAmpRedirects = sharedPreferences.getBoolean(getString(R.string.amp_redirects_key), true)
- proxyMode = sharedPreferences.getString(getString(R.string.proxy_key), getString(R.string.proxy_default_value))!!
- fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean(getString(R.string.full_screen_browsing_mode_key), false)
- hideAppBar = sharedPreferences.getBoolean(getString(R.string.hide_app_bar_key), true)
- downloadWithExternalApp = sharedPreferences.getBoolean(getString(R.string.download_with_external_app_key), false)
- scrollAppBar = sharedPreferences.getBoolean(getString(R.string.scroll_app_bar_key), true)
-
- // Apply the saved proxy mode if the app has been restarted.
- if (savedProxyMode != null) {
- // Apply the saved proxy mode.
- proxyMode = savedProxyMode!!
-
- // Reset the saved proxy mode.
- savedProxyMode = null
- }
-
- // Get the search string.
- val searchString = sharedPreferences.getString(getString(R.string.search_key), getString(R.string.search_default_value))!!
-
- // Set the search string, using the custom search URL if specified.
- searchURL = if (searchString == getString(R.string.custom_url_item))
- sharedPreferences.getString(getString(R.string.search_custom_url_key), getString(R.string.search_custom_url_default_value))!!
- else
- searchString
-
- // Apply the proxy.
- applyProxy(false)
-
- // Adjust the layout and scrolling parameters according to the position of the app bar.
- if (bottomAppBar) { // The app bar is on the bottom.
- // Adjust the UI.
- if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) { // The app bar scrolls or full screen browsing mode is engaged with the app bar hidden.
- // Reset the WebView padding to fill the available space.
- swipeRefreshLayout.setPadding(0, 0, 0, 0)
- } else { // The app bar doesn't scroll or full screen browsing mode is not engaged with the app bar hidden.
- // Move the WebView above the app bar layout.
- swipeRefreshLayout.setPadding(0, 0, 0, appBarHeight)
-
- // Show the app bar if it is scrolled off the screen.
- if (appBarLayout.translationY != 0f) {
- // Animate the bottom app bar onto the screen.
- objectAnimator = ObjectAnimator.ofFloat(appBarLayout, "translationY", 0f)
-
- // Make it so.
- objectAnimator.start()
- }
- }
- } else { // The app bar is on the top.
- // Get the current layout parameters. Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
- val swipeRefreshLayoutParams = swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams
- val toolbarLayoutParams = toolbar.layoutParams as AppBarLayout.LayoutParams
- val findOnPageLayoutParams = findOnPageLinearLayout.layoutParams as AppBarLayout.LayoutParams
- val tabsLayoutParams = tabsLinearLayout.layoutParams as AppBarLayout.LayoutParams
-
- // Add the scrolling behavior to the layout parameters.
- if (scrollAppBar) {
- // Enable scrolling of the app bar.
- swipeRefreshLayoutParams.behavior = AppBarLayout.ScrollingViewBehavior()
- toolbarLayoutParams.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS or AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
- findOnPageLayoutParams.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS or AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
- tabsLayoutParams.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS or AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP
- } else {
- // Disable scrolling of the app bar.
- swipeRefreshLayoutParams.behavior = null
- toolbarLayoutParams.scrollFlags = 0
- findOnPageLayoutParams.scrollFlags = 0
- tabsLayoutParams.scrollFlags = 0
-
- // Expand the app bar if it is currently collapsed.
- appBarLayout.setExpanded(true)
- }
-
- // Set the app bar scrolling for each WebView.
- for (i in 0 until webViewPagerAdapter!!.count) {
- // Get the WebView tab fragment.
- val webViewTabFragment = webViewPagerAdapter!!.getPageFragment(i)
-
- // Get the fragment view.
- val fragmentView = webViewTabFragment.view
-
- // Only modify the WebViews if they exist.
- if (fragmentView != null) {
- // Get the nested scroll WebView from the tab fragment.
- val nestedScrollWebView = fragmentView.findViewById<NestedScrollWebView>(R.id.nestedscroll_webview)
-
- // Set the app bar scrolling.
- nestedScrollWebView.isNestedScrollingEnabled = scrollAppBar
- }
- }
- }
-
- // Update the full screen browsing mode settings.
- if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) { // Privacy Browser is currently in full screen browsing mode.
- // Update the visibility of the app bar, which might have changed in the settings.
- if (hideAppBar) {
- // Hide the tab linear layout.
- tabsLinearLayout.visibility = View.GONE
-
- // Hide the app bar.
- appBar.hide()
- } else {
- // Show the tab linear layout.
- tabsLinearLayout.visibility = View.VISIBLE
-
- // Show the app bar.
- appBar.show()
- }
-
- /* Hide the system bars.
- * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
- * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
- * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
- * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
- */
-
- // The deprecated command can be switched to `WindowInsetsController` once the minimum API >= 30.
- @Suppress("DEPRECATION")
- rootFrameLayout.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- } else { // Privacy Browser is not in full screen browsing mode.
- // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
- inFullScreenBrowsingMode = false
-
- // Show the tab linear layout.
- tabsLinearLayout.visibility = View.VISIBLE
-
- // Show the app bar.
- appBar.show()
-
- // Remove the `SYSTEM_UI` flags from the root frame layout. The deprecated command can be switched to `WindowInsetsController` once the minimum API >= 30.
- @Suppress("DEPRECATION")
- rootFrameLayout.systemUiVisibility = 0
- }
- }
-
- override fun navigateHistory(url: String, steps: Int) {
- // Apply the domain settings.
- applyDomainSettings(currentWebView!!, url, resetTab = false, reloadWebsite = false, loadUrl = false)
-
- // Load the history entry.
- currentWebView!!.goBackOrForward(steps)
- }
-
- override fun pinnedErrorGoBack() {
- // Get the current web back forward list.
- val webBackForwardList = currentWebView!!.copyBackForwardList()
-
- // Get the previous entry URL.
- val previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1).url
-
- // Apply the domain settings.
- applyDomainSettings(currentWebView!!, previousUrl, resetTab = false, reloadWebsite = false, loadUrl = false)
-
- // Go back.
- currentWebView!!.goBack()
- }
-
- // `reloadWebsite` is used if returning from the Domains activity. Otherwise JavaScript might not function correctly if it is newly enabled.
- @SuppressLint("SetJavaScriptEnabled")
- private fun applyDomainSettings(nestedScrollWebView: NestedScrollWebView, url: String?, resetTab: Boolean, reloadWebsite: Boolean, loadUrl: Boolean) {
- // Store the current URL.
- nestedScrollWebView.currentUrl = url!!
-
- // Parse the URL into a URI.
- val uri = Uri.parse(url)
-
- // Extract the domain from the URI.
- var newHostName = uri.host
-
- // Strings don't like to be null.
- if (newHostName == null)
- newHostName = ""
-
- // Apply the domain settings if a new domain is being loaded or if the new domain is blank. This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
- if (nestedScrollWebView.currentDomainName != newHostName || newHostName == "") {
- // Set the new host name as the current domain name.
- nestedScrollWebView.currentDomainName = newHostName
-
- // Reset the ignoring of pinned domain information.
- nestedScrollWebView.ignorePinnedDomainInformation = false
-
- // Clear any pinned SSL certificate or IP addresses.
- nestedScrollWebView.clearPinnedSslCertificate()
- nestedScrollWebView.pinnedIpAddresses = ""
-
- // Reset the favorite icon if specified.
- if (resetTab) {
- // Initialize the favorite icon.
- nestedScrollWebView.initializeFavoriteIcon()
-
- // Get the current page position.
- val currentPagePosition = webViewPagerAdapter!!.getPositionForId(nestedScrollWebView.webViewFragmentId)
-
- // Get the corresponding tab.
- val tab = tabLayout.getTabAt(currentPagePosition)
-
- // Update the tab if it isn't null, which sometimes happens when restarting from the background.
- if (tab != null) {
- // Get the tab custom view.
- val tabCustomView = tab.customView!!
-
- // Get the tab views.
- val tabFavoriteIconImageView = tabCustomView.findViewById<ImageView>(R.id.favorite_icon_imageview)
- val tabTitleTextView = tabCustomView.findViewById<TextView>(R.id.title_textview)
-
- // Set the default favorite icon as the favorite icon for this tab.
- tabFavoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(nestedScrollWebView.getFavoriteIcon(), 64, 64, true))
-
- // Set the loading title text.
- tabTitleTextView.setText(R.string.loading)
- }
- }
-
- // Initialize the domain name in database variable.
- var domainNameInDatabase: String? = null
-
- // Check the hostname against the domain settings set.
- if (domainsSettingsSet.contains(newHostName)) { // The hostname is contained in the domain settings set.
- // Record the domain name in the database.
- domainNameInDatabase = newHostName
-
- // Set the domain settings applied tracker to true.
- nestedScrollWebView.domainSettingsApplied = true
- } else { // The hostname is not contained in the domain settings set.
- // Set the domain settings applied tracker to false.
- nestedScrollWebView.domainSettingsApplied = false
- }
-
- // Check all the subdomains of the host name against wildcard domains in the domain cursor.
- while (!nestedScrollWebView.domainSettingsApplied && newHostName!!.contains(".")) { // Stop checking if domain settings are already applied or there are no more `.` in the hostname.
- if (domainsSettingsSet.contains("*.$newHostName")) { // Check the host name prepended by `*.`.
- // Set the domain settings applied tracker to true.
- nestedScrollWebView.domainSettingsApplied = true
-
- // Store the applied domain names as it appears in the database.
- domainNameInDatabase = "*.$newHostName"
- }
-
- // Strip out the lowest subdomain of of the host name.
- newHostName = newHostName.substring(newHostName.indexOf(".") + 1)
- }
-
- // Store the general preference information.
- val defaultFontSizeString = sharedPreferences.getString(getString(R.string.font_size_key), getString(R.string.font_size_default_value))
- val defaultUserAgentName = sharedPreferences.getString(getString(R.string.user_agent_key), getString(R.string.user_agent_default_value))
- val defaultSwipeToRefresh = sharedPreferences.getBoolean(getString(R.string.swipe_to_refresh_key), true)
- val webViewTheme = sharedPreferences.getString(getString(R.string.webview_theme_key), getString(R.string.webview_theme_default_value))
- val wideViewport = sharedPreferences.getBoolean(getString(R.string.wide_viewport_key), true)
- val displayWebpageImages = sharedPreferences.getBoolean(getString(R.string.display_webpage_images_key), true)
-
- // Get the WebView theme entry values string array.
- val webViewThemeEntryValuesStringArray = resources.getStringArray(R.array.webview_theme_entry_values)
-
- // Initialize the user agent array adapter and string array.
- val userAgentNamesArray = ArrayAdapter.createFromResource(this, R.array.user_agent_names, R.layout.spinner_item)
- val userAgentDataArray = resources.getStringArray(R.array.user_agent_data)
-
- // Apply either the domain settings for the default settings.
- if (nestedScrollWebView.domainSettingsApplied) { // The url has custom domain settings.
- // Get a cursor for the current host.
- val currentDomainSettingsCursor = domainsDatabaseHelper!!.getCursorForDomainName(domainNameInDatabase!!)
-
- // Move to the first position.
- currentDomainSettingsCursor.moveToFirst()