diff options
Diffstat (limited to 'app')
10 files changed, 116 insertions, 52 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index 41c6ff4b..0c762b41 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -45,7 +45,9 @@ import com.raizlabs.android.dbflow.config.DatabaseConfig import com.raizlabs.android.dbflow.config.FlowConfig import com.raizlabs.android.dbflow.config.FlowManager import com.raizlabs.android.dbflow.runtime.ContentResolverNotifier -import org.koin.android.ext.android.startKoin +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin import java.util.Random import kotlin.reflect.KClass @@ -134,7 +136,12 @@ class FrostApp : Application() { L.d { "Activity ${activity.localClassName} created" } } }) - startKoin(this, listOf(FrostDatabase.module(this))) + startKoin { + if (BuildConfig.DEBUG) + androidLogger() + androidContext(this@FrostApp) + modules(FrostDatabase.module(this@FrostApp)) + } } private fun initBugsnag() { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt b/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt index 67372e23..cd5fce02 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt @@ -21,8 +21,8 @@ import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import com.pitchedapps.frost.BuildConfig -import org.koin.dsl.module.module -import org.koin.standalone.StandAloneContext +import org.koin.core.context.GlobalContext +import org.koin.dsl.module interface FrostPrivateDao { fun cookieDao(): CookieDao @@ -101,6 +101,6 @@ class FrostDatabase(private val privateDb: FrostPrivateDatabase, private val pub * Get from koin * For the most part, you can retrieve directly from other koin components */ - fun get(): FrostDatabase = StandAloneContext.getKoin().koinContext.get() + fun get(): FrostDatabase = GlobalContext.get().koin.get() } } 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 76ffd8cd..557980af 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -186,7 +186,8 @@ fun MaterialDialog.Builder.theme(): MaterialDialog.Builder { } fun Activity.setFrostTheme(forceTransparent: Boolean = false) { - val isTransparent = (Color.alpha(Prefs.bgColor) != 255) || (Color.alpha(Prefs.headerColor) != 255) || forceTransparent + val isTransparent = + (Color.alpha(Prefs.bgColor) != 255) || (Color.alpha(Prefs.headerColor) != 255) || forceTransparent if (Prefs.bgColor.isColorDark) setTheme(if (isTransparent) R.style.FrostTheme_Transparent else R.style.FrostTheme) else @@ -357,7 +358,7 @@ val dependentSegments = arrayOf( ) inline val String?.isExplicitIntent - get() = this != null && startsWith("intent://") + get() = this != null && (startsWith("intent://") || startsWith("market://")) fun Context.frostChangelog() = showChangelog(R.xml.frost_changelog, Prefs.textColor) { theme() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt index 81ade98f..b06208a6 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt @@ -75,7 +75,6 @@ fun FrostWebView.requestWebOverlay(url: String): Boolean { } if (!Prefs.overlayEnabled) return false if (context is WebOverlayActivityBase) { - L.v { "Check web request from overlay" } val shouldUseDesktop = url.formattedFbUrl.shouldUseDesktopAgent //already overlay; manage user agent if (userAgentString != USER_AGENT_DESKTOP && shouldUseDesktop) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt b/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt index 594da00a..66176aba 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt @@ -47,8 +47,8 @@ import com.pitchedapps.frost.services.NotificationContent import com.pitchedapps.frost.services.NotificationType import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.toReadableTime -import org.koin.standalone.KoinComponent -import org.koin.standalone.inject +import org.koin.core.KoinComponent +import org.koin.core.inject class NotificationWidget : AppWidgetProvider() { diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 560b1111..93124f75 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -9,7 +9,8 @@ <version title="v2.3.0" /> <item text="Converted internals of Facebook data storage; auto migration will only work from 2.2.x to 2.3.x" /> <item text="Added notification widget" /> - <item text="" /> + <item text="Update theme" /> + <item text="Update translations" /> <item text="" /> <item text="" /> <item text="" /> diff --git a/app/src/test/kotlin/com/pitchedapps/frost/utils/CoroutineTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/utils/CoroutineTest.kt index e93f507c..5cde7323 100644 --- a/app/src/test/kotlin/com/pitchedapps/frost/utils/CoroutineTest.kt +++ b/app/src/test/kotlin/com/pitchedapps/frost/utils/CoroutineTest.kt @@ -246,6 +246,8 @@ class CoroutineTest { } } + class TestException(msg: String) : RuntimeException(msg) + @Test fun exceptionChecks() { val mainTag = "main-test" @@ -257,7 +259,7 @@ class CoroutineTest { val job = SupervisorJob() val flyweight = Flyweight<Int, Int>(GlobalScope, 200L) { - throw java.lang.RuntimeException("Flyweight exception") + throw TestException("Flyweight exception") } suspend fun crash(): Boolean = withContext(Dispatchers.IO) { @@ -266,7 +268,7 @@ class CoroutineTest { flyweight.fetch(0).await() } true - } catch (e: java.lang.Exception) { + } catch (e: TestException) { false } } @@ -282,10 +284,6 @@ class CoroutineTest { println("B") channel.offer(1) } -// launch { -// delay(2000) -// job.cancel() -// } } } } diff --git a/app/src/web/scss/core/_core_text.scss b/app/src/web/scss/core/_core_text.scss index 63622610..2142ef73 100644 --- a/app/src/web/scss/core/_core_text.scss +++ b/app/src/web/scss/core/_core_text.scss @@ -19,7 +19,7 @@ textarea:not([style*="color: rgb"]), ._24pi, ._4en9, ._1kb, ._5p7j, ._2klz, ._57 div.sharerSelector, .footer, ._4pv_, ._1dbp, ._3kad, ._20zc, ._2i5v, ._2i5w, a, ._5fpq, ._4gux, ._3bg5 ._52x1, ._3bg5 ._52x2, ._6dsj ._3gin, ._hdn._hdn, .mentions-input:not([style*="color: rgb"]), .mentions-placeholder:not([style*="color: rgb"]), -.largeStatusBox .placeHolder, .fcw, ._2rgt, ._67i4 ._5hu6 ._59tt, +.largeStatusBox .placeHolder, .fcw, ._2rgt, ._67i4 ._5hu6 ._59tt, ._2bu3, ._2bu4, ._5-7t, .fcl, ._4qas, .thread-title, .title, ._46pa, ._336p, ._1rrd, ._2om4, ._3m1m, ._2om2, ._5n_e, .appListExplanation, ._5yt8, ._8he, ._2luw, ._5rgs, h1, h2, h3, h4, h5, h6 { diff --git a/app/src/web/ts/click_a.ts b/app/src/web/ts/click_a.ts index 5023610e..8d1d545b 100644 --- a/app/src/web/ts/click_a.ts +++ b/app/src/web/ts/click_a.ts @@ -1,34 +1,81 @@ (function () { let prevented = false; + /** + * Go up at most [depth] times, to retrieve a parent matching the provided predicate + * If one is found, it is returned immediately. + * Otherwise, null is returned. + */ + function _parentEl(el: HTMLElement, depth: number, predicate: (el: HTMLElement) => boolean): HTMLElement | null { + for (let i = 0; i < depth + 1; i++) { + if (predicate(el)) { + return el + } + const parent = el.parentElement; + if (!parent) { + return null + } + el = parent + } + return null + } + + /** + * Attempts to find a url entry at most [depth] away + * A url is found if the element has tag 'A', and the url isn't blank + */ + function _parentUrl(el: HTMLElement, depth: number): string | null { + const element = _parentEl(el, depth, (el) => el.tagName === 'A'); + if (!element) { + return null; + } + const url = element.getAttribute('href'); + if (!url || url === '#') { + return null; + } + return url + } + + /** + * Given event and target, return true if handled and false otherwise. + */ + type EventHandler = (e: Event, target: HTMLElement) => Boolean + + const _frostGeneral: EventHandler = (e, target) => { + // Notifications are two layers under + const url = _parentUrl(target, 2); + return Frost.loadUrl(url); + }; + + const _frostLaunchpadClick: EventHandler = (e, target) => { + if (!_parentEl(target, 6, (el) => el.id === 'launchpad')) { + return false + } + console.log('Clicked launchpad'); + const url = _parentUrl(target, 5); + return Frost.loadUrl(url); + }; + + const handlers: EventHandler[] = [_frostLaunchpadClick, _frostGeneral]; + const _frostAClick = (e: Event) => { - // check for valid target + if (prevented) { + console.log("Click intercept prevented"); + return + } + /* + * Commonality; check for valid target + */ const target = e.target || e.currentTarget || e.srcElement; - if (!(target instanceof Element)) { + if (!(target instanceof HTMLElement)) { console.log("No element found"); return } - let element: Element = target; - // Notifications are two layers under - for (let i = 0; i < 2; i++) { - if (element.tagName !== 'A') { - element = <Element>element.parentElement; - } - } - if (element.tagName === 'A') { - if (!prevented) { - const url = element.getAttribute('href'); - if (!url || url === '#') { - return - } - console.log(`Click intercept ${url}`); - // If Frost is injected, check if loading the url through an overlay works - if (Frost.loadUrl(url)) { - e.stopPropagation(); - e.preventDefault(); - } - } else { - console.log("Click intercept prevented") + for (const h of handlers) { + if (h(e, target)) { + e.stopPropagation(); + e.preventDefault(); + return } } }; diff --git a/app/src/web/ts/context_a.ts b/app/src/web/ts/context_a.ts index 5eec7611..b9549f3f 100644 --- a/app/src/web/ts/context_a.ts +++ b/app/src/web/ts/context_a.ts @@ -7,6 +7,25 @@ let longClick = false; /** + * Go up at most [depth] times, to retrieve a parent matching the provided predicate + * If one is found, it is returned immediately. + * Otherwise, null is returned. + */ + function _parentEl(el: HTMLElement, depth: number, predicate: (el: HTMLElement) => boolean): HTMLElement | null { + for (let i = 0; i < depth + 1; i++) { + if (predicate(el)) { + return el + } + const parent = el.parentElement; + if (!parent) { + return null + } + el = parent + } + return null + } + + /** * Given event and target, return true if handled and false otherwise. */ type EventHandler = (e: Event, target: HTMLElement) => Boolean @@ -55,16 +74,8 @@ * Opens image activity for posts with just one image */ const _frostImage: EventHandler = (e, target) => { - let element: Element = target; - // Notifications are two layers under - for (let i = 0; i < 2; i++) { - if (element.tagName !== 'A') { - element = <Element>element.parentElement; - } else { - break - } - } - if (element.tagName !== 'A') { + const element = _parentEl(target, 2, (el) => el.tagName === 'A'); + if (!element) { return false; } const url = element.getAttribute('href'); @@ -92,7 +103,7 @@ return true; }; - const handlers = [_frostImage, _frostCopyComment, _frostCopyPost]; + const handlers: EventHandler[] = [_frostImage, _frostCopyComment, _frostCopyPost]; const _frostAContext = (e: Event) => { Frost.longClick(true); |