/*
- * Copyright © 2019-2021 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2019-2023 Soren Stoutner <soren@stoutner.com>.
*
- * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
+ * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
*
- * Privacy Browser is free software: you can redistribute it and/or modify
+ * Privacy Browser Android is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
- * Privacy Browser is distributed in the hope that it will be useful,
+ * Privacy Browser Android is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
+ * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
*/
package com.stoutner.privacybrowser.views
+import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.os.Bundle
import android.util.AttributeSet
import android.view.MotionEvent
-import android.webkit.WebView
-import android.webkit.SslErrorHandler
import android.webkit.HttpAuthHandler
+import android.webkit.SslErrorHandler
+import android.webkit.WebView
-import androidx.core.content.ContextCompat
+import androidx.appcompat.content.res.AppCompatResources.getDrawable
import androidx.core.view.NestedScrollingChild2
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat
import com.stoutner.privacybrowser.R
+import com.stoutner.privacybrowser.activities.MainWebViewActivity
import java.util.Collections
import java.util.Date
import kotlin.collections.ArrayList
-import kotlin.jvm.JvmOverloads
-
-// Define the saved state constants.
+// Define the public constants.
+const val BLOCKED_REQUESTS = 0
+const val EASYLIST = 1
+const val EASYPRIVACY = 2
+const val FANBOYS_ANNOYANCE_LIST = 3
+const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
+const val ULTRALIST = 5
+const val ULTRAPRIVACY = 6
+const val THIRD_PARTY_REQUESTS = 7
+
+// Define the private class constants.
private const val DOMAIN_SETTINGS_APPLIED = "domain_settings_applied"
private const val DOMAIN_SETTINGS_DATABASE_ID = "domain_settings_database_id"
private const val CURRENT_DOMAIN_NAME = "current_domain_name"
class NestedScrollWebView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defaultStyle: Int = android.R.attr.webViewStyle) : WebView(context, attributeSet, defaultStyle),
NestedScrollingChild2 {
- companion object {
- // Define the companion object blocklists constants. These can be moved to class constants once all of the code has transitioned to Kotlin.
- const val BLOCKED_REQUESTS = 0
- const val EASYLIST = 1
- const val EASYPRIVACY = 2
- const val FANBOYS_ANNOYANCE_LIST = 3
- const val FANBOYS_SOCIAL_BLOCKING_LIST = 4
- const val ULTRALIST = 5
- const val ULTRAPRIVACY = 6
- const val THIRD_PARTY_REQUESTS = 7
- }
-
// Define the public variables.
var acceptCookies = false
var blockAllThirdPartyRequests = false
var waitingForProxyUrlString = ""
var webViewFragmentId: Long = 0
-
// Define the private variables.
private val nestedScrollingChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
- private lateinit var favoriteOrDefaultIcon: Bitmap
+ private lateinit var favoriteIcon: Bitmap
+ private var favoriteIconHeight = 0
private var previousYPosition = 0 // The previous Y position needs to be tracked between motion events.
private var hasPinnedSslCertificate = false
private var pinnedSslIssuedToCName = ""
initializeFavoriteIcon()
}
-
// Favorite or default icon.
fun initializeFavoriteIcon() {
- // Get the default favorite icon drawable. `ContextCompat` must be used until API >= 21.
- val favoriteIconDrawable = ContextCompat.getDrawable(context, R.drawable.world)
+ // Get the default favorite icon drawable.
+ val favoriteIconDrawable = getDrawable(context, R.drawable.world)
// Cast the favorite icon drawable to a bitmap drawable.
val favoriteIconBitmapDrawable = (favoriteIconDrawable as BitmapDrawable?)!!
// Store the default icon bitmap.
- favoriteOrDefaultIcon = favoriteIconBitmapDrawable.bitmap
+ favoriteIcon = favoriteIconBitmapDrawable.bitmap
+
+ // Set the favorite icon height to be 0. This way any favorite icons presented by the website will overwrite it.
+ favoriteIconHeight = 0
}
- fun setFavoriteOrDefaultIcon(icon: Bitmap) {
+ fun setFavoriteIcon(icon: Bitmap) {
+ // Store the current favorite icon height.
+ favoriteIconHeight = icon.height
+
// Scale the favorite icon bitmap down if it is larger than 256 x 256. Filtering uses bilinear interpolation.
- favoriteOrDefaultIcon = if (icon.height > 256 || icon.width > 256) {
+ favoriteIcon = if (icon.height > 256 || icon.width > 256) {
Bitmap.createScaledBitmap(icon, 256, 256, true)
} else {
// Store the icon as presented.
}
}
- fun getFavoriteOrDefaultIcon(): Bitmap {
- // Return the favorite or default icon. This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
- return favoriteOrDefaultIcon
+ fun getFavoriteIcon(): Bitmap {
+ // Return the favorite icon. This is the only way to return a non-nullable variable while retaining the custom initialization and setter functions above.
+ return favoriteIcon
}
+ fun getFavoriteIconHeight(): Int {
+ // Return the favorite icon height.
+ return favoriteIconHeight
+ }
// Reset the handlers.
fun resetSslErrorHandler() {
hasPinnedSslCertificate = true
}
- fun getPinnedSslCertificate(): ArrayList<Any> {
- // Initialize an array list.
- val arrayList = ArrayList<Any>()
-
+ fun getPinnedSslCertificate(): Pair<Array<String>, Array<Date>> {
// Create the SSL certificate string array.
val sslCertificateStringArray = arrayOf(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName)
// Create the SSL certificate date array.
val sslCertificateDateArray = arrayOf(pinnedSslStartDate, pinnedSslEndDate)
- // Add the arrays to the array list.
- arrayList.add(sslCertificateStringArray)
- arrayList.add(sslCertificateDateArray)
-
- // Return the pinned SSL certificate array list.
- return arrayList
+ // Return the pinned SSL certificate pair.
+ return Pair(sslCertificateStringArray, sslCertificateDateArray)
}
fun clearPinnedSslCertificate() {
return computeVerticalScrollRange()
}
+ override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
+ // Run the default commands.
+ super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
+
+ // Display the bottom app bar if it has been hidden and the WebView was over-scrolled at the top of the screen.
+ if ((MainWebViewActivity.appBarLayout.translationY != 0f) && (scrollY == 0) && clampedY) {
+ // Animate the bottom app bar onto the screen.
+ val objectAnimator = ObjectAnimator.ofFloat(MainWebViewActivity.appBarLayout, "translationY", 0f)
+
+ // Make it so.
+ objectAnimator.start()
+ }
+ }
// Handle touches.
@SuppressLint("ClickableViewAccessibility")
}
- // Save and restore state.
+ // Save the state.
fun saveNestedScrollWebViewState(): Bundle {
// Create a saved state bundle.
val savedState = Bundle()
return savedState
}
+ // Restore the state.
fun restoreNestedScrollWebViewState(savedState: Bundle) {
// Restore the class variables.
domainSettingsApplied = savedState.getBoolean(DOMAIN_SETTINGS_APPLIED)
// Dispatch a nested fling with the specified velocity.
return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)
}
-}
\ No newline at end of file
+}