1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-08 12:02:33 +01:00

Merge pull request #1647 from AllanWang/kpref

Remove stateful singletons
This commit is contained in:
Allan Wang 2020-02-23 17:50:22 -08:00 committed by GitHub
commit 654cc6b943
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 835 additions and 570 deletions

View File

@ -48,7 +48,7 @@ android {
throw new GradleException("Version name mismatch, expected ${androidGitVersion.name()}, got $versionName")
}
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "com.pitchedapps.frost.FrostTestRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/src/schemas".toString()]

View File

@ -0,0 +1,89 @@
/*
* Copyright 2020 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost
import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
import ca.allanwang.kau.kpref.KPrefFactory
import ca.allanwang.kau.kpref.KPrefFactoryInMemory
import com.pitchedapps.frost.db.FrostDatabase
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.Showcase
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.KoinComponent
import org.koin.core.context.startKoin
import org.koin.core.get
import org.koin.core.module.Module
import org.koin.dsl.module
class FrostTestRunner : AndroidJUnitRunner() {
override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(cl, FrostTestApp::class.java.name, context)
}
}
class FrostTestRule : TestRule {
override fun apply(base: Statement, description: Description): Statement =
object : Statement(), KoinComponent {
override fun evaluate() {
// Reset prefs
get<Prefs>().reset()
get<Showcase>().reset()
base.evaluate()
}
}
}
class FrostTestApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidLogger()
androidContext(this@FrostTestApp)
modules(
listOf(
FrostDatabase.module(),
prefFactoryModule(),
Prefs.module(),
Showcase.module(),
FbCookie.module()
)
)
}
}
companion object {
fun prefFactoryModule(): Module = module {
single<KPrefFactory> {
KPrefFactoryInMemory
}
}
}
}

View File

@ -19,6 +19,7 @@ package com.pitchedapps.frost.activities
import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import com.pitchedapps.frost.FrostTestRule
import com.pitchedapps.frost.helper.getResource
import com.pitchedapps.frost.utils.ARG_COOKIE
import com.pitchedapps.frost.utils.ARG_IMAGE_URL
@ -39,16 +40,20 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.rules.Timeout
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ImageActivityTest {
@get:Rule
val activity: ActivityTestRule<ImageActivity> =
ActivityTestRule(ImageActivity::class.java, true, false)
@get:Rule
val rule: TestRule = RuleChain.outerRule(FrostTestRule()).around(activity)
@get:Rule
val globalTimeout: Timeout = Timeout.seconds(15)
@ -124,7 +129,6 @@ class ImageActivityTest {
fun invalidImageTest() {
launchActivity(mockServer.url("text").toString())
mockServer.takeRequest()
activity.activity.isFinishing
with(activity.activity) {
assertEquals(1, mockServer.requestCount, "One http request expected")
assertEquals(

View File

@ -19,11 +19,15 @@ package com.pitchedapps.frost
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.Log
import ca.allanwang.kau.kpref.KPrefFactory
import ca.allanwang.kau.kpref.KPrefFactoryAndroid
import ca.allanwang.kau.logging.KL
import ca.allanwang.kau.utils.buildIsLollipopAndUp
import com.bugsnag.android.Bugsnag
import com.bugsnag.android.Configuration
import com.pitchedapps.frost.db.FrostDatabase
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.services.scheduleNotificationsFromPrefs
import com.pitchedapps.frost.services.setupNotificationChannels
import com.pitchedapps.frost.utils.BuildUtils
@ -34,18 +38,42 @@ import com.pitchedapps.frost.utils.Showcase
import java.util.Random
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.KoinComponent
import org.koin.core.context.startKoin
import org.koin.core.get
import org.koin.core.module.Module
import org.koin.dsl.module
/**
* Created by Allan Wang on 2017-05-28.
*/
class FrostApp : Application() {
class FrostApp : Application(), KoinComponent {
private lateinit var showcasePrefs: Showcase
private lateinit var prefs: Prefs
override fun onCreate() {
startKoin {
if (BuildConfig.DEBUG) {
androidLogger()
}
androidContext(this@FrostApp)
modules(
listOf(
FrostDatabase.module(),
prefFactoryModule(),
Prefs.module(),
Showcase.module(),
FbCookie.module()
)
)
}
if (!buildIsLollipopAndUp) { // not supported
super.onCreate()
return
}
prefs = get()
showcasePrefs = get()
initPrefs()
initBugsnag()
@ -54,9 +82,9 @@ class FrostApp : Application() {
super.onCreate()
setupNotificationChannels(applicationContext)
setupNotificationChannels(this, prefs)
scheduleNotificationsFromPrefs()
scheduleNotificationsFromPrefs(prefs)
if (BuildConfig.DEBUG) {
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
@ -77,27 +105,27 @@ class FrostApp : Application() {
}
})
}
startKoin {
if (BuildConfig.DEBUG) {
androidLogger()
}
androidContext(this@FrostApp)
modules(FrostDatabase.module(this@FrostApp))
}
}
private fun initPrefs() {
Showcase.initialize(this, "${BuildConfig.APPLICATION_ID}.showcase")
Prefs.initialize(this, "${BuildConfig.APPLICATION_ID}.prefs")
prefs.deleteKeys("search_bar")
showcasePrefs.deleteKeys("shown_release", "experimental_by_default")
KL.shouldLog = { BuildConfig.DEBUG }
Prefs.verboseLogging = false
if (Prefs.installDate == -1L) {
Prefs.installDate = System.currentTimeMillis()
L.shouldLog = {
when (it) {
Log.VERBOSE -> BuildConfig.DEBUG
Log.INFO, Log.ERROR -> true
else -> BuildConfig.DEBUG || prefs.verboseLogging
}
}
if (Prefs.identifier == -1) {
Prefs.identifier = Random().nextInt(Int.MAX_VALUE)
prefs.verboseLogging = false
if (prefs.installDate == -1L) {
prefs.installDate = System.currentTimeMillis()
}
Prefs.lastLaunch = System.currentTimeMillis()
if (prefs.identifier == -1) {
prefs.identifier = Random().nextInt(Int.MAX_VALUE)
}
prefs.lastLaunch = System.currentTimeMillis()
}
private fun initBugsnag() {
@ -113,12 +141,12 @@ class FrostApp : Application() {
appVersion = version.versionName
releaseStage = BuildUtils.getStage(BuildConfig.BUILD_TYPE)
notifyReleaseStages = BuildUtils.getAllStages()
autoCaptureSessions = Prefs.analytics
enableExceptionHandler = Prefs.analytics
autoCaptureSessions = prefs.analytics
enableExceptionHandler = prefs.analytics
}
Bugsnag.init(this, config)
L.bugsnagInit = true
Bugsnag.setUserId(Prefs.frostId)
L.hasAnalytics = { prefs.analytics }
Bugsnag.setUserId(prefs.frostId)
Bugsnag.addToTab("Build", "Application", BuildConfig.APPLICATION_ID)
Bugsnag.addToTab("Build", "Version", BuildConfig.VERSION_NAME)
@ -129,4 +157,12 @@ class FrostApp : Application() {
}
}
}
companion object {
fun prefFactoryModule(): Module = module {
single<KPrefFactory> {
KPrefFactoryAndroid(get())
}
}
}
}

View File

@ -52,6 +52,8 @@ import org.koin.android.ext.android.inject
*/
class StartActivity : KauBaseActivity() {
private val fbCookie: FbCookie by inject()
private val prefs: Prefs by inject()
private val cookieDao: CookieDao by inject()
private val genericDao: GenericDao by inject()
@ -67,13 +69,14 @@ class StartActivity : KauBaseActivity() {
// TODO add better descriptions
CookieManager.getInstance()
} catch (e: Exception) {
L.e(e) { "No cookiemanager instance" }
showInvalidWebView()
}
launch {
try {
val authDefer = BiometricUtils.authenticate(this@StartActivity)
FbCookie.switchBackUser()
val authDefer = BiometricUtils.authenticate(this@StartActivity, prefs)
fbCookie.switchBackUser()
val cookies = ArrayList(cookieDao.selectAll())
L.i { "Cookies loaded at time ${System.currentTimeMillis()}" }
L._d {
@ -82,12 +85,12 @@ class StartActivity : KauBaseActivity() {
transform = CookieEntity::toSensitiveString
)}"
}
loadAssets()
loadAssets(prefs)
authDefer.await()
when {
cookies.isEmpty() -> launchNewTask<LoginActivity>()
// Has cookies but no selected account
Prefs.userId == -1L -> launchNewTask<SelectorActivity>(cookies)
prefs.userId == -1L -> launchNewTask<SelectorActivity>(cookies)
else -> startActivity<MainActivity>(intentBuilder = {
putParcelableArrayListExtra(EXTRA_COOKIES, cookies)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or

View File

@ -49,20 +49,25 @@ import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-06-26.
*/
class AboutActivity : AboutActivityBase(null, {
textColor = Prefs.textColor
accentColor = Prefs.accentColor
backgroundColor = Prefs.bgColor.withMinAlpha(200)
cutoutForeground = Prefs.accentColor
cutoutDrawableRes = R.drawable.frost_f_200
faqPageTitleRes = R.string.faq_title
faqXmlRes = R.xml.frost_faq
faqParseNewLine = false
}) {
class AboutActivity : AboutActivityBase(null) {
private val prefs: Prefs by inject()
override fun Configs.buildConfigs() {
textColor = prefs.textColor
accentColor = prefs.accentColor
backgroundColor = prefs.bgColor.withMinAlpha(200)
cutoutForeground = prefs.accentColor
cutoutDrawableRes = R.drawable.frost_f_200
faqPageTitleRes = R.string.faq_title
faqXmlRes = R.xml.frost_faq
faqParseNewLine = false
}
override fun getLibraries(libs: Libs): List<Library> {
val include = arrayOf(
@ -121,8 +126,8 @@ class AboutActivity : AboutActivityBase(null, {
clickCount++
lastClick = now
if (clickCount == 8) {
if (!Prefs.debugSettings) {
Prefs.debugSettings = true
if (!prefs.debugSettings) {
prefs.debugSettings = true
L.d { "Debugging section enabled" }
toast(R.string.debug_toast_enabled)
} else {

View File

@ -21,13 +21,19 @@ import android.os.Bundle
import ca.allanwang.kau.internal.KauBaseActivity
import ca.allanwang.kau.searchview.SearchViewHolder
import com.pitchedapps.frost.contracts.VideoViewHolder
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.setFrostTheme
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-06-12.
*/
abstract class BaseActivity : KauBaseActivity() {
val fbCookie: FbCookie by inject()
val prefs: Prefs by inject()
/**
* Inherited consumer to customize back press
*/

View File

@ -93,7 +93,6 @@ import com.pitchedapps.frost.db.GenericDao
import com.pitchedapps.frost.db.currentCookie
import com.pitchedapps.frost.db.getTabs
import com.pitchedapps.frost.enums.MainActivityLayout
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.parsers.FrostSearch
import com.pitchedapps.frost.facebook.parsers.SearchParser
@ -108,7 +107,6 @@ import com.pitchedapps.frost.utils.BiometricUtils
import com.pitchedapps.frost.utils.EXTRA_COOKIES
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.MAIN_TIMEOUT_DURATION
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.REQUEST_FAB
import com.pitchedapps.frost.utils.REQUEST_NAV
import com.pitchedapps.frost.utils.REQUEST_NOTIFICATION
@ -177,7 +175,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
val start = System.currentTimeMillis()
drawerWrapperBinding = ActivityMainDrawerWrapperBinding.inflate(layoutInflater)
setContentView(drawerWrapperBinding.root)
contentBinding = when (Prefs.mainActivityLayout) {
contentBinding = when (prefs.mainActivityLayout) {
MainActivityLayout.TOP_BAR -> {
val binding = ActivityMainBinding.inflate(layoutInflater)
object : ActivityMainContentBinding {
@ -203,7 +201,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
}
drawerWrapperBinding.mainContainer.addView(contentBinding.root)
with(contentBinding) {
setFrostColors {
setFrostColors(prefs) {
toolbar(toolbar)
themeWindow = false
header(appbar)
@ -211,7 +209,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
}
setSupportActionBar(toolbar)
viewpager.adapter = adapter
tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor())
tabs.setBackgroundColor(prefs.mainActivityLayout.backgroundColor(prefs))
}
onNestedCreate(savedInstanceState)
L.i { "Main finished loading UI in ${System.currentTimeMillis() - start} ms" }
@ -219,18 +217,18 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
adapter.setPages(genericDao.getTabs())
}
controlWebview = WebView(this)
if (BuildConfig.VERSION_CODE > Prefs.versionCode) {
Prefs.prevVersionCode = Prefs.versionCode
Prefs.versionCode = BuildConfig.VERSION_CODE
if (BuildConfig.VERSION_CODE > prefs.versionCode) {
prefs.prevVersionCode = prefs.versionCode
prefs.versionCode = BuildConfig.VERSION_CODE
if (!BuildConfig.DEBUG) {
frostChangelog()
frostEvent(
"Version",
"Version code" to BuildConfig.VERSION_CODE,
"Prev version code" to Prefs.prevVersionCode,
"Prev version code" to prefs.prevVersionCode,
"Version name" to BuildConfig.VERSION_NAME,
"Build type" to BuildConfig.BUILD_TYPE,
"Frost id" to Prefs.frostId
"Frost id" to prefs.frostId
)
}
}
@ -289,7 +287,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
drawer.addDrawerListener(toggle)
toggle.syncState()
val foregroundColor = ColorStateList.valueOf(Prefs.textColor)
val foregroundColor = ColorStateList.valueOf(prefs.textColor)
with(navigation) {
FrostMenuBuilder(this@BaseMainActivity, menu).apply {
@ -315,12 +313,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
val item = FbItem.values[it.itemId]
frostEvent("Drawer Tab", "name" to item.name)
drawer.closeDrawer(navigation)
launchWebOverlay(item.url)
launchWebOverlay(item.url, fbCookie)
false
}
val navBg = Prefs.bgColor.withMinAlpha(200)
val navBg = prefs.bgColor.withMinAlpha(200)
setBackgroundColor(navBg)
itemBackground = createNavDrawable(Prefs.accentColor, navBg)
itemBackground = createNavDrawable(prefs.accentColor, navBg)
itemTextColor = foregroundColor
itemIconTintList = foregroundColor
@ -332,7 +330,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
private fun ActivityMainContentBinding.initFab() {
hasFab = false
shouldShow = false
fab.backgroundTintList = ColorStateList.valueOf(Prefs.headerColor.withMinAlpha(200))
fab.backgroundTintList = ColorStateList.valueOf(prefs.headerColor.withMinAlpha(200))
fab.hide()
appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
if (!hasFab) return@OnOffsetChangedListener
@ -352,12 +350,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
if (shouldShow) {
if (fab.isShown) {
fab.fadeScaleTransition {
setIcon(iicon, color = Prefs.iconColor)
setIcon(iicon, color = prefs.iconColor)
}
return
}
}
fab.setIcon(iicon, color = Prefs.iconColor)
fab.setIcon(iicon, color = prefs.iconColor)
fab.showIf(shouldShow)
}
}
@ -384,12 +382,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
private var pendingUpdate: Boolean = false
private val binding = ViewNavHeaderBinding.inflate(layoutInflater)
val root: View get() = binding.root
private val optionsBackground = Prefs.bgColor.withMinAlpha(200).colorToForeground(
private val optionsBackground = prefs.bgColor.withMinAlpha(200).colorToForeground(
0.1f
)
init {
setPrimary(Prefs.userId)
setPrimary(prefs.userId)
binding.updateAccounts()
with(drawerWrapperBinding) {
drawer.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() {
@ -449,7 +447,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
animator.start()
}
val textColor = Prefs.textColor
val textColor = prefs.textColor
fun TextView.setOptionsIcon(iicon: IIcon) {
setCompoundDrawablesRelativeWithIntrinsicBounds(
@ -459,7 +457,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
null
)
setTextColor(textColor)
background = createNavDrawable(Prefs.accentColor, optionsBackground)
background = createNavDrawable(prefs.accentColor, optionsBackground)
}
with(optionsLogout) {
@ -469,7 +467,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
val currentCookie = cookieDao.currentCookie()
if (currentCookie == null) {
toast(R.string.account_not_found)
FbCookie.reset()
fbCookie.reset()
launchLogin(cookies(), true)
} else {
materialDialog {
@ -478,12 +476,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
text =
String.format(
string(R.string.kau_logout_confirm_as_x),
currentCookie.name ?: Prefs.userId.toString()
currentCookie.name ?: prefs.userId.toString()
)
)
positiveButton(R.string.kau_yes) {
this@BaseMainActivity.launch {
FbCookie.logout(this@BaseMainActivity)
fbCookie.logout(this@BaseMainActivity)
}
}
negativeButton(R.string.kau_no)
@ -507,7 +505,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
arrow.setImageDrawable(
GoogleMaterial.Icon.gmd_arrow_drop_down.toDrawable(
this@BaseMainActivity,
color = Prefs.textColor
color = prefs.textColor
)
)
}
@ -532,10 +530,10 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
avatarTertiary.setAccount(orderedAccounts.getOrNull(2), false)
optionsAccountsContainer.removeAllViews()
name.text = orderedAccounts.getOrNull(0)?.name
name.setTextColor(Prefs.textColor)
name.setTextColor(prefs.textColor)
val glide = Glide.with(root)
val accountSize = dimenPixelSize(R.dimen.drawer_account_avatar_size)
val textColor = Prefs.textColor
val textColor = prefs.textColor
orderedAccounts.forEach { cookie ->
val tv =
TextView(
@ -569,7 +567,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
})
tv.text = cookie.name
tv.setTextColor(textColor)
tv.background = createNavDrawable(Prefs.accentColor, optionsBackground)
tv.background = createNavDrawable(prefs.accentColor, optionsBackground)
tv.setOnClickListener {
switchAccount(cookie.id)
}
@ -598,7 +596,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
.into(this)
setOnClickListener {
if (primary) {
launchWebOverlay(FbItem.PROFILE.url)
launchWebOverlay(FbItem.PROFILE.url, fbCookie)
} else {
switchAccount(cookie.id)
}
@ -608,12 +606,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
}
private fun switchAccount(id: Long) {
if (Prefs.userId == id) return
if (prefs.userId == id) return
setPrimary(id)
pendingUpdate = true
closeDrawer()
launch {
FbCookie.switchUser(id)
fbCookie.switchUser(id)
tabsForEachView { _, view -> view.badgeText = null }
refreshAll()
}
@ -627,9 +625,9 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
contentBinding.toolbar.tint(Prefs.iconColor)
contentBinding.toolbar.tint(prefs.iconColor)
setMenuIcons(
menu, Prefs.iconColor,
menu, prefs.iconColor,
R.id.action_settings to GoogleMaterial.Icon.gmd_settings,
R.id.action_search to GoogleMaterial.Icon.gmd_search
)
@ -639,13 +637,13 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
private fun bindSearchView(menu: Menu) {
searchViewBindIfNull {
bindSearchView(menu, R.id.action_search, Prefs.iconColor) {
bindSearchView(menu, R.id.action_search, prefs.iconColor) {
textCallback = { query, searchView ->
val results = searchViewCache[query]
if (results != null)
searchView.results = results
else {
val data = SearchParser.query(FbCookie.webCookie, query)?.data?.results
val data = SearchParser.query(fbCookie.webCookie, query)?.data?.results
if (data != null) {
val items = data.mapTo(mutableListOf(), FrostSearch::toSearchItem)
if (items.isNotEmpty())
@ -663,11 +661,11 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
}
textDebounceInterval = 300
searchCallback =
{ query, _ -> launchWebOverlay("${FbItem._SEARCH.url}/?q=$query"); true }
{ query, _ -> launchWebOverlay("${FbItem._SEARCH.url}/?q=$query", fbCookie); true }
closeListener = { _ -> searchViewCache.clear() }
foregroundColor = Prefs.textColor
backgroundColor = Prefs.bgColor.withMinAlpha(200)
onItemClick = { _, key, _, _ -> launchWebOverlay(key) }
foregroundColor = prefs.textColor
backgroundColor = prefs.bgColor.withMinAlpha(200)
onItemClick = { _, key, _, _ -> launchWebOverlay(key, fbCookie) }
}
}
}
@ -737,7 +735,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
fragmentChannel.offer(lastPosition)
}
if (hasRequest(REQUEST_NOTIFICATION)) {
scheduleNotificationsFromPrefs()
scheduleNotificationsFromPrefs(prefs)
}
}
}
@ -758,10 +756,10 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
lastAccessTime = System.currentTimeMillis() // precaution to avoid loops
controlWebview?.resumeTimers()
launch {
val authDefer = BiometricUtils.authenticate(this@BaseMainActivity)
FbCookie.switchBackUser()
val authDefer = BiometricUtils.authenticate(this@BaseMainActivity, prefs)
fbCookie.switchBackUser()
authDefer.await()
if (shouldReload && Prefs.autoRefreshFeed) {
if (shouldReload && prefs.autoRefreshFeed) {
refreshAll()
}
}
@ -794,14 +792,14 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
}
}
if (currentFragment.onBackPressed()) return true
if (Prefs.exitConfirmation) {
if (prefs.exitConfirmation) {
materialDialog {
title(R.string.kau_exit)
message(R.string.kau_exit_confirmation)
positiveButton(R.string.kau_yes) { finish() }
negativeButton(R.string.kau_no)
checkBoxPrompt(R.string.kau_do_not_show_again, isCheckedDefault = false) {
Prefs.exitConfirmation = !it
prefs.exitConfirmation = !it
}
}
return true
@ -879,6 +877,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
val item = pages[position]
return BaseFragment(
item.fragmentCreator,
prefs,
forcedFallbacks.contains(item.name),
item,
position
@ -901,7 +900,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
override val lowerVideoPadding: PointF
get() {
if (Prefs.mainActivityLayout == MainActivityLayout.BOTTOM_BAR)
if (prefs.mainActivityLayout == MainActivityLayout.BOTTOM_BAR)
lowerVideoPaddingPointF.set(0f, contentBinding.toolbar.height.toFloat())
else
lowerVideoPaddingPointF.set(0f, 0f)

View File

@ -38,6 +38,8 @@ import java.io.File
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineExceptionHandler
import org.koin.android.ext.android.inject
import org.koin.core.inject
/**
* Created by Allan Wang on 05/01/18.
@ -51,6 +53,8 @@ class DebugActivity : KauBaseActivity() {
fun baseDir(context: Context) = File(context.externalCacheDir, "offline_debug")
}
private val prefs: Prefs by inject()
lateinit var binding: ActivityDebugBinding
override fun onCreate(savedInstanceState: Bundle?) {
@ -68,7 +72,7 @@ class DebugActivity : KauBaseActivity() {
}
setTitle(R.string.debug_frost)
setFrostColors {
setFrostColors(prefs) {
toolbar(toolbar)
}
debugWebview.loadUrl(FbItem.FEED.url)
@ -76,8 +80,8 @@ class DebugActivity : KauBaseActivity() {
swipeRefresh.setOnRefreshListener(debugWebview::reload)
fab.visible().setIcon(GoogleMaterial.Icon.gmd_bug_report, Prefs.iconColor)
fab.backgroundTintList = ColorStateList.valueOf(Prefs.accentColor)
fab.visible().setIcon(GoogleMaterial.Icon.gmd_bug_report, prefs.iconColor)
fab.backgroundTintList = ColorStateList.valueOf(prefs.accentColor)
fab.setOnClickListener { _ ->
fab.hide()

View File

@ -81,12 +81,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-07-15.
*/
class ImageActivity : KauBaseActivity() {
private val prefs: Prefs by inject()
@Volatile
internal var errorRef: Throwable? = null
@ -106,7 +110,7 @@ class ImageActivity : KauBaseActivity() {
set(value) {
if (field == value) return
field = value
value.update(binding.imageFab)
value.update(binding.imageFab, prefs)
}
private lateinit var dragHelper: ViewDragHelper
@ -144,8 +148,8 @@ class ImageActivity : KauBaseActivity() {
private lateinit var binding: ActivityImageBinding
private var bottomBehavior: BottomSheetBehavior<View>? = null
private val baseBackgroundColor = if (Prefs.blackMediaBg) Color.BLACK
else Prefs.bgColor.withMinAlpha(235)
private val baseBackgroundColor = if (prefs.blackMediaBg) Color.BLACK
else prefs.bgColor.withMinAlpha(235)
private fun loadError(e: Throwable) {
if (e.message?.contains("<!DOCTYPE html>") == true) {
@ -195,9 +199,9 @@ class ImageActivity : KauBaseActivity() {
if (text.isNullOrBlank()) {
imageText.gone()
} else {
imageText.setTextColor(if (Prefs.blackMediaBg) Color.WHITE else Prefs.textColor)
imageText.setTextColor(if (prefs.blackMediaBg) Color.WHITE else prefs.textColor)
imageText.setBackgroundColor(
(if (Prefs.blackMediaBg) Color.BLACK else Prefs.bgColor)
(if (prefs.blackMediaBg) Color.BLACK else prefs.bgColor)
.colorToForeground(0.2f).withAlpha(255)
)
imageText.text = text
@ -217,7 +221,7 @@ class ImageActivity : KauBaseActivity() {
imageText.bringToFront()
}
}
imageProgress.tint(if (Prefs.blackMediaBg) Color.WHITE else Prefs.accentColor)
imageProgress.tint(if (prefs.blackMediaBg) Color.WHITE else prefs.accentColor)
imageFab.setOnClickListener { fabAction.onClick(this@ImageActivity) }
imagePhoto.setOnImageEventListener(object :
SubsamplingScaleImageView.DefaultOnImageEventListener() {
@ -225,7 +229,7 @@ class ImageActivity : KauBaseActivity() {
loadError(e)
}
})
setFrostColors {
setFrostColors(prefs) {
themeWindow = false
}
dragHelper = ViewDragHelper.create(imageDrag, ViewDragCallback()).apply {
@ -405,10 +409,10 @@ class ImageActivity : KauBaseActivity() {
internal enum class FabStates(
val iicon: IIcon,
val iconColor: Int = Prefs.iconColor,
val iconColorProvider: (Prefs) -> Int = { it.iconColor },
val backgroundTint: Int = Int.MAX_VALUE
) {
ERROR(GoogleMaterial.Icon.gmd_error, Color.WHITE, Color.RED) {
ERROR(GoogleMaterial.Icon.gmd_error, { Color.WHITE }, Color.RED) {
override fun onClick(activity: ImageActivity) {
val err =
activity.errorRef?.takeIf { it !is FileNotFoundException && it.message != "Image failed to decode using JPEG decoder" }
@ -460,8 +464,9 @@ internal enum class FabStates(
* https://github.com/AllanWang/KAU/issues/184
*
*/
fun update(fab: FloatingActionButton) {
val tint = if (backgroundTint != Int.MAX_VALUE) backgroundTint else Prefs.accentColor
fun update(fab: FloatingActionButton, prefs: Prefs) {
val tint = if (backgroundTint != Int.MAX_VALUE) backgroundTint else prefs.accentColor
val iconColor = iconColorProvider(prefs)
if (fab.isHidden) {
fab.setIcon(iicon, color = iconColor)
fab.backgroundTintList = ColorStateList.valueOf(tint)

View File

@ -55,6 +55,7 @@ import com.pitchedapps.frost.utils.setFrostTheme
import com.pitchedapps.frost.widgets.NotificationWidget
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-07-25.
@ -62,8 +63,10 @@ import kotlinx.coroutines.launch
* A beautiful intro activity
* Phone showcases are drawn via layers
*/
class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.OnPageChangeListener {
class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer,
ViewPager.OnPageChangeListener {
private val prefs: Prefs by inject()
lateinit var binding: ActivityIntroBinding
private var barHasNext = true
@ -97,17 +100,17 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
else finish(next.x + next.pivotX, next.y + next.pivotY)
}
skip.setOnClickListener { finish() }
ripple.set(Prefs.bgColor)
ripple.set(prefs.bgColor)
theme()
}
fun theme() {
statusBarColor = Prefs.headerColor
navigationBarColor = Prefs.headerColor
statusBarColor = prefs.headerColor
navigationBarColor = prefs.headerColor
with(binding) {
skip.setTextColor(Prefs.textColor)
next.imageTintList = ColorStateList.valueOf(Prefs.textColor)
indicator.setColour(Prefs.textColor)
skip.setTextColor(prefs.textColor)
next.imageTintList = ColorStateList.valueOf(prefs.textColor)
indicator.setColour(prefs.textColor)
indicator.invalidate()
}
fragments.forEach { it.themeFragment() }
@ -149,21 +152,21 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
).forEach {
it?.animate()?.alpha(0f)?.setDuration(600)?.start()
}
if (Prefs.textColor != Color.WHITE) {
if (prefs.textColor != Color.WHITE) {
val f = lastView?.findViewById<ImageView>(R.id.intro_image)?.drawable
if (f != null)
ValueAnimator.ofFloat(0f, 1f).apply {
addUpdateListener {
f.setTint(Prefs.textColor.blendWith(Color.WHITE, it.animatedValue as Float))
f.setTint(prefs.textColor.blendWith(Color.WHITE, it.animatedValue as Float))
}
duration = 600
start()
}
}
if (Prefs.headerColor != blue) {
if (prefs.headerColor != blue) {
ValueAnimator.ofFloat(0f, 1f).apply {
addUpdateListener {
val c = Prefs.headerColor.blendWith(blue, it.animatedValue as Float)
val c = prefs.headerColor.blendWith(blue, it.animatedValue as Float)
statusBarColor = c
navigationBarColor = c
}
@ -175,7 +178,7 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
override fun finish() {
launch(NonCancellable) {
loadAssets()
loadAssets(prefs)
NotificationWidget.forceUpdate(this@IntroActivity)
launchNewTask<MainActivity>(cookies(), false)
super.finish()
@ -206,7 +209,7 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
binding.next.fadeScaleTransition {
setIcon(
if (barHasNext) GoogleMaterial.Icon.gmd_navigate_next else GoogleMaterial.Icon.gmd_done,
color = Prefs.textColor
color = prefs.textColor
)
}
binding.skip.animate().scaleXY(if (barHasNext) 1f else 0f)

View File

@ -36,7 +36,6 @@ import com.pitchedapps.frost.db.CookieDao
import com.pitchedapps.frost.db.CookieEntity
import com.pitchedapps.frost.db.save
import com.pitchedapps.frost.db.selectAll
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.profilePictureUrl
import com.pitchedapps.frost.glide.FrostGlide
@ -73,6 +72,7 @@ class LoginActivity : BaseActivity() {
private val textview: AppCompatTextView by bindView(R.id.textview)
private val profile: ImageView by bindView(R.id.profile)
private val cookieDao: CookieDao by inject()
private val showcasePrefs: Showcase by inject()
private lateinit var profileLoader: RequestManager
private val refreshChannel = Channel<Boolean>(10)
@ -82,7 +82,7 @@ class LoginActivity : BaseActivity() {
setContentView(R.layout.activity_login)
setSupportActionBar(toolbar)
setTitle(R.string.kau_login)
setFrostColors {
setFrostColors(prefs) {
toolbar(toolbar)
}
profileLoader = GlideApp.with(profile)
@ -96,7 +96,7 @@ class LoginActivity : BaseActivity() {
launch {
val cookie = web.loadLogin { refresh(it != 100) }.await()
L.d { "Login found" }
FbCookie.save(cookie.id)
fbCookie.save(cookie.id)
webFadeOut()
profile.fadeIn()
loadInfo(cookie)
@ -138,7 +138,7 @@ class LoginActivity : BaseActivity() {
*/
val cookies = ArrayList(cookieDao.selectAll())
delay(1000)
if (Showcase.intro)
if (showcasePrefs.intro)
launchNewTask<IntroActivity>(cookies, true)
else
launchNewTask<MainActivity>(cookies, true)

View File

@ -27,7 +27,6 @@ import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.FastItemAdapter
import com.mikepenz.fastadapter.listeners.ClickEventHook
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.cookies
import com.pitchedapps.frost.utils.launchNewTask
import com.pitchedapps.frost.utils.setFrostColors
@ -63,12 +62,12 @@ class SelectorActivity : BaseActivity() {
) {
if (item.cookie == null) this@SelectorActivity.launchNewTask<LoginActivity>()
else launch {
FbCookie.switchUser(item.cookie)
fbCookie.switchUser(item.cookie)
launchNewTask<MainActivity>(cookies())
}
}
})
setFrostColors {
setFrostColors(prefs) {
text(text)
background(container)
}

View File

@ -41,6 +41,7 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.pitchedapps.frost.R
import com.pitchedapps.frost.enums.Support
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.settings.getAppearancePrefs
import com.pitchedapps.frost.settings.getBehaviourPrefs
import com.pitchedapps.frost.settings.getDebugPrefs
@ -61,12 +62,16 @@ import com.pitchedapps.frost.utils.loadAssets
import com.pitchedapps.frost.utils.setFrostTheme
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-06-06.
*/
class SettingsActivity : KPrefActivity() {
val fbCookie: FbCookie by inject()
val prefs: Prefs by inject()
private var resultFlag = Activity.RESULT_CANCELED
companion object {
@ -117,11 +122,11 @@ class SettingsActivity : KPrefActivity() {
}
when (requestCode) {
REQUEST_NOTIFICATION_RINGTONE -> {
Prefs.notificationRingtone = uriString
prefs.notificationRingtone = uriString
reloadByTitle(R.string.notification_ringtone)
}
REQUEST_MESSAGE_RINGTONE -> {
Prefs.messageRingtone = uriString
prefs.messageRingtone = uriString
reloadByTitle(R.string.message_ringtone)
}
}
@ -129,8 +134,8 @@ class SettingsActivity : KPrefActivity() {
}
override fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit = {
textColor = { Prefs.textColor }
accentColor = { Prefs.accentColor }
textColor = { prefs.textColor }
accentColor = { prefs.accentColor }
}
override fun onCreateKPrefs(savedInstanceState: Bundle?): KPrefAdapterBuilder.() -> Unit = {
@ -195,7 +200,7 @@ class SettingsActivity : KPrefActivity() {
subItems(R.string.debug_frost, getDebugPrefs()) {
descRes = R.string.debug_frost_desc
iicon = CommunityMaterial.Icon.cmd_android_debug_bridge
visible = { Prefs.debugSettings }
visible = { prefs.debugSettings }
}
}
@ -215,15 +220,15 @@ class SettingsActivity : KPrefActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setFrostTheme(true)
super.onCreate(savedInstanceState)
animate = Prefs.animate
animate = prefs.animate
themeExterior(false)
}
fun themeExterior(animate: Boolean = true) {
if (animate) bgCanvas.fade(Prefs.bgColor)
else bgCanvas.set(Prefs.bgColor)
if (animate) toolbarCanvas.ripple(Prefs.headerColor, RippleCanvas.MIDDLE, RippleCanvas.END)
else toolbarCanvas.set(Prefs.headerColor)
if (animate) bgCanvas.fade(prefs.bgColor)
else bgCanvas.set(prefs.bgColor)
if (animate) toolbarCanvas.ripple(prefs.headerColor, RippleCanvas.MIDDLE, RippleCanvas.END)
else toolbarCanvas.set(prefs.headerColor)
frostNavigationBar()
}
@ -231,7 +236,7 @@ class SettingsActivity : KPrefActivity() {
if (!super.backPress()) {
setResult(resultFlag)
launch(NonCancellable) {
loadAssets()
loadAssets(prefs)
finishSlideOut()
}
}
@ -239,9 +244,9 @@ class SettingsActivity : KPrefActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_settings, menu)
toolbar.tint(Prefs.iconColor)
toolbar.tint(prefs.iconColor)
setMenuIcons(
menu, Prefs.iconColor,
menu, prefs.iconColor,
R.id.action_email to GoogleMaterial.Icon.gmd_email,
R.id.action_changelog to GoogleMaterial.Icon.gmd_info
)

View File

@ -42,7 +42,6 @@ import com.pitchedapps.frost.db.saveTabs
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.iitems.TabIItem
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.setFrostColors
import java.util.Collections
import kotlinx.coroutines.NonCancellable
@ -70,15 +69,15 @@ class TabCustomizerActivity : BaseActivity() {
}
fun ActivityTabCustomizerBinding.init() {
pseudoToolbar.setBackgroundColor(Prefs.headerColor)
pseudoToolbar.setBackgroundColor(prefs.headerColor)
tabRecycler.layoutManager =
GridLayoutManager(this@TabCustomizerActivity, TAB_COUNT, RecyclerView.VERTICAL, false)
tabRecycler.adapter = adapter
tabRecycler.setHasFixedSize(true)
divider.setBackgroundColor(Prefs.textColor.withAlpha(30))
instructions.setTextColor(Prefs.textColor)
divider.setBackgroundColor(prefs.textColor.withAlpha(30))
instructions.setTextColor(prefs.textColor)
launch {
val tabs = genericDao.getTabs().toMutableList()
@ -95,8 +94,8 @@ class TabCustomizerActivity : BaseActivity() {
setResult(Activity.RESULT_CANCELED)
fabSave.setIcon(GoogleMaterial.Icon.gmd_check, Prefs.iconColor)
fabSave.backgroundTintList = ColorStateList.valueOf(Prefs.accentColor)
fabSave.setIcon(GoogleMaterial.Icon.gmd_check, prefs.iconColor)
fabSave.backgroundTintList = ColorStateList.valueOf(prefs.accentColor)
fabSave.setOnClickListener {
launchMain(NonCancellable) {
val tabs = adapter.adapterItems.subList(0, TAB_COUNT).map(TabIItem::item)
@ -105,10 +104,10 @@ class TabCustomizerActivity : BaseActivity() {
finish()
}
}
fabCancel.setIcon(GoogleMaterial.Icon.gmd_close, Prefs.iconColor)
fabCancel.backgroundTintList = ColorStateList.valueOf(Prefs.accentColor)
fabCancel.setIcon(GoogleMaterial.Icon.gmd_close, prefs.iconColor)
fabCancel.backgroundTintList = ColorStateList.valueOf(prefs.accentColor)
fabCancel.setOnClickListener { finish() }
setFrostColors {
setFrostColors(prefs) {
themeWindow = true
}
}

View File

@ -56,7 +56,6 @@ import com.pitchedapps.frost.contracts.FrostContentContainer
import com.pitchedapps.frost.contracts.VideoViewHolder
import com.pitchedapps.frost.enums.OverlayContext
import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.USER_AGENT
import com.pitchedapps.frost.facebook.USER_AGENT_DESKTOP_CONST
@ -67,7 +66,6 @@ import com.pitchedapps.frost.utils.ARG_URL
import com.pitchedapps.frost.utils.ARG_USER_ID
import com.pitchedapps.frost.utils.BiometricUtils
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.Showcase
import com.pitchedapps.frost.utils.frostSnackbar
import com.pitchedapps.frost.utils.setFrostColors
@ -78,6 +76,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-06-01.
@ -170,6 +169,8 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
get() = content.coreView
private val coordinator: CoordinatorLayout by bindView(R.id.overlay_main_content)
private val showcasePrefs: Showcase by inject()
private inline val urlTest: String?
get() = intent.getStringExtra(ARG_URL) ?: intent.dataString
@ -184,7 +185,7 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
override val baseEnum: FbItem? = null
private inline val userId: Long
get() = intent.getLongExtra(ARG_USER_ID, Prefs.userId)
get() = intent.getLongExtra(ARG_USER_ID, prefs.userId)
private val overlayContext: OverlayContext?
get() = OverlayContext[intent.extras]
@ -205,14 +206,14 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar.navigationIcon = GoogleMaterial.Icon.gmd_close.toDrawable(this, 16, Prefs.iconColor)
toolbar.navigationIcon = GoogleMaterial.Icon.gmd_close.toDrawable(this, 16, prefs.iconColor)
toolbar.setNavigationOnClickListener { finishSlideOut() }
setFrostColors {
setFrostColors(prefs) {
toolbar(toolbar)
themeWindow = false
}
coordinator.setBackgroundColor(Prefs.bgColor.withAlpha(255))
coordinator.setBackgroundColor(prefs.bgColor.withAlpha(255))
content.bind(this)
@ -222,15 +223,15 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
with(web) {
userAgentString = userAgent
Prefs.prevId = Prefs.userId
prefs.prevId = prefs.userId
launch {
val authDefer = BiometricUtils.authenticate(this@WebOverlayActivityBase)
if (userId != Prefs.userId) {
FbCookie.switchUser(userId)
val authDefer = BiometricUtils.authenticate(this@WebOverlayActivityBase, prefs)
if (userId != prefs.userId) {
fbCookie.switchUser(userId)
}
authDefer.await()
reloadBase(true)
if (Showcase.firstWebOverlay) {
if (showcasePrefs.firstWebOverlay) {
coordinator.frostSnackbar(R.string.web_overlay_swipe_hint) {
duration = BaseTransientBottomBar.LENGTH_INDEFINITE
setAction(R.string.kau_got_it) { dismiss() }
@ -240,7 +241,7 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
}
swipeBack = kauSwipeOnCreate {
if (!Prefs.overlayFullScreenSwipe) edgeSize = 20.dpToPx
if (!prefs.overlayFullScreenSwipe) edgeSize = 20.dpToPx
transitionSystemBars = false
}
}
@ -271,13 +272,13 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
* Our theme for the overlay should be fully opaque
*/
fun theme() {
val opaqueAccent = Prefs.headerColor.withAlpha(255)
val opaqueAccent = prefs.headerColor.withAlpha(255)
statusBarColor = opaqueAccent.darken()
navigationBarColor = opaqueAccent
toolbar.setBackgroundColor(opaqueAccent)
toolbar.setTitleTextColor(Prefs.iconColor)
coordinator.setBackgroundColor(Prefs.bgColor.withAlpha(255))
toolbar.overflowIcon?.setTint(Prefs.iconColor)
toolbar.setTitleTextColor(prefs.iconColor)
coordinator.setBackgroundColor(prefs.bgColor.withAlpha(255))
toolbar.overflowIcon?.setTint(prefs.iconColor)
}
override fun onResume() {
@ -312,7 +313,7 @@ abstract class WebOverlayActivityBase(private val userAgent: String = USER_AGENT
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_web, menu)
overlayContext?.onMenuCreate(this, menu)
toolbar.tint(Prefs.iconColor)
toolbar.tint(prefs.iconColor)
return true
}

View File

@ -68,4 +68,4 @@ suspend fun CookieDao.selectById(id: Long) = dao { _selectById(id) }
suspend fun CookieDao.save(cookie: CookieEntity) = dao { _save(cookie) }
suspend fun CookieDao.save(cookies: List<CookieEntity>) = dao { _save(cookies) }
suspend fun CookieDao.deleteById(id: Long) = dao { _deleteById(id) }
suspend fun CookieDao.currentCookie() = selectById(Prefs.userId)
suspend fun CookieDao.currentCookie() = selectById(Prefs.get().userId)

View File

@ -93,8 +93,8 @@ class FrostDatabase(
return FrostDatabase(privateDb, publicDb)
}
fun module(context: Context) = module {
single { create(context) }
fun module() = module {
single { create(get()) }
single { get<FrostDatabase>().cookieDao() }
single { get<FrostDatabase>().cacheDao() }
single { get<FrostDatabase>().notifDao() }

View File

@ -24,17 +24,17 @@ import com.pitchedapps.frost.utils.Prefs
*/
enum class MainActivityLayout(
val titleRes: Int,
val backgroundColor: () -> Int,
val iconColor: () -> Int
val backgroundColor: (Prefs) -> Int,
val iconColor: (Prefs) -> Int
) {
TOP_BAR(R.string.top_bar,
{ Prefs.headerColor },
{ Prefs.iconColor }),
{ it.headerColor },
{ it.iconColor }),
BOTTOM_BAR(R.string.bottom_bar,
{ Prefs.bgColor },
{ Prefs.textColor });
{ it.bgColor },
{ it.textColor });
companion object {
val values = values() // save one instance

View File

@ -33,11 +33,11 @@ const val BLUE_LIGHT = 0xff5d86dd.toInt()
enum class Theme(
@StringRes val textRes: Int,
val injector: InjectorContract,
private val textColorGetter: () -> Int,
private val accentColorGetter: () -> Int,
private val backgroundColorGetter: () -> Int,
private val headerColorGetter: () -> Int,
private val iconColorGetter: () -> Int
val textColorGetter: (Prefs) -> Int,
val accentColorGetter: (Prefs) -> Int,
val backgroundColorGetter: (Prefs) -> Int,
val headerColorGetter: (Prefs) -> Int,
val iconColorGetter: (Prefs) -> Int
) {
DEFAULT(R.string.kau_default,
@ -82,26 +82,11 @@ enum class Theme(
CUSTOM(R.string.kau_custom,
CssAssets.CUSTOM,
{ Prefs.customTextColor },
{ Prefs.customAccentColor },
{ Prefs.customBackgroundColor },
{ Prefs.customHeaderColor },
{ Prefs.customIconColor });
val textColor: Int
get() = textColorGetter()
val accentColor: Int
get() = accentColorGetter()
val bgColor: Int
get() = backgroundColorGetter()
val headerColor: Int
get() = headerColorGetter()
val iconColor: Int
get() = iconColorGetter()
{ it.customTextColor },
{ it.customAccentColor },
{ it.customBackgroundColor },
{ it.customHeaderColor },
{ it.customIconColor });
companion object {
val values = values() // save one instance

View File

@ -21,7 +21,6 @@ import android.content.Context
import android.webkit.CookieManager
import com.pitchedapps.frost.db.CookieDao
import com.pitchedapps.frost.db.CookieEntity
import com.pitchedapps.frost.db.FrostDatabase
import com.pitchedapps.frost.db.deleteById
import com.pitchedapps.frost.db.save
import com.pitchedapps.frost.db.selectById
@ -35,27 +34,30 @@ import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import org.koin.dsl.module
/**
* Created by Allan Wang on 2017-05-30.
*
* The following component manages all cookie transfers.
*/
object FbCookie {
class FbCookie(private val prefs: Prefs, private val cookieDao: CookieDao) {
const val COOKIE_DOMAIN = FB_URL_BASE
companion object {
private const val COOKIE_DOMAIN = FB_URL_BASE
fun module() = module {
single { FbCookie(get(), get()) }
}
}
/**
* Retrieves the facebook cookie if it exists
* Note that this is a synchronized call
*/
inline val webCookie: String?
val webCookie: String?
get() = CookieManager.getInstance().getCookie(COOKIE_DOMAIN)
private val cookieDao: CookieDao by lazy {
FrostDatabase.get().cookieDao()
}
private suspend fun CookieManager.suspendSetWebCookie(cookie: String?): Boolean {
cookie ?: return true
return withContext(NonCancellable) {
@ -86,14 +88,14 @@ object FbCookie {
suspend fun save(id: Long) {
L.d { "New cookie found" }
Prefs.userId = id
prefs.userId = id
CookieManager.getInstance().flush()
val cookie = CookieEntity(Prefs.userId, null, webCookie)
val cookie = CookieEntity(prefs.userId, null, webCookie)
cookieDao.save(cookie)
}
suspend fun reset() {
Prefs.userId = -1L
prefs.userId = -1L
with(CookieManager.getInstance()) {
removeAllCookies()
flush()
@ -112,7 +114,7 @@ object FbCookie {
}
withContext(NonCancellable) {
L.d { "Switching User" }
Prefs.userId = cookie.id
prefs.userId = cookie.id
CookieManager.getInstance().suspendSetWebCookie(cookie.cookie)
}
}
@ -124,8 +126,8 @@ object FbCookie {
suspend fun logout(context: Context) {
val cookies = arrayListOf<CookieEntity>()
if (context is Activity)
cookies.addAll(context.cookies().filter { it.id != Prefs.userId })
logout(Prefs.userId)
cookies.addAll(context.cookies().filter { it.id != prefs.userId })
logout(prefs.userId)
context.launchLogin(cookies, true)
}
@ -145,13 +147,13 @@ object FbCookie {
* When coming back to the main app, switch back to our original account before continuing
*/
suspend fun switchBackUser() {
if (Prefs.prevId == -1L) return
val prevId = Prefs.prevId
Prefs.prevId = -1L
if (prevId != Prefs.userId) {
if (prefs.prevId == -1L) return
val prevId = prefs.prevId
prefs.prevId = -1L
if (prevId != prefs.userId) {
switchUser(prevId)
L.d { "Switch back user" }
L._d { "${Prefs.userId} to $prevId" }
L._d { "${prefs.userId} to $prevId" }
}
}
}

View File

@ -32,6 +32,7 @@ import com.pitchedapps.frost.contracts.FrostContentParent
import com.pitchedapps.frost.contracts.MainActivityContract
import com.pitchedapps.frost.contracts.MainFabContract
import com.pitchedapps.frost.enums.FeedSort
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.utils.ARG_URL
import com.pitchedapps.frost.utils.L
@ -47,6 +48,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-11-07.
@ -55,7 +57,8 @@ import kotlinx.coroutines.launch
* Must be attached to activities implementing [MainActivityContract]
*/
@UseExperimental(ExperimentalCoroutinesApi::class)
abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, DynamicUiContract {
abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract,
DynamicUiContract {
companion object {
private const val ARG_POSITION = "arg_position"
@ -63,12 +66,13 @@ abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, Dyna
internal operator fun invoke(
base: () -> BaseFragment,
prefs: Prefs,
useFallback: Boolean,
data: FbItem,
position: Int
): BaseFragment {
val fragment = if (useFallback) WebFragment() else base()
val d = if (data == FbItem.FEED) FeedSort(Prefs.feedSort).item else data
val d = if (data == FbItem.FEED) FeedSort(prefs.feedSort).item else data
fragment.withArguments(
ARG_URL to d.url,
ARG_POSITION to position
@ -78,6 +82,8 @@ abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, Dyna
}
}
protected val fbCookie: FbCookie by inject()
protected val prefs: Prefs by inject()
open lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = ContextHelper.dispatcher + job
@ -195,10 +201,10 @@ abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, Dyna
protected fun FloatingActionButton.update(iicon: IIcon, click: () -> Unit) {
if (isShown) {
fadeScaleTransition {
setIcon(iicon, Prefs.iconColor)
setIcon(iicon, prefs.iconColor)
}
} else {
setIcon(iicon, Prefs.iconColor)
setIcon(iicon, prefs.iconColor)
show()
}
setOnClickListener { click() }

View File

@ -23,7 +23,6 @@ import com.mikepenz.fastadapter.GenericItem
import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.adapters.ModelAdapter
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.parsers.FrostParser
import com.pitchedapps.frost.facebook.parsers.ParseData
import com.pitchedapps.frost.facebook.parsers.ParseResponse
@ -130,7 +129,7 @@ abstract class FrostParserFragment<T : ParseData, Item : GenericItem> :
override suspend fun reloadImpl(progress: (Int) -> Unit): List<Item>? =
withContext(Dispatchers.IO) {
progress(10)
val cookie = FbCookie.webCookie
val cookie = fbCookie.webCookie
val doc = getDoc(cookie)
progress(60)
val response = try {

View File

@ -40,6 +40,6 @@ class NotificationFragment : FrostParserFragment<FrostNotifs, NotificationIItem>
response.data.notifs.map { NotificationIItem(it, response.cookie) }
override fun bindImpl(recyclerView: FrostRecyclerView) {
NotificationIItem.bindEvents(adapter)
NotificationIItem.bindEvents(adapter, fbCookie)
}
}

View File

@ -23,7 +23,6 @@ import com.pitchedapps.frost.contracts.MainFabContract
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.injectors.JsActions
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.views.FrostWebView
import com.pitchedapps.frost.web.FrostWebViewClient
import com.pitchedapps.frost.web.FrostWebViewClientMenu
@ -52,9 +51,9 @@ class WebFragment : BaseFragment() {
L.e { "Webview not found in fragment $baseEnum" }
return super.updateFab(contract)
}
if (baseEnum.isFeed && Prefs.showCreateFab) {
if (baseEnum.isFeed && prefs.showCreateFab) {
contract.showFab(GoogleMaterial.Icon.gmd_edit) {
JsActions.CREATE_POST.inject(web)
JsActions.CREATE_POST.inject(web, prefs)
}
return
}

View File

@ -30,6 +30,8 @@ import com.pitchedapps.frost.facebook.FbCookie
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 28/12/17.
@ -63,10 +65,13 @@ class FrostGlideModule : AppGlideModule() {
private fun getFrostHttpClient(): OkHttpClient =
OkHttpClient.Builder().addInterceptor(FrostCookieInterceptor()).build()
class FrostCookieInterceptor : Interceptor {
class FrostCookieInterceptor : Interceptor, KoinComponent {
private val fbCookie: FbCookie by inject()
override fun intercept(chain: Interceptor.Chain): Response {
val origRequest = chain.request()
val cookie = FbCookie.webCookie ?: return chain.proceed(origRequest)
val cookie = fbCookie.webCookie ?: return chain.proceed(origRequest)
val request = origRequest.newBuilder().addHeader("Cookie", cookie).build()
return chain.proceed(request)
}

View File

@ -27,8 +27,11 @@ import com.mikepenz.fastadapter.GenericItem
import com.mikepenz.fastadapter.IAdapter
import com.mikepenz.fastadapter.select.selectExtension
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.launchWebOverlay
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 30/12/17.
@ -41,20 +44,20 @@ interface ClickableIItemContract {
val url: String?
fun click(context: Context) {
fun click(context: Context, fbCookie: FbCookie) {
val url = url ?: return
context.launchWebOverlay(url)
context.launchWebOverlay(url, fbCookie)
}
companion object {
fun bindEvents(adapter: IAdapter<GenericItem>) {
fun bindEvents(adapter: IAdapter<GenericItem>, fbCookie: FbCookie) {
adapter.fastAdapter?.apply {
selectExtension {
isSelectable = false
}
onClickListener = { v, _, item, _ ->
if (item is ClickableIItemContract) {
item.click(v!!.context)
item.click(v!!.context, fbCookie)
true
} else
false
@ -73,14 +76,16 @@ open class HeaderIItem(
itemId: Int = R.layout.iitem_header
) : KauIItem<HeaderIItem.ViewHolder>(R.layout.iitem_header, ::ViewHolder, itemId) {
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<HeaderIItem>(itemView) {
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<HeaderIItem>(itemView), KoinComponent {
private val prefs: Prefs by inject()
val text: TextView by bindView(R.id.item_header_text)
override fun bindView(item: HeaderIItem, payloads: MutableList<Any>) {
text.setTextColor(Prefs.accentColor)
text.setTextColor(prefs.accentColor)
text.text = item.text
text.setBackgroundColor(Prefs.nativeBgColor)
text.setBackgroundColor(prefs.nativeBgColor)
}
override fun unbindView(item: HeaderIItem) {
@ -100,14 +105,16 @@ open class TextIItem(
) : KauIItem<TextIItem.ViewHolder>(R.layout.iitem_text, ::ViewHolder, itemId),
ClickableIItemContract {
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TextIItem>(itemView) {
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TextIItem>(itemView), KoinComponent {
private val prefs: Prefs by inject()
val text: TextView by bindView(R.id.item_text_view)
override fun bindView(item: TextIItem, payloads: MutableList<Any>) {
text.setTextColor(Prefs.textColor)
text.setTextColor(prefs.textColor)
text.text = item.text
text.background = createSimpleRippleDrawable(Prefs.bgColor, Prefs.nativeBgColor)
text.background = createSimpleRippleDrawable(prefs.bgColor, prefs.nativeBgColor)
}
override fun unbindView(item: TextIItem) {

View File

@ -31,6 +31,7 @@ import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.diff.DiffCallback
import com.mikepenz.fastadapter.select.selectExtension
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.parsers.FrostNotif
import com.pitchedapps.frost.glide.FrostGlide
@ -38,6 +39,8 @@ import com.pitchedapps.frost.glide.GlideApp
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.isIndependent
import com.pitchedapps.frost.utils.launchWebOverlay
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 27/12/17.
@ -48,7 +51,7 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) :
) {
companion object {
fun bindEvents(adapter: ItemAdapter<NotificationIItem>) {
fun bindEvents(adapter: ItemAdapter<NotificationIItem>, fbCookie: FbCookie) {
adapter.fastAdapter?.apply {
selectExtension {
isSelectable = false
@ -62,7 +65,7 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) :
)
}
// TODO temp fix. If url is dependent, we cannot load it directly
v!!.context.launchWebOverlay(if (notif.url.isIndependent) notif.url else FbItem.NOTIFICATIONS.url)
v!!.context.launchWebOverlay(if (notif.url.isIndependent) notif.url else FbItem.NOTIFICATIONS.url, fbCookie)
true
}
}
@ -93,7 +96,9 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) :
}
}
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<NotificationIItem>(itemView) {
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<NotificationIItem>(itemView), KoinComponent {
private val prefs: Prefs by inject()
private val frame: ViewGroup by bindView(R.id.item_frame)
private val avatar: ImageView by bindView(R.id.item_avatar)
@ -107,11 +112,11 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) :
override fun bindView(item: NotificationIItem, payloads: MutableList<Any>) {
val notif = item.notification
frame.background = createSimpleRippleDrawable(
Prefs.textColor,
Prefs.nativeBgColor(notif.unread)
prefs.textColor,
prefs.nativeBgColor(notif.unread)
)
content.setTextColor(Prefs.textColor)
date.setTextColor(Prefs.textColor.withAlpha(150))
content.setTextColor(prefs.textColor)
date.setTextColor(prefs.textColor.withAlpha(150))
val glide = glide
glide.load(notif.img)

View File

@ -30,6 +30,8 @@ import com.mikepenz.fastadapter.drag.IDraggable
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.utils.Prefs
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 26/11/17.
@ -41,14 +43,16 @@ class TabIItem(val item: FbItem) : KauIItem<TabIItem.ViewHolder>(
override val isDraggable: Boolean = true
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TabIItem>(itemView) {
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TabIItem>(itemView), KoinComponent {
private val prefs: Prefs by inject()
val image: ImageView by bindView(R.id.image)
val text: TextView by bindView(R.id.text)
override fun bindView(item: TabIItem, payloads: MutableList<Any>) {
val isInToolbar = adapterPosition < 4
val color = if (isInToolbar) Prefs.iconColor else Prefs.textColor
val color = if (isInToolbar) prefs.iconColor else prefs.textColor
image.setIcon(item.item.icon, 20, color)
if (isInToolbar)
text.invisible()

View File

@ -20,7 +20,6 @@ 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
import ca.allanwang.kau.utils.colorToForeground
@ -50,29 +49,38 @@ enum class CssAssets(val folder: String = THEME_FOLDER) : InjectorContract {
/**
* Note that while this can be loaded from any thread, it is typically done through [load]
*/
private val injector = lazyContext {
private var injector: JsInjector? = null
private fun injector(context: Context, prefs: Prefs): JsInjector =
injector ?: createInjector(context, prefs).also { injector = it }
/**
* Note that while this can be loaded from any thread, it is typically done through [load]
*/
private fun createInjector(context: Context, prefs: Prefs): JsInjector =
try {
var content =
it.assets.open("css/$folder/$file").bufferedReader().use(BufferedReader::readText)
context.assets.open("css/$folder/$file").bufferedReader()
.use(BufferedReader::readText)
if (this == CUSTOM) {
val bt = if (Color.alpha(Prefs.bgColor) == 255)
Prefs.bgColor.toRgbaString()
val bt = if (Color.alpha(prefs.bgColor) == 255)
prefs.bgColor.toRgbaString()
else
"transparent"
val bb = Prefs.bgColor.colorToForeground(0.35f)
val bb = prefs.bgColor.colorToForeground(0.35f)
content = content
.replace("\$T\$", Prefs.textColor.toRgbaString())
.replace("\$TT\$", Prefs.textColor.colorToBackground(0.05f).toRgbaString())
.replace("\$A\$", Prefs.accentColor.toRgbaString())
.replace("\$AT\$", Prefs.iconColor.toRgbaString())
.replace("\$B\$", Prefs.bgColor.toRgbaString())
.replace("\$T\$", prefs.textColor.toRgbaString())
.replace("\$TT\$", prefs.textColor.colorToBackground(0.05f).toRgbaString())
.replace("\$A\$", prefs.accentColor.toRgbaString())
.replace("\$AT\$", prefs.iconColor.toRgbaString())
.replace("\$B\$", prefs.bgColor.toRgbaString())
.replace("\$BT\$", bt)
.replace("\$BBT\$", bb.withAlpha(51).toRgbaString())
.replace("\$O\$", Prefs.bgColor.withAlpha(255).toRgbaString())
.replace("\$O\$", prefs.bgColor.withAlpha(255).toRgbaString())
.replace("\$OO\$", bb.withAlpha(255).toRgbaString())
.replace("\$D\$", Prefs.textColor.adjustAlpha(0.3f).toRgbaString())
.replace("\$D\$", prefs.textColor.adjustAlpha(0.3f).toRgbaString())
.replace("\$TI\$", bb.withAlpha(60).toRgbaString())
.replace("\$C\$", bt)
}
@ -81,24 +89,24 @@ enum class CssAssets(val folder: String = THEME_FOLDER) : InjectorContract {
L.e(e) { "CssAssets file not found" }
JsInjector(JsActions.EMPTY.function)
}
}
override fun inject(webView: WebView) =
injector(webView.context).inject(webView)
override fun inject(webView: WebView, prefs: Prefs) =
injector(webView.context, prefs).inject(webView, prefs)
fun reset() {
injector.invalidate()
injector = null
}
companion object {
// Ensures that all non themes and the selected theme are loaded
suspend fun load(context: Context) {
suspend fun load(context: Context, prefs: Prefs) {
withContext(Dispatchers.IO) {
val currentTheme = Prefs.t.injector as? CssAssets
val currentTheme = prefs.t.injector as? CssAssets
val (themes, others) = CssAssets.values().partition { it.folder == THEME_FOLDER }
themes.filter { it != currentTheme }.forEach { it.reset() }
currentTheme?.injector?.invoke(context)
others.forEach { it.injector.invoke(context) }
currentTheme?.injector(context, prefs)
others.forEach { it.injector(context, prefs) }
}
}
}

View File

@ -17,6 +17,7 @@
package com.pitchedapps.frost.injectors
import android.webkit.WebView
import com.pitchedapps.frost.utils.Prefs
/**
* Created by Allan Wang on 2017-05-31.
@ -53,6 +54,6 @@ enum class CssHider(vararg val items: String) : InjectorContract {
.single(name).build()
}
override fun inject(webView: WebView) =
injector.inject(webView)
override fun inject(webView: WebView, prefs: Prefs) =
injector.inject(webView, prefs)
}

View File

@ -18,6 +18,7 @@ package com.pitchedapps.frost.injectors
import android.webkit.WebView
import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.utils.Prefs
/**
* Created by Allan Wang on 2017-05-31.
@ -42,8 +43,8 @@ enum class JsActions(body: String) : InjectorContract {
val function = "(function(){$body})();"
override fun inject(webView: WebView) =
JsInjector(function).inject(webView)
override fun inject(webView: WebView, prefs: Prefs) =
JsInjector(function).inject(webView, prefs)
}
@Suppress("NOTHING_TO_INLINE")

View File

@ -21,6 +21,7 @@ import android.webkit.WebView
import androidx.annotation.VisibleForTesting
import ca.allanwang.kau.kotlin.lazyContext
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import java.io.BufferedReader
import java.io.FileNotFoundException
import java.util.Locale
@ -49,14 +50,14 @@ enum class JsAssets : InjectorContract {
}
}
override fun inject(webView: WebView) =
injector(webView.context).inject(webView)
override fun inject(webView: WebView, prefs: Prefs) =
injector(webView.context).inject(webView, prefs)
companion object {
// Ensures that all non themes and the selected theme are loaded
suspend fun load(context: Context) {
withContext(Dispatchers.IO) {
JsAssets.values().forEach { it.injector.invoke(context) }
values().forEach { it.injector.invoke(context) }
}
}
}

View File

@ -19,6 +19,7 @@ package com.pitchedapps.frost.injectors
import android.webkit.WebView
import androidx.annotation.VisibleForTesting
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.web.FrostWebViewClient
import kotlin.random.Random
import org.apache.commons.text.StringEscapeUtils
@ -83,7 +84,7 @@ class JsBuilder {
* Contract for all injectors to allow it to interact properly with a webview
*/
interface InjectorContract {
fun inject(webView: WebView)
fun inject(webView: WebView, prefs: Prefs)
/**
* Toggle the injector (usually through Prefs
* If false, will fallback to an empty action
@ -94,19 +95,19 @@ interface InjectorContract {
/**
* Helper method to inject multiple functions simultaneously with a single callback
*/
fun WebView.jsInject(vararg injectors: InjectorContract) {
fun WebView.jsInject(vararg injectors: InjectorContract, prefs: Prefs) {
injectors.filter { it != JsActions.EMPTY }.forEach {
it.inject(this)
it.inject(this, prefs)
}
}
fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract) = web.jsInject(*injectors)
fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract, prefs: Prefs) = web.jsInject(*injectors, prefs = prefs)
/**
* Wrapper class to convert a function into an injector
*/
class JsInjector(val function: String) : InjectorContract {
override fun inject(webView: WebView) =
override fun inject(webView: WebView, prefs: Prefs) =
webView.evaluateJavascript(function, null)
}

View File

@ -23,7 +23,6 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.IntroActivity
import com.pitchedapps.frost.databinding.IntroThemeBinding
import com.pitchedapps.frost.enums.Theme
import com.pitchedapps.frost.utils.Prefs
/**
* Created by Allan Wang on 2017-07-28.
@ -56,7 +55,7 @@ class IntroFragmentTheme : BaseIntroFragment(R.layout.intro_theme) {
introThemeDark.setThemeClick(Theme.DARK)
introThemeAmoled.setThemeClick(Theme.AMOLED)
introThemeGlass.setThemeClick(Theme.GLASS)
val currentTheme = Prefs.theme - 1
val currentTheme = prefs.theme - 1
if (currentTheme in 0..3)
themeList.forEachIndexed { index, v ->
v.scaleXY = if (index == currentTheme) 1.6f else 0.8f
@ -65,9 +64,9 @@ class IntroFragmentTheme : BaseIntroFragment(R.layout.intro_theme) {
private fun View.setThemeClick(theme: Theme) {
setOnClickListener { v ->
Prefs.theme = theme.ordinal
prefs.theme = theme.ordinal
(activity as IntroActivity).apply {
binding.ripple.ripple(Prefs.bgColor, v.x + v.pivotX, v.y + v.pivotY)
binding.ripple.ripple(prefs.bgColor, v.x + v.pivotX, v.y + v.pivotY)
theme()
}
themeList.forEach { it.animate().scaleXY(if (it == this) 1.6f else 0.8f).start() }

View File

@ -29,7 +29,6 @@ import ca.allanwang.kau.utils.visible
import ca.allanwang.kau.utils.withAlpha
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.launchTabCustomizerActivity
import kotlin.math.abs
@ -58,10 +57,10 @@ abstract class BaseImageIntroFragment(
override fun themeFragmentImpl() {
super.themeFragmentImpl()
title.setTextColor(Prefs.textColor)
desc.setTextColor(Prefs.textColor)
phone.tint(Prefs.textColor)
screen.tint(Prefs.bgColor)
title.setTextColor(prefs.textColor)
desc.setTextColor(prefs.textColor)
phone.tint(prefs.textColor)
screen.tint(prefs.bgColor)
}
fun themeImageComponent(color: Int, vararg id: Int) {
@ -97,9 +96,9 @@ class IntroAccountFragment : BaseImageIntroFragment(
override fun themeFragmentImpl() {
super.themeFragmentImpl()
themeImageComponent(Prefs.iconColor, R.id.intro_phone_avatar_1, R.id.intro_phone_avatar_2)
themeImageComponent(Prefs.bgColor.colorToForeground(), R.id.intro_phone_nav)
themeImageComponent(Prefs.headerColor, R.id.intro_phone_header)
themeImageComponent(prefs.iconColor, R.id.intro_phone_avatar_1, R.id.intro_phone_avatar_2)
themeImageComponent(prefs.bgColor.colorToForeground(), R.id.intro_phone_nav)
themeImageComponent(prefs.headerColor, R.id.intro_phone_header)
}
override fun onPageScrolledImpl(positionOffset: Float) {
@ -123,14 +122,14 @@ class IntroTabTouchFragment : BaseImageIntroFragment(
override fun themeFragmentImpl() {
super.themeFragmentImpl()
themeImageComponent(
Prefs.iconColor,
prefs.iconColor,
R.id.intro_phone_icon_1,
R.id.intro_phone_icon_2,
R.id.intro_phone_icon_3,
R.id.intro_phone_icon_4
)
themeImageComponent(Prefs.headerColor, R.id.intro_phone_tab)
themeImageComponent(Prefs.textColor.withAlpha(80), R.id.intro_phone_icon_ripple)
themeImageComponent(prefs.headerColor, R.id.intro_phone_tab)
themeImageComponent(prefs.textColor.withAlpha(80), R.id.intro_phone_icon_ripple)
}
}
@ -142,21 +141,21 @@ class IntroTabContextFragment : BaseImageIntroFragment(
override fun themeFragmentImpl() {
super.themeFragmentImpl()
themeImageComponent(Prefs.headerColor, R.id.intro_phone_toolbar)
themeImageComponent(Prefs.bgColor.colorToForeground(0.1f), R.id.intro_phone_image)
themeImageComponent(prefs.headerColor, R.id.intro_phone_toolbar)
themeImageComponent(prefs.bgColor.colorToForeground(0.1f), R.id.intro_phone_image)
themeImageComponent(
Prefs.bgColor.colorToForeground(0.2f),
prefs.bgColor.colorToForeground(0.2f),
R.id.intro_phone_like,
R.id.intro_phone_share
)
themeImageComponent(Prefs.bgColor.colorToForeground(0.3f), R.id.intro_phone_comment)
themeImageComponent(prefs.bgColor.colorToForeground(0.3f), R.id.intro_phone_comment)
themeImageComponent(
Prefs.bgColor.colorToForeground(0.1f),
prefs.bgColor.colorToForeground(0.1f),
R.id.intro_phone_card_1,
R.id.intro_phone_card_2
)
themeImageComponent(
Prefs.textColor,
prefs.textColor,
R.id.intro_phone_image_indicator,
R.id.intro_phone_comment_indicator,
R.id.intro_phone_card_indicator

View File

@ -38,6 +38,7 @@ import com.pitchedapps.frost.activities.IntroActivity
import com.pitchedapps.frost.databinding.IntroAnalyticsBinding
import com.pitchedapps.frost.utils.Prefs
import kotlin.math.abs
import org.koin.android.ext.android.inject
/**
* Created by Allan Wang on 2017-07-28.
@ -50,6 +51,8 @@ import kotlin.math.abs
*/
abstract class BaseIntroFragment(val layoutRes: Int) : Fragment() {
protected val prefs: Prefs by inject()
val screenWidth
get() = resources.displayMetrics.widthPixels
@ -105,7 +108,7 @@ abstract class BaseIntroFragment(val layoutRes: Int) : Fragment() {
}
protected open fun themeFragmentImpl() {
(view as? ViewGroup)?.children?.forEach { (it as? TextView)?.setTextColor(Prefs.textColor) }
(view as? ViewGroup)?.children?.forEach { (it as? TextView)?.setTextColor(prefs.textColor) }
}
protected val viewArray: Array<Array<out View>> by lazyResettableRegistered { viewArray() }
@ -134,7 +137,7 @@ class IntroFragmentWelcome : BaseIntroFragment(R.layout.intro_welcome) {
override fun themeFragmentImpl() {
super.themeFragmentImpl()
image.imageTintList = ColorStateList.valueOf(Prefs.textColor)
image.imageTintList = ColorStateList.valueOf(prefs.textColor)
}
}
@ -153,7 +156,7 @@ class IntroFragmentAnalytics : BaseIntroFragment(R.layout.intro_analytics) {
override fun themeFragmentImpl() {
super.themeFragmentImpl()
image.imageTintList = ColorStateList.valueOf(Prefs.textColor)
image.imageTintList = ColorStateList.valueOf(prefs.textColor)
}
@SuppressLint("ClickableViewAccessibility")
@ -165,9 +168,9 @@ class IntroFragmentAnalytics : BaseIntroFragment(R.layout.intro_analytics) {
private fun IntroAnalyticsBinding.init() {
image.setIcon(GoogleMaterial.Icon.gmd_bug_report, 120)
introSwitch.isSelected = Prefs.analytics
introSwitch.isSelected = prefs.analytics
introSwitch.setOnCheckedChangeListener { _, isChecked ->
Prefs.analytics = isChecked
prefs.analytics = isChecked
}
}
}
@ -180,7 +183,7 @@ class IntroFragmentEnd : BaseIntroFragment(R.layout.intro_end) {
override fun themeFragmentImpl() {
super.themeFragmentImpl()
image.imageTintList = ColorStateList.valueOf(Prefs.textColor)
image.imageTintList = ColorStateList.valueOf(prefs.textColor)
}
@SuppressLint("ClickableViewAccessibility")

View File

@ -67,7 +67,7 @@ enum class NotificationType(
private val overlayContext: OverlayContext,
private val fbItem: FbItem,
private val parser: FrostParser<ParseNotification>,
private val ringtone: () -> String
private val ringtoneProvider: (Prefs) -> String
) {
GENERAL(
@ -75,7 +75,7 @@ enum class NotificationType(
OverlayContext.NOTIFICATION,
FbItem.NOTIFICATIONS,
NotifParser,
Prefs::notificationRingtone
{ it.notificationRingtone }
),
MESSAGE(
@ -83,7 +83,7 @@ enum class NotificationType(
OverlayContext.MESSAGE,
FbItem.MESSAGES,
MessageParser,
Prefs::messageRingtone
{ it.messageRingtone }
);
private val groupPrefix = "frost_${name.toLowerCase(Locale.CANADA)}"
@ -112,7 +112,7 @@ enum class NotificationType(
* Returns the number of notifications generated,
* or -1 if an error occurred
*/
suspend fun fetch(context: Context, data: CookieEntity): Int {
suspend fun fetch(context: Context, data: CookieEntity, prefs: Prefs): Int {
val notifDao = FrostDatabase.get().notifDao()
val response = try {
parser.parse(data.cookie)
@ -129,7 +129,7 @@ enum class NotificationType(
*/
fun validText(text: String?): Boolean {
val t = text ?: return true
return Prefs.notificationKeywords.none {
return prefs.notificationKeywords.none {
t.contains(it, true)
}
}
@ -167,7 +167,7 @@ enum class NotificationType(
frostEvent("Notifications", "Type" to name, "Count" to notifs.size)
if (notifs.size > 1)
summaryNotification(context, userId, notifs.size).notify(context)
val ringtone = ringtone()
val ringtone = ringtoneProvider(prefs)
notifs.forEachIndexed { i, notif ->
// Ring at most twice
notif.withAlert(context, i < 2, ringtone).notify(context)
@ -316,9 +316,9 @@ data class FrostNotification(
NotificationManagerCompat.from(context).notify(tag, id, notif.build())
}
fun Context.scheduleNotificationsFromPrefs(): Boolean {
val shouldSchedule = Prefs.hasNotifications
return if (shouldSchedule) scheduleNotifications(Prefs.notificationFreq)
fun Context.scheduleNotificationsFromPrefs(prefs: Prefs): Boolean {
val shouldSchedule = prefs.hasNotifications
return if (shouldSchedule) scheduleNotifications(prefs.notificationFreq)
else scheduleNotifications(-1)
}

View File

@ -34,6 +34,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import org.koin.android.ext.android.inject
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-06-14.
@ -45,7 +46,8 @@ import org.koin.android.ext.android.inject
*/
class NotificationService : BaseJobService() {
val cookieDao: CookieDao by inject()
private val prefs: Prefs by inject()
private val cookieDao: CookieDao by inject()
override fun onStopJob(params: JobParameters?): Boolean {
super.onStopJob(params)
@ -64,7 +66,7 @@ class NotificationService : BaseJobService() {
frostEvent(
"NotificationTime",
"Type" to (if (abrupt) "Service force stop" else "Service"),
"IM Included" to Prefs.notificationsInstantMessages,
"IM Included" to prefs.notificationsInstantMessages,
"Duration" to time
)
}
@ -86,7 +88,7 @@ class NotificationService : BaseJobService() {
private suspend fun sendNotifications(params: JobParameters?): Unit =
withContext(Dispatchers.Default) {
val currentId = Prefs.userId
val currentId = prefs.userId
val cookies = cookieDao.selectAll()
yield()
val jobId = params?.extras?.getInt(NOTIFICATION_PARAM_ID, -1) ?: -1
@ -94,12 +96,12 @@ class NotificationService : BaseJobService() {
for (cookie in cookies) {
yield()
val current = cookie.id == currentId
if (Prefs.notificationsGeneral &&
(current || Prefs.notificationAllAccounts)
if (prefs.notificationsGeneral &&
(current || prefs.notificationAllAccounts)
)
notifCount += fetch(jobId, NotificationType.GENERAL, cookie)
if (Prefs.notificationsInstantMessages &&
(current || Prefs.notificationsImAllAccounts)
if (prefs.notificationsInstantMessages &&
(current || prefs.notificationsImAllAccounts)
)
notifCount += fetch(jobId, NotificationType.MESSAGE, cookie)
}
@ -117,7 +119,7 @@ class NotificationService : BaseJobService() {
* Also normalized the output to return the number of notifications received
*/
private suspend fun fetch(jobId: Int, type: NotificationType, cookie: CookieEntity): Int {
val count = type.fetch(this, cookie)
val count = type.fetch(this, cookie, prefs)
if (count < 0) {
if (jobId == NOTIFICATION_JOB_NOW)
generalNotification(666, R.string.error_notification, BuildConfig.DEBUG)
@ -133,7 +135,7 @@ class NotificationService : BaseJobService() {
private fun generalNotification(id: Int, textRes: Int, withDefaults: Boolean) {
val notifBuilder = frostNotification(NOTIF_CHANNEL_GENERAL)
.setFrostAlert(this, withDefaults, Prefs.notificationRingtone)
.setFrostAlert(this, withDefaults, prefs.notificationRingtone)
.setContentTitle(string(R.string.frost_name))
.setContentText(string(textRes))
NotificationManagerCompat.from(this).notify(id, notifBuilder.build())

View File

@ -41,13 +41,13 @@ import com.pitchedapps.frost.utils.frostUri
const val NOTIF_CHANNEL_GENERAL = "general"
const val NOTIF_CHANNEL_MESSAGES = "messages"
fun setupNotificationChannels(c: Context) {
fun setupNotificationChannels(c: Context, prefs: Prefs) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
val manager = c.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val appName = c.string(R.string.frost_name)
val msg = c.string(R.string.messages)
manager.createNotificationChannel(NOTIF_CHANNEL_GENERAL, appName)
manager.createNotificationChannel(NOTIF_CHANNEL_MESSAGES, "$appName: $msg")
manager.createNotificationChannel(NOTIF_CHANNEL_GENERAL, appName, prefs)
manager.createNotificationChannel(NOTIF_CHANNEL_MESSAGES, "$appName: $msg", prefs)
manager.notificationChannels
.filter {
it.id != NOTIF_CHANNEL_GENERAL &&
@ -60,14 +60,15 @@ fun setupNotificationChannels(c: Context) {
@RequiresApi(Build.VERSION_CODES.O)
private fun NotificationManager.createNotificationChannel(
id: String,
name: String
name: String,
prefs: Prefs
): NotificationChannel {
val channel = NotificationChannel(
id,
name, NotificationManager.IMPORTANCE_DEFAULT
)
channel.enableLights(true)
channel.lightColor = Prefs.accentColor
channel.lightColor = prefs.accentColor
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
createNotificationChannel(channel)
return channel
@ -93,6 +94,8 @@ fun NotificationCompat.Builder.setFrostAlert(
enable: Boolean,
ringtone: String
): NotificationCompat.Builder {
val prefs = Prefs.get()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setGroupAlertBehavior(
if (enable) NotificationCompat.GROUP_ALERT_CHILDREN
@ -102,12 +105,12 @@ fun NotificationCompat.Builder.setFrostAlert(
setDefaults(0)
} else {
var defaults = 0
if (Prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE
if (Prefs.notificationSound) {
if (prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE
if (prefs.notificationSound) {
if (ringtone.isNotBlank()) setSound(context.frostUri(ringtone))
else defaults = defaults or Notification.DEFAULT_SOUND
}
if (Prefs.notificationLights) defaults = defaults or Notification.DEFAULT_LIGHTS
if (prefs.notificationLights) defaults = defaults or Notification.DEFAULT_LIGHTS
setDefaults(defaults)
}
return this

View File

@ -21,17 +21,21 @@ import android.content.Context
import android.content.Intent
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-05-31.
*
* Receiver that is triggered whenever the app updates so it can bind the notifications again
*/
class UpdateReceiver : BroadcastReceiver() {
class UpdateReceiver : BroadcastReceiver(), KoinComponent {
private val prefs: Prefs by inject()
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != Intent.ACTION_MY_PACKAGE_REPLACED) return
L.d { "Frost has updated" }
context.scheduleNotifications(Prefs.notificationFreq) // Update notifications
context.scheduleNotifications(prefs.notificationFreq) // Update notifications
}
}

View File

@ -28,7 +28,6 @@ import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.enums.MainActivityLayout
import com.pitchedapps.frost.enums.Theme
import com.pitchedapps.frost.injectors.CssAssets
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.REQUEST_NAV
import com.pitchedapps.frost.utils.REQUEST_TEXT_ZOOM
import com.pitchedapps.frost.utils.frostEvent
@ -45,7 +44,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
header(R.string.theme_customization)
text(R.string.theme, Prefs::theme, { Prefs.theme = it }) {
text(R.string.theme, prefs::theme, { prefs.theme = it }) {
onClick = {
materialDialog {
title(R.string.theme)
@ -71,7 +70,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
}
fun KPrefColorPicker.KPrefColorContract.dependsOnCustom() {
enabler = Prefs::isCustomTheme
enabler = prefs::isCustomTheme
onDisabledClick = { frostSnackbar(R.string.requires_custom_theme) }
allowCustom = true
}
@ -80,8 +79,8 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
CssAssets.CUSTOM.reset()
}
colorPicker(R.string.text_color, Prefs::customTextColor, {
Prefs.customTextColor = it
colorPicker(R.string.text_color, prefs::customTextColor, {
prefs.customTextColor = it
reload()
invalidateCustomTheme()
shouldRestartMain()
@ -90,8 +89,8 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
allowCustomAlpha = false
}
colorPicker(R.string.accent_color, Prefs::customAccentColor, {
Prefs.customAccentColor = it
colorPicker(R.string.accent_color, prefs::customAccentColor, {
prefs.customAccentColor = it
reload()
invalidateCustomTheme()
shouldRestartMain()
@ -100,8 +99,8 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
allowCustomAlpha = false
}
colorPicker(R.string.background_color, Prefs::customBackgroundColor, {
Prefs.customBackgroundColor = it
colorPicker(R.string.background_color, prefs::customBackgroundColor, {
prefs.customBackgroundColor = it
bgCanvas.ripple(it, duration = 500L)
invalidateCustomTheme()
setFrostTheme(true)
@ -111,8 +110,8 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
allowCustomAlpha = true
}
colorPicker(R.string.header_color, Prefs::customHeaderColor, {
Prefs.customHeaderColor = it
colorPicker(R.string.header_color, prefs::customHeaderColor, {
prefs.customHeaderColor = it
frostNavigationBar()
toolbarCanvas.ripple(it, RippleCanvas.MIDDLE, RippleCanvas.END, duration = 500L)
reload()
@ -122,8 +121,8 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
allowCustomAlpha = true
}
colorPicker(R.string.icon_color, Prefs::customIconColor, {
Prefs.customIconColor = it
colorPicker(R.string.icon_color, prefs::customIconColor, {
prefs.customIconColor = it
invalidateOptionsMenu()
shouldRestartMain()
}) {
@ -135,9 +134,9 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
text(
R.string.main_activity_layout,
Prefs::mainActivityLayoutType,
{ Prefs.mainActivityLayoutType = it }) {
textGetter = { string(Prefs.mainActivityLayout.titleRes) }
prefs::mainActivityLayoutType,
{ prefs.mainActivityLayoutType = it }) {
textGetter = { string(prefs.mainActivityLayout.titleRes) }
onClick = {
materialDialog {
title(R.string.main_activity_layout_desc)
@ -160,8 +159,8 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
onClick = { launchTabCustomizerActivity() }
}
checkbox(R.string.tint_nav, Prefs::tintNavBar, {
Prefs.tintNavBar = it
checkbox(R.string.tint_nav, prefs::tintNavBar, {
prefs.tintNavBar = it
frostNavigationBar()
setFrostResult(REQUEST_NAV)
}) {
@ -172,15 +171,15 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
KPrefTextSeekbar(
KPrefSeekbar.KPrefSeekbarBuilder(
globalOptions,
R.string.web_text_scaling, Prefs::webTextScaling
R.string.web_text_scaling, prefs::webTextScaling
) {
Prefs.webTextScaling = it
prefs.webTextScaling = it
setFrostResult(REQUEST_TEXT_ZOOM)
})
)
checkbox(R.string.enforce_black_media_bg, Prefs::blackMediaBg, {
Prefs.blackMediaBg = it
checkbox(R.string.enforce_black_media_bg, prefs::blackMediaBg, {
prefs.blackMediaBg = it
}) {
descRes = R.string.enforce_black_media_bg_desc
}

View File

@ -19,54 +19,53 @@ package com.pitchedapps.frost.settings
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.Prefs
/**
* Created by Allan Wang on 2017-06-30.
*/
fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = {
checkbox(R.string.auto_refresh_feed, Prefs::autoRefreshFeed, { Prefs.autoRefreshFeed = it }) {
checkbox(R.string.auto_refresh_feed, prefs::autoRefreshFeed, { prefs.autoRefreshFeed = it }) {
descRes = R.string.auto_refresh_feed_desc
}
checkbox(R.string.fancy_animations, Prefs::animate, { Prefs.animate = it; animate = it }) {
checkbox(R.string.fancy_animations, prefs::animate, { prefs.animate = it; animate = it }) {
descRes = R.string.fancy_animations_desc
}
checkbox(
R.string.overlay_swipe,
Prefs::overlayEnabled,
{ Prefs.overlayEnabled = it; shouldRefreshMain() }) {
prefs::overlayEnabled,
{ prefs.overlayEnabled = it; shouldRefreshMain() }) {
descRes = R.string.overlay_swipe_desc
}
checkbox(
R.string.overlay_full_screen_swipe,
Prefs::overlayFullScreenSwipe,
{ Prefs.overlayFullScreenSwipe = it }) {
prefs::overlayFullScreenSwipe,
{ prefs.overlayFullScreenSwipe = it }) {
descRes = R.string.overlay_full_screen_swipe_desc
}
checkbox(
R.string.open_links_in_default,
Prefs::linksInDefaultApp,
{ Prefs.linksInDefaultApp = it }) {
prefs::linksInDefaultApp,
{ prefs.linksInDefaultApp = it }) {
descRes = R.string.open_links_in_default_desc
}
checkbox(R.string.viewpager_swipe, Prefs::viewpagerSwipe, { Prefs.viewpagerSwipe = it }) {
checkbox(R.string.viewpager_swipe, prefs::viewpagerSwipe, { prefs.viewpagerSwipe = it }) {
descRes = R.string.viewpager_swipe_desc
}
checkbox(
R.string.force_message_bottom,
Prefs::messageScrollToBottom,
{ Prefs.messageScrollToBottom = it }) {
prefs::messageScrollToBottom,
{ prefs.messageScrollToBottom = it }) {
descRes = R.string.force_message_bottom_desc
}
checkbox(R.string.enable_pip, Prefs::enablePip, { Prefs.enablePip = it }) {
checkbox(R.string.enable_pip, prefs::enablePip, { prefs.enablePip = it }) {
descRes = R.string.enable_pip_desc
}
@ -78,11 +77,11 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = {
// }
// }
checkbox(R.string.exit_confirmation, Prefs::exitConfirmation, { Prefs.exitConfirmation = it }) {
checkbox(R.string.exit_confirmation, prefs::exitConfirmation, { prefs.exitConfirmation = it }) {
descRes = R.string.exit_confirmation_desc
}
checkbox(R.string.analytics, Prefs::analytics, { Prefs.analytics = it }) {
checkbox(R.string.analytics, prefs::analytics, { prefs.analytics = it }) {
descRes = R.string.analytics_desc
}
}

View File

@ -32,7 +32,6 @@ import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.activities.SettingsActivity.Companion.ACTIVITY_REQUEST_DEBUG
import com.pitchedapps.frost.debugger.OfflineWebsite
import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.parsers.FrostParser
import com.pitchedapps.frost.facebook.parsers.MessageParser
import com.pitchedapps.frost.facebook.parsers.NotifParser
@ -87,7 +86,7 @@ fun SettingsActivity.getDebugPrefs(): KPrefAdapterBuilder.() -> Unit = {
attempt = launch(Dispatchers.IO) {
try {
val data = parser.parse(FbCookie.webCookie)
val data = parser.parse(fbCookie.webCookie)
withMainContext {
loading.dismiss()
createEmail(parser, data?.data)
@ -113,7 +112,8 @@ private const val ZIP_NAME = "debug"
fun SettingsActivity.sendDebug(url: String, html: String?) {
val downloader = OfflineWebsite(
url, FbCookie.webCookie ?: "",
url,
cookie = fbCookie.webCookie ?: "",
baseUrl = FB_URL_BASE,
html = html,
baseDir = DebugActivity.baseDir(this)

View File

@ -21,7 +21,6 @@ import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import ca.allanwang.kau.logging.KL
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.REQUEST_RESTART_APPLICATION
/**
@ -37,8 +36,8 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = {
// Experimental content ends here --------------------
checkbox(R.string.verbose_logging, Prefs::verboseLogging, {
Prefs.verboseLogging = it
checkbox(R.string.verbose_logging, prefs::verboseLogging, {
prefs.verboseLogging = it
KL.shouldLog = { it != Log.VERBOSE }
}) {
descRes = R.string.verbose_logging_desc

View File

@ -23,7 +23,6 @@ import com.afollestad.materialdialogs.list.listItemsSingleChoice
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.enums.FeedSort
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.REQUEST_FAB
/**
@ -31,7 +30,7 @@ import com.pitchedapps.frost.utils.REQUEST_FAB
*/
fun SettingsActivity.getFeedPrefs(): KPrefAdapterBuilder.() -> Unit = {
text(R.string.newsfeed_sort, Prefs::feedSort, { Prefs.feedSort = it }) {
text(R.string.newsfeed_sort, prefs::feedSort, { prefs.feedSort = it }) {
descRes = R.string.newsfeed_sort_desc
onClick = {
materialDialog {
@ -50,50 +49,50 @@ fun SettingsActivity.getFeedPrefs(): KPrefAdapterBuilder.() -> Unit = {
textGetter = { string(FeedSort(it).textRes) }
}
checkbox(R.string.aggressive_recents, Prefs::aggressiveRecents, {
Prefs.aggressiveRecents = it
checkbox(R.string.aggressive_recents, prefs::aggressiveRecents, {
prefs.aggressiveRecents = it
shouldRefreshMain()
}) {
descRes = R.string.aggressive_recents_desc
}
checkbox(R.string.composer, Prefs::showComposer, {
Prefs.showComposer = it
checkbox(R.string.composer, prefs::showComposer, {
prefs.showComposer = it
shouldRefreshMain()
}) {
descRes = R.string.composer_desc
}
checkbox(R.string.create_fab, Prefs::showCreateFab, {
Prefs.showCreateFab = it
checkbox(R.string.create_fab, prefs::showCreateFab, {
prefs.showCreateFab = it
setFrostResult(REQUEST_FAB)
}) {
descRes = R.string.create_fab_desc
}
checkbox(R.string.suggested_friends, Prefs::showSuggestedFriends, {
Prefs.showSuggestedFriends = it
checkbox(R.string.suggested_friends, prefs::showSuggestedFriends, {
prefs.showSuggestedFriends = it
shouldRefreshMain()
}) {
descRes = R.string.suggested_friends_desc
}
checkbox(R.string.suggested_groups, Prefs::showSuggestedGroups, {
Prefs.showSuggestedGroups = it
checkbox(R.string.suggested_groups, prefs::showSuggestedGroups, {
prefs.showSuggestedGroups = it
shouldRefreshMain()
}) {
descRes = R.string.suggested_groups_desc
}
checkbox(R.string.show_stories, Prefs::showStories, {
Prefs.showStories = it
checkbox(R.string.show_stories, prefs::showStories, {
prefs.showStories = it
shouldRefreshMain()
}) {
descRes = R.string.show_stories_desc
}
checkbox(R.string.facebook_ads, Prefs::showFacebookAds, {
Prefs.showFacebookAds = it
checkbox(R.string.facebook_ads, prefs::showFacebookAds, {
prefs.showFacebookAds = it
shouldRefreshMain()
}) {
descRes = R.string.facebook_ads_desc

View File

@ -19,7 +19,6 @@ package com.pitchedapps.frost.settings
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.Prefs
/**
* Created by Allan Wang on 2017-08-08.
@ -28,8 +27,8 @@ fun SettingsActivity.getNetworkPrefs(): KPrefAdapterBuilder.() -> Unit = {
checkbox(
R.string.network_media_on_metered,
{ !Prefs.loadMediaOnMeteredNetwork },
{ Prefs.loadMediaOnMeteredNetwork = !it }) {
{ !prefs.loadMediaOnMeteredNetwork },
{ prefs.loadMediaOnMeteredNetwork = !it }) {
descRes = R.string.network_media_on_metered_desc
}
}

View File

@ -41,6 +41,7 @@ import com.pitchedapps.frost.utils.frostSnackbar
import com.pitchedapps.frost.utils.frostUri
import com.pitchedapps.frost.views.Keywords
import kotlinx.coroutines.launch
import org.koin.android.ext.android.get
/**
* Created by Allan Wang on 2017-06-29.
@ -54,8 +55,8 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
text(
R.string.notification_frequency,
Prefs::notificationFreq,
{ Prefs.notificationFreq = it }) {
prefs::notificationFreq,
{ prefs.notificationFreq = it }) {
val options = longArrayOf(15, 30, 60, 120, 180, 300, 1440, 2880)
val texts =
options.map { if (it <= 0) string(R.string.no_notifications) else minuteToText(it) }
@ -71,7 +72,7 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
}
}
}
enabler = { Prefs.hasNotifications }
enabler = { prefs.hasNotifications }
textGetter = { minuteToText(it) }
}
@ -88,36 +89,36 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
}
}
checkbox(R.string.notification_general, Prefs::notificationsGeneral,
checkbox(R.string.notification_general, prefs::notificationsGeneral,
{
Prefs.notificationsGeneral = it
prefs.notificationsGeneral = it
reloadByTitle(R.string.notification_general_all_accounts)
if (!Prefs.notificationsInstantMessages)
if (!prefs.notificationsInstantMessages)
reloadByTitle(R.string.notification_frequency)
}) {
descRes = R.string.notification_general_desc
}
checkbox(R.string.notification_general_all_accounts, Prefs::notificationAllAccounts,
{ Prefs.notificationAllAccounts = it }) {
checkbox(R.string.notification_general_all_accounts, prefs::notificationAllAccounts,
{ prefs.notificationAllAccounts = it }) {
descRes = R.string.notification_general_all_accounts_desc
enabler = { Prefs.notificationsGeneral }
enabler = { prefs.notificationsGeneral }
}
checkbox(R.string.notification_messages, Prefs::notificationsInstantMessages,
checkbox(R.string.notification_messages, prefs::notificationsInstantMessages,
{
Prefs.notificationsInstantMessages = it
prefs.notificationsInstantMessages = it
reloadByTitle(R.string.notification_messages_all_accounts)
if (!Prefs.notificationsGeneral)
if (!prefs.notificationsGeneral)
reloadByTitle(R.string.notification_frequency)
}) {
descRes = R.string.notification_messages_desc
}
checkbox(R.string.notification_messages_all_accounts, Prefs::notificationsImAllAccounts,
{ Prefs.notificationsImAllAccounts = it }) {
checkbox(R.string.notification_messages_all_accounts, prefs::notificationsImAllAccounts,
{ prefs.notificationsImAllAccounts = it }) {
descRes = R.string.notification_messages_all_accounts_desc
enabler = { Prefs.notificationsInstantMessages }
enabler = { prefs.notificationsInstantMessages }
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -130,8 +131,8 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
}
}
} else {
checkbox(R.string.notification_sound, Prefs::notificationSound, {
Prefs.notificationSound = it
checkbox(R.string.notification_sound, prefs::notificationSound, {
prefs.notificationSound = it
reloadByTitle(
R.string.notification_ringtone,
R.string.message_ringtone
@ -139,7 +140,7 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
})
fun KPrefText.KPrefTextContract<String>.ringtone(code: Int) {
enabler = Prefs::notificationSound
enabler = prefs::notificationSound
textGetter = {
if (it.isBlank()) string(R.string.kau_default)
else RingtoneManager.getRingtone(this@getNotificationPrefs, frostUri(it))
@ -163,21 +164,21 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
}
}
text(R.string.notification_ringtone, Prefs::notificationRingtone,
{ Prefs.notificationRingtone = it }) {
text(R.string.notification_ringtone, prefs::notificationRingtone,
{ prefs.notificationRingtone = it }) {
ringtone(SettingsActivity.REQUEST_NOTIFICATION_RINGTONE)
}
text(R.string.message_ringtone, Prefs::messageRingtone,
{ Prefs.messageRingtone = it }) {
text(R.string.message_ringtone, prefs::messageRingtone,
{ prefs.messageRingtone = it }) {
ringtone(SettingsActivity.REQUEST_MESSAGE_RINGTONE)
}
checkbox(R.string.notification_vibrate, Prefs::notificationVibrate,
{ Prefs.notificationVibrate = it })
checkbox(R.string.notification_vibrate, prefs::notificationVibrate,
{ prefs.notificationVibrate = it })
checkbox(R.string.notification_lights, Prefs::notificationLights,
{ Prefs.notificationLights = it })
checkbox(R.string.notification_lights, prefs::notificationLights,
{ prefs.notificationLights = it })
}
if (BuildConfig.DEBUG) {

View File

@ -20,7 +20,6 @@ import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.BiometricUtils
import com.pitchedapps.frost.utils.Prefs
import kotlinx.coroutines.launch
/**
@ -32,15 +31,15 @@ fun SettingsActivity.getSecurityPrefs(): KPrefAdapterBuilder.() -> Unit = {
descRes = R.string.security_disclaimer_info
}
checkbox(R.string.enable_biometrics, Prefs::biometricsEnabled, {
checkbox(R.string.enable_biometrics, prefs::biometricsEnabled, {
launch {
/*
* For security, we should request authentication when:
* - enabling to ensure that it is supported
* - disabling to ensure that it is permitted
*/
BiometricUtils.authenticate(this@getSecurityPrefs, force = true).await()
Prefs.biometricsEnabled = it
BiometricUtils.authenticate(this@getSecurityPrefs, prefs, force = true).await()
prefs.biometricsEnabled = it
reloadByTitle(R.string.enable_biometrics)
}
}) {

View File

@ -64,8 +64,8 @@ object BiometricUtils {
private fun getOrCreatePoolSync(): Executor =
pool ?: Executors.newSingleThreadExecutor().also { pool = it }
private fun shouldPrompt(context: Context): Boolean {
return Prefs.biometricsEnabled && System.currentTimeMillis() - lastUnlockTime > UNLOCK_TIME_INTERVAL
private fun shouldPrompt(context: Context, prefs: Prefs): Boolean {
return prefs.biometricsEnabled && System.currentTimeMillis() - lastUnlockTime > UNLOCK_TIME_INTERVAL
}
/**
@ -73,9 +73,9 @@ object BiometricUtils {
* Note that the underlying request will call [androidx.fragment.app.FragmentTransaction.commit],
* so this cannot happen after onSaveInstanceState.
*/
fun authenticate(activity: FragmentActivity, force: Boolean = false): BiometricDeferred {
fun authenticate(activity: FragmentActivity, prefs: Prefs, force: Boolean = false): BiometricDeferred {
val deferred: BiometricDeferred = CompletableDeferred()
if (!force && !shouldPrompt(activity)) {
if (!force && !shouldPrompt(activity, prefs)) {
deferred.complete(null)
return deferred
}

View File

@ -16,7 +16,6 @@
*/
package com.pitchedapps.frost.utils
import android.util.Log
import ca.allanwang.kau.logging.KauLogger
import ca.allanwang.kau.logging.KauLoggerExtension
import com.bugsnag.android.Bugsnag
@ -27,13 +26,7 @@ import com.pitchedapps.frost.BuildConfig
*
* Logging for frost
*/
object L : KauLogger("Frost", {
when (it) {
Log.VERBOSE -> BuildConfig.DEBUG
Log.INFO, Log.ERROR -> true
else -> BuildConfig.DEBUG || Prefs.verboseLogging
}
}) {
object L : KauLogger("Frost") {
inline fun test(message: () -> Any?) {
_d {
@ -59,7 +52,7 @@ object L : KauLogger("Frost", {
}
}
var bugsnagInit = false
var hasAnalytics: () -> Boolean = { false }
override fun logImpl(priority: Int, message: String?, t: Throwable?) {
/*
@ -67,7 +60,7 @@ object L : KauLogger("Frost", {
* bugsnagInit is changed per application and helps prevent crashes (if calling pre init)
* analytics is changed by the user, and may be toggled throughout the app
*/
if (BuildConfig.DEBUG || !bugsnagInit || !Prefs.analytics) {
if (BuildConfig.DEBUG || !hasAnalytics()) {
super.logImpl(priority, message, t)
} else {
if (message != null) {

View File

@ -19,6 +19,7 @@ package com.pitchedapps.frost.utils
import android.graphics.Color
import ca.allanwang.kau.kotlin.lazyResettable
import ca.allanwang.kau.kpref.KPref
import ca.allanwang.kau.kpref.KPrefFactory
import ca.allanwang.kau.utils.colorToForeground
import ca.allanwang.kau.utils.isColorVisibleOn
import ca.allanwang.kau.utils.withAlpha
@ -29,13 +30,15 @@ import com.pitchedapps.frost.enums.FeedSort
import com.pitchedapps.frost.enums.MainActivityLayout
import com.pitchedapps.frost.enums.Theme
import com.pitchedapps.frost.injectors.InjectorContract
import org.koin.core.context.GlobalContext
import org.koin.dsl.module
/**
* Created by Allan Wang on 2017-05-28.
*
* Shared Preference object with lazy cached retrievals
*/
object Prefs : KPref() {
class Prefs(factory: KPrefFactory) : KPref("${BuildConfig.APPLICATION_ID}.prefs", factory) {
var lastLaunch: Long by kpref("last_launch", -1L)
@ -69,15 +72,15 @@ object Prefs : KPref() {
var identifier: Int by kpref("identifier", -1)
private val loader = lazyResettable { Theme.values[Prefs.theme] }
private val loader = lazyResettable { Theme.values[theme] }
val t: Theme by loader
val textColor: Int
get() = t.textColor
get() = t.textColorGetter(this)
val accentColor: Int
get() = t.accentColor
get() = t.accentColorGetter(this)
inline val accentColorForWhite: Int
get() = when {
@ -87,20 +90,20 @@ object Prefs : KPref() {
}
inline val nativeBgColor: Int
get() = Prefs.bgColor.withAlpha(30)
get() = bgColor.withAlpha(30)
fun nativeBgColor(unread: Boolean) = Prefs.bgColor
fun nativeBgColor(unread: Boolean) = bgColor
.colorToForeground(if (unread) 0.7f else 0.0f)
.withAlpha(30)
val bgColor: Int
get() = t.bgColor
get() = t.backgroundColorGetter(this)
val headerColor: Int
get() = t.headerColor
get() = t.headerColorGetter(this)
val iconColor: Int
get() = t.iconColor
get() = t.iconColorGetter(this)
val themeInjector: InjectorContract
get() = t.injector
@ -155,6 +158,10 @@ object Prefs : KPref() {
var enablePip: Boolean by kpref("enable_pip", true)
/**
* Despite the naming, this toggle currently only enables debug logging.
* Verbose is never logged in release builds.
*/
var verboseLogging: Boolean by kpref("verbose_logging", false)
var analytics: Boolean by kpref("analytics", false) {
@ -194,5 +201,11 @@ object Prefs : KPref() {
inline val mainActivityLayout: MainActivityLayout
get() = MainActivityLayout(mainActivityLayoutType)
override fun deleteKeys() = arrayOf("search_bar")
companion object {
fun get(): Prefs = GlobalContext.get().koin.get()
fun module() = module {
single { Prefs(get()) }
}
}
}

View File

@ -17,18 +17,25 @@
package com.pitchedapps.frost.utils
import ca.allanwang.kau.kpref.KPref
import ca.allanwang.kau.kpref.KPrefFactory
import com.pitchedapps.frost.BuildConfig
import org.koin.dsl.module
/**
* Created by Allan Wang on 2017-07-03.
*
* Showcase prefs that offer one time helpers to guide new users
*/
object Showcase : KPref() {
class Showcase(factory: KPrefFactory) : KPref("${BuildConfig.APPLICATION_ID}.showcase", factory) {
// check if this is the first time launching the web overlay; show snackbar if true
val firstWebOverlay: Boolean by kprefSingle("first_web_overlay")
val intro: Boolean by kprefSingle("intro_pages")
override fun deleteKeys() = arrayOf("shown_release", "experimental_by_default")
companion object {
fun module() = module {
single { Showcase(get()) }
}
}
}

View File

@ -123,27 +123,34 @@ fun Activity.cookies(): ArrayList<CookieEntity> {
* Note that most requests may need to first check if the url can be launched as an overlay
* See [requestWebOverlay] to verify the launch
*/
private inline fun <reified T : WebOverlayActivityBase> Context.launchWebOverlayImpl(url: String) {
private inline fun <reified T : WebOverlayActivityBase> Context.launchWebOverlayImpl(
url: String,
fbCookie: FbCookie
) {
val prefs = Prefs.get()
val argUrl = url.formattedFbUrl
L.v { "Launch received: $url\nLaunch web overlay: $argUrl" }
if (argUrl.isFacebookUrl && argUrl.contains("/logout.php")) {
L.d { "Logout php found" }
ctxCoroutine.launch {
FbCookie.logout(this@launchWebOverlayImpl)
fbCookie.logout(this@launchWebOverlayImpl)
}
} else if (!(Prefs.linksInDefaultApp && resolveActivityForUri(Uri.parse(argUrl))))
} else if (!(prefs.linksInDefaultApp && resolveActivityForUri(Uri.parse(argUrl)))) {
startActivity<T>(false, intentBuilder = {
putExtra(ARG_URL, argUrl)
})
}
}
fun Context.launchWebOverlay(url: String) = launchWebOverlayImpl<WebOverlayActivity>(url)
fun Context.launchWebOverlay(url: String, fbCookie: FbCookie) =
launchWebOverlayImpl<WebOverlayActivity>(url, fbCookie)
// TODO Currently, default is overlay. Switch this if default changes
fun Context.launchWebOverlayDesktop(url: String) = launchWebOverlay(url)
fun Context.launchWebOverlayDesktop(url: String, fbCookie: FbCookie) =
launchWebOverlay(url, fbCookie)
fun Context.launchWebOverlayMobile(url: String) =
launchWebOverlayImpl<WebOverlayMobileActivity>(url)
fun Context.launchWebOverlayMobile(url: String, fbCookie: FbCookie) =
launchWebOverlayImpl<WebOverlayMobileActivity>(url, fbCookie)
private fun Context.fadeBundle() = ActivityOptions.makeCustomAnimation(
this,
@ -172,16 +179,17 @@ fun WebOverlayActivity.url(): String {
}
fun Activity.setFrostTheme(forceTransparent: Boolean = false) {
val prefs = Prefs.get()
val isTransparent =
forceTransparent || (Color.alpha(Prefs.bgColor) != 255) || (Color.alpha(Prefs.headerColor) != 255)
if (Prefs.bgColor.isColorDark) {
forceTransparent || (Color.alpha(prefs.bgColor) != 255) || (Color.alpha(prefs.headerColor) != 255)
if (prefs.bgColor.isColorDark) {
setTheme(if (isTransparent) R.style.FrostTheme_Transparent else R.style.FrostTheme)
} else {
setTheme(if (isTransparent) R.style.FrostTheme_Light_Transparent else R.style.FrostTheme_Light)
}
}
class ActivityThemeUtils {
class ActivityThemeUtils(val prefs: Prefs) {
private var toolbar: Toolbar? = null
var themeWindow = true
@ -207,21 +215,21 @@ class ActivityThemeUtils {
fun theme(activity: Activity) {
with(activity) {
statusBarColor = Prefs.headerColor.darken(0.1f).withAlpha(255)
if (Prefs.tintNavBar) navigationBarColor = Prefs.headerColor
if (themeWindow) window.setBackgroundDrawable(ColorDrawable(Prefs.bgColor))
toolbar?.setBackgroundColor(Prefs.headerColor)
toolbar?.setTitleTextColor(Prefs.iconColor)
toolbar?.overflowIcon?.setTint(Prefs.iconColor)
texts.forEach { it.setTextColor(Prefs.textColor) }
headers.forEach { it.setBackgroundColor(Prefs.headerColor) }
backgrounds.forEach { it.setBackgroundColor(Prefs.bgColor) }
statusBarColor = prefs.headerColor.darken(0.1f).withAlpha(255)
if (prefs.tintNavBar) navigationBarColor = prefs.headerColor
if (themeWindow) window.setBackgroundDrawable(ColorDrawable(prefs.bgColor))
toolbar?.setBackgroundColor(prefs.headerColor)
toolbar?.setTitleTextColor(prefs.iconColor)
toolbar?.overflowIcon?.setTint(prefs.iconColor)
texts.forEach { it.setTextColor(prefs.textColor) }
headers.forEach { it.setBackgroundColor(prefs.headerColor) }
backgrounds.forEach { it.setBackgroundColor(prefs.bgColor) }
}
}
}
inline fun Activity.setFrostColors(builder: ActivityThemeUtils.() -> Unit) {
val themer = ActivityThemeUtils()
inline fun Activity.setFrostColors(prefs: Prefs, builder: ActivityThemeUtils.() -> Unit) {
val themer = ActivityThemeUtils(prefs)
themer.builder()
themer.theme(this)
}
@ -248,18 +256,20 @@ fun View.frostSnackbar(@StringRes text: Int, builder: Snackbar.() -> Unit = {})
@SuppressLint("RestrictedApi")
private inline fun frostSnackbar(crossinline builder: Snackbar.() -> Unit): Snackbar.() -> Unit = {
val prefs = Prefs.get()
builder()
// hacky workaround, but it has proper checks and shouldn't crash
((view as? FrameLayout)?.getChildAt(0) as? SnackbarContentLayout)?.apply {
messageView.setTextColor(Prefs.textColor)
actionView.setTextColor(Prefs.accentColor)
messageView.setTextColor(prefs.textColor)
actionView.setTextColor(prefs.accentColor)
// only set if previous text colors are set
view.setBackgroundColor(Prefs.bgColor.withAlpha(255).colorToForeground(0.1f))
view.setBackgroundColor(prefs.bgColor.withAlpha(255).colorToForeground(0.1f))
}
}
fun Activity.frostNavigationBar() {
navigationBarColor = if (Prefs.tintNavBar) Prefs.headerColor else Color.BLACK
val prefs = Prefs.get()
navigationBarColor = if (prefs.tintNavBar) prefs.headerColor else Color.BLACK
}
@Throws(IOException::class)
@ -393,15 +403,14 @@ inline fun Context.sendFrostEmail(subjectId: String, crossinline builder: EmailB
}
fun EmailBuilder.addFrostDetails() {
addItem("Prev version", Prefs.prevVersionCode.toString())
val prefs = Prefs.get()
addItem("Prev version", prefs.prevVersionCode.toString())
val proTag = "FO"
// if (IS_FROST_PRO) "TY" else "FP"
addItem("Random Frost ID", "${Prefs.frostId}-$proTag")
addItem("Random Frost ID", "${prefs.frostId}-$proTag")
addItem("Locale", Locale.getDefault().displayName)
}
fun frostJsoup(url: String): Document = frostJsoup(FbCookie.webCookie, url)
fun frostJsoup(cookie: String?, url: String): Document =
Jsoup.connect(url).run {
if (cookie.isNullOrBlank()) this
@ -438,7 +447,7 @@ fun String.unescapeHtml(): String =
.replace("\\u003C", "<")
.replace("\\\"", "\"")
suspend fun Context.loadAssets(): Unit = coroutineScope {
CssAssets.load(this@loadAssets)
suspend fun Context.loadAssets(prefs: Prefs): Unit = coroutineScope {
CssAssets.load(this@loadAssets, prefs)
JsAssets.load(this@loadAssets)
}

View File

@ -25,12 +25,13 @@ import com.afollestad.materialdialogs.callbacks.onDismiss
import com.afollestad.materialdialogs.list.listItems
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.formattedFbUrl
/**
* Created by Allan Wang on 2017-07-07.
*/
fun Context.showWebContextMenu(wc: WebContext) {
fun Context.showWebContextMenu(wc: WebContext, fbCookie: FbCookie) {
if (wc.isEmpty) return
var title = wc.url ?: string(R.string.menu)
title =
@ -43,7 +44,7 @@ fun Context.showWebContextMenu(wc: WebContext) {
materialDialog {
title(text = title)
listItems(items = menuItems.map { string(it.textId) }) { _, position, _ ->
menuItems[position].onClick(this@showWebContextMenu, wc)
menuItems[position].onClick(this@showWebContextMenu, wc, fbCookie)
}
onDismiss {
// showing the dialog interrupts the touch down event, so we must ensure that the viewpager's swipe is enabled
@ -65,16 +66,16 @@ class WebContext(val unformattedUrl: String?, val text: String?) {
enum class WebContextType(
val textId: Int,
val constraint: (wc: WebContext) -> Boolean,
val onClick: (c: Context, wc: WebContext) -> Unit
val onClick: (c: Context, wc: WebContext, fc: FbCookie) -> Unit
) {
OPEN_LINK(
R.string.open_link,
{ it.hasUrl },
{ c, wc -> c.launchWebOverlay(wc.url!!) }),
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, wc, fc -> c.launchWebOverlay(wc.url!!, fc) }),
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.materialDialog {
title(R.string.debug_link)
message(R.string.debug_link_desc)

View File

@ -38,18 +38,23 @@ import com.pitchedapps.frost.facebook.profilePictureUrl
import com.pitchedapps.frost.glide.FrostGlide
import com.pitchedapps.frost.glide.GlideApp
import com.pitchedapps.frost.utils.Prefs
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-06-05.
*/
class AccountItem(val cookie: CookieEntity?) : KauIItem<AccountItem.ViewHolder>
(R.layout.view_account, { ViewHolder(it) }, R.id.item_account) {
class AccountItem(val cookie: CookieEntity?) :
KauIItem<AccountItem.ViewHolder>(R.layout.view_account, { ViewHolder(it) }, R.id.item_account),
KoinComponent {
private val prefs: Prefs by inject()
override fun bindView(holder: ViewHolder, payloads: MutableList<Any>) {
super.bindView(holder, payloads)
with(holder) {
text.invisible()
text.setTextColor(Prefs.textColor)
text.setTextColor(prefs.textColor)
if (cookie != null) {
text.text = cookie.name
GlideApp.with(itemView).load(profilePictureUrl(cookie.id))
@ -81,7 +86,7 @@ class AccountItem(val cookie: CookieEntity?) : KauIItem<AccountItem.ViewHolder>
GoogleMaterial.Icon.gmd_add_circle_outline.toDrawable(
itemView.context,
100,
Prefs.textColor
prefs.textColor
)
)
text.text = itemView.context.getString(R.string.kau_add_account)

View File

@ -30,6 +30,8 @@ import ca.allanwang.kau.utils.withAlpha
import com.mikepenz.iconics.typeface.IIcon
import com.pitchedapps.frost.databinding.ViewBadgedIconBinding
import com.pitchedapps.frost.utils.Prefs
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-06-19.
@ -38,8 +40,9 @@ class BadgedIcon @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
) : ConstraintLayout(context, attrs, defStyleAttr), KoinComponent {
private val prefs: Prefs by inject()
private val binding: ViewBadgedIconBinding =
ViewBadgedIconBinding.inflate(LayoutInflater.from(context), this, true)
@ -49,7 +52,7 @@ class BadgedIcon @JvmOverloads constructor(
fun ViewBadgedIconBinding.init() {
val badgeColor =
Prefs.mainActivityLayout.backgroundColor().withAlpha(255).colorToForeground(0.2f)
prefs.mainActivityLayout.backgroundColor(prefs).withAlpha(255).colorToForeground(0.2f)
val badgeBackground =
GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP,
@ -57,7 +60,7 @@ class BadgedIcon @JvmOverloads constructor(
)
badgeBackground.cornerRadius = 13.dpToPx.toFloat()
badgeText.background = badgeBackground
badgeText.setTextColor(Prefs.mainActivityLayout.iconColor())
badgeText.setTextColor(prefs.mainActivityLayout.iconColor(prefs))
}
var iicon: IIcon? = null
@ -67,13 +70,13 @@ class BadgedIcon @JvmOverloads constructor(
value?.toDrawable(
context,
sizeDp = 20,
color = Prefs.mainActivityLayout.iconColor()
color = prefs.mainActivityLayout.iconColor(prefs)
)
)
}
fun setAllAlpha(alpha: Float) {
// badgeTextView.setTextColor(Prefs.textColor.withAlpha(alpha.toInt()))
// badgeTextView.setTextColor(prefs.textColor.withAlpha(alpha.toInt()))
binding.badgeImage.drawable.alpha = alpha.toInt()
}

View File

@ -47,6 +47,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.channels.ReceiveChannel
import org.koin.core.KoinComponent
import org.koin.core.inject
class FrostContentWeb @JvmOverloads constructor(
context: Context,
@ -75,8 +77,9 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes),
FrostContentParent where T : View, T : FrostContentCore {
FrostContentParent, KoinComponent where T : View, T : FrostContentCore {
private val prefs: Prefs by inject()
private val refresh: SwipeRefreshLayout by bindView(R.id.content_refresh)
private val progress: ProgressBar by bindView(R.id.content_progress)
val coreView: T by bindView(R.id.content_core)
@ -153,9 +156,9 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
}
override fun reloadThemeSelf() {
progress.tint(Prefs.textColor.withAlpha(180))
refresh.setColorSchemeColors(Prefs.iconColor)
refresh.setProgressBackgroundColorSchemeColor(Prefs.headerColor.withAlpha(255))
progress.tint(prefs.textColor.withAlpha(180))
refresh.setColorSchemeColors(prefs.iconColor)
refresh.setProgressBackgroundColorSchemeColor(prefs.headerColor.withAlpha(255))
}
override fun reloadTextSizeSelf() {
@ -192,7 +195,7 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
if (isVisible)
fadeOut(duration = 200L)
} else if (loading) {
if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
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" }
receiver.cancel()

View File

@ -30,6 +30,8 @@ import com.pitchedapps.frost.fragments.RecyclerContentContract
import com.pitchedapps.frost.utils.Prefs
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-05-29.
@ -41,8 +43,11 @@ class FrostRecyclerView @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr),
KoinComponent,
FrostContentCore {
private val prefs: Prefs by inject()
override fun reload(animate: Boolean) = reloadBase(animate)
override lateinit var parent: FrostContentParent
@ -71,13 +76,13 @@ class FrostRecyclerView @JvmOverloads constructor(
var onReloadClear: () -> Unit = {}
override fun reloadBase(animate: Boolean) {
if (Prefs.animate) fadeOut(onFinish = onReloadClear)
if (prefs.animate) fadeOut(onFinish = onReloadClear)
scope.launch {
parent.refreshChannel.offer(true)
recyclerContract.reload { parent.progressChannel.offer(it) }
parent.progressChannel.offer(100)
parent.refreshChannel.offer(false)
if (Prefs.animate) circularReveal()
if (prefs.animate) circularReveal()
}
}

View File

@ -47,6 +47,8 @@ import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.ctxCoroutine
import com.pitchedapps.frost.utils.frostDownload
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-10-13.
@ -55,7 +57,7 @@ class FrostVideoViewer @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), FrostVideoViewerContract {
) : FrameLayout(context, attrs, defStyleAttr), FrostVideoViewerContract, KoinComponent {
companion object {
/**
@ -85,6 +87,8 @@ class FrostVideoViewer @JvmOverloads constructor(
}
}
private val prefs: Prefs by inject()
private val binding: ViewVideoBinding =
ViewVideoBinding.inflate(LayoutInflater.from(context), this, true)
@ -95,8 +99,8 @@ class FrostVideoViewer @JvmOverloads constructor(
fun ViewVideoBinding.init() {
alpha = 0f
videoBackground.setBackgroundColor(
if (!Prefs.blackMediaBg && Prefs.bgColor.isColorDark)
Prefs.bgColor.withMinAlpha(200)
if (!prefs.blackMediaBg && prefs.bgColor.isColorDark)
prefs.bgColor.withMinAlpha(200)
else
Color.BLACK
)
@ -104,7 +108,7 @@ class FrostVideoViewer @JvmOverloads constructor(
video.pause()
videoToolbar.inflateMenu(R.menu.menu_video)
context.setMenuIcons(
videoToolbar.menu, Prefs.iconColor,
videoToolbar.menu, prefs.iconColor,
R.id.action_pip to GoogleMaterial.Icon.gmd_picture_in_picture_alt,
R.id.action_download to GoogleMaterial.Icon.gmd_file_download
)

View File

@ -22,6 +22,8 @@ import android.util.AttributeSet
import android.view.MotionEvent
import androidx.viewpager.widget.ViewPager
import com.pitchedapps.frost.utils.Prefs
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-07-07.
@ -29,12 +31,14 @@ import com.pitchedapps.frost.utils.Prefs
* Basic override to allow us to control swiping
*/
class FrostViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ViewPager(context, attrs) {
ViewPager(context, attrs), KoinComponent {
private val prefs: Prefs by inject()
var enableSwipe = true
override fun onInterceptTouchEvent(ev: MotionEvent?) =
try {
Prefs.viewpagerSwipe && enableSwipe && super.onInterceptTouchEvent(ev)
prefs.viewpagerSwipe && enableSwipe && super.onInterceptTouchEvent(ev)
} catch (e: IllegalArgumentException) {
false
}
@ -42,7 +46,7 @@ class FrostViewPager @JvmOverloads constructor(context: Context, attrs: Attribut
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent?): Boolean =
try {
Prefs.viewpagerSwipe && enableSwipe && super.onTouchEvent(ev)
prefs.viewpagerSwipe && enableSwipe && super.onTouchEvent(ev)
} catch (e: IllegalArgumentException) {
false
}

View File

@ -31,6 +31,7 @@ import com.pitchedapps.frost.contracts.FrostContentParent
import com.pitchedapps.frost.db.FrostDatabase
import com.pitchedapps.frost.db.currentCookie
import com.pitchedapps.frost.facebook.FB_HOME_URL
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.USER_AGENT
import com.pitchedapps.frost.fragments.WebFragment
import com.pitchedapps.frost.utils.L
@ -44,6 +45,8 @@ import com.pitchedapps.frost.web.NestedWebView
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-05-29.
@ -54,7 +57,11 @@ class FrostWebView @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : NestedWebView(context, attrs, defStyleAttr),
FrostContentCore {
FrostContentCore,
KoinComponent {
val fbCookie: FbCookie by inject()
val prefs: Prefs by inject()
override fun reload(animate: Boolean) {
if (parent.registerTransition(false, animate))
@ -75,7 +82,7 @@ class FrostWebView @JvmOverloads constructor(
javaScriptEnabled = true
mediaPlaybackRequiresUserGesture = false // TODO check if we need this
allowFileAccess = true
textZoom = Prefs.webTextScaling
textZoom = prefs.webTextScaling
domStorageEnabled = true
}
setLayerType(LAYER_TYPE_HARDWARE, null)
@ -208,7 +215,7 @@ class FrostWebView @JvmOverloads constructor(
}
override fun reloadTextSizeSelf() {
settings.textZoom = Prefs.webTextScaling
settings.textZoom = prefs.webTextScaling
}
override fun destroy() {

View File

@ -38,6 +38,8 @@ import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.Prefs
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-06-19.
@ -46,8 +48,9 @@ class Keywords @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
) : ConstraintLayout(context, attrs, defStyleAttr), KoinComponent {
private val prefs: Prefs by inject()
val editText: AppCompatEditText by bindView(R.id.edit_text)
val addIcon: ImageView by bindView(R.id.add_icon)
val recycler: RecyclerView by bindView(R.id.recycler)
@ -55,8 +58,8 @@ class Keywords @JvmOverloads constructor(
init {
inflate(context, R.layout.view_keywords, this)
editText.tint(Prefs.textColor)
addIcon.setImageDrawable(GoogleMaterial.Icon.gmd_add.keywordDrawable(context))
editText.tint(prefs.textColor)
addIcon.setImageDrawable(GoogleMaterial.Icon.gmd_add.keywordDrawable(context, prefs))
addIcon.setOnClickListener {
if (editText.text.isNullOrEmpty()) editText.error =
context.string(R.string.empty_keyword)
@ -65,7 +68,7 @@ class Keywords @JvmOverloads constructor(
editText.text?.clear()
}
}
adapter.add(Prefs.notificationKeywords.map { KeywordItem(it) })
adapter.add(prefs.notificationKeywords.map { KeywordItem(it) })
recycler.layoutManager = LinearLayoutManager(context)
recycler.adapter = adapter
adapter.addEventHook(object : ClickEventHook<KeywordItem>() {
@ -84,12 +87,12 @@ class Keywords @JvmOverloads constructor(
}
fun save() {
Prefs.notificationKeywords = adapter.adapterItems.mapTo(mutableSetOf()) { it.keyword }
prefs.notificationKeywords = adapter.adapterItems.mapTo(mutableSetOf()) { it.keyword }
}
}
private fun IIcon.keywordDrawable(context: Context): Drawable =
toDrawable(context, 20, Prefs.textColor)
private fun IIcon.keywordDrawable(context: Context, prefs: Prefs): Drawable =
toDrawable(context, 20, prefs.textColor)
class KeywordItem(val keyword: String) : AbstractItem<KeywordItem.ViewHolder>() {
@ -111,13 +114,15 @@ class KeywordItem(val keyword: String) : AbstractItem<KeywordItem.ViewHolder>()
holder.text.text = null
}
class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
class ViewHolder(v: View) : RecyclerView.ViewHolder(v), KoinComponent {
private val prefs: Prefs by inject()
val text: AppCompatTextView by bindView(R.id.keyword_text)
val delete: ImageView by bindView(R.id.keyword_delete)
init {
text.setTextColor(Prefs.textColor)
delete.setImageDrawable(GoogleMaterial.Icon.gmd_delete.keywordDrawable(itemView.context))
text.setTextColor(prefs.textColor)
delete.setImageDrawable(GoogleMaterial.Icon.gmd_delete.keywordDrawable(itemView.context, prefs))
}
}
}

View File

@ -34,6 +34,8 @@ import com.pitchedapps.frost.utils.isFacebookUrl
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2018-01-05.
@ -44,8 +46,9 @@ class DebugWebView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr) {
) : WebView(context, attrs, defStyleAttr), KoinComponent {
private val prefs: Prefs by inject()
var onPageFinished: (String?) -> Unit = {}
init {
@ -93,7 +96,7 @@ class DebugWebView @JvmOverloads constructor(
private fun injectBackgroundColor() {
setBackgroundColor(
if (url.isFacebookUrl) Prefs.bgColor.withAlpha(255)
if (url.isFacebookUrl) prefs.bgColor.withAlpha(255)
else Color.WHITE
)
}
@ -104,15 +107,16 @@ class DebugWebView @JvmOverloads constructor(
if (url.isFacebookUrl)
view.jsInject(
// CssHider.CORE,
CssHider.COMPOSER.maybe(!Prefs.showComposer),
CssHider.STORIES.maybe(!Prefs.showStories),
CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!Prefs.showSuggestedFriends),
CssHider.SUGGESTED_GROUPS.maybe(!Prefs.showSuggestedGroups),
Prefs.themeInjector,
CssHider.COMPOSER.maybe(!prefs.showComposer),
CssHider.STORIES.maybe(!prefs.showStories),
CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!prefs.showSuggestedFriends),
CssHider.SUGGESTED_GROUPS.maybe(!prefs.showSuggestedGroups),
prefs.themeInjector,
CssHider.NON_RECENT.maybe(
(url?.contains("?sk=h_chr") ?: false) &&
Prefs.aggressiveRecents
)
prefs.aggressiveRecents
),
prefs = prefs
)
}
}

View File

@ -41,6 +41,8 @@ import kotlinx.coroutines.launch
*/
class FrostJSI(val web: FrostWebView) {
private val fbCookie: FbCookie get() = web.fbCookie
private val prefs: Prefs get() = web.prefs
private val context: Context = web.context
private val activity: MainActivity? = context as? MainActivity
private val header: SendChannel<String>? = activity?.headerBadgeChannel
@ -57,7 +59,7 @@ class FrostJSI(val web: FrostWebView) {
@JavascriptInterface
fun loadVideo(url: String?, isGif: Boolean): Boolean =
if (url != null && Prefs.enablePip) {
if (url != null && prefs.enablePip) {
web.post {
(context as? VideoViewHolder)?.showVideo(url, isGif)
?: L.e { "Could not load video; contract not implemented" }
@ -79,7 +81,7 @@ class FrostJSI(val web: FrostWebView) {
@JavascriptInterface
fun contextMenu(url: String?, text: String?) {
// url will be formatted through webcontext
web.post { context.showWebContextMenu(WebContext(url.takeIf { it.isIndependent }, text)) }
web.post { context.showWebContextMenu(WebContext(url.takeIf { it.isIndependent }, text), fbCookie) }
}
/**
@ -113,7 +115,7 @@ class FrostJSI(val web: FrostWebView) {
fun loadLogin() {
L.d { "Sign up button found; load login" }
context.ctxCoroutine.launch {
FbCookie.logout(context)
fbCookie.logout(context)
}
}

View File

@ -20,12 +20,10 @@ import ca.allanwang.kau.utils.runOnUiThread
import com.pitchedapps.frost.activities.WebOverlayActivity
import com.pitchedapps.frost.activities.WebOverlayActivityBase
import com.pitchedapps.frost.contracts.VideoViewHolder
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.USER_AGENT_DESKTOP_CONST
import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.isFacebookUrl
import com.pitchedapps.frost.utils.isImageUrl
import com.pitchedapps.frost.utils.isIndependent
@ -69,32 +67,32 @@ fun FrostWebView.requestWebOverlay(url: String): Boolean {
}
if (url.isIndirectImageUrl) {
L.d { "Found indirect fb image" }
context.launchImageActivity(url, cookie = FbCookie.webCookie)
context.launchImageActivity(url, cookie = fbCookie.webCookie)
return true
}
if (!url.isIndependent) {
L.d { "Forbid overlay switch" }
return false
}
if (!Prefs.overlayEnabled) return false
if (!prefs.overlayEnabled) return false
if (context is WebOverlayActivityBase) {
val shouldUseDesktop = url.isFacebookUrl
// already overlay; manage user agent
if (userAgentString != USER_AGENT_DESKTOP_CONST && shouldUseDesktop) {
L._i { "Switch to desktop agent overlay" }
context.launchWebOverlayDesktop(url)
context.launchWebOverlayDesktop(url, fbCookie)
return true
}
if (userAgentString == USER_AGENT_DESKTOP_CONST && !shouldUseDesktop) {
L._i { "Switch from desktop agent" }
context.launchWebOverlayMobile(url)
context.launchWebOverlayMobile(url, fbCookie)
return true
}
L._i { "return false switch" }
return false
}
L.v { "Request web overlay passed" }
context.launchWebOverlay(url)
context.launchWebOverlay(url, fbCookie)
return true
}

View File

@ -67,6 +67,8 @@ open class BaseWebViewClient : WebViewClient() {
*/
open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
private val fbCookie: FbCookie get() = web.fbCookie
private val prefs: Prefs get() = web.prefs
private val refresh: SendChannel<Boolean> = web.parent.refreshChannel
private val isMain = web.parent.baseEnum != null
/**
@ -102,7 +104,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
web.setBackgroundColor(
when {
isMain -> Color.TRANSPARENT
web.url.isFacebookUrl -> Prefs.bgColor.withAlpha(255)
web.url.isFacebookUrl -> prefs.bgColor.withAlpha(255)
else -> Color.WHITE
}
)
@ -115,21 +117,22 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
view.jsInject(
// CssHider.CORE,
CssHider.HEADER,
CssHider.COMPOSER.maybe(!Prefs.showComposer),
CssHider.STORIES.maybe(!Prefs.showStories),
CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!Prefs.showSuggestedFriends),
CssHider.SUGGESTED_GROUPS.maybe(!Prefs.showSuggestedGroups),
Prefs.themeInjector,
CssHider.COMPOSER.maybe(!prefs.showComposer),
CssHider.STORIES.maybe(!prefs.showStories),
CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!prefs.showSuggestedFriends),
CssHider.SUGGESTED_GROUPS.maybe(!prefs.showSuggestedGroups),
prefs.themeInjector,
CssHider.NON_RECENT.maybe(
(web.url?.contains("?sk=h_chr") ?: false) &&
Prefs.aggressiveRecents
prefs.aggressiveRecents
),
JsAssets.DOCUMENT_WATCHER,
JsAssets.HORIZONTAL_SCROLLING,
JsAssets.CLICK_A,
CssHider.ADS.maybe(!Prefs.showFacebookAds),
CssHider.ADS.maybe(!prefs.showFacebookAds),
JsAssets.CONTEXT_A,
JsAssets.MEDIA
JsAssets.MEDIA,
prefs = prefs
)
} else {
refresh.offer(false)
@ -147,7 +150,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
}
internal open fun onPageFinishedActions(url: String) {
if (url.startsWith("${FbItem.MESSAGES.url}/read/") && Prefs.messageScrollToBottom) {
if (url.startsWith("${FbItem.MESSAGES.url}/read/") && prefs.messageScrollToBottom) {
web.pageDown(true)
}
injectAndFinish()
@ -160,7 +163,8 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
web.jsInject(
JsActions.LOGIN_CHECK,
JsAssets.TEXTAREA_LISTENER,
JsAssets.HEADER_BADGES.maybe(isMain)
JsAssets.HEADER_BADGES.maybe(isMain),
prefs = prefs
)
}
@ -202,12 +206,12 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() {
return launchRequest(request)
}
if (url.isIndirectImageUrl) {
return launchImage(url.formattedFbUrl, cookie = FbCookie.webCookie)
return launchImage(url.formattedFbUrl, cookie = fbCookie.webCookie)
}
if (url.isImageUrl) {
return launchImage(url.formattedFbUrl)
}
if (Prefs.linksInDefaultApp && view.context.resolveActivityForUri(request.url)) {
if (prefs.linksInDefaultApp && view.context.resolveActivityForUri(request.url)) {
return true
}
// Convert desktop urls to mobile ones
@ -229,12 +233,14 @@ private const val EMIT_FINISH = 0
*/
class FrostWebViewClientMenu(web: FrostWebView) : FrostWebViewClient(web) {
private val prefs: Prefs get() = web.prefs
override fun onPageFinished(view: WebView, url: String?) {
super.onPageFinished(view, url)
if (url == null) {
return
}
jsInject(JsAssets.MENU)
jsInject(JsAssets.MENU, prefs = prefs)
}
override fun emit(flag: Int) {

View File

@ -42,6 +42,8 @@ import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.isFacebookUrl
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.coroutineScope
import org.koin.core.KoinComponent
import org.koin.core.inject
/**
* Created by Allan Wang on 2017-05-29.
@ -50,8 +52,10 @@ class LoginWebView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr) {
) : WebView(context, attrs, defStyleAttr), KoinComponent {
private val fbCookie: FbCookie by inject()
private val prefs: Prefs by inject()
private val completable: CompletableDeferred<CookieEntity> = CompletableDeferred()
private lateinit var progressCallback: (Int) -> Unit
@ -69,7 +73,7 @@ class LoginWebView @JvmOverloads constructor(
this@LoginWebView.progressCallback = progressCallback
L.d { "Begin loading login" }
launchMain {
FbCookie.reset()
fbCookie.reset()
setupWebview()
loadUrl(FB_LOGIN_URL)
}
@ -101,7 +105,8 @@ class LoginWebView @JvmOverloads constructor(
if (url.isFacebookUrl)
view.jsInject(
CssHider.CORE,
Prefs.themeInjector
prefs.themeInjector,
prefs = prefs
)
}

View File

@ -50,7 +50,9 @@ import com.pitchedapps.frost.utils.toReadableTime
import org.koin.core.KoinComponent
import org.koin.core.inject
class NotificationWidget : AppWidgetProvider() {
class NotificationWidget : AppWidgetProvider(), KoinComponent {
private val prefs: Prefs by inject()
override fun onUpdate(
context: Context,
@ -59,19 +61,19 @@ class NotificationWidget : AppWidgetProvider() {
) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
val type = NotificationType.GENERAL
val userId = Prefs.userId
val userId = prefs.userId
val intent = NotificationWidgetService.createIntent(context, type, userId)
for (id in appWidgetIds) {
val views = RemoteViews(context.packageName, R.layout.widget_notifications)
views.setBackgroundColor(R.id.widget_layout_toolbar, Prefs.headerColor)
views.setIcon(R.id.img_frost, context, R.drawable.frost_f_24, Prefs.iconColor)
views.setBackgroundColor(R.id.widget_layout_toolbar, prefs.headerColor)
views.setIcon(R.id.img_frost, context, R.drawable.frost_f_24, prefs.iconColor)
views.setOnClickPendingIntent(
R.id.img_frost,
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0)
)
views.setBackgroundColor(R.id.widget_notification_list, Prefs.bgColor)
views.setBackgroundColor(R.id.widget_notification_list, prefs.bgColor)
views.setRemoteAdapter(R.id.widget_notification_list, intent)
val pendingIntentTemplate = PendingIntent.getActivity(
@ -149,6 +151,8 @@ class NotificationWidgetDataProvider(val context: Context, val intent: Intent) :
RemoteViewsService.RemoteViewsFactory,
KoinComponent {
private val prefs: Prefs by inject()
private val notifDao: NotificationDao by inject()
@Volatile
private var content: List<NotificationContent> = emptyList()
@ -182,10 +186,10 @@ class NotificationWidgetDataProvider(val context: Context, val intent: Intent) :
val views = RemoteViews(context.packageName, R.layout.widget_notification_item)
try {
val notif = content[position]
views.setBackgroundColor(R.id.item_frame, Prefs.nativeBgColor(notif.unread))
views.setTextColor(R.id.item_content, Prefs.textColor)
views.setBackgroundColor(R.id.item_frame, prefs.nativeBgColor(notif.unread))
views.setTextColor(R.id.item_content, prefs.textColor)
views.setTextViewText(R.id.item_content, notif.text)
views.setTextColor(R.id.item_date, Prefs.textColor.withAlpha(150))
views.setTextColor(R.id.item_date, prefs.textColor.withAlpha(150))
views.setTextViewText(R.id.item_date, notif.timestamp.toReadableTime(context))
val avatar = glide.load(notif.profileUrl).transform(FrostGlide.circleCrop)

View File

@ -16,7 +16,7 @@ org.gradle.daemon = true
APP_ID=Frost
APP_GROUP=com.pitchedapps
KAU=e4bba6f
KAU=5038b93
android.useAndroidX=true
android.enableJetifier=true