aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2018-01-18 23:23:56 -0500
committerAllan Wang <me@allanwang.ca>2018-01-20 16:42:37 -0500
commitd766100c297bc094491de150f24c04719ffa8f4e (patch)
treeb69863dace2fcb45d5fd25c276ca450e07305c44 /app/src/main/kotlin
parent78b3cc41e4c9f8d141ad46ee75e476fa2d177f19 (diff)
downloadfrost-d766100c297bc094491de150f24c04719ffa8f4e.tar.gz
frost-d766100c297bc094491de150f24c04719ffa8f4e.tar.bz2
frost-d766100c297bc094491de150f24c04719ffa8f4e.zip
Enhancement/speed up (#650)
* Revert back to m.facebook * Add initial speedup * Update theme * Fix link press for event status * Move web states to fb const * Fix images and email * Fix up flyweight for requests * Ensure frost request is synchronous * Prepare diff utils * Improve speed and fix blank overlay * Update comments * Add debugger and fix searchview * Theme discover pages. Resolves #654 * Fix duplicate reload * Fix image loading * Update changelog * Update tests * Rename test Update dependencies Update gitignore
Diffstat (limited to 'app/src/main/kotlin')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt68
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt32
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt7
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt7
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt14
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt14
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt27
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt11
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt70
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/rx/RxFlyweight.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt33
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt57
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/WebStates.kt11
31 files changed, 296 insertions, 159 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
index 67785a60..a2c48bd5 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
@@ -29,6 +29,7 @@ import com.raizlabs.android.dbflow.config.FlowConfig
import com.raizlabs.android.dbflow.config.FlowManager
import com.raizlabs.android.dbflow.runtime.ContentResolverNotifier
import io.fabric.sdk.android.Fabric
+import io.reactivex.plugins.RxJavaPlugins
import java.util.*
import kotlin.reflect.KClass
@@ -111,6 +112,11 @@ class FrostApp : Application() {
L.d { "Activity ${activity.localClassName} created" }
}
})
+
+ RxJavaPlugins.setErrorHandler {
+ L.e(it) { "RxJava error" }
+ }
+
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt
index 3a01e05b..743ac474 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt
@@ -67,8 +67,6 @@ import com.pitchedapps.frost.utils.iab.IabMain
import com.pitchedapps.frost.views.BadgedIcon
import com.pitchedapps.frost.views.FrostVideoViewer
import com.pitchedapps.frost.views.FrostViewPager
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
/**
* Created by Allan Wang on 20/12/17.
@@ -94,10 +92,30 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
override var searchView: SearchView? = null
private val searchViewCache = mutableMapOf<String, List<SearchItem>>()
- private lateinit var controlWebview: WebView
+ private var controlWebview: WebView? = null
- override fun onCreate(savedInstanceState: Bundle?) {
+ override final fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val start = System.currentTimeMillis()
+ setFrameContentView(Prefs.mainActivityLayout.layoutRes)
+ setFrostColors {
+ toolbar(toolbar)
+ themeWindow = false
+ header(appBar)
+ background(viewPager)
+ }
+ L.i { "Main AAA ${System.currentTimeMillis() - start} ms" }
+ setSupportActionBar(toolbar)
+ adapter = SectionsPagerAdapter(loadFbTabs())
+ viewPager.adapter = adapter
+ viewPager.offscreenPageLimit = TAB_COUNT
+ L.i { "Main BBB ${System.currentTimeMillis() - start} ms" }
+ L.i { "Main CCC ${System.currentTimeMillis() - start} ms" }
+ tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor())
+ onNestedCreate(savedInstanceState)
+ L.i { "Main finished loading UI in ${System.currentTimeMillis() - start} ms" }
+ controlWebview = WebView(this)
+ onCreateBilling()
if (BuildConfig.VERSION_CODE > Prefs.versionCode) {
Prefs.prevVersionCode = Prefs.versionCode
Prefs.versionCode = BuildConfig.VERSION_CODE
@@ -111,24 +129,15 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
"Frost id" to Prefs.frostId)
}
}
- controlWebview = WebView(this)
- setFrameContentView(Prefs.mainActivityLayout.layoutRes)
- setSupportActionBar(toolbar)
- adapter = SectionsPagerAdapter(loadFbTabs())
- viewPager.adapter = adapter
- viewPager.offscreenPageLimit = TAB_COUNT
setupDrawer(savedInstanceState)
-
- setFrostColors {
- toolbar(toolbar)
- themeWindow = false
- header(appBar)
- background(viewPager)
- }
- tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor())
- onCreateBilling()
+ L.i { "Main started in ${System.currentTimeMillis() - start} ms" }
}
+ /**
+ * Injector to handle creation for sub classes
+ */
+ protected abstract fun onNestedCreate(savedInstanceState: Bundle?)
+
fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) {
(0 until tabs.tabCount).asSequence().forEach { i ->
@@ -263,19 +272,20 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
R.id.action_search to GoogleMaterial.Icon.gmd_search)
searchViewBindIfNull {
bindSearchView(menu, R.id.action_search, Prefs.iconColor) {
- textCallback = { query, _ ->
+ textCallback = { query, searchView ->
val results = searchViewCache[query]
if (results != null)
- runOnUiThread { searchView?.results = results }
- else
- doAsync {
- val data = SearchParser.query(FbCookie.webCookie, query)?.data?.results ?: return@doAsync
+ searchView.results = results
+ else {
+ val data = SearchParser.query(FbCookie.webCookie, query)?.data?.results
+ if (data != null) {
val items = data.map(FrostSearch::toSearchItem).toMutableList()
if (items.isNotEmpty())
items.add(SearchItem("${FbItem._SEARCH.url}?q=$query", string(R.string.show_all_results), iicon = null))
searchViewCache.put(query, items)
- uiThread { searchView?.results = items }
+ searchView.results = items
}
+ }
}
textDebounceInterval = 300
searchCallback = { query, _ -> launchWebOverlay("${FbItem._SEARCH.url}/?q=$query"); true }
@@ -351,12 +361,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
override fun onResume() {
super.onResume()
- FbCookie.switchBackUser { }
- controlWebview.resumeTimers()
+ FbCookie.switchBackUser {}
+ controlWebview?.resumeTimers()
}
override fun onPause() {
- controlWebview.pauseTimers()
+ controlWebview?.pauseTimers()
L.v { "Pause main web timers" }
super.onPause()
}
@@ -371,7 +381,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
override fun onDestroy() {
onDestroyBilling()
- controlWebview.destroy()
+ controlWebview?.destroy()
super.onDestroy()
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
index cdde8311..e563ff8a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
@@ -88,15 +88,18 @@ class ImageActivity : KauBaseActivity() {
val IMAGE_URL: String by lazy { intent.getStringExtra(ARG_IMAGE_URL).trim('"') }
- val TEXT: String? by lazy { intent.getStringExtra(ARG_TEXT) }
+ private val TEXT: String? by lazy { intent.getStringExtra(ARG_TEXT) }
// a unique image identifier based on the id (if it exists), and its hash
- val IMAGE_HASH: String by lazy { "${Math.abs(FB_IMAGE_ID_MATCHER.find(IMAGE_URL)[1]?.hashCode() ?: 0)}_${Math.abs(IMAGE_URL.hashCode())}" }
+ private val IMAGE_HASH: String by lazy {
+ "${Math.abs(FB_IMAGE_ID_MATCHER.find(IMAGE_URL)[1]?.hashCode() ?: 0)}_${Math.abs(IMAGE_URL.hashCode())}"
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
intent?.extras ?: return finish()
L.i { "Displaying image" }
+ L.v { "Displaying image $IMAGE_URL" }
val layout = if (!TEXT.isNullOrBlank()) R.layout.activity_image else R.layout.activity_image_textless
setContentView(layout)
container.setBackgroundColor(Prefs.bgColor.withMinAlpha(222))
@@ -116,6 +119,8 @@ class ImageActivity : KauBaseActivity() {
override fun onImageLoadError(e: Exception?) {
errorRef = e
e.logFrostAnswers("Image load error")
+ L.e { "Failed to load image $IMAGE_URL" }
+ tempFile?.delete()
fabAction = FabStates.ERROR
}
})
@@ -125,9 +130,12 @@ class ImageActivity : KauBaseActivity() {
doAsync({
L.e(it) { "Failed to load image $IMAGE_HASH" }
errorRef = it
+ runOnUiThread { progress.fadeOut() }
+ tempFile?.delete()
fabAction = FabStates.ERROR
}) {
loadImage { file ->
+ uiThread { progress.fadeOut() }
if (file == null) {
fabAction = FabStates.ERROR
return@loadImage
@@ -153,11 +161,7 @@ class ImageActivity : KauBaseActivity() {
L.d { "Loading from local cache ${local.absolutePath}" }
return callback(local)
}
- val response = Request.Builder()
- .url(IMAGE_URL)
- .get()
- .call()
- .execute()
+ val response = getImageResponse()
if (!response.isSuccessful) {
L.e { "Unsuccessful response for image" }
@@ -198,14 +202,16 @@ class ImageActivity : KauBaseActivity() {
return File.createTempFile(imageFileName, IMG_EXTENSION, frostDir)
}
+ private fun getImageResponse() = Request.Builder()
+ .url(IMAGE_URL)
+ .get()
+ .call()
+ .execute()
+
@Throws(IOException::class)
private fun downloadImageTo(file: File) {
- val body = Request.Builder()
- .url(IMAGE_URL)
- .get()
- .call()
- .execute()
- .body() ?: throw IOException("Failed to retrieve image body")
+ val body = getImageResponse().body()
+ ?: throw IOException("Failed to retrieve image body")
body.byteStream().use { input ->
file.outputStream().use { output ->
input.copyTo(output)
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 e672d7d8..d153b5d9 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
@@ -17,10 +17,9 @@ class MainActivity : BaseMainActivity() {
var lastPosition = -1
val headerBadgeObservable = PublishSubject.create<String>()!!
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setupViewPager()
+ override fun onNestedCreate(savedInstanceState: Bundle?) {
setupTabs()
+ setupViewPager()
}
private fun setupViewPager() {
@@ -63,7 +62,8 @@ class MainActivity : BaseMainActivity() {
(tab.customView as BadgedIcon).badgeText = null
}
})
- headerBadgeObservable.throttleFirst(15, TimeUnit.SECONDS).subscribeOn(Schedulers.newThread())
+ headerBadgeObservable.throttleFirst(15, TimeUnit.SECONDS)
+ .subscribeOn(Schedulers.newThread())
.map { Jsoup.parse(it) }
.filter { it.select("[data-sigil=count]").size >= 0 } //ensure headers exist
.map {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
index 1bd3ede2..9fe4ae22 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
@@ -84,7 +84,7 @@ class FrostWebActivity : WebOverlayActivityBase(false) {
} else {
L.i { "Sharing url through overlay" }
L._i { "Url: $url" }
- intent.putExtra(ARG_URL, "${FB_URL_BASE}/sharer/sharer.php?u=$url")
+ intent.putExtra(ARG_URL, "${FB_URL_BASE}sharer/sharer.php?u=$url")
return true
}
}
@@ -154,7 +154,6 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
coordinator.setBackgroundColor(Prefs.bgColor.withAlpha(255))
content.bind(this)
- web.reloadBase(true)
content.titleObservable
.observeOn(AndroidSchedulers.mainThread())
@@ -175,7 +174,7 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
}
FrostRunnable.propagate(this, intent)
- L.e { "Done propagation" }
+ L.v { "Done propagation" }
kauSwipeOnCreate {
if (!Prefs.overlayFullScreenSwipe) edgeSize = 20.dpToPx
@@ -190,8 +189,8 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
*/
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
- val newUrl = (intent.getStringExtra(ARG_URL) ?: intent.dataString ?: return).formattedFbUrl
L.d { "New intent" }
+ val newUrl = (intent.getStringExtra(ARG_URL) ?: intent.dataString)?.formattedFbUrl ?: return
if (baseUrl != newUrl) {
this.intent = intent
content.baseUrl = newUrl
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt
index 117a1d36..ab3a40ec 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt
@@ -76,8 +76,13 @@ interface FrostContentParent : DynamicUiContract {
* Animate toggles between the fancy ripple and the basic fade
* The cycle only starts on the first load since
* there may have been another process when this is registered
+ *
+ * Returns true to proceed with load
+ * In some cases when the url has not changed,
+ * it may not be advisable to proceed with the load
+ * For those cases, we will return false to stop it
*/
- fun registerTransition(animate: Boolean)
+ fun registerTransition(urlChanged: Boolean, animate: Boolean): Boolean
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
index d98241f1..f1bf55b1 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
@@ -6,10 +6,22 @@ package com.pitchedapps.frost.facebook
const val FACEBOOK_COM = "facebook.com"
const val HTTPS_FACEBOOK_COM = "https://$FACEBOOK_COM"
-const val FB_URL_BASE = "https://touch.$FACEBOOK_COM/"
+const val FB_URL_BASE = "https://m.$FACEBOOK_COM/"
fun PROFILE_PICTURE_URL(id: Long) = "https://graph.facebook.com/$id/picture?type=large"
const val FB_LOGIN_URL = "${FB_URL_BASE}login"
const val USER_AGENT_FULL = "Mozilla/5.0 (Linux; Android 4.4.2; en-us; SAMSUNG SM-G900T Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36"
const val USER_AGENT_BASIC = "Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.4633 Mobile Safari/537.10+"
-const val USER_AGENT_MESSENGER = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" \ No newline at end of file
+const val USER_AGENT_MESSENGER = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"
+
+/**
+ * Animation transition delay, just to ensure that the styles
+ * have properly set in
+ */
+const val WEB_LOAD_DELAY = 50L
+/**
+ * Additional delay for transition when called from commit.
+ * Note that transitions are also called from onFinish, so this value
+ * will never make a load slower than it is
+ */
+const val WEB_COMMIT_LOAD_DELAY = 200L \ No newline at end of file
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 ccb23b93..e5f0b8fe 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt
@@ -33,12 +33,14 @@ object FbCookie {
L.d { "Setting cookie" }
val cookies = cookie.split(";").map { Pair(it, SingleSubject.create<Boolean>()) }
cookies.forEach { (cookie, callback) -> setCookie(FB_URL_BASE, cookie, { callback.onSuccess(it) }) }
- Observable.zip<Boolean, Unit>(cookies.map { (_, callback) -> callback.toObservable() }, {}).subscribeOn(AndroidSchedulers.mainThread()).subscribe {
- callback?.invoke()
- L.d { "Cookies set" }
- L._d { cookie }
- flush()
- }
+ Observable.zip<Boolean, Unit>(cookies.map { (_, callback) -> callback.toObservable() }, {})
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ callback?.invoke()
+ L.d { "Cookies set" }
+ L._d { cookie }
+ flush()
+ }
}
}
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
index 76eb2c0c..cbb6087a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
@@ -32,8 +32,10 @@ class FbUrlFormatter(url: String) {
fun clean(url: String): String {
if (url.isBlank()) return ""
var cleanedUrl = url
+ if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2)
+ val urlRef = cleanedUrl
discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "", true) }
- val changed = cleanedUrl != url
+ val changed = cleanedUrl != urlRef
converter.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
try {
cleanedUrl = URLDecoder.decode(cleanedUrl, StandardCharsets.UTF_8.name())
@@ -54,7 +56,6 @@ class FbUrlFormatter(url: String) {
discardableQueries.forEach { queries.remove(it) }
//final cleanup
misc.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
- if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2)
if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1)
cleanedUrl = cleanedUrl.replaceFirst(".facebook.com//", ".facebook.com/") //sometimes we are given a bad url
L.v { "Formatted url from $url to $cleanedUrl" }
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
index 3ca37bb4..45545336 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
@@ -21,6 +21,7 @@ private class RxAuth : RxFlyweight<String, Long, RequestAuth>() {
System.currentTimeMillis() - cond < 3600000 // valid for an hour
override fun cache(input: String) = System.currentTimeMillis()
+
}
private val auth = RxAuth()
@@ -32,13 +33,12 @@ private val auth = RxAuth()
*/
fun String?.fbRequest(fail: () -> Unit = {}, action: RequestAuth.() -> Unit) {
if (this == null) return fail()
- auth(this).subscribe { a: RequestAuth?, _ ->
- if (a?.isValid == true)
- a.action()
- else {
- L.e { "Failed auth for ${hashCode()}" }
- fail()
- }
+ try {
+ val auth = auth(this).blockingGet()
+ auth.action()
+ } catch (e: Exception) {
+ L.e { "Failed auth for ${hashCode()}: ${e.message}" }
+ fail()
}
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
index fa78bbfa..094e7fc9 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
@@ -67,7 +67,7 @@ class HdImageLoading : ModelLoader<HdImageMaybe, InputStream> {
override fun buildLoadData(model: HdImageMaybe,
width: Int,
height: Int,
- options: Options?): ModelLoader.LoadData<InputStream>? =
+ options: Options): ModelLoader.LoadData<InputStream>? =
if (!model.isValid) null
else ModelLoader.LoadData(ObjectKey(model), HdImageFetcher(model))
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
index 72b6e0a4..6f327662 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt
@@ -117,7 +117,7 @@ data class MenuHeader(val id: String? = null,
) : this(id, header, visible ?: emptyList(), all ?: emptyList())
override val isValid: Boolean
- get() = header != null
+ get() = !header.isNullOrBlank()
}
@JsonIgnoreProperties(ignoreUnknown = true)
@@ -139,7 +139,7 @@ data class MenuItem(val id: String? = null,
) : this(id, name, pic?.formattedFbUrl, url?.formattedFbUrl, count ?: 0, countDetails)
override val isValid: Boolean
- get() = name != null && url != null
+ get() = !name.isNullOrBlank() && !url.isNullOrBlank()
}
data class MenuFooter(val data: List<MenuFooterItem> = emptyList(),
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt
index 8bc2c1fe..d1f9eaf4 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt
@@ -12,6 +12,7 @@ import ca.allanwang.kau.utils.visible
import ca.allanwang.kau.utils.withAlpha
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
+import com.mikepenz.fastadapter.commons.utils.DiffCallback
import com.pitchedapps.frost.R
import com.pitchedapps.frost.glide.FrostGlide
import com.pitchedapps.frost.glide.GlideApp
@@ -40,15 +41,31 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) : KauI
true
}
}
+
+ //todo see if necessary
+ val DIFF: DiffCallback<NotificationIItem> by lazy(::Diff)
+ }
+
+ private class Diff : DiffCallback<NotificationIItem> {
+
+ override fun areItemsTheSame(oldItem: NotificationIItem, newItem: NotificationIItem) =
+ oldItem.notification.id == newItem.notification.id
+
+ override fun areContentsTheSame(oldItem: NotificationIItem, newItem: NotificationIItem) =
+ oldItem.notification == newItem.notification
+
+ override fun getChangePayload(oldItem: NotificationIItem, oldItemPosition: Int, newItem: NotificationIItem, newItemPosition: Int): Any? {
+ return newItem
+ }
}
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<NotificationIItem>(itemView) {
- val frame: ViewGroup by bindView(R.id.item_frame)
- val avatar: ImageView by bindView(R.id.item_avatar)
- val content: TextView by bindView(R.id.item_content)
- val date: TextView by bindView(R.id.item_date)
- val thumbnail: ImageView by bindView(R.id.item_thumbnail)
+ private val frame: ViewGroup by bindView(R.id.item_frame)
+ private val avatar: ImageView by bindView(R.id.item_avatar)
+ private val content: TextView by bindView(R.id.item_content)
+ private val date: TextView by bindView(R.id.item_date)
+ private val thumbnail: ImageView by bindView(R.id.item_thumbnail)
private val glide
get() = GlideApp.with(itemView)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt
index 015b5e2d..033e482f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt
@@ -51,7 +51,7 @@ enum class CssAssets(val folder: String = "themes") : InjectorContract {
}
}
- override fun inject(webView: WebView, callback: ((String) -> Unit)?) {
+ override fun inject(webView: WebView, callback: (() -> Unit)?) {
injector(webView.context).inject(webView, callback)
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt
index de268360..4a390d9a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt
@@ -9,11 +9,10 @@ import android.webkit.WebView
*/
enum class CssHider(vararg val items: String) : InjectorContract {
CORE("[data-sigil=m_login_upsell]", "role=progressbar"),
- HEADER("#header[data-sigil=MTopBlueBarHeader]", "#header-notices", "[data-sigil*=m-promo-jewel-header]"),
- ADS(
- "article[data-xt*=sponsor]",
- "article[data-store*=sponsor]"
- ),
+ HEADER("#header", "[data-sigil=MTopBlueBarHeader]",
+ "#header-notices", "[data-sigil*=m-promo-jewel-header]"),
+ ADS("article[data-xt*=sponsor]",
+ "article[data-store*=sponsor]"),
PEOPLE_YOU_MAY_KNOW("article._d2r"),
SUGGESTED_GROUPS("article[data-ft*=\"ei\":]"),
COMPOSER("#MComposer"),
@@ -26,7 +25,7 @@ enum class CssHider(vararg val items: String) : InjectorContract {
.single(name).build()
}
- override fun inject(webView: WebView, callback: ((String) -> Unit)?) {
+ override fun inject(webView: WebView, callback: (() -> Unit)?) {
injector.inject(webView, callback)
}
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 3fa03bcc..53eb9ede 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt
@@ -23,5 +23,6 @@ enum class JsActions(body: String) : InjectorContract {
val function = "!function(){$body}();"
- override fun inject(webView: WebView, callback: ((String) -> Unit)?) = JsInjector(function).inject(webView, callback)
+ override fun inject(webView: WebView, callback: (() -> Unit)?) =
+ JsInjector(function).inject(webView, callback)
} \ No newline at end of file
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 74ab7d37..9ff129d4 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt
@@ -12,7 +12,8 @@ import java.util.*
* The enum name must match the css file name
*/
enum class JsAssets : InjectorContract {
- MENU, MENU_DEBUG, CLICK_A, CONTEXT_A, MEDIA, HEADER_BADGES, TEXTAREA_LISTENER, NOTIF_MSG
+ MENU, MENU_DEBUG, CLICK_A, CONTEXT_A, MEDIA, HEADER_BADGES, TEXTAREA_LISTENER, NOTIF_MSG,
+ DOCUMENT_WATCHER
;
var file = "${name.toLowerCase(Locale.CANADA)}.js"
@@ -26,7 +27,7 @@ enum class JsAssets : InjectorContract {
}
}
- override fun inject(webView: WebView, callback: ((String) -> Unit)?) {
+ override fun inject(webView: WebView, callback: (() -> Unit)?) {
injector(webView.context).inject(webView, callback)
}
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 dd73209f..2d067e44 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt
@@ -3,7 +3,7 @@ package com.pitchedapps.frost.injectors
import android.webkit.WebView
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.web.FrostWebViewClient
-import io.reactivex.Observable
+import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.subjects.SingleSubject
import org.apache.commons.text.StringEscapeUtils
@@ -26,30 +26,35 @@ class JsBuilder {
}
fun single(tag: String): JsBuilder {
- this.tag = tag
+ this.tag = "_frost_${tag.toLowerCase(Locale.CANADA)}"
return this
}
fun build() = JsInjector(toString())
override fun toString(): String {
- val builder = StringBuilder().append("!function(){")
- if (css.isNotBlank()) {
- val cssMin = css.replace(Regex("\\s*\n\\s*"), "")
- builder.append("var a=document.createElement('style');a.innerHTML='$cssMin';document.head.appendChild(a);")
+ val tag = this.tag
+ val builder = StringBuilder().apply {
+ append("!function(){")
+ if (css.isNotBlank()) {
+ val cssMin = css.replace(Regex("\\s*\n\\s*"), "")
+ append("var a=document.createElement('style');")
+ append("a.innerHTML='$cssMin';")
+ if (tag != null) append("a.id='$tag';")
+ append("document.head.appendChild(a);")
+ }
+ if (js.isNotBlank())
+ append(js)
}
- if (js.isNotBlank())
- builder.append(js)
var content = builder.append("}()").toString()
- if (tag != null) content = singleInjector(tag!!, content)
+ if (tag != null) content = singleInjector(tag, content)
return content
}
private fun singleInjector(tag: String, content: String) = StringBuilder().apply {
- val name = "_frost_${tag.toLowerCase(Locale.CANADA)}"
- append("if (!window.hasOwnProperty(\"$name\")) {")
- append("console.log(\"Registering $name\");")
- append("window.$name = true;")
+ append("if (!window.hasOwnProperty(\"$tag\")) {")
+ append("console.log(\"Registering $tag\");")
+ append("window.$tag = true;")
append(content)
append("}")
}.toString()
@@ -60,7 +65,7 @@ class JsBuilder {
*/
interface InjectorContract {
fun inject(webView: WebView) = inject(webView, null)
- fun inject(webView: WebView, callback: ((String) -> Unit)?)
+ fun inject(webView: WebView, callback: (() -> Unit)?)
/**
* Toggle the injector (usually through Prefs
* If false, will fallback to an empty action
@@ -71,24 +76,39 @@ interface InjectorContract {
/**
* Helper method to inject multiple functions simultaneously with a single callback
*/
-fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Array<String>) -> Unit) = {}) {
+fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Int) -> Unit)? = null) {
val validInjectors = injectors.filter { it != JsActions.EMPTY }
- if (validInjectors.isEmpty()) return callback(emptyArray())
- val observables = Array(validInjectors.size, { SingleSubject.create<String>() })
- L.d { "Injecting ${observables.size} items" }
- Observable.zip<String, Array<String>>(observables.map(SingleSubject<String>::toObservable),
- { it.map(Any::toString).toTypedArray() })
- .subscribeOn(AndroidSchedulers.mainThread()).subscribe({ callback(it) })
- (0 until validInjectors.size).forEach { i -> validInjectors[i].inject(this, { observables[i].onSuccess(it) }) }
+ if (validInjectors.isEmpty()) {
+ callback?.invoke(0)
+ return
+ }
+ L.d { "Injecting ${validInjectors.size} items" }
+ if (callback == null) {
+ validInjectors.forEach { it.inject(this) }
+ return
+ }
+ val observables = Array(validInjectors.size, { SingleSubject.create<Unit>() })
+ Single.zip<Unit, Int>(observables.asList(), { it.size })
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe { res, _ ->
+ callback(res)
+ }
+ (0 until validInjectors.size).forEach { i ->
+ validInjectors[i].inject(this, {
+ observables[i].onSuccess(Unit)
+ })
+ }
}
-fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract, callback: ((Array<String>) -> Unit) = {}) = web.jsInject(*injectors, callback = callback)
+fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract,
+ callback: ((Int) -> Unit)? = null)
+ = web.jsInject(*injectors, callback = callback)
/**
* Wrapper class to convert a function into an injector
*/
class JsInjector(val function: String) : InjectorContract {
- override fun inject(webView: WebView, callback: ((String) -> Unit)?) {
- webView.evaluateJavascript(function, { value -> callback?.invoke(value) })
+ override fun inject(webView: WebView, callback: (() -> Unit)?) {
+ webView.evaluateJavascript(function, { callback?.invoke() })
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/rx/RxFlyweight.kt b/app/src/main/kotlin/com/pitchedapps/frost/rx/RxFlyweight.kt
index 159a9bf2..5d2c78ed 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/rx/RxFlyweight.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/rx/RxFlyweight.kt
@@ -73,7 +73,7 @@ abstract class RxFlyweight<in T : Any, C : Any, R : Any> {
*/
open protected fun createNewSource(input: T): Single<R> =
Single.fromCallable { call(input) }
- .timeout(20, TimeUnit.SECONDS)
+ .timeout(15, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
fun reset() {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
index 0bacd6f1..286bce4f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
@@ -87,7 +87,7 @@ class FrostNotificationTarget(val context: Context,
val builder: NotificationCompat.Builder
) : SimpleTarget<Bitmap>(40.dpToPx, 40.dpToPx) {
- override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>) {
+ override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
builder.setLargeIcon(resource)
NotificationManagerCompat.from(context).notify(notifTag, notifId, builder.build())
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt
index d5311fc0..3fd5ee7b 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt
@@ -168,14 +168,16 @@ class FrostRequestService : JobService() {
L.eThrow("Launched ${this::class.java.simpleName} without command")
return false
}
- val now = System.currentTimeMillis()
future = doAsync {
+ val now = System.currentTimeMillis()
+ var failed = true
cookie.fbRequest {
L.d { "Requesting frost service for ${command.name}" }
command.invoke(this, bundle)
+ failed = false
}
L.d {
- "Finished frost service for ${command.name} in ${System.currentTimeMillis()} - now} ms"
+ "${if (failed) "Failed" else "Finished"} frost service for ${command.name} in ${System.currentTimeMillis() - now} ms"
}
jobFinished(params, false)
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt
index 3b37d1c3..df0fdef2 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt
@@ -1,5 +1,6 @@
package com.pitchedapps.frost.settings
+import android.content.Context
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import ca.allanwang.kau.utils.materialDialog
import ca.allanwang.kau.utils.startActivityForResult
@@ -11,6 +12,7 @@ import com.pitchedapps.frost.activities.SettingsActivity.Companion.ACTIVITY_REQU
import com.pitchedapps.frost.debugger.OfflineWebsite
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
+import com.pitchedapps.frost.parsers.FrostParser
import com.pitchedapps.frost.parsers.MessageParser
import com.pitchedapps.frost.parsers.NotifParser
import com.pitchedapps.frost.parsers.SearchParser
@@ -21,6 +23,7 @@ import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import org.jetbrains.anko.uiThread
import java.io.File
+import java.util.concurrent.Future
/**
* Created by Allan Wang on 2017-06-30.
@@ -49,7 +52,29 @@ fun SettingsActivity.getDebugPrefs(): KPrefAdapterBuilder.() -> Unit = {
items(parsers.map { string(it.nameRes) })
itemsCallback { dialog, _, position, _ ->
dialog.dismiss()
- // todo add debugging
+ val parser = parsers[position]
+ var attempt: Future<Unit>? = null
+ val loading = materialDialog {
+ content(parser.nameRes)
+ progress(true, 100)
+ negativeText(R.string.kau_cancel)
+ onNegative { dialog, _ ->
+ attempt?.cancel(true)
+ dialog.dismiss()
+ }
+ canceledOnTouchOutside(false)
+ }
+
+ attempt = loading.doAsync({
+ createEmail(parser, "Error: ${it.message}")
+ }) {
+ val data = parser.parse(FbCookie.webCookie)
+ uiThread {
+ if (it.isCancelled) return@uiThread
+ it.dismiss()
+ createEmail(parser, data?.data)
+ }
+ }
}
}
@@ -57,6 +82,12 @@ fun SettingsActivity.getDebugPrefs(): KPrefAdapterBuilder.() -> Unit = {
}
}
+private fun Context.createEmail(parser: FrostParser<*>, content: Any?) =
+ sendFrostEmail("${string(R.string.debug_report)}: ${parser::class.java.simpleName}") {
+ addItem("Url", parser.url)
+ addItem("Contents", "$content")
+ }
+
private const val ZIP_NAME = "debug"
fun SettingsActivity.sendDebug(urlOrig: String) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
index 486fbae1..6a3b80ca 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
@@ -245,7 +245,7 @@ fun Context.resolveActivityForUri(uri: Uri): Boolean {
* [true] if url contains [FACEBOOK_COM]
*/
inline val String?.isFacebookUrl
- get() = this != null && contains(FACEBOOK_COM)
+ get() = this != null && (contains(FACEBOOK_COM) || contains("fbcdn.net"))
/**
* [true] if url is a video and can be accepted by VideoViewer
@@ -257,8 +257,7 @@ inline val String.isVideoUrl
* [true] if url is or redirects to an explicit facebook image
*/
inline val String.isImageUrl
- get() = (contains("fbcdn.net") && (contains(".png") || contains(".jpg")))
- || contains("/photo/view_full_size")
+ get() = contains("fbcdn.net") && (contains(".png") || contains(".jpg"))
/**
* [true] if url can be displayed in a different webview
@@ -274,6 +273,7 @@ inline val String?.isIndependent: Boolean
val dependentSegments = arrayOf(
"photoset_token", "direct_action_execute", "messages/?pageNum", "sharer.php",
+ "events/permalink",
/**
* Editing images
*/
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt
index 00f99878..f45e842d 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt
@@ -176,7 +176,7 @@ class IabMain : IabBinder() {
val load = weakRef.get()?.loadOwnedPurchasesFromGoogle() ?: false
L.d { "IAB main load from google $load" }
onComplete {
- if (!(weakRef.get()?.isPurchased(FROST_PRO) ?: false)) {
+ if (weakRef.get()?.isPurchased(FROST_PRO) != true) {
if (Prefs.pro) activity.playStoreNoLongerPro()
} else {
if (!Prefs.pro) activity.playStoreFoundPro()
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt
index 809b6090..43653382 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt
@@ -14,8 +14,9 @@ import com.pitchedapps.frost.contracts.FrostContentCore
import com.pitchedapps.frost.contracts.FrostContentParent
import com.pitchedapps.frost.contracts.MainActivityContract
import com.pitchedapps.frost.facebook.FbItem
+import com.pitchedapps.frost.facebook.WEB_LOAD_DELAY
+import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
-import com.pitchedapps.frost.web.WEB_LOAD_DELAY
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.BehaviorSubject
@@ -84,10 +85,13 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
else
progress.progress = it
}
- refreshObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
- refresh.isRefreshing = it
- refresh.isEnabled = true
- }
+
+ refreshObservable
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ refresh.isRefreshing = it
+ refresh.isEnabled = true
+ }
refresh.setOnRefreshListener { coreView.reload(true) }
reloadThemeSelf()
@@ -127,25 +131,42 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
core.destroy()
}
+ private var dispose: Disposable? = null
+ private var transitionStart: Long = -1
+
/**
* Hook onto the refresh observable for one cycle
* Animate toggles between the fancy ripple and the basic fade
* The cycle only starts on the first load since there may have been another process when this is registered
*/
- override fun registerTransition(animate: Boolean) {
+ override fun registerTransition(urlChanged: Boolean, animate: Boolean): Boolean {
+ if (!urlChanged && dispose != null) {
+ L.v { "Consuming url load" }
+ return false // still in progress; do not bother with load
+ }
+ L.v { "Registered transition" }
with(coreView) {
- var dispose: Disposable? = null
- var loading = false
- dispose = refreshObservable.subscribeOn(AndroidSchedulers.mainThread()).subscribe {
- if (it) {
- loading = true
- if (isVisible) fadeOut(duration = 200L)
- } else if (loading) {
- dispose?.dispose()
- if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
- else fadeIn(duration = 100L)
- }
- }
+ var loading = dispose != null
+ dispose?.dispose()
+ dispose = refreshObservable
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ if (it) {
+ loading = true
+ transitionStart = System.currentTimeMillis()
+ clearAnimation()
+ if (isVisible)
+ fadeOut(duration = 200L)
+ } else if (loading) {
+ loading = false
+ if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
+ else fadeIn(duration = 200L, offset = WEB_LOAD_DELAY)
+ L.v { "Transition loaded in ${System.currentTimeMillis() - transitionStart} ms" }
+ dispose?.dispose()
+ dispose = null
+ }
+ }
}
+ return true
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
index 1dc401a5..ce2b8d46 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
@@ -85,7 +85,7 @@ class FrostVideoViewer @JvmOverloads constructor(
fun setVideo(url: String, repeat: Boolean = false) {
val formattedUrl = url.formattedFbUrl
L.d { "Load video; repeat: $repeat" }
- L._d { "Url\t\t\t$url\nformatted\t$formattedUrl" }
+ L._d { "Video Url\t\t\t$url\nformatted\t$formattedUrl" }
animate().alpha(1f).setDuration(FrostVideoView.ANIMATION_DURATION).start()
video.setVideoURI(Uri.parse(formattedUrl))
video.repeat = repeat
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt
index c35f1bb8..7972e813 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt
@@ -27,8 +27,8 @@ class FrostWebView @JvmOverloads constructor(
FrostContentCore {
override fun reload(animate: Boolean) {
- parent.registerTransition(animate)
- super.reload()
+ if (parent.registerTransition(false, animate))
+ super.reload()
}
override lateinit var parent: FrostContentParent
@@ -80,8 +80,8 @@ class FrostWebView @JvmOverloads constructor(
fun loadUrl(url: String?, animate: Boolean) {
if (url == null) return
- parent.registerTransition(animate)
- super.loadUrl(url)
+ if (parent.registerTransition(this.url != url, animate))
+ super.loadUrl(url)
}
override fun reloadBase(animate: Boolean) {
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 8c016e5c..4afdd8d2 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
@@ -30,14 +30,13 @@ class FrostChromeClient(web: FrostWebView) : WebChromeClient() {
private val context = web.context!!
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
- if (consoleBlacklist.any { consoleMessage.message().contains(it) }) return true
L.v { "Chrome Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}" }
return true
}
override fun onReceivedTitle(view: WebView, title: String) {
super.onReceivedTitle(view, title)
- if (title.contains("http") || this.title.value == title) return
+ if (title.startsWith("http") || this.title.value == title) return
this.title.onNext(title)
}
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 9264ea52..d735fd50 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
@@ -17,6 +17,7 @@ class FrostJSI(val web: FrostWebView) {
private val context = web.context
private val activity = context as? MainActivity
private val header: Subject<String>? = activity?.headerBadgeObservable
+ private val refresh: Subject<Boolean> = web.parent.refreshObservable
private val cookies = activity?.cookies() ?: arrayListOf()
/**
@@ -89,6 +90,11 @@ class FrostJSI(val web: FrostWebView) {
}
@JavascriptInterface
+ fun isReady() {
+ refresh.onNext(false)
+ }
+
+ @JavascriptInterface
fun handleHtml(html: String?) {
html ?: return
web.post { web.frostWebClient.handleHtml(html) }
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 d0f7d490..f1b30795 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
@@ -73,7 +73,10 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
CssHider.SUGGESTED_GROUPS.maybe(!Prefs.showSuggestedGroups && IS_FROST_PRO),
Prefs.themeInjector,
CssHider.NON_RECENT.maybe((web.url?.contains("?sk=h_chr") ?: false)
- && Prefs.aggressiveRecents))
+ && Prefs.aggressiveRecents),
+ JsAssets.DOCUMENT_WATCHER)
+ else
+ refresh.onNext(false)
}
override fun onPageFinished(view: WebView, url: String?) {
@@ -142,7 +145,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
return true
}
if (path.startsWith("/composer/")) return launchRequest(request)
- if (url.contains("scontent-sea1-1.xx.fbcdn.net") && (path.endsWith(".jpg") || path.endsWith(".png")))
+ if (url.isImageUrl)
return launchImage(url)
if (Prefs.linksInDefaultApp && view.context.resolveActivityForUri(request.url)) return true
return super.shouldOverrideUrlLoading(view, request)
@@ -150,6 +153,11 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
}
+private const val EMIT_THEME = 0b1
+private const val EMIT_ID = 0b10
+private const val EMIT_COMPLETE = EMIT_THEME or EMIT_ID
+private const val EMIT_FINISH = 0
+
/**
* Client variant for the menu view
*/
@@ -171,7 +179,9 @@ class FrostWebViewClientMenu(web: FrostWebView) : FrostWebViewClient(web) {
override fun emit(flag: Int) {
super.emit(flag)
- super.injectAndFinish()
+ when (flag) {
+ EMIT_FINISH -> super.injectAndFinish()
+ }
}
override fun onPageFinishedActions(url: String) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/WebStates.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/WebStates.kt
deleted file mode 100644
index d07e67df..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/WebStates.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.pitchedapps.frost.web
-
-/**
- * Created by Allan Wang on 2017-08-08.
- *
- * Global variables that are define states or constants for web contents
- */
-const val WEB_LOAD_DELAY = 50L
-//var shouldLoadImages = false
-
-val consoleBlacklist = setOf("edge-chat") \ No newline at end of file