aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2019-02-05 23:16:29 -0500
committerGitHub <noreply@github.com>2019-02-05 23:16:29 -0500
commit8e3bfc168009c8682c4f6191d655f3ca10ae9f21 (patch)
treecf8419a06e193c08622ead5e6854b995a5eeba77 /app/src/main/kotlin
parent83fb36f666fbb934b74b5f763b8ffb2e56ca7761 (diff)
parentddfc310fde5f50ba52ef930287449c2e08faaca8 (diff)
downloadfrost-8e3bfc168009c8682c4f6191d655f3ca10ae9f21.tar.gz
frost-8e3bfc168009c8682c4f6191d655f3ca10ae9f21.tar.bz2
frost-8e3bfc168009c8682c4f6191d655f3ca10ae9f21.zip
Merge pull request #1334 from AllanWang/fix/offline-crash
Fix/offline crash
Diffstat (limited to 'app/src/main/kotlin')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/kotlin/Flyweight.kt92
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt44
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt5
9 files changed, 92 insertions, 74 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
index 8d849bff..f4c1244f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
@@ -27,6 +27,7 @@ import ca.allanwang.kau.about.LibraryIItem
import ca.allanwang.kau.adapters.FastItemThemedAdapter
import ca.allanwang.kau.adapters.ThemableIItem
import ca.allanwang.kau.adapters.ThemableIItemDelegate
+import ca.allanwang.kau.logging.KL
import ca.allanwang.kau.utils.bindView
import ca.allanwang.kau.utils.dimenPixelSize
import ca.allanwang.kau.utils.resolveDrawable
@@ -79,7 +80,8 @@ class AboutActivity : AboutActivityBase(null, {
)
val l = libs.prepareLibraries(this, include, null, false, true, true)
-// l.forEach { KL.d{"Lib ${it.definedName}"} }
+ if (BuildConfig.DEBUG)
+ l.forEach { KL.d { "Lib ${it.definedName}" } }
return l
}
@@ -155,7 +157,7 @@ class AboutActivity : AboutActivityBase(null, {
val c = itemView.context
val size = c.dimenPixelSize(R.dimen.kau_avatar_bounds)
images = arrayOf<Pair<IIcon, () -> Unit>>(
- GoogleMaterial.Icon.gmd_arrow_downward to { c.startLink(R.string.github_downloads_url) },
+ GoogleMaterial.Icon.gmd_file_download to { c.startLink(R.string.github_downloads_url) },
CommunityMaterial.Icon2.cmd_reddit to { c.startLink(R.string.reddit_url) },
CommunityMaterial.Icon.cmd_github_circle to { c.startLink(R.string.github_url) },
CommunityMaterial.Icon2.cmd_slack to { c.startLink(R.string.slack_url) },
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt
index 6cf6f41b..9ee34ab7 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt
@@ -46,7 +46,7 @@ enum class FbItem(
FEED_TOP_STORIES(R.string.top_stories, GoogleMaterial.Icon.gmd_star, "home.php?sk=h_nor"),
FRIENDS(R.string.friends, GoogleMaterial.Icon.gmd_person_add, "friends/center/requests"),
GROUPS(R.string.groups, GoogleMaterial.Icon.gmd_group, "groups"),
- MARKETPLACE(R.string.marketplace, CommunityMaterial.Icon2.cmd_home_currency_usd, "marketplace"),
+ MARKETPLACE(R.string.marketplace, GoogleMaterial.Icon.gmd_store, "marketplace"),
MENU(R.string.menu, GoogleMaterial.Icon.gmd_menu, "settings", ::MenuFragment),
MESSAGES(R.string.messages, MaterialDesignIconic.Icon.gmi_comments, "messages"),
NOTES(R.string.notes, CommunityMaterial.Icon2.cmd_note, "notes"),
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt
index 9f26f3f7..37af690b 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt
@@ -30,6 +30,7 @@ import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.frostJsoup
import com.pitchedapps.frost.views.FrostRecyclerView
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
/**
@@ -53,7 +54,7 @@ abstract class RecyclerFragment<T, Item : IItem<*, *>> : BaseFragment(), Recycle
val data = try {
reloadImpl(progress)
} catch (e: Exception) {
- L.e(e) { "Recycler reload fail" }
+ L.e(e) { "Recycler reload fail $baseUrl" }
null
}
withMainContext {
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 0caeda1a..a466feec 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt
@@ -19,6 +19,7 @@ package com.pitchedapps.frost.injectors
import android.content.Context
import android.graphics.Color
import android.webkit.WebView
+import androidx.annotation.VisibleForTesting
import ca.allanwang.kau.kotlin.lazyContext
import ca.allanwang.kau.utils.adjustAlpha
import ca.allanwang.kau.utils.colorToBackground
@@ -43,7 +44,8 @@ enum class CssAssets(val folder: String = THEME_FOLDER) : InjectorContract {
MATERIAL_LIGHT, MATERIAL_DARK, MATERIAL_AMOLED, MATERIAL_GLASS, CUSTOM, ROUND_ICONS("components")
;
- private val file = "${name.toLowerCase(Locale.CANADA)}.css"
+ @VisibleForTesting
+ internal val file = "${name.toLowerCase(Locale.CANADA)}.css"
/**
* Note that while this can be loaded from any thread, it is typically done through [load]
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 4b1bde43..e0be7977 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsAssets.kt
@@ -18,6 +18,7 @@ package com.pitchedapps.frost.injectors
import android.content.Context
import android.webkit.WebView
+import androidx.annotation.VisibleForTesting
import ca.allanwang.kau.kotlin.lazyContext
import com.pitchedapps.frost.utils.L
import kotlinx.coroutines.Dispatchers
@@ -32,11 +33,12 @@ import java.util.Locale
* The enum name must match the css file name
*/
enum class JsAssets : InjectorContract {
- MENU, MENU_DEBUG, CLICK_A, CONTEXT_A, MEDIA, HEADER_BADGES, HEADER_HIDER, TEXTAREA_LISTENER, NOTIF_MSG,
+ MENU, CLICK_A, CONTEXT_A, MEDIA, HEADER_BADGES, HEADER_HIDER, TEXTAREA_LISTENER, NOTIF_MSG,
DOCUMENT_WATCHER
;
- private val file = "${name.toLowerCase(Locale.CANADA)}.js"
+ @VisibleForTesting
+ internal val file = "${name.toLowerCase(Locale.CANADA)}.js"
private val injector = lazyContext {
try {
val content = it.assets.open("js/$file").bufferedReader().use(BufferedReader::readText)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/kotlin/Flyweight.kt b/app/src/main/kotlin/com/pitchedapps/frost/kotlin/Flyweight.kt
index 914ce151..56acfc11 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/kotlin/Flyweight.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/kotlin/Flyweight.kt
@@ -16,11 +16,14 @@
*/
package com.pitchedapps.frost.kotlin
+import com.pitchedapps.frost.utils.L
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
@@ -64,57 +67,60 @@ class Flyweight<K, V>(
completeExceptionally(result.exceptionOrNull()!!)
}
+ private val errHandler = CoroutineExceptionHandler { _, throwable -> L.d { "FbAuth failed ${throwable.message}" } }
+
init {
- job = scope.launch(Dispatchers.IO) {
- launch {
- while (isActive) {
- select<Unit> {
- /*
- * New request received. Continuation should be fulfilled eventually
- */
- actionChannel.onReceive { (key, completable) ->
- val lastUpdate = conditionMap[key]
- val lastResult = resultMap[key]
- // Valid value, retrieved within acceptable time
- if (lastResult != null && lastUpdate != null && System.currentTimeMillis() - lastUpdate < maxAge) {
- completable.completeWith(lastResult)
- } else {
- val valueRequestPending = key in pendingMap
- pendingMap.getOrPut(key) { mutableListOf() }.add(completable)
- if (!valueRequestPending)
- fulfill(key)
+ job =
+ scope.launch(Dispatchers.IO + SupervisorJob() + errHandler) {
+ launch {
+ while (isActive) {
+ select<Unit> {
+ /*
+ * New request received. Continuation should be fulfilled eventually
+ */
+ actionChannel.onReceive { (key, completable) ->
+ val lastUpdate = conditionMap[key]
+ val lastResult = resultMap[key]
+ // Valid value, retrieved within acceptable time
+ if (lastResult != null && lastUpdate != null && System.currentTimeMillis() - lastUpdate < maxAge) {
+ completable.completeWith(lastResult)
+ } else {
+ val valueRequestPending = key in pendingMap
+ pendingMap.getOrPut(key) { mutableListOf() }.add(completable)
+ if (!valueRequestPending)
+ fulfill(key)
+ }
}
- }
- /*
- * Invalidator received. Existing result associated with key should not be used.
- * Note that any unfulfilled request and future requests should still operate, but with a new value.
- */
- invalidatorChannel.onReceive { key ->
- if (key !in resultMap) {
- // Nothing to invalidate.
- // If pending requests exist, they are already in the process of being updated.
- return@onReceive
+ /*
+ * Invalidator received. Existing result associated with key should not be used.
+ * Note that any unfulfilled request and future requests should still operate, but with a new value.
+ */
+ invalidatorChannel.onReceive { key ->
+ if (key !in resultMap) {
+ // Nothing to invalidate.
+ // If pending requests exist, they are already in the process of being updated.
+ return@onReceive
+ }
+ conditionMap.remove(key)
+ resultMap.remove(key)
+ if (pendingMap[key]?.isNotEmpty() == true)
+ // Refetch value for pending requests
+ fulfill(key)
}
- conditionMap.remove(key)
- resultMap.remove(key)
- if (pendingMap[key]?.isNotEmpty() == true)
- // Refetch value for pending requests
- fulfill(key)
- }
- /*
- * Value request fulfilled. Should now fulfill pending requests
- */
- receiverChannel.onReceive { (key, result) ->
- conditionMap[key] = System.currentTimeMillis()
- resultMap[key] = result
- pendingMap.remove(key)?.forEach {
- it.completeWith(result)
+ /*
+ * Value request fulfilled. Should now fulfill pending requests
+ */
+ receiverChannel.onReceive { (key, result) ->
+ conditionMap[key] = System.currentTimeMillis()
+ resultMap[key] = result
+ pendingMap.remove(key)?.forEach {
+ it.completeWith(result)
+ }
}
}
}
}
}
- }
}
/*
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt
index 62330e4d..fbaa4574 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt
@@ -20,7 +20,6 @@ import android.content.Context
import ca.allanwang.kau.utils.copyToClipboard
import ca.allanwang.kau.utils.shareText
import ca.allanwang.kau.utils.string
-import ca.allanwang.kau.utils.toast
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.facebook.formattedFbUrl
@@ -29,19 +28,19 @@ import com.pitchedapps.frost.facebook.formattedFbUrl
* Created by Allan Wang on 2017-07-07.
*/
fun Context.showWebContextMenu(wc: WebContext) {
-
- var title = wc.url
+ if (wc.isEmpty) return
+ var title = wc.url ?: string(R.string.menu)
title = title.substring(title.indexOf("m/") + 1) //just so if defaults to 0 in case it's not .com/
if (title.length > 100) title = title.substring(0, 100) + '\u2026'
+ val menuItems = WebContextType.values
+ .filter { it.constraint(wc) }
+
materialDialogThemed {
title(title)
- items(WebContextType.values.map {
- if (it == WebContextType.COPY_TEXT && wc.text == null) return@map null
- this@showWebContextMenu.string(it.textId)
- }.filterNotNull())
+ items(menuItems.map { string(it.textId) })
itemsCallback { _, _, position, _ ->
- WebContextType[position].onClick(this@showWebContextMenu, wc)
+ menuItems[position].onClick(this@showWebContextMenu, wc)
}
dismissListener {
//showing the dialog interrupts the touch down event, so we must ensure that the viewpager's swipe is enabled
@@ -50,18 +49,23 @@ fun Context.showWebContextMenu(wc: WebContext) {
}
}
-class WebContext(val unformattedUrl: String, val text: String?) {
- val url = unformattedUrl.formattedFbUrl
+class WebContext(val unformattedUrl: String?, val text: String?) {
+ val url: String? = unformattedUrl?.formattedFbUrl
+ inline val hasUrl get() = unformattedUrl != null
+ inline val hasText get() = text != null
+ inline val isEmpty get() = !hasUrl && !hasText
}
-enum class WebContextType(val textId: Int, val onClick: (c: Context, wc: WebContext) -> Unit) {
- OPEN_LINK(R.string.open_link, { c, wc -> c.launchWebOverlay(wc.unformattedUrl) }),
- COPY_LINK(R.string.copy_link, { c, wc -> c.copyToClipboard(wc.url) }),
- COPY_TEXT(
- R.string.copy_text,
- { c, wc -> if (wc.text != null) c.copyToClipboard(wc.text) else c.toast(R.string.no_text) }),
- SHARE_LINK(R.string.share_link, { c, wc -> c.shareText(wc.url) }),
- DEBUG_LINK(R.string.debug_link, { c, wc ->
+enum class WebContextType(
+ val textId: Int,
+ val constraint: (wc: WebContext) -> Boolean,
+ val onClick: (c: Context, wc: WebContext) -> Unit
+) {
+ OPEN_LINK(R.string.open_link, { it.hasUrl }, { c, wc -> c.launchWebOverlay(wc.unformattedUrl!!) }),
+ COPY_LINK(R.string.copy_link, { it.hasUrl }, { c, wc -> c.copyToClipboard(wc.url) }),
+ COPY_TEXT(R.string.copy_text, { it.hasText }, { c, wc -> c.copyToClipboard(wc.text) }),
+ SHARE_LINK(R.string.share_link, { it.hasUrl }, { c, wc -> c.shareText(wc.url) }),
+ DEBUG_LINK(R.string.debug_link, { it.hasUrl }, { c, wc ->
c.materialDialogThemed {
title(R.string.debug_link)
content(R.string.debug_link_desc)
@@ -69,8 +73,8 @@ enum class WebContextType(val textId: Int, val onClick: (c: Context, wc: WebCont
onPositive { _, _ ->
c.sendFrostEmail(R.string.debug_link_subject) {
message = c.string(R.string.debug_link_content)
- addItem("Unformatted url", wc.unformattedUrl)
- addItem("Formatted url", wc.url)
+ addItem("Unformatted url", wc.unformattedUrl!!)
+ addItem("Formatted url", wc.url!!)
}
}
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt
index 860bf36c..ce7437a7 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostRecyclerView.kt
@@ -27,8 +27,10 @@ import com.pitchedapps.frost.contracts.FrostContentContainer
import com.pitchedapps.frost.contracts.FrostContentCore
import com.pitchedapps.frost.contracts.FrostContentParent
import com.pitchedapps.frost.fragments.RecyclerContentContract
+import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
/**
@@ -74,7 +76,7 @@ class FrostRecyclerView @JvmOverloads constructor(
if (Prefs.animate) fadeOut(onFinish = onReloadClear)
scope.launch {
parent.refreshChannel.offer(true)
- val loaded = recyclerContract.reload { parent.progressChannel.offer(it) }
+ recyclerContract.reload { parent.progressChannel.offer(it) }
parent.progressChannel.offer(100)
parent.refreshChannel.offer(false)
if (Prefs.animate) circularReveal()
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 19d16e87..50a5e2e1 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
@@ -76,10 +76,9 @@ class FrostJSI(val web: FrostWebView) {
}
@JavascriptInterface
- fun contextMenu(url: String, text: String?) {
- if (!text.isIndependent) return
+ fun contextMenu(url: String?, text: String?) {
//url will be formatted through webcontext
- web.post { context.showWebContextMenu(WebContext(url, text)) }
+ web.post { context.showWebContextMenu(WebContext(url.takeIf { it.isIndependent }, text)) }
}
/**