diff options
Diffstat (limited to 'app/src/main')
16 files changed, 247 insertions, 130 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4135ab59..5758f38c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> - <!--<uses-permission android:name="android.permission.VIBRATE" />--> + <uses-permission android:name="android.permission.VIBRATE" /> <!--<uses-permission android:name="android.permission.USE_FINGERPRINT" />--> <uses-permission android:name="com.android.vending.BILLING" /> diff --git a/app/src/main/assets/js/context_a.js b/app/src/main/assets/js/context_a.js index c7601764..689c6f0d 100644 --- a/app/src/main/assets/js/context_a.js +++ b/app/src/main/assets/js/context_a.js @@ -25,8 +25,9 @@ if (!window.hasOwnProperty('frost_context_a')) { if (!url) return; var text = element.parentNode.innerText; - //check if image item exists - var image = element.parentNode.querySelector('[style*="background-image: url("]'); + //check if image item exists, first in children and then in parent + var image = element.querySelector('[style*="background-image: url("]'); + if (!image) image = element.parentNode.querySelector('[style*="background-image: url("]'); if (image) { var imageUrl = window.getComputedStyle(image, null).backgroundImage.slice(5, -2); console.log('Context image', imageUrl); diff --git a/app/src/main/assets/js/context_a.min.js b/app/src/main/assets/js/context_a.min.js index 5c5f033a..97799c33 100644 --- a/app/src/main/assets/js/context_a.min.js +++ b/app/src/main/assets/js/context_a.min.js @@ -9,8 +9,9 @@ longClick=!0 "A"!==t.tagName&&(t=t.parentNode),"A"===t.tagName&&"#"!==t.getAttribute("href"))){ var o=t.getAttribute("href") ;if(!o)return -;var n=t.parentNode.innerText,r=t.parentNode.querySelector('[style*="background-image: url("]') -;if(r){ +;var n=t.parentNode.innerText,r=t.querySelector('[style*="background-image: url("]') +;if(r||(r=t.parentNode.querySelector('[style*="background-image: url("]')), +r){ var a=window.getComputedStyle(r,null).backgroundImage.slice(5,-2) ;console.log("Context image",a), "undefined"!=typeof Frost&&Frost.loadImage(a,n) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index ba76e594..1227fd6b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -55,7 +55,7 @@ import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.utils.iab.validatePro import com.pitchedapps.frost.views.BadgedIcon import com.pitchedapps.frost.views.FrostViewPager -import com.pitchedapps.frost.web.FrostWebViewSearch +import com.pitchedapps.frost.web.SearchWebView import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers @@ -63,7 +63,7 @@ import io.reactivex.subjects.PublishSubject import org.jsoup.Jsoup import java.util.concurrent.TimeUnit -class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, +class MainActivity : BaseActivity(), SearchWebView.SearchContract, ActivityWebContract, FileChooserContract by FileChooserDelegate() { lateinit var adapter: SectionsPagerAdapter @@ -78,13 +78,13 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, var webFragmentObservable = PublishSubject.create<Int>()!! var lastPosition = -1 val headerBadgeObservable = PublishSubject.create<String>() - var hiddenSearchView: FrostWebViewSearch? = null + var hiddenSearchView: SearchWebView? = null var firstLoadFinished = false set(value) { L.d("First fragment load has finished") field = value if (value && hiddenSearchView == null) { - hiddenSearchView = FrostWebViewSearch(this, this) + hiddenSearchView = SearchWebView(this, this) } } var searchView: SearchView? = null @@ -354,7 +354,7 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, R.id.action_settings to GoogleMaterial.Icon.gmd_settings, R.id.action_search to GoogleMaterial.Icon.gmd_search) if (Prefs.searchBar) { - if (firstLoadFinished && hiddenSearchView == null) hiddenSearchView = FrostWebViewSearch(this, this) + if (firstLoadFinished && hiddenSearchView == null) hiddenSearchView = SearchWebView(this, this) if (searchView == null) searchView = bindSearchView(menu, R.id.action_search, Prefs.iconColor) { textObserver = { observable, _ -> diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt index 875f1c49..3b0125be 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt @@ -94,12 +94,14 @@ object FbCookie { * When coming back to the main app, switch back to our original account before continuing */ fun switchBackUser(callback: () -> Unit) { - if (Prefs.prevId != -1L && Prefs.prevId != Prefs.userId) { - switchUser(Prefs.prevId) { - L.d("Switch back user", "${Prefs.userId} to ${Prefs.prevId}") + if (Prefs.prevId == -1L) return callback() + val prevId = Prefs.prevId + Prefs.prevId = -1L + if (prevId != Prefs.userId) { + switchUser(prevId) { + L.d("Switch back user", "${Prefs.userId} to ${prevId}") callback() } } else callback() - if (Prefs.prevId != -1L) Prefs.prevId = -1L } }
\ 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 4c44c1bf..de270948 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt @@ -15,6 +15,7 @@ enum class JsActions(body: String) : InjectorContract { */ LOGIN_CHECK("document.getElementById('signup-button')&&Frost.loadLogin();"), BASE_HREF("document.write(\"<base href='$FB_URL_BASE'/>\");"), + GET_MESSAGES("setTimeout(function(){Frost.handleHtml(document.getElementById('threadlist_rows').outerHtml)},1000)"), EMPTY(""); val function = "!function(){$body}();" diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt index 38282bf7..ad977d1a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt @@ -17,7 +17,9 @@ import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.frostAnswersCustom +import com.pitchedapps.frost.web.MessageWebView import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread import org.jsoup.Jsoup import org.jsoup.nodes.Element import java.util.concurrent.Future @@ -45,17 +47,29 @@ class NotificationService : JobService() { return false } + override fun onStartJob(params: JobParameters?): Boolean { future = doAsync { if (Prefs.notificationAllAccounts) { - loadFbCookiesSync().forEach { - data -> - fetchNotifications(data) - } + val cookies = loadFbCookiesSync() + cookies.forEach { fetchGeneralNotifications(it) } +// if (Prefs.notificationsInstantMessages) { +// Prefs.prevId = Prefs.userId +// uiThread { +// val messageWebView = MessageWebView(this@NotificationService, params) +// cookies.forEach { messageWebView.request(it) } +// } +// return@doAsync +// } } else { val currentCookie = loadFbCookie(Prefs.userId) - if (currentCookie != null) - fetchNotifications(currentCookie) + if (currentCookie != null) { + fetchGeneralNotifications(currentCookie) +// if (Prefs.notificationsInstantMessages) { +// uiThread { MessageWebView(this@NotificationService, params).request(currentCookie) } +// return@doAsync +// } + } } L.d("Finished notifications") jobFinished(params, false) @@ -69,12 +83,6 @@ class NotificationService : JobService() { return null } - fun fetchNotifications(data: CookieModel) { - fetchGeneralNotifications(data) -// fetchMessageNotifications(data) - debugNotification("Hello") - } - fun fetchGeneralNotifications(data: CookieModel) { L.i("Notif fetch for $data") val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).userAgent(USER_AGENT_BASIC).get() @@ -96,7 +104,8 @@ class NotificationService : JobService() { newLatestEpoch = notif.timestamp notifCount++ } - if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epoch = newLatestEpoch).update() + if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epoch = newLatestEpoch).save() + L.d("Notif new latest epoch ${lastNotificationTime(data.id).epoch}") frostAnswersCustom("Notifications") { putCustomAttribute("Type", "General") putCustomAttribute("Count", notifCount) @@ -120,10 +129,9 @@ class NotificationService : JobService() { return NotificationContent(data, notifId.toInt(), a.attr("href"), null, text, epoch, pUrl) } - fun fetchMessageNotifications(data: CookieModel) { - if (!Prefs.notificationsInstantMessages) return + fun fetchMessageNotifications(data: CookieModel, content: String) { L.i("Notif IM fetch for $data") - val doc = Jsoup.connect(FbTab.MESSAGES.url).cookie(FACEBOOK_COM, data.cookie).userAgent(USER_AGENT_BASIC).get() + val doc = Jsoup.parseBodyFragment(content) val unreadNotifications = (doc.getElementById("threadlist_rows") ?: return L.eThrow("Notification messages not found")).getElementsByClass("aclb") var notifCount = 0 L.d("IM notif count ${unreadNotifications.size}") @@ -146,7 +154,8 @@ class NotificationService : JobService() { newLatestEpoch = notif.timestamp notifCount++ } -// if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epochIm = newLatestEpoch).update() + if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epochIm = newLatestEpoch).save() + L.d("Notif new latest im epoch ${lastNotificationTime(data.id).epochIm}") frostAnswersCustom("Notifications") { putCustomAttribute("Type", "Message") putCustomAttribute("Count", notifCount) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt deleted file mode 100644 index 09241254..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.pitchedapps.frost.web - -import android.webkit.WebResourceRequest -import android.webkit.WebResourceResponse -import android.webkit.WebView -import android.webkit.WebViewClient - -/** - * Created by Allan Wang on 2017-07-13. - */ -open class BaseWebViewClient : WebViewClient() { - - override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? - = shouldFrostInterceptRequest(view, request) - -}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt index 4df6d6a7..b8ba0d1d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt @@ -14,6 +14,19 @@ import io.reactivex.subjects.Subject /** * Created by Allan Wang on 2017-05-31. + * + * Collection of chrome clients + */ + +/** + * Nothing more than a client without logging + */ +class QuietChromeClient : WebChromeClient() { + override fun onConsoleMessage(consoleMessage: ConsoleMessage) = true +} + +/** + * The default chrome client */ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() { 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 45dc83aa..3f2891d0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt @@ -1,17 +1,12 @@ package com.pitchedapps.frost.web -import android.graphics.Bitmap.CompressFormat import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import ca.allanwang.kau.utils.use -import com.pitchedapps.frost.utils.GlideApp import com.pitchedapps.frost.utils.L -import com.pitchedapps.frost.utils.Prefs import okhttp3.HttpUrl import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.InputStream /** @@ -62,6 +57,23 @@ fun shouldFrostInterceptRequest(view: WebView, request: WebResourceRequest): Web return null } +/** + * Wrapper to ensure that null exceptions are not reached + */ +fun WebResourceRequest.query(action: (url: String) -> Boolean): Boolean { + return action(url?.path ?: return false) +} + +/** + * Generic filter passthrough + * If Resource is already nonnull, pass it, otherwise check if filter is met and override the response accordingly + */ +fun WebResourceResponse?.filter(request: WebResourceRequest, filter: (url: String) -> Boolean): WebResourceResponse? + = this ?: if (request.query { filter(it) }) blankResource else null + fun WebResourceResponse?.filterCss(request: WebResourceRequest): WebResourceResponse? - = this ?: if (request.url.path.endsWith(".css")) blankResource else null + = filter(request) { it.endsWith(".css") } + +fun WebResourceResponse?.filterImage(request: WebResourceRequest): WebResourceResponse? + = filter(request) { it.contains(".jpg") || it.contains(".png") } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt deleted file mode 100644 index 10648e73..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.pitchedapps.frost.web - -import android.webkit.WebView -import com.pitchedapps.frost.facebook.FB_URL_BASE -import com.pitchedapps.frost.injectors.JsAssets -import com.pitchedapps.frost.injectors.jsInject - -/** - * Created by Allan Wang on 2017-05-31. - */ -class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(webCore) { - - private val String.shouldInjectMenu - get() = when (removePrefix(FB_URL_BASE)) { - "settings", - "settings#", - "settings#!/settings?soft=bookmarks" -> true - else -> false - } - - override fun onPageFinished(view: WebView, url: String) { - super.onPageFinished(view, url) - if (url.shouldInjectMenu) jsInject(JsAssets.MENU) - } - - override fun emit(flag: Int) { - super.emit(flag) - super.injectAndFinish() - } - - override fun onPageFinishedActions(url: String) { - if (!url.shouldInjectMenu) injectAndFinish() - } - -}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt index 5b2b4bfd..3e6ddd06 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt @@ -3,11 +3,14 @@ package com.pitchedapps.frost.web import android.content.Context import android.graphics.Bitmap import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse import android.webkit.WebView +import android.webkit.WebViewClient import com.pitchedapps.frost.activities.LoginActivity import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.activities.SelectorActivity 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.* import com.pitchedapps.frost.utils.* @@ -15,6 +18,23 @@ import io.reactivex.subjects.Subject /** * Created by Allan Wang on 2017-05-31. + * + * Collection of webview clients + */ + +/** + * The base of all webview clients + * Used to ensure that resources are properly intercepted + */ +open class BaseWebViewClient : WebViewClient() { + + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? + = shouldFrostInterceptRequest(view, request) + +} + +/** + * The default webview client */ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient() { @@ -96,9 +116,57 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient return super.shouldOverrideUrlLoading(view, request) } -// override fun onPageCommitVisible(view: WebView?, url: String?) { -// L.d("ASDF PCV") -// super.onPageCommitVisible(view, url) -// } +} + +/** + * Client variant for the menu view + */ +class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(webCore) { + + private val String.shouldInjectMenu + get() = when (removePrefix(FB_URL_BASE)) { + "settings", + "settings#", + "settings#!/settings?soft=bookmarks" -> true + else -> false + } + + override fun onPageFinished(view: WebView, url: String) { + super.onPageFinished(view, url) + if (url.shouldInjectMenu) jsInject(JsAssets.MENU) + } + + override fun emit(flag: Int) { + super.emit(flag) + super.injectAndFinish() + } + + override fun onPageFinishedActions(url: String) { + if (!url.shouldInjectMenu) injectAndFinish() + } +} + +/** + * Headless client that injects content after a page load + * The JSI is meant to handle everything else + */ +class HeadlessWebViewClient(val tag: String, val postInjection: InjectorContract) : BaseWebViewClient() { + + override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + L.d("Headless Page $tag Started", url) + } + + override fun onPageFinished(view: WebView, url: String) { + super.onPageFinished(view, url) + L.d("Headless Page $tag Finished", url) + postInjection.inject(view) + } + + /** + * In addition to general filtration, we will also strip away css and images + */ + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? + = super.shouldInterceptRequest(view, request).filterCss(request).filterImage(request) }
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/MessageWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/MessageWebView.kt new file mode 100644 index 00000000..0f3a12b6 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/MessageWebView.kt @@ -0,0 +1,81 @@ +package com.pitchedapps.frost.web + +import android.annotation.SuppressLint +import android.app.job.JobParameters +import android.webkit.JavascriptInterface +import android.webkit.WebView +import ca.allanwang.kau.utils.gone +import com.pitchedapps.frost.dbflow.CookieModel +import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.facebook.FbTab +import com.pitchedapps.frost.facebook.USER_AGENT_BASIC +import com.pitchedapps.frost.injectors.JsActions +import com.pitchedapps.frost.services.NotificationService +import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.frostAnswersCustom +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.runOnUiThread + +@SuppressLint("ViewConstructor") +/** + * Created by Allan Wang on 2017-07-17. + * + * Bare boned headless view made solely to extract conversation info + */ +class MessageWebView(val service: NotificationService, val params: JobParameters?) : WebView(service) { + + init { + gone() + setupWebview() + } + + @SuppressLint("SetJavaScriptEnabled") + private fun setupWebview() { + settings.javaScriptEnabled = true + settings.userAgentString = USER_AGENT_BASIC + webViewClient = HeadlessWebViewClient("MessageNotifs", JsActions.GET_MESSAGES) + webChromeClient = QuietChromeClient() + addJavascriptInterface(MessageJSI(), "Frost") + } + + private val startTime = System.currentTimeMillis() + private val endTime: Long by lazy { System.currentTimeMillis() } + private var inProgress = false + private val pendingRequests: MutableList<CookieModel> = mutableListOf() + private lateinit var data: CookieModel + + fun request(data: CookieModel) { + pendingRequests.add(data) + if (inProgress) return + inProgress = true + load(data) + } + + private fun load(data: CookieModel) { + L.d("Notif retrieving messages", data.toString()) + this.data = data + FbCookie.setWebCookie(data.cookie) { context.runOnUiThread { L.d("Notif messages load"); loadUrl(FbTab.MESSAGES.url) } } + } + + inner class MessageJSI { + @JavascriptInterface + fun handleHtml(html: String) { + L.d("Notif messages received", data.toString()) + doAsync { service.fetchMessageNotifications(data, html) } + pendingRequests.remove(data) + if (pendingRequests.isEmpty()) { + val time = endTime - startTime + L.d("Notif messages finished $time") + frostAnswersCustom("Notifications") { + putCustomAttribute("Message retrieval duration", time) + } + post { destroy() } + service.jobFinished(params, false) + service.future = null + } else { + load(pendingRequests.first()) + } + } + } + +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/SearchWebView.kt index bcadf32a..325d0333 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/SearchWebView.kt @@ -3,14 +3,14 @@ package com.pitchedapps.frost.web import android.annotation.SuppressLint import android.content.Context import android.view.View -import android.webkit.* +import android.webkit.JavascriptInterface +import android.webkit.WebView import ca.allanwang.kau.searchview.SearchItem import ca.allanwang.kau.utils.gone import com.pitchedapps.frost.facebook.FbTab import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.injectors.JsAssets import com.pitchedapps.frost.injectors.JsBuilder -import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import io.reactivex.schedulers.Schedulers @@ -23,11 +23,10 @@ import java.util.concurrent.TimeUnit /** * Created by Allan Wang on 2017-06-25. * - * A bare bone search view meant solely to extract data from the web - * This should be hidden + * A bare bone headless search view meant solely to extract search results from the web * Having a single webview allows us to avoid loading the whole page with each query */ -class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebView(context) { +class SearchWebView(context: Context, val contract: SearchContract) : WebView(context) { val searchSubject = PublishSubject.create<String>() @@ -50,12 +49,11 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi } @SuppressLint("SetJavaScriptEnabled") - fun setupWebview() { + private fun setupWebview() { settings.javaScriptEnabled = true settings.userAgentString = USER_AGENT_BASIC - setLayerType(View.LAYER_TYPE_HARDWARE, null) - webViewClient = SearchWebViewClient() - webChromeClient = SearchChromeClient() + webViewClient = HeadlessWebViewClient("Search", JsAssets.SEARCH) + webChromeClient = QuietChromeClient() addJavascriptInterface(SearchJSI(), "Frost") searchSubject.debounce(300, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.newThread()) .map { @@ -105,29 +103,6 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi JsBuilder().js("var e=document.getElementById('main-search-input');if(e){e.value='$input';var n=new Event('input',{bubbles:!0,cancelable:!0});e.dispatchEvent(n),e.dispatchEvent(new Event('focus'))}else console.log('Input field not found');").build().inject(this) } - /** - * Created by Allan Wang on 2017-05-31. - * - * Barebones client that does what [FrostWebViewSearch] needs - */ - inner class SearchWebViewClient : BaseWebViewClient() { - - override fun onPageFinished(view: WebView, url: String) { - super.onPageFinished(view, url) - L.i("Search Page finished $url") - view.jsInject(JsAssets.SEARCH) - } - - override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? - = super.shouldInterceptRequest(view, request).filterCss(request) - } - - class SearchChromeClient : WebChromeClient() { - - //mute console - override fun onConsoleMessage(consoleMessage: ConsoleMessage) = true - } - inner class SearchJSI { @JavascriptInterface fun handleHtml(html: String) { diff --git a/app/src/main/play/en-CA/listing/fulldescription b/app/src/main/play/en-CA/listing/fulldescription index 61c94833..32cf0503 100644 --- a/app/src/main/play/en-CA/listing/fulldescription +++ b/app/src/main/play/en-CA/listing/fulldescription @@ -3,6 +3,7 @@ While being a web wrapper, Frost contains many unique and native features such a • True multi user interactions - More than just an option in a settings menu, Frost's account switcher is right in the drawer. You are one tap away from switching accounts, and everything refreshes on the switch so that you can view other accounts instantaneously. Furthermore, the notification service will fetch notifications from all accounts, and will let you know which account has the new notification. • Better multitasking - Frost contains an overlaying web browser that can be drawn on top of your foreground task. Open links and notifications with a full screen view, then swipe away to get back to your previous task. +• Contextual awareness - Frost integrates additional features via long presses. Need to copy a block of text or share a link? Long press the text. Need to zoom into an image or download it? Long press the image! • Material Design - Built for lollipop and up, Frost focuses strongly on a good UI, and embraces material transitions and dimensions. • Complete theme engine - Frost contains very comprehensive themes that customize all components of the app. Frost is also the only app to support transparent themes. • Fully opened - Nothing speaks for privacy more than being open sourced. Frost is proud to be one of those apps, and can be found on github (Link in the app's about section) @@ -11,7 +12,9 @@ Permissions used and why: • Internet, Network State, Wifi State - Frost fetches the pages from Facebook's mobile website. It also needs the network state so as to limit internet usage when you are on a metered network. • Receive Boot Completed - Frost notifications persist on reboot, and need this permission to be added each time. -• Read external storage - Needed to upload photos in a new status +• Read/write external storage - Needed to upload photos in a new status and save photos when prompted +• Vibrate - Needed to vibrate phone for notifications; this can be toggled in the settings +• Billing - For purchasing pro and unlocking all of Frost's features • That's it! No privacy intrusion and no extra demands. Permissions NOT used and why: diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml index cf56eaeb..cb1f5f64 100644 --- a/app/src/main/res/xml/changelog.xml +++ b/app/src/main/res/xml/changelog.xml @@ -9,16 +9,18 @@ --> <version title="Beta Updates" /> - <item text="Update IAB helper" /> - <item text="Create image viewing and downloading; long press any image!" /> - <item text="Start filtering out unnecessary loads" /> - <item text="" /> - <item text="" /> + <item text="Fix notification duplicates" /> + <item text="Fix long pressing album images" /> <item text="" /> <item text="" /> <item text="" /> <item text="" /> + <version title="v1.4" /> + <item text="Update IAB helper" /> + <item text="Create image viewing and downloading; long press any image!" /> + <item text="Start filtering out unnecessary loads" /> + <version title="v1.3" /> <item text="Create toggle for notifications only from primary account" /> <item text="Micro string optimizations" /> |