diff options
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/web')
6 files changed, 98 insertions, 48 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt index 61711092..386c5339 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt @@ -4,9 +4,10 @@ import android.net.Uri import android.webkit.* import ca.allanwang.kau.permissions.PERMISSION_ACCESS_FINE_LOCATION import ca.allanwang.kau.permissions.kauRequestPermissions -import ca.allanwang.kau.utils.snackbar +import com.pitchedapps.frost.R import com.pitchedapps.frost.contracts.ActivityWebContract import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.frostSnackbar import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.Subject @@ -52,7 +53,7 @@ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() { } override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri>>, fileChooserParams: FileChooserParams): Boolean { - activityContract?.openFileChooser(filePathCallback, fileChooserParams) ?: webView.snackbar("File chooser not found") + activityContract?.openFileChooser(filePathCallback, fileChooserParams) ?: webView.frostSnackbar(R.string.file_chooser_not_found) return activityContract != null } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt index f24a7a51..2abc9b25 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -25,10 +25,14 @@ class FrostJSI(val webView: FrostWebViewCore) { val cookies: ArrayList<CookieModel> get() = activity?.cookies() ?: arrayListOf() + /** + * Attempts to load the url in an overlay + * Returns {@code true} if successful, meaning the event is consumed, + * or {@code false} otherwise, meaning the event should be propagated + */ @JavascriptInterface - fun loadUrl(url: String) { - context.launchWebOverlay(url) - } + fun loadUrl(url: String?): Boolean + = if (url == null) false else context.requestWebOverlay(url) @JavascriptInterface fun reloadBaseUrl(animate: Boolean) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt index 1a907f7f..2dfdda89 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt @@ -3,6 +3,8 @@ package com.pitchedapps.frost.web import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView +import ca.allanwang.kau.kotlin.LazyContext +import ca.allanwang.kau.kotlin.lazyContext import ca.allanwang.kau.utils.use import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs @@ -19,41 +21,37 @@ import java.io.ByteArrayInputStream private val blankResource: WebResourceResponse by lazy { WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream("".toByteArray())) } //these hosts will redirect to a blank resource -private val blacklistHost: Set<String> by lazy { - setOf( - "edge-chat.facebook.com" - ) -} +private val blacklistHost: Set<String> = + setOf( + "edge-chat.facebook.com" + ) //these hosts will return null and skip logging -private val whitelistHost: Set<String> by lazy { - setOf( - "static.xx.fbcdn.net", - "m.facebook.com", - "touch.facebook.com" - ) -} +private val whitelistHost: Set<String> = + setOf( + "static.xx.fbcdn.net", + "m.facebook.com", + "touch.facebook.com" + ) //these hosts will skip ad inspection //this list does not have to include anything from the two above -private val adWhitelistHost: Set<String> by lazy { - setOf( - "scontent-sea1-1.xx.fbcdn.net" - ) -} +private val adWhitelistHost: Set<String> = + setOf( + "scontent-sea1-1.xx.fbcdn.net" + ) -private var adblock: Set<String>? = null +private val adblock: LazyContext<Set<String>> = lazyContext { + it.assets.open("adblock.txt").bufferedReader().use { it.readLines().toSet() } +} -fun shouldFrostInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { +fun WebView.shouldFrostInterceptRequest(request: WebResourceRequest): WebResourceResponse? { val httpUrl = HttpUrl.parse(request.url?.toString() ?: return null) ?: return null val host = httpUrl.host() val url = httpUrl.toString() if (blacklistHost.contains(host)) return blankResource if (whitelistHost.contains(host)) return null - if (!adWhitelistHost.contains(host)) { - if (adblock == null) adblock = view.context.assets.open("adblock.txt").bufferedReader().use { it.readLines().toSet() } - if (adblock?.any { url.contains(it) } ?: false) return blankResource - } + if (!adWhitelistHost.contains(host) && adblock(context).any { url.contains(it) }) return blankResource if (!shouldLoadImages && !Prefs.loadMediaOnMeteredNetwork && request.isMedia) return blankResource L.v("Intercept Request", "$host $url") return null diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt new file mode 100644 index 00000000..0a1878b3 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt @@ -0,0 +1,48 @@ +package com.pitchedapps.frost.web + +import android.content.Context +import com.pitchedapps.frost.facebook.formattedFbUrl +import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.isFacebookUrl +import com.pitchedapps.frost.utils.launchWebOverlay + +/** + * Created by Allan Wang on 2017-08-15. + * + * Due to the nature of facebook href's, many links + * cannot be resolved on a new window and must instead + * by loaded in the current page + * This helper method will collect all known cases and launch the overlay accordingly + * Returns {@code true} (default) if overlay is launcher, {@code false} otherwise + */ +fun Context.requestWebOverlay(url: String): Boolean { + if (url == "#") return false + /* + * Non facebook urls can be loaded + */ + if (!url.formattedFbUrl.isFacebookUrl) { + launchWebOverlay(url) + L.d("Request web overlay is not a facebook url", url) + return true + } + /* + * Check blacklist + */ + if (overlayBlacklist.any { url.contains(it) }) return false + /* + * Facebook messages have the following cases for the tid query + * mid* or id* for newer threads, which can be launched in new windows + * or a hash for old threads, which must be loaded on old threads + */ + if (url.contains("/messages/read/?tid=")) { + if (!url.contains("?tid=id") && !url.contains("?tid=mid")) return false + } + L.v("Request web overlay passed", url) + launchWebOverlay(url) + return true +} + +/** + * The following components should never be launched in a new overlay + */ +val overlayBlacklist = setOf("messages/?pageNum", "photoset_token")
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt index 5f679c65..b1466ee8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt @@ -11,7 +11,6 @@ import com.pitchedapps.frost.activities.LoginActivity import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.activities.SelectorActivity import com.pitchedapps.frost.activities.WebOverlayActivity -import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem @@ -34,7 +33,7 @@ import org.jetbrains.anko.withAlpha open class BaseWebViewClient : WebViewClient() { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? - = shouldFrostInterceptRequest(view, request) + = view.shouldFrostInterceptRequest(request) } @@ -51,7 +50,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient if (url == null) return L.i("FWV Loading", url) refreshObservable.onNext(true) - if (!url.contains(FACEBOOK_COM)) return + if (!url.isFacebookUrl) return if (url.contains("logout.php")) FbCookie.logout(Prefs.userId, { launchLogin(view.context) }) else if (url.contains("login.php")) FbCookie.reset({ launchLogin(view.context) }) } @@ -71,18 +70,19 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient override fun onPageCommitVisible(view: WebView, url: String?) { super.onPageCommitVisible(view, url) injectBackgroundColor() - view.jsInject( - CssAssets.ROUND_ICONS.maybe(Prefs.showRoundedIcons), - CssHider.HEADER, - CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!Prefs.showSuggestedFriends && IS_FROST_PRO), - Prefs.themeInjector, - CssHider.NON_RECENT.maybe(webCore.url?.contains("?sk=h_chr") ?: false)) + if (url.isFacebookUrl) + view.jsInject( + CssAssets.ROUND_ICONS.maybe(Prefs.showRoundedIcons), + CssHider.HEADER, + CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!Prefs.showSuggestedFriends && IS_FROST_PRO), + Prefs.themeInjector, + CssHider.NON_RECENT.maybe(webCore.url?.contains("?sk=h_chr") ?: false)) } override fun onPageFinished(view: WebView, url: String?) { url ?: return L.i("Page finished", url) - if (!url.contains(FACEBOOK_COM)) { + if (!url.isFacebookUrl) { refreshObservable.onNext(false) return } @@ -124,9 +124,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient */ private fun launchRequest(request: WebResourceRequest): Boolean { L.d("Launching Url", request.url?.toString() ?: "null") - if (webCore.context is WebOverlayActivity) return false - webCore.context.launchWebOverlay(request.url.toString()) - return true + return webCore.context !is WebOverlayActivity && webCore.context.requestWebOverlay(request.url.toString()) } private fun launchImage(url: String, text: String? = null): Boolean { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt index aea25337..dd8e2390 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt @@ -8,13 +8,13 @@ import android.webkit.* import ca.allanwang.kau.utils.fadeIn import ca.allanwang.kau.utils.isVisible import com.pitchedapps.frost.dbflow.CookieModel -import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.injectors.CssHider import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs +import com.pitchedapps.frost.utils.isFacebookUrl import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread @@ -55,17 +55,18 @@ class LoginWebView @JvmOverloads constructor( override fun onPageFinished(view: WebView, url: String?) { super.onPageFinished(view, url) - val containsFacebook = url?.contains(FACEBOOK_COM) ?: false checkForLogin(url) { id, cookie -> loginCallback(CookieModel(id, "", cookie)) } - view.jsInject(CssHider.HEADER.maybe(containsFacebook), - CssHider.CORE.maybe(containsFacebook), - Prefs.themeInjector.maybe(containsFacebook), - callback = { if (!view.isVisible) view.fadeIn(offset = WEB_LOAD_DELAY) }) + if (url.isFacebookUrl) + view.jsInject(CssHider.HEADER, + CssHider.CORE, + Prefs.themeInjector, + callback = { if (!view.isVisible) view.fadeIn(offset = WEB_LOAD_DELAY) }) + else if (!view.isVisible) view.fadeIn() } fun checkForLogin(url: String?, onFound: (id: Long, cookie: String) -> Unit) { doAsync { - if (url == null || !url.contains(FACEBOOK_COM)) return@doAsync + if (!url.isFacebookUrl) return@doAsync val cookie = CookieManager.getInstance().getCookie(url) ?: return@doAsync L.d("Checking cookie for login", cookie) val id = userMatcher.find(cookie)?.groups?.get(1)?.value?.toLong() ?: return@doAsync |