aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-17 12:38:42 -0700
committerGitHub <noreply@github.com>2017-07-17 12:38:42 -0700
commite4679b1663fa78a99c6c8225e454595c6c6f4e38 (patch)
treea777ee782207c89d6e9243399471b9ad936736d0
parent7bf93ea74b00ab7e8ceedac2aac6ad08fdf099ab (diff)
downloadfrost-e4679b1663fa78a99c6c8225e454595c6c6f4e38.tar.gz
frost-e4679b1663fa78a99c6c8225e454595c6c6f4e38.tar.bz2
frost-e4679b1663fa78a99c6c8225e454595c6c6f4e38.zip
Fix notifications and long press for albums (#69)v1.3.2
* Allow for album images to be viewed * Update listing info * Web refractoring * Test message notifications * Fix notifications and context press
-rw-r--r--README.md3
-rw-r--r--app/build.gradle1
-rw-r--r--app/src/main/AndroidManifest.xml2
-rw-r--r--app/src/main/assets/js/context_a.js5
-rw-r--r--app/src/main/assets/js/context_a.min.js5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt10
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt10
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt43
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt)13
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt24
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt35
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt)76
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/MessageWebView.kt81
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/SearchWebView.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt)39
-rw-r--r--app/src/main/play/en-CA/listing/fulldescription5
-rw-r--r--app/src/main/res/xml/changelog.xml12
18 files changed, 250 insertions, 131 deletions
diff --git a/README.md b/README.md
index 7f6203da..0a347c2b 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ It contains many features, including:
* Full theming across all activities
* Overlaying browser to read posts and get right back to your previous task
* Extensive notification support, with bundling, filtering, battery friendly scheduling, icons, and multi user support
-* Context menu from any link through long press
+* Context menu from any link via long press
+* Native image viewer and downloader via long press
* Reactive based loading
* The transparency of open sourced development
diff --git a/app/build.gradle b/app/build.gradle
index a354d2e9..8729ed69 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -71,6 +71,7 @@ android {
versionNameSuffix "-debug"
resValue "string", "app_name", "Frost Debug"
resValue "string", "frost_web", "Frost Web Debug"
+ ext.enableCrashlytics = false
}
releaseTest {
minifyEnabled true
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" />