From b9ea80d5b5a06d050ce2c7ca46ed597f4cb499ff Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Fri, 16 Jun 2017 00:53:10 -0700 Subject: Add listener logic --- app/src/main/assets/.gitignore | 1 + app/src/main/assets/js/click_interceptor.js | 16 ++--- app/src/main/assets/js/click_interceptor.min.js | 10 ++- app/src/main/assets/js/click_interceptor_bak.js | 14 +++++ app/src/main/assets/js/menu.js | 73 +++++++++++----------- app/src/main/assets/js/menu.min.js | 28 ++++++++- app/src/main/assets/js/menu_click.js | 15 +++-- app/src/main/assets/js/menu_click.min.js | 9 ++- .../com/pitchedapps/frost/injectors/JsActions.kt | 2 + .../com/pitchedapps/frost/injectors/JsAssets.kt | 2 +- .../com/pitchedapps/frost/injectors/JsInjector.kt | 7 ++- .../kotlin/com/pitchedapps/frost/web/FrostJSI.kt | 9 ++- .../pitchedapps/frost/web/FrostWebViewClient.kt | 12 +--- .../frost/web/FrostWebViewClientMenu.kt | 33 +++++----- 14 files changed, 147 insertions(+), 84 deletions(-) create mode 100644 app/src/main/assets/.gitignore create mode 100644 app/src/main/assets/js/click_interceptor_bak.js diff --git a/app/src/main/assets/.gitignore b/app/src/main/assets/.gitignore new file mode 100644 index 00000000..62c89355 --- /dev/null +++ b/app/src/main/assets/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/app/src/main/assets/js/click_interceptor.js b/app/src/main/assets/js/click_interceptor.js index 68dbac2b..238b31d3 100644 --- a/app/src/main/assets/js/click_interceptor.js +++ b/app/src/main/assets/js/click_interceptor.js @@ -1,13 +1,15 @@ // generic click handler -document.onclick = function(e) { +document.on('click', function (e) { e = e || window.event; + e.preventDefault(); var element = e.target || e.srcElement; if (element.tagName !== 'A') - element = element.parentNode; + element = element.parentNode; if (element.tagName === 'A') { - var url = element.href; - console.log('Generic Click Intercept'); - console.log(url); - Frost.loadUrl(url); + var url = element.href; + console.log('Generic Click Intercept'); + console.log(url); + // Frost.loadUrl(url); } -}; + return false; +}); diff --git a/app/src/main/assets/js/click_interceptor.min.js b/app/src/main/assets/js/click_interceptor.min.js index 5f10dd2b..3acb9265 100644 --- a/app/src/main/assets/js/click_interceptor.min.js +++ b/app/src/main/assets/js/click_interceptor.min.js @@ -1 +1,9 @@ -document.onclick=function(c){c=c||window.event;var b=c.target||c.srcElement;if(b.tagName!=="A"){b=b.parentNode}if(b.tagName==="A"){var a=b.href;console.log("Generic Click Intercept");console.log(a);Frost.loadUrl(a)}}; \ No newline at end of file +document.querySelector("body").addEventListener("click",function(e){ +e=e||window.event +;var t=e.target||e.srcElement +;if("A"!==t.tagName&&(t=t.parentNode),"A"===t.tagName){ +var n=t.href +;console.log("Generic Click Intercept"),console.log(n) +} +return!1 +}); \ No newline at end of file diff --git a/app/src/main/assets/js/click_interceptor_bak.js b/app/src/main/assets/js/click_interceptor_bak.js new file mode 100644 index 00000000..d541ccd2 --- /dev/null +++ b/app/src/main/assets/js/click_interceptor_bak.js @@ -0,0 +1,14 @@ +// generic click handler +document.onclick = function(e) { + e = e || window.event; + var element = e.target || e.srcElement; + if (element.tagName !== 'A') + element = element.parentNode; + if (element.tagName === 'A') { + var url = element.href; + console.log('Generic Click Intercept'); + console.log(url); + // Frost.loadUrl(url); + } + return false; +}; diff --git a/app/src/main/assets/js/menu.js b/app/src/main/assets/js/menu.js index 2c517ed2..4a39a93b 100644 --- a/app/src/main/assets/js/menu.js +++ b/app/src/main/assets/js/menu.js @@ -1,35 +1,38 @@ -var viewport = document.getElementById('viewport'); -var root = document.getElementById('root'); -var y = new MutationObserver(function(mutations) { - viewport.removeAttribute('style'); - root.removeAttribute('style'); -}); -y.observe(viewport, { - attributes: true -}); -y.observe(root, { - attributes: true -}); -var x = new MutationObserver(function(mutations) { - var menuChildren = document.getElementsByClassName('mSideMenu'); - if (menuChildren.length > 0) { - x.disconnect(); - console.log('Found side menu'); - var menu = menuChildren[0]; - while (root.firstChild) - root.removeChild(root.firstChild); - while (menu.childNodes.length) - root.appendChild(menu.childNodes[0]); - Frost.emit(0); - setTimeout(function() { - y.disconnect(); - console.log('Unhook styler'); - Frost.handleHtml(document.documentElement.outerHTML); - }, 500); - } -}); -x.observe(document.getElementById('mJewelNav'), { - childList: true, - subtree: true -}); -document.getElementById('bookmarks_jewel').getElementsByTagName('a')[0].click(); +if (document.querySelector('#mJewelNav') !== null) { + console.log('Fetching menu'); + var viewport = document.querySelector('#viewport'); + var root = document.querySelector('#root'); + var y = new MutationObserver(function(mutations) { + viewport.removeAttribute('style'); + root.removeAttribute('style'); + }); + y.observe(viewport, { + attributes: true + }); + y.observe(root, { + attributes: true + }); + var x = new MutationObserver(function(mutations) { + var menu = document.querySelector('.mSideMenu'); + if (menu !== null) { + x.disconnect(); + console.log('Found side menu'); + while (root.firstChild) + root.removeChild(root.firstChild); + while (menu.childNodes.length) + root.appendChild(menu.childNodes[0]); + Frost.emit(0); + setTimeout(function() { + y.disconnect(); + console.log('Unhook styler'); + Frost.handleHtml(document.documentElement.outerHTML); + }, 500); + } + }); + x.observe(document.querySelector('#mJewelNav'), { + childList: true, + subtree: true + }); + document.querySelector('#bookmarks_jewel').querySelector('a').click(); +} +//otherwise we've already found the side nav and this is unnecessary diff --git a/app/src/main/assets/js/menu.min.js b/app/src/main/assets/js/menu.min.js index 7aa05351..0f218ea5 100644 --- a/app/src/main/assets/js/menu.min.js +++ b/app/src/main/assets/js/menu.min.js @@ -1 +1,27 @@ -var viewport=document.getElementById("viewport");var root=document.getElementById("root");var y=new MutationObserver(function(a){viewport.removeAttribute("style");root.removeAttribute("style")});y.observe(viewport,{attributes:true});y.observe(root,{attributes:true});var x=new MutationObserver(function(a){var c=document.getElementsByClassName("mSideMenu");if(c.length>0){x.disconnect();console.log("Found side menu");var b=c[0];while(root.firstChild){root.removeChild(root.firstChild)}while(b.childNodes.length){root.appendChild(b.childNodes[0])}Frost.emit(0);setTimeout(function(){y.disconnect();console.log("Unhook styler");Frost.handleHtml(document.documentElement.outerHTML)},500)}});x.observe(document.getElementById("mJewelNav"),{childList:true,subtree:true});document.getElementById("bookmarks_jewel").getElementsByTagName("a")[0].click(); \ No newline at end of file +if(null!==document.querySelector("#mJewelNav")){ +console.log("Fetching menu") +;var viewport=document.querySelector("#viewport"),root=document.querySelector("#root"),y=new MutationObserver(function(e){ +viewport.removeAttribute("style"), +root.removeAttribute("style") +}) +;y.observe(viewport,{ +attributes:!0 +}),y.observe(root,{ +attributes:!0 +}) +;var x=new MutationObserver(function(e){ +var o=document.querySelector(".mSideMenu") +;if(null!==o){ +for(x.disconnect(),console.log("Found side menu");root.firstChild;)root.removeChild(root.firstChild) +;for(;o.childNodes.length;)root.appendChild(o.childNodes[0]) +;Frost.emit(0),setTimeout(function(){ +y.disconnect(),console.log("Unhook styler"), +Frost.handleHtml(document.documentElement.outerHTML) +},500) +} +}) +;x.observe(document.querySelector("#mJewelNav"),{ +childList:!0, +subtree:!0 +}),document.querySelector("#bookmarks_jewel").querySelector("a").click() +} \ No newline at end of file diff --git a/app/src/main/assets/js/menu_click.js b/app/src/main/assets/js/menu_click.js index 3e23617a..82041b0e 100644 --- a/app/src/main/assets/js/menu_click.js +++ b/app/src/main/assets/js/menu_click.js @@ -1,16 +1,15 @@ // we will handle click events -document.onclick = function(e) { - e = e || window.event; +console.log('Registering menu click'); +document.addEventListener('click', function(e) { var element = e.target || e.srcElement; if (element.tagName !== 'A') element = element.parentNode; - if (element.tagName === 'A') { + if (element.tagName === 'A' && element.getAttribute('href') !== '#') { var url = element.href; console.log('Click Intercept'); console.log(url); - if (url !== "https://m.facebook.com/settings" && url !== "https://m.facebook.com/settings#" && url !== "https://m.facebook.com/settings#!/settings?soft=bookmarks") { - Frost.loadUrl(url); - Frost.reloadBaseUrl(false); //reinject base view - } + Frost.loadUrl(url); + e.stopPropagation(); + e.preventDefault(); } -}; +}, true); diff --git a/app/src/main/assets/js/menu_click.min.js b/app/src/main/assets/js/menu_click.min.js index 580f0467..4d508f71 100644 --- a/app/src/main/assets/js/menu_click.min.js +++ b/app/src/main/assets/js/menu_click.min.js @@ -1 +1,8 @@ -document.onclick=function(c){c=c||window.event;var b=c.target||c.srcElement;if(b.tagName!=="A"){b=b.parentNode}if(b.tagName==="A"){var a=b.href;console.log("Click Intercept");console.log(a);if(a!=="https://m.facebook.com/settings"&&a!=="https://m.facebook.com/settings#"&&a!=="https://m.facebook.com/settings#!/settings?soft=bookmarks"){Frost.loadUrl(a);Frost.reloadBaseUrl(false)}}}; \ No newline at end of file +console.log("Registering menu click"),document.addEventListener("click",function(e){ +var t=e.target||e.srcElement +;if("A"!==t.tagName&&(t=t.parentNode),"A"===t.tagName&&"#"!==t.getAttribute("href")){ +var o=t.href +;console.log("Click Intercept"),console.log(o),Frost.loadUrl(o),e.stopPropagation(), +e.preventDefault() +} +},!0); \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt index 17d7ca81..70df1bfa 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt @@ -1,6 +1,7 @@ package com.pitchedapps.frost.injectors import android.webkit.WebView +import com.pitchedapps.frost.facebook.FB_URL_BASE /** * Created by Allan Wang on 2017-05-31. @@ -11,6 +12,7 @@ enum class JsActions(body: String) : InjectorContract { * see [com.pitchedapps.frost.web.FrostJSI.loadLogin] */ LOGIN_CHECK("document.getElementById('signup-button')&&Frost.loadLogin();"), + BASE_HREF("document.write(\"\");"), EMPTY(""); val function = "!function(){$body}();" diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt index d0bf9deb..864f95ea 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt @@ -29,4 +29,4 @@ enum class JsAssets : InjectorContract { injector = null } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt index 0000ecf1..2d8d42e1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -1,8 +1,7 @@ package com.pitchedapps.frost.injectors import android.webkit.WebView -import com.pitchedapps.frost.facebook.FbCookie -import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.web.FrostWebViewClient import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.subjects.SingleSubject @@ -43,7 +42,7 @@ interface InjectorContract { /** * Helper method to inject multiple functions simultaneously with a single callback */ -fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Array) -> Unit)) { +fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Array) -> Unit) = {}) { val observables = Array(injectors.size, { SingleSubject.create() }) Observable.zip>(observables.map { it.toObservable() }, { it.map { it.toString() }.toTypedArray() }).subscribeOn(AndroidSchedulers.mainThread()).subscribe({ callback.invoke(it) @@ -54,6 +53,8 @@ fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Array) -> Unit) = {}) = webCore.jsInject(*injectors, callback = callback) + class JsInjector(val function: String) : InjectorContract { override fun inject(webView: WebView, callback: ((String) -> Unit)?) { webView.evaluateJavascript(function, { value -> callback?.invoke(value) }) 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 bb482c3c..f81c6a15 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -19,8 +19,15 @@ class FrostJSI(val context: Context, val webView: FrostWebViewCore) { val cookies: ArrayList get() = (context as? MainActivity)?.cookies() ?: arrayListOf() + var lastUrl: String = "" + @JavascriptInterface - fun loadUrl(url: String) = context.launchWebOverlay(url) + fun loadUrl(url: String) { + if (url != lastUrl) { + lastUrl = url + context.launchWebOverlay(url) + } + } @JavascriptInterface fun reloadBaseUrl(animate: Boolean) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt index 07b9a949..8cd36b86 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt @@ -62,7 +62,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { L.d("Page finished reveal") webCore.jsInject(CssHider.HEADER, Prefs.themeInjector, - JsAssets.CLICK_INTERCEPTOR, +// JsAssets.CLICK_INTERCEPTOR, callback = { L.d("Finished ${it.contentToString()}") refreshObservable.onNext(false) @@ -77,14 +77,6 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { L.d("Emit $flag") } - fun inject(jsAssets: JsAssets, view: WebView, callback: (String) -> Unit = {}) { - L.i("Post inject ${jsAssets.name}") - jsAssets.inject(view, { - L.i("Post injection done $it") - callback.invoke(it) - }) - } - override fun shouldOverrideKeyEvent(view: WebView, event: KeyEvent): Boolean { L.d("Key event ${event.keyCode}") return super.shouldOverrideKeyEvent(view, event) @@ -95,6 +87,8 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { return super.shouldOverrideUrlLoading(view, request) } + + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest?): WebResourceResponse? { if (request == null || !(request.url.host?.contains(FACEBOOK_COM) ?: false)) return super.shouldInterceptRequest(view, request) L.v("Url intercept ${request.url.path}") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt index 9a00c563..2a1a5b74 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt @@ -2,7 +2,9 @@ package com.pitchedapps.frost.web import android.graphics.Bitmap import android.webkit.WebView +import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.injectors.JsAssets +import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.utils.L import io.reactivex.subjects.Subject @@ -13,17 +15,18 @@ class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(web var content: String? = null val progressObservable: Subject = webCore.progressObservable + private val contentBaseUrl = "https://touch.facebook.com/notifications" override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) if (content != null) { - when (url) { - "https://m.facebook.com/settings", - "https://m.facebook.com/settings#", - "https://m.facebook.com/settings#!/settings?soft=bookmarks" -> { + when (url.removePrefix(FB_URL_BASE)) { + "settings", + "settings#", + "settings#!/settings?soft=bookmarks" -> { L.d("Load from stored $url") view.stopLoading() - view.loadDataWithBaseURL("https://touch.facebook.com/notifications", content, "text/html", "utf-8", "https://google.ca/test") + view.loadDataWithBaseURL(contentBaseUrl, content, "text/html", "utf-8", "https://google.ca/test") } } } @@ -31,27 +34,23 @@ class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(web override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) - if (url == webCore.baseUrl) { - progressObservable.onNext(99) - inject(JsAssets.MENU, webCore, { - inject(JsAssets.MENU_CLICK, webCore) //menu injection must be after or we will have a loop from the click listener + if (url == webCore.baseUrl && content == null) { + jsInject(JsAssets.MENU, callback = { + jsInject(JsAssets.MENU_CLICK) //menu injection must be after or we will have a loop from the click listener }) - } else { - inject(JsAssets.MENU_CLICK, webCore) - } + } else if (url == contentBaseUrl) jsInject(JsAssets.MENU_CLICK) } override fun emit(flag: Int) { super.emit(flag) - progressObservable.onNext(100) super.injectAndFinish() } override fun onPageFinishedActions(url: String?) { - when (url) { - "https://m.facebook.com/settings", - "https://m.facebook.com/settings#", - "https://m.facebook.com/settings#!/settings?soft=bookmarks" -> { + when (url?.removePrefix(FB_URL_BASE)) { + "settings", + "settings#", + "settings#!/settings?soft=bookmarks" -> { //do nothing; we will further inject before revealing } else -> injectAndFinish() -- cgit v1.2.3