diff options
96 files changed, 1085 insertions, 509 deletions
diff --git a/app/build.gradle b/app/build.gradle index c6e1aef8..24fdd28e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-kapt' //apply plugin: 'com.getkeepsafe.dexcount' apply plugin: 'com.gladed.androidgitversion' @@ -204,11 +204,6 @@ android { } } - androidExtensions { - experimental = true - features = ["parcelize"] - } - } node { @@ -314,7 +309,7 @@ dependencies { implementation "androidx.room:room-ktx:${Versions.room}" implementation "androidx.room:room-runtime:${Versions.room}" kapt "androidx.room:room-compiler:${Versions.room}" - testImplementation "androidx.room:room-testing:${Versions.room}" + androidTestImplementation "androidx.room:room-testing:${Versions.room}" } diff --git a/app/src/androidTest/kotlin/com/pitchedapps/frost/FrostTestApp.kt b/app/src/androidTest/kotlin/com/pitchedapps/frost/FrostTestApp.kt index 5103a0cf..9d0caae6 100644 --- a/app/src/androidTest/kotlin/com/pitchedapps/frost/FrostTestApp.kt +++ b/app/src/androidTest/kotlin/com/pitchedapps/frost/FrostTestApp.kt @@ -23,6 +23,7 @@ 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.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import org.junit.rules.TestRule import org.junit.runner.Description @@ -70,7 +71,8 @@ class FrostTestApp : Application() { FrostDatabase.module(), prefFactoryModule(), Prefs.module(), - FbCookie.module() + FbCookie.module(), + ThemeProvider.module() ) ) } diff --git a/app/src/androidTest/kotlin/com/pitchedapps/frost/db/CookieMigrationTest.kt b/app/src/androidTest/kotlin/com/pitchedapps/frost/db/CookieMigrationTest.kt new file mode 100644 index 00000000..6ba6e0b6 --- /dev/null +++ b/app/src/androidTest/kotlin/com/pitchedapps/frost/db/CookieMigrationTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2021 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.db + +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import kotlin.test.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class CookieMigrationTest { + + private val TEST_DB = "cookie_migration_test" + + private val ALL_MIGRATIONS = arrayOf(COOKIES_MIGRATION_1_2) + + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + FrostPrivateDatabase::class.java.canonicalName, + FrameworkSQLiteOpenHelperFactory() + ) + + @Test + fun migrateAll() { + // Create earliest version of the database. + helper.createDatabase(TEST_DB, 1).apply { + close() + } + + // Open latest version of the database. Room will validate the schema + // once all migrations execute. + Room.databaseBuilder( + InstrumentationRegistry.getInstrumentation().targetContext, + FrostPrivateDatabase::class.java, + TEST_DB + ).addMigrations(*ALL_MIGRATIONS).build().apply { + openHelper.writableDatabase + close() + } + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index 40333d25..100aeecb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -26,6 +26,7 @@ import ca.allanwang.kau.logging.KL import ca.allanwang.kau.utils.buildIsLollipopAndUp import com.pitchedapps.frost.db.FrostDatabase import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.services.scheduleNotificationsFromPrefs import com.pitchedapps.frost.services.setupNotificationChannels @@ -46,6 +47,7 @@ import org.koin.dsl.module class FrostApp : Application(), KoinComponent { private lateinit var prefs: Prefs + private lateinit var themeProvider: ThemeProvider override fun onCreate() { startKoin { @@ -58,7 +60,8 @@ class FrostApp : Application(), KoinComponent { FrostDatabase.module(), prefFactoryModule(), Prefs.module(), - FbCookie.module() + FbCookie.module(), + ThemeProvider.module() ) ) } @@ -67,6 +70,7 @@ class FrostApp : Application(), KoinComponent { return } prefs = get() + themeProvider = get() initPrefs() L.i { "Begin Frost for Facebook" } @@ -74,7 +78,7 @@ class FrostApp : Application(), KoinComponent { super.onCreate() - setupNotificationChannels(this, prefs) + setupNotificationChannels(this, themeProvider) scheduleNotificationsFromPrefs(prefs) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt index b2031f96..3248eb14 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt @@ -37,6 +37,7 @@ import com.pitchedapps.frost.db.CookieEntity import com.pitchedapps.frost.db.GenericDao import com.pitchedapps.frost.db.selectAll import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.BiometricUtils import com.pitchedapps.frost.utils.EXTRA_COOKIES @@ -54,6 +55,7 @@ class StartActivity : KauBaseActivity() { private val fbCookie: FbCookie by inject() private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private val cookieDao: CookieDao by inject() private val genericDao: GenericDao by inject() @@ -85,7 +87,7 @@ class StartActivity : KauBaseActivity() { transform = CookieEntity::toSensitiveString )}" } - loadAssets(prefs) + loadAssets(themeProvider) authDefer.await() when { cookies.isEmpty() -> launchNewTask<LoginActivity>() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt index 17cac703..74d876cb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt @@ -47,9 +47,11 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.R +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import org.koin.android.ext.android.inject +import org.koin.core.component.inject /** * Created by Allan Wang on 2017-06-26. @@ -57,12 +59,13 @@ import org.koin.android.ext.android.inject class AboutActivity : AboutActivityBase(null) { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() override fun Configs.buildConfigs() { - textColor = prefs.textColor - accentColor = prefs.accentColor - backgroundColor = prefs.bgColor.withMinAlpha(200) - cutoutForeground = prefs.accentColor + textColor = themeProvider.textColor + accentColor = themeProvider.accentColor + backgroundColor = themeProvider.bgColor.withMinAlpha(200) + cutoutForeground = themeProvider.accentColor cutoutDrawableRes = R.drawable.frost_f_200 faqPageTitleRes = R.string.faq_title faqXmlRes = R.xml.frost_faq diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt index d2ba0a92..0553086c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt @@ -22,9 +22,11 @@ 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.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.setFrostTheme import org.koin.android.ext.android.inject +import org.koin.core.component.inject /** * Created by Allan Wang on 2017-06-12. @@ -33,6 +35,7 @@ abstract class BaseActivity : KauBaseActivity() { val fbCookie: FbCookie by inject() val prefs: Prefs by inject() + val themeProvider: ThemeProvider by inject() /** * Inherited consumer to customize back press @@ -48,7 +51,7 @@ abstract class BaseActivity : KauBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (this !is WebOverlayActivityBase) setFrostTheme(prefs) + if (this !is WebOverlayActivityBase) setFrostTheme(themeProvider) } override fun onStop() { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt index 0cb037a5..394969cb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt @@ -202,7 +202,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, } drawerWrapperBinding.mainContainer.addView(contentBinding.root) with(contentBinding) { - setFrostColors(prefs) { + setFrostColors { toolbar(toolbar) themeWindow = false header(appbar) @@ -210,7 +210,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, } setSupportActionBar(toolbar) viewpager.adapter = adapter - tabs.setBackgroundColor(prefs.mainActivityLayout.backgroundColor(prefs)) + tabs.setBackgroundColor(prefs.mainActivityLayout.backgroundColor(themeProvider)) } onNestedCreate(savedInstanceState) L.i { "Main finished loading UI in ${System.currentTimeMillis() - start} ms" } @@ -288,7 +288,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, drawer.addDrawerListener(toggle) toggle.syncState() - val foregroundColor = ColorStateList.valueOf(prefs.textColor) + val foregroundColor = ColorStateList.valueOf(themeProvider.textColor) with(navigation) { FrostMenuBuilder(this@BaseMainActivity, menu).apply { @@ -317,9 +317,9 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, launchWebOverlay(item.url, fbCookie, prefs) false } - val navBg = prefs.bgColor.withMinAlpha(200) + val navBg = themeProvider.bgColor.withMinAlpha(200) setBackgroundColor(navBg) - itemBackground = createNavDrawable(prefs.accentColor, navBg) + itemBackground = createNavDrawable(themeProvider.accentColor, navBg) itemTextColor = foregroundColor itemIconTintList = foregroundColor @@ -331,7 +331,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(themeProvider.headerColor.withMinAlpha(200)) fab.hide() appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> if (!hasFab) return@OnOffsetChangedListener @@ -351,12 +351,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, if (shouldShow) { if (fab.isShown) { fab.fadeScaleTransition { - setIcon(iicon, color = prefs.iconColor) + setIcon(iicon, color = themeProvider.iconColor) } return } } - fab.setIcon(iicon, color = prefs.iconColor) + fab.setIcon(iicon, color = themeProvider.iconColor) fab.showIf(shouldShow) } } @@ -383,7 +383,7 @@ 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 = themeProvider.bgColor.withMinAlpha(200).colorToForeground( 0.1f ) @@ -448,7 +448,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, animator.start() } - val textColor = prefs.textColor + val textColor = themeProvider.textColor fun TextView.setOptionsIcon(iicon: IIcon) { setCompoundDrawablesRelativeWithIntrinsicBounds( @@ -458,7 +458,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, null ) setTextColor(textColor) - background = createNavDrawable(prefs.accentColor, optionsBackground) + background = createNavDrawable(themeProvider.accentColor, optionsBackground) } with(optionsLogout) { @@ -506,7 +506,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, arrow.setImageDrawable( GoogleMaterial.Icon.gmd_arrow_drop_down.toDrawable( this@BaseMainActivity, - color = prefs.textColor + color = themeProvider.textColor ) ) } @@ -531,10 +531,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(themeProvider.textColor) val glide = Glide.with(root) val accountSize = dimenPixelSize(R.dimen.drawer_account_avatar_size) - val textColor = prefs.textColor + val textColor = themeProvider.textColor orderedAccounts.forEach { cookie -> val tv = TextView( @@ -568,7 +568,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, }) tv.text = cookie.name tv.setTextColor(textColor) - tv.background = createNavDrawable(prefs.accentColor, optionsBackground) + tv.background = createNavDrawable(themeProvider.accentColor, optionsBackground) tv.setOnClickListener { switchAccount(cookie.id) } @@ -626,9 +626,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(themeProvider.iconColor) setMenuIcons( - menu, prefs.iconColor, + menu, themeProvider.iconColor, R.id.action_settings to GoogleMaterial.Icon.gmd_settings, R.id.action_search to GoogleMaterial.Icon.gmd_search ) @@ -638,7 +638,7 @@ 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, themeProvider.iconColor) { textCallback = { query, searchView -> val results = searchViewCache[query] if (results != null) @@ -671,8 +671,8 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, ); true } closeListener = { _ -> searchViewCache.clear() } - foregroundColor = prefs.textColor - backgroundColor = prefs.bgColor.withMinAlpha(200) + foregroundColor = themeProvider.textColor + backgroundColor = themeProvider.bgColor.withMinAlpha(200) onItemClick = { _, key, _, _ -> launchWebOverlay(key, fbCookie, prefs) } } } @@ -731,7 +731,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, fragmentChannel.offer(REQUEST_REFRESH) } if (hasRequest(REQUEST_NAV)) { - frostNavigationBar(prefs) + frostNavigationBar(prefs, themeProvider) } if (hasRequest(REQUEST_TEXT_ZOOM)) { fragmentChannel.offer(REQUEST_TEXT_ZOOM) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt index 54baa184..935b88c9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt @@ -30,7 +30,7 @@ import com.pitchedapps.frost.R import com.pitchedapps.frost.databinding.ActivityDebugBinding import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.injectors.JsActions -import com.pitchedapps.frost.prefs.Prefs +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.createFreshDir import com.pitchedapps.frost.utils.setFrostColors @@ -39,6 +39,7 @@ import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineExceptionHandler import org.koin.android.ext.android.inject +import org.koin.core.component.inject /** * Created by Allan Wang on 05/01/18. @@ -52,7 +53,7 @@ class DebugActivity : KauBaseActivity() { fun baseDir(context: Context) = File(context.externalCacheDir, "offline_debug") } - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() lateinit var binding: ActivityDebugBinding @@ -71,7 +72,7 @@ class DebugActivity : KauBaseActivity() { } setTitle(R.string.debug_frost) - setFrostColors(prefs) { + setFrostColors { toolbar(toolbar) } debugWebview.loadUrl(FbItem.FEED.url) @@ -79,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, themeProvider.iconColor) + fab.backgroundTintList = ColorStateList.valueOf(themeProvider.accentColor) fab.setOnClickListener { _ -> fab.hide() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt index 06626752..7b8ee4d3 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt @@ -55,6 +55,7 @@ import com.pitchedapps.frost.facebook.get import com.pitchedapps.frost.facebook.requests.call import com.pitchedapps.frost.facebook.requests.getFullSizedImageUrl import com.pitchedapps.frost.facebook.requests.requestBuilder +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.services.LocalService import com.pitchedapps.frost.utils.ARG_COOKIE @@ -78,6 +79,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject +import org.koin.core.component.inject /** * Created by Allan Wang on 2017-07-15. @@ -85,6 +87,7 @@ import org.koin.android.ext.android.inject class ImageActivity : KauBaseActivity() { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() @Volatile internal var errorRef: Throwable? = null @@ -128,7 +131,7 @@ class ImageActivity : KauBaseActivity() { private var bottomBehavior: BottomSheetBehavior<View>? = null private val baseBackgroundColor = if (prefs.blackMediaBg) Color.BLACK - else prefs.bgColor.withMinAlpha(235) + else themeProvider.bgColor.withMinAlpha(235) private fun loadError(e: Throwable) { if (e.message?.contains("<!DOCTYPE html>") == true) { @@ -178,7 +181,7 @@ 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 themeProvider.textColor) imageText.setBackgroundColor( baseBackgroundColor.colorToForeground(0.2f).withAlpha(255) ) @@ -197,7 +200,7 @@ class ImageActivity : KauBaseActivity() { imageText.bringToFront() } } - val foregroundTint = if (prefs.blackMediaBg) Color.WHITE else prefs.accentColor + val foregroundTint = if (prefs.blackMediaBg) Color.WHITE else themeProvider.accentColor fun ImageView.setState(state: FabStates) { setIcon(state.iicon, color = foregroundTint, sizeDp = 24) @@ -221,7 +224,7 @@ class ImageActivity : KauBaseActivity() { loadError(e) } }) - setFrostColors(prefs) { + setFrostColors { themeWindow = false } dragHelper = ViewDragHelper.create(imageDrag, ViewDragCallback()).apply { @@ -352,7 +355,7 @@ class ImageActivity : KauBaseActivity() { internal enum class FabStates( val iicon: IIcon, - val iconColorProvider: (Prefs) -> Int = { it.iconColor }, + val iconColorProvider: (ThemeProvider) -> Int = { it.iconColor }, val backgroundTint: Int = Int.MAX_VALUE ) { ERROR(GoogleMaterial.Icon.gmd_error, { Color.WHITE }, Color.RED) { @@ -405,9 +408,9 @@ internal enum class FabStates( * https://github.com/AllanWang/KAU/issues/184 * */ - fun update(fab: FloatingActionButton, prefs: Prefs) { - val tint = if (backgroundTint != Int.MAX_VALUE) backgroundTint else prefs.accentColor - val iconColor = iconColorProvider(prefs) + fun update(fab: FloatingActionButton, themeProvider: ThemeProvider) { + val tint = if (backgroundTint != Int.MAX_VALUE) backgroundTint else themeProvider.accentColor + val iconColor = iconColorProvider(themeProvider) if (fab.isHidden) { fab.setIcon(iicon, color = iconColor) fab.backgroundTintList = ColorStateList.valueOf(tint) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/IntroActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/IntroActivity.kt index 337c9678..f06c3a37 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/IntroActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/IntroActivity.kt @@ -39,6 +39,7 @@ import ca.allanwang.kau.utils.statusBarColor import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.pitchedapps.frost.R import com.pitchedapps.frost.databinding.ActivityIntroBinding +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.intro.BaseIntroFragment import com.pitchedapps.frost.intro.IntroAccountFragment import com.pitchedapps.frost.intro.IntroFragmentEnd @@ -66,6 +67,7 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.OnPageChangeListener { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() lateinit var binding: ActivityIntroBinding private var barHasNext = true @@ -98,21 +100,21 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, else finish(next.x + next.pivotX, next.y + next.pivotY) } skip.setOnClickListener { finish() } - ripple.set(prefs.bgColor) + ripple.set(themeProvider.bgColor) theme() } fun theme() { - statusBarColor = prefs.headerColor - navigationBarColor = prefs.headerColor + statusBarColor = themeProvider.headerColor + navigationBarColor = themeProvider.headerColor with(binding) { - skip.setTextColor(prefs.textColor) - next.imageTintList = ColorStateList.valueOf(prefs.textColor) - indicator.setColour(prefs.textColor) + skip.setTextColor(themeProvider.textColor) + next.imageTintList = ColorStateList.valueOf(themeProvider.textColor) + indicator.setColour(themeProvider.textColor) indicator.invalidate() } fragments.forEach { it.themeFragment() } - setFrostTheme(prefs, true) + setFrostTheme(themeProvider, true) } /** @@ -150,21 +152,21 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ).forEach { it?.animate()?.alpha(0f)?.setDuration(600)?.start() } - if (prefs.textColor != Color.WHITE) { + if (themeProvider.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(themeProvider.textColor.blendWith(Color.WHITE, it.animatedValue as Float)) } duration = 600 start() } } - if (prefs.headerColor != blue) { + if (themeProvider.headerColor != blue) { ValueAnimator.ofFloat(0f, 1f).apply { addUpdateListener { - val c = prefs.headerColor.blendWith(blue, it.animatedValue as Float) + val c = themeProvider.headerColor.blendWith(blue, it.animatedValue as Float) statusBarColor = c navigationBarColor = c } @@ -176,7 +178,7 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, override fun finish() { launch(NonCancellable) { - loadAssets(prefs) + loadAssets(themeProvider) NotificationWidget.forceUpdate(this@IntroActivity) launchNewTask<MainActivity>(cookies(), false) super.finish() @@ -207,7 +209,7 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, binding.next.fadeScaleTransition { setIcon( if (barHasNext) GoogleMaterial.Icon.gmd_navigate_next else GoogleMaterial.Icon.gmd_done, - color = prefs.textColor + color = themeProvider.textColor ) } binding.skip.animate().scaleXY(if (barHasNext) 1f else 0f) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt index be53f2c2..5cbbfafe 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt @@ -80,15 +80,13 @@ class LoginActivity : BaseActivity() { setContentView(R.layout.activity_login) setSupportActionBar(toolbar) setTitle(R.string.kau_login) - setFrostColors(prefs) { + setFrostColors { toolbar(toolbar) } profileLoader = GlideApp.with(profile) launch { for (refreshing in refreshChannel.uniqueOnly(this)) { - if (refreshing) swipeRefresh.isEnabled = true swipeRefresh.isRefreshing = refreshing - if (!refreshing) swipeRefresh.isEnabled = false } } launch { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt index a0e3ae42..33215c7e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt @@ -67,7 +67,7 @@ class SelectorActivity : BaseActivity() { } } }) - setFrostColors(prefs) { + setFrostColors { text(text) background(container) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt index aac714ce..9ce16ec7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt @@ -39,6 +39,7 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.pitchedapps.frost.R import com.pitchedapps.frost.db.NotificationDao import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.settings.getAppearancePrefs import com.pitchedapps.frost.settings.getBehaviourPrefs @@ -69,6 +70,7 @@ class SettingsActivity : KPrefActivity() { val fbCookie: FbCookie by inject() val notifDao: NotificationDao by inject() val prefs: Prefs by inject() + val themeProvider: ThemeProvider by inject() private var resultFlag = Activity.RESULT_CANCELED @@ -132,8 +134,8 @@ class SettingsActivity : KPrefActivity() { } override fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit = { - textColor = { prefs.textColor } - accentColor = { prefs.accentColor } + textColor = { themeProvider.textColor } + accentColor = { themeProvider.accentColor } } override fun onCreateKPrefs(savedInstanceState: Bundle?): KPrefAdapterBuilder.() -> Unit = { @@ -216,25 +218,25 @@ class SettingsActivity : KPrefActivity() { @SuppressLint("MissingSuperCall") override fun onCreate(savedInstanceState: Bundle?) { - setFrostTheme(prefs, true) + setFrostTheme(themeProvider, true) super.onCreate(savedInstanceState) 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) - frostNavigationBar(prefs) + if (animate) bgCanvas.fade(themeProvider.bgColor) + else bgCanvas.set(themeProvider.bgColor) + if (animate) toolbarCanvas.ripple(themeProvider.headerColor, RippleCanvas.MIDDLE, RippleCanvas.END) + else toolbarCanvas.set(themeProvider.headerColor) + frostNavigationBar(prefs, themeProvider) } override fun onBackPressed() { if (!super.backPress()) { setResult(resultFlag) launch(NonCancellable) { - loadAssets(prefs) + loadAssets(themeProvider) finishSlideOut() } } @@ -242,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(themeProvider.iconColor) setMenuIcons( - menu, prefs.iconColor, + menu, themeProvider.iconColor, R.id.action_github to CommunityMaterial.Icon2.cmd_github, R.id.action_changelog to GoogleMaterial.Icon.gmd_info ) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/TabCustomizerActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/TabCustomizerActivity.kt index 11126803..f2827397 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/TabCustomizerActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/TabCustomizerActivity.kt @@ -69,15 +69,15 @@ class TabCustomizerActivity : BaseActivity() { } fun ActivityTabCustomizerBinding.init() { - pseudoToolbar.setBackgroundColor(prefs.headerColor) + pseudoToolbar.setBackgroundColor(themeProvider.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(themeProvider.textColor.withAlpha(30)) + instructions.setTextColor(themeProvider.textColor) launch { val tabs = genericDao.getTabs().toMutableList() @@ -94,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, themeProvider.iconColor) + fabSave.backgroundTintList = ColorStateList.valueOf(themeProvider.accentColor) fabSave.setOnClickListener { launchMain(NonCancellable) { val tabs = adapter.adapterItems.subList(0, TAB_COUNT).map(TabIItem::item) @@ -104,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, themeProvider.iconColor) + fabCancel.backgroundTintList = ColorStateList.valueOf(themeProvider.accentColor) fabCancel.setOnClickListener { finish() } - setFrostColors(prefs) { + setFrostColors { themeWindow = true } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt index abc07d57..42d84eb7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -202,14 +202,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, themeProvider.iconColor) toolbar.setNavigationOnClickListener { finishSlideOut() } - setFrostColors(prefs) { + setFrostColors { toolbar(toolbar) themeWindow = false } - coordinator.setBackgroundColor(prefs.bgColor.withAlpha(255)) + coordinator.setBackgroundColor(themeProvider.bgColor.withAlpha(255)) content.bind(this) @@ -268,13 +268,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 = themeProvider.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(themeProvider.iconColor) + coordinator.setBackgroundColor(themeProvider.bgColor.withAlpha(255)) + toolbar.overflowIcon?.setTint(themeProvider.iconColor) } override fun onResume() { @@ -309,7 +309,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(themeProvider.iconColor) return true } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt index 8a6e57af..b8d0d86f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FrostContentContract.kt @@ -73,11 +73,17 @@ interface FrostContentParent : DynamicUiContract { var baseEnum: FbItem? + val swipeEnabled: Boolean get() = swipeAllowedByPage && !swipeDisabledByAction + + /** + * Temporary disable swiping based on action + */ + var swipeDisabledByAction: Boolean + /** - * Toggle state for allowing swipes - * Allowed on any thread + * Decides if swipe should be allowed for the current page */ - var swipeEnabled: Boolean + var swipeAllowedByPage: Boolean /** * Binds the container to self diff --git a/app/src/main/kotlin/com/pitchedapps/frost/db/CookiesDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/db/CookiesDb.kt index 388edfe6..8c2e32a7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/db/CookiesDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/db/CookiesDb.kt @@ -23,6 +23,8 @@ import androidx.room.Entity import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase import com.pitchedapps.frost.prefs.Prefs import kotlinx.android.parcel.Parcelize @@ -37,11 +39,13 @@ data class CookieEntity( @ColumnInfo(name = "cookie_id") val id: Long, val name: String?, - val cookie: String? + val cookie: String?, + val cookieMessenger: String? = null // Version 2 ) : Parcelable { override fun toString(): String = "CookieEntity(${hashCode()})" - fun toSensitiveString(): String = "CookieEntity(id=$id, name=$name, cookie=$cookie)" + fun toSensitiveString(): String = + "CookieEntity(id=$id, name=$name, cookie=$cookie cookieMessenger=$cookieMessenger)" } @Dao @@ -61,6 +65,9 @@ interface CookieDao { @Query("DELETE FROM cookies WHERE cookie_id = :id") fun _deleteById(id: Long) + + @Query("UPDATE cookies SET cookieMessenger = :cookie WHERE cookie_id = :id") + fun _updateMessengerCookie(id: Long, cookie: String?) } suspend fun CookieDao.selectAll() = dao { _selectAll() } @@ -69,3 +76,11 @@ 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(prefs: Prefs) = selectById(prefs.userId) +suspend fun CookieDao.updateMessengerCookie(id: Long, cookie: String?) = + dao { _updateMessengerCookie(id, cookie) } + +val COOKIES_MIGRATION_1_2 = object : Migration(1, 2) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE cookies ADD COLUMN cookieMessenger TEXT") + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt b/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt index 21a2f1dc..bd0b4ee0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/db/Database.kt @@ -31,7 +31,7 @@ interface FrostPrivateDao { @Database( entities = [CookieEntity::class, NotificationEntity::class, CacheEntity::class], - version = 1, + version = 2, exportSchema = true ) abstract class FrostPrivateDatabase : RoomDatabase(), FrostPrivateDao { @@ -84,7 +84,7 @@ class FrostDatabase( val privateDb = Room.databaseBuilder( context, FrostPrivateDatabase::class.java, FrostPrivateDatabase.DATABASE_NAME - ).frostBuild() + ).addMigrations(COOKIES_MIGRATION_1_2).frostBuild() val publicDb = Room.databaseBuilder( context, FrostPublicDatabase::class.java, FrostPublicDatabase.DATABASE_NAME diff --git a/app/src/main/kotlin/com/pitchedapps/frost/enums/MainActivityLayout.kt b/app/src/main/kotlin/com/pitchedapps/frost/enums/MainActivityLayout.kt index 79a6f188..ec438df1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/enums/MainActivityLayout.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/enums/MainActivityLayout.kt @@ -17,15 +17,15 @@ package com.pitchedapps.frost.enums import com.pitchedapps.frost.R -import com.pitchedapps.frost.prefs.Prefs +import com.pitchedapps.frost.injectors.ThemeProvider /** * Created by Allan Wang on 2017-08-19. */ enum class MainActivityLayout( val titleRes: Int, - val backgroundColor: (Prefs) -> Int, - val iconColor: (Prefs) -> Int + val backgroundColor: (ThemeProvider) -> Int, + val iconColor: (ThemeProvider) -> Int ) { TOP_BAR(R.string.top_bar, diff --git a/app/src/main/kotlin/com/pitchedapps/frost/enums/Theme.kt b/app/src/main/kotlin/com/pitchedapps/frost/enums/Theme.kt index bb2514f5..1c9d6aa5 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/enums/Theme.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/enums/Theme.kt @@ -18,11 +18,10 @@ package com.pitchedapps.frost.enums import android.graphics.Color import androidx.annotation.StringRes +import androidx.annotation.VisibleForTesting import com.pitchedapps.frost.R -import com.pitchedapps.frost.injectors.CssAssets -import com.pitchedapps.frost.injectors.InjectorContract -import com.pitchedapps.frost.injectors.JsActions import com.pitchedapps.frost.prefs.sections.ThemePrefs +import java.util.Locale /** * Created by Allan Wang on 2017-06-14. @@ -32,7 +31,7 @@ const val BLUE_LIGHT = 0xff5d86dd.toInt() enum class Theme( @StringRes val textRes: Int, - val injector: InjectorContract, + file: String?, val textColorGetter: (ThemePrefs) -> Int, val accentColorGetter: (ThemePrefs) -> Int, val backgroundColorGetter: (ThemePrefs) -> Int, @@ -41,7 +40,7 @@ enum class Theme( ) { DEFAULT(R.string.kau_default, - JsActions.EMPTY, + "default", { 0xde000000.toInt() }, { FACEBOOK_BLUE }, { 0xfffafafa.toInt() }, @@ -49,7 +48,7 @@ enum class Theme( { Color.WHITE }), LIGHT(R.string.kau_light, - CssAssets.MATERIAL_LIGHT, + "material_light", { 0xde000000.toInt() }, { FACEBOOK_BLUE }, { 0xfffafafa.toInt() }, @@ -57,7 +56,7 @@ enum class Theme( { Color.WHITE }), DARK(R.string.kau_dark, - CssAssets.MATERIAL_DARK, + "material_dark", { Color.WHITE }, { BLUE_LIGHT }, { 0xff303030.toInt() }, @@ -65,7 +64,7 @@ enum class Theme( { Color.WHITE }), AMOLED(R.string.kau_amoled, - CssAssets.MATERIAL_AMOLED, + "material_amoled", { Color.WHITE }, { BLUE_LIGHT }, { Color.BLACK }, @@ -73,7 +72,7 @@ enum class Theme( { Color.WHITE }), GLASS(R.string.kau_glass, - CssAssets.MATERIAL_GLASS, + "material_glass", { Color.WHITE }, { BLUE_LIGHT }, { 0x80000000.toInt() }, @@ -81,15 +80,26 @@ enum class Theme( { Color.WHITE }), CUSTOM(R.string.kau_custom, - CssAssets.CUSTOM, + "custom", { it.customTextColor }, { it.customAccentColor }, { it.customBackgroundColor }, { it.customHeaderColor }, { it.customIconColor }); + @VisibleForTesting + internal val file = file?.let { "$it.css" } + companion object { val values = values() // save one instance operator fun invoke(index: Int) = values[index] } } + +enum class ThemeCategory { + FACEBOOK, MESSENGER + ; + + @VisibleForTesting + internal val folder = name.toLowerCase(Locale.CANADA) +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt index 61745b95..b0846864 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt @@ -21,8 +21,12 @@ package com.pitchedapps.frost.facebook */ const val FACEBOOK_COM = "facebook.com" +const val MESSENGER_COM = "messenger.com" const val FBCDN_NET = "fbcdn.net" const val WWW_FACEBOOK_COM = "www.$FACEBOOK_COM" +const val WWW_MESSENGER_COM = "www.$MESSENGER_COM" +const val HTTPS_FACEBOOK_COM = "https://$WWW_FACEBOOK_COM" +const val HTTPS_MESSENGER_COM = "https://$WWW_MESSENGER_COM" const val FACEBOOK_BASE_COM = "m.$FACEBOOK_COM" const val FB_URL_BASE = "https://$FACEBOOK_BASE_COM/" const val FACEBOOK_MBASIC_COM = "mbasic.$FACEBOOK_COM" @@ -30,6 +34,7 @@ const val FB_URL_MBASIC_BASE = "https://$FACEBOOK_MBASIC_COM/" fun profilePictureUrl(id: Long) = "https://graph.facebook.com/$id/picture?type=large" const val FB_LOGIN_URL = "${FB_URL_BASE}login" const val FB_HOME_URL = "${FB_URL_BASE}home.php" +const val MESSENGER_THREAD_PREFIX = "$HTTPS_MESSENGER_COM/t/" /* * User agent candidates. diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt index e66eaf27..4e932d09 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt @@ -30,6 +30,7 @@ import com.pitchedapps.frost.utils.cookies import com.pitchedapps.frost.utils.launchLogin import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -44,7 +45,8 @@ import org.koin.dsl.module class FbCookie(private val prefs: Prefs, private val cookieDao: CookieDao) { companion object { - private const val COOKIE_DOMAIN = FB_URL_BASE + private const val FB_COOKIE_DOMAIN = HTTPS_FACEBOOK_COM + private const val MESSENGER_COOKIE_DOMAIN = HTTPS_MESSENGER_COM fun module() = module { single { FbCookie(get(), get()) } @@ -56,36 +58,40 @@ class FbCookie(private val prefs: Prefs, private val cookieDao: CookieDao) { * Note that this is a synchronized call */ val webCookie: String? - get() = CookieManager.getInstance().getCookie(COOKIE_DOMAIN) + get() = CookieManager.getInstance().getCookie(FB_COOKIE_DOMAIN) - private suspend fun CookieManager.suspendSetWebCookie(cookie: String?): Boolean { + val messengerCookie: String? + get() = CookieManager.getInstance().getCookie(HTTPS_MESSENGER_COM) + + private suspend fun CookieManager.suspendSetWebCookie( + domain: String, + cookie: String? + ): Boolean { cookie ?: return true return withContext(NonCancellable) { - removeAllCookies() // Save all cookies regardless of result, then check if all succeeded val result = cookie.split(";") - .map { async { setSingleWebCookie(it) } } + .map { async { setSingleWebCookie(domain, it) } } .awaitAll().all { it } - flush() L.d { "Cookies set" } L._d { "Set $cookie\n\tResult $webCookie" } result } } + private suspend fun CookieManager.setSingleWebCookie(domain: String, cookie: String): Boolean = + suspendCoroutine { cont -> + setCookie(domain, cookie.trim()) { + cont.resume(it) + } + } + private suspend fun CookieManager.removeAllCookies(): Boolean = suspendCoroutine { cont -> removeAllCookies { cont.resume(it) } } - private suspend fun CookieManager.setSingleWebCookie(cookie: String): Boolean = - suspendCoroutine { cont -> - setCookie(COOKIE_DOMAIN, cookie.trim()) { - cont.resume(it) - } - } - suspend fun save(id: Long) { L.d { "New cookie found" } prefs.userId = id @@ -96,9 +102,11 @@ class FbCookie(private val prefs: Prefs, private val cookieDao: CookieDao) { suspend fun reset() { prefs.userId = -1L - with(CookieManager.getInstance()) { - removeAllCookies() - flush() + withContext(Dispatchers.Main + NonCancellable) { + with(CookieManager.getInstance()) { + removeAllCookies() + flush() + } } } @@ -108,14 +116,19 @@ class FbCookie(private val prefs: Prefs, private val cookieDao: CookieDao) { } suspend fun switchUser(cookie: CookieEntity?) { - if (cookie == null) { + if (cookie?.cookie == null) { L.d { "Switching User; null cookie" } return } - withContext(NonCancellable) { + withContext(Dispatchers.Main + NonCancellable) { L.d { "Switching User" } prefs.userId = cookie.id - CookieManager.getInstance().suspendSetWebCookie(cookie.cookie) + CookieManager.getInstance().apply { + removeAllCookies() + suspendSetWebCookie(FB_COOKIE_DOMAIN, cookie.cookie) + suspendSetWebCookie(MESSENGER_COOKIE_DOMAIN, cookie.cookieMessenger) + flush() + } } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt index 4c33fe8d..9b08c775 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt @@ -48,6 +48,7 @@ enum class FbItem( MARKETPLACE(R.string.marketplace, GoogleMaterial.Icon.gmd_store, "marketplace"), MENU(R.string.menu, GoogleMaterial.Icon.gmd_menu, "settings"), MESSAGES(R.string.messages, MaterialDesignIconic.Icon.gmi_comments, "messages"), + MESSENGER(R.string.messenger, CommunityMaterial.Icon2.cmd_facebook_messenger, "", prefix = HTTPS_MESSENGER_COM), NOTES(R.string.notes, CommunityMaterial.Icon3.cmd_note, "notes"), NOTIFICATIONS(R.string.notifications, MaterialDesignIconic.Icon.gmi_globe, "notifications"), ON_THIS_DAY(R.string.on_this_day, GoogleMaterial.Icon.gmd_today, "onthisday"), diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt index 0232694e..c07884bc 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt @@ -34,6 +34,7 @@ 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.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.ARG_URL import com.pitchedapps.frost.utils.L @@ -49,6 +50,7 @@ import kotlinx.coroutines.channels.ReceiveChannel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import org.koin.android.ext.android.inject +import org.koin.core.component.inject /** * Created by Allan Wang on 2017-11-07. @@ -84,6 +86,7 @@ abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, protected val fbCookie: FbCookie by inject() protected val prefs: Prefs by inject() + protected val themeProvider: ThemeProvider by inject() open lateinit var job: Job override val coroutineContext: CoroutineContext get() = ContextHelper.dispatcher + job @@ -201,10 +204,10 @@ abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, protected fun FloatingActionButton.update(iicon: IIcon, click: () -> Unit) { if (isShown) { fadeScaleTransition { - setIcon(iicon, prefs.iconColor) + setIcon(iicon, themeProvider.iconColor) } } else { - setIcon(iicon, prefs.iconColor) + setIcon(iicon, themeProvider.iconColor) show() } setOnClickListener { click() } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt index 502c37fb..3cac92af 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt @@ -26,6 +26,7 @@ import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.views.FrostWebView import com.pitchedapps.frost.web.FrostWebViewClient import com.pitchedapps.frost.web.FrostWebViewClientMenu +import com.pitchedapps.frost.web.FrostWebViewClientMessenger /** * Created by Allan Wang on 27/12/17. @@ -41,6 +42,7 @@ class WebFragment : BaseFragment() { * Given a webview, output a client */ fun client(web: FrostWebView) = when (baseEnum) { + FbItem.MESSENGER -> FrostWebViewClientMessenger(web) FbItem.MENU -> FrostWebViewClientMenu(web) else -> FrostWebViewClient(web) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/iitems/GenericIItems.kt b/app/src/main/kotlin/com/pitchedapps/frost/iitems/GenericIItems.kt index 47a362b4..0c53a2b1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/iitems/GenericIItems.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/iitems/GenericIItems.kt @@ -28,6 +28,7 @@ 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.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.launchWebOverlay import org.koin.core.component.KoinComponent @@ -79,14 +80,14 @@ open class HeaderIItem( class ViewHolder(itemView: View) : FastAdapter.ViewHolder<HeaderIItem>(itemView), KoinComponent { - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() val text: TextView by bindView(R.id.item_header_text) override fun bindView(item: HeaderIItem, payloads: List<Any>) { - text.setTextColor(prefs.accentColor) + text.setTextColor(themeProvider.accentColor) text.text = item.text - text.setBackgroundColor(prefs.nativeBgColor) + text.setBackgroundColor(themeProvider.nativeBgColor) } override fun unbindView(item: HeaderIItem) { @@ -108,14 +109,14 @@ open class TextIItem( class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TextIItem>(itemView), KoinComponent { - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() val text: TextView by bindView(R.id.item_text_view) override fun bindView(item: TextIItem, payloads: List<Any>) { - text.setTextColor(prefs.textColor) + text.setTextColor(themeProvider.textColor) text.text = item.text - text.background = createSimpleRippleDrawable(prefs.bgColor, prefs.nativeBgColor) + text.background = createSimpleRippleDrawable(themeProvider.bgColor, themeProvider.nativeBgColor) } override fun unbindView(item: TextIItem) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt index 56e27ccd..8e0d5bec 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt @@ -36,6 +36,7 @@ import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.parsers.FrostNotif import com.pitchedapps.frost.glide.FrostGlide import com.pitchedapps.frost.glide.GlideApp +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.isIndependent import com.pitchedapps.frost.utils.launchWebOverlay @@ -103,7 +104,7 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) : class ViewHolder(itemView: View) : FastAdapter.ViewHolder<NotificationIItem>(itemView), KoinComponent { - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private val frame: ViewGroup by bindView(R.id.item_frame) private val avatar: ImageView by bindView(R.id.item_avatar) @@ -117,11 +118,11 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) : override fun bindView(item: NotificationIItem, payloads: List<Any>) { val notif = item.notification frame.background = createSimpleRippleDrawable( - prefs.textColor, - prefs.nativeBgColor(notif.unread) + themeProvider.textColor, + themeProvider.nativeBgColor(notif.unread) ) - content.setTextColor(prefs.textColor) - date.setTextColor(prefs.textColor.withAlpha(150)) + content.setTextColor(themeProvider.textColor) + date.setTextColor(themeProvider.textColor.withAlpha(150)) val glide = glide glide.load(notif.img) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/iitems/TabIItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/iitems/TabIItem.kt index 6f278fe2..0fd39d5c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/iitems/TabIItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/iitems/TabIItem.kt @@ -29,7 +29,7 @@ import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.drag.IDraggable import com.pitchedapps.frost.R import com.pitchedapps.frost.facebook.FbItem -import com.pitchedapps.frost.prefs.Prefs +import com.pitchedapps.frost.injectors.ThemeProvider import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -45,14 +45,14 @@ class TabIItem(val item: FbItem) : KauIItem<TabIItem.ViewHolder>( class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TabIItem>(itemView), KoinComponent { - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() val image: ImageView by bindView(R.id.image) val text: TextView by bindView(R.id.text) override fun bindView(item: TabIItem, payloads: List<Any>) { val isInToolbar = adapterPosition < 4 - val color = if (isInToolbar) prefs.iconColor else prefs.textColor + val color = if (isInToolbar) themeProvider.iconColor else themeProvider.textColor image.setIcon(item.item.icon, 20, color) if (isInToolbar) text.invisible() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssSmallAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAsset.kt index 30ee7a8f..b384efad 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssSmallAssets.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAsset.kt @@ -22,7 +22,7 @@ import com.pitchedapps.frost.prefs.Prefs /** * Small misc inline css assets */ -enum class CssSmallAssets(private val content: String) : InjectorContract { +enum class CssAsset(private val content: String) : InjectorContract { FullSizeImage("div._4prr[style*=\"max-width\"][style*=\"max-height\"]{max-width:none !important;max-height:none !important}") ; diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt deleted file mode 100644 index 5329046f..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2018 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.injectors - -import android.content.Context -import android.graphics.Color -import android.webkit.WebView -import androidx.annotation.VisibleForTesting -import ca.allanwang.kau.utils.adjustAlpha -import ca.allanwang.kau.utils.colorToBackground -import ca.allanwang.kau.utils.colorToForeground -import ca.allanwang.kau.utils.toRgbaString -import ca.allanwang.kau.utils.use -import ca.allanwang.kau.utils.withAlpha -import com.pitchedapps.frost.prefs.Prefs -import com.pitchedapps.frost.utils.L -import java.io.BufferedReader -import java.io.FileNotFoundException -import java.util.Locale -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -/** - * Created by Allan Wang on 2017-05-31. - * Mapping of the available assets - * The enum name must match the css file name - */ -enum class CssAssets(val folder: String = THEME_FOLDER) : InjectorContract { - MATERIAL_LIGHT, MATERIAL_DARK, MATERIAL_AMOLED, MATERIAL_GLASS, CUSTOM - ; - - @VisibleForTesting - internal val file = "${name.toLowerCase(Locale.CANADA)}.css" - - /** - * Note that while this can be loaded from any thread, it is typically done through [load] - */ - 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 = - context.assets.open("css/$folder/$file").bufferedReader() - .use(BufferedReader::readText) - if (this == CUSTOM) { - val bt = if (Color.alpha(prefs.bgColor) == 255) - prefs.bgColor.toRgbaString() - else - "transparent" - - 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("\$BT\$", bt) - .replace("\$BBT\$", bb.withAlpha(51).toRgbaString()) - .replace("\$O\$", prefs.bgColor.withAlpha(255).toRgbaString()) - .replace("\$OO\$", bb.withAlpha(255).toRgbaString()) - .replace("\$D\$", prefs.textColor.adjustAlpha(0.3f).toRgbaString()) - .replace("\$TI\$", bb.withAlpha(60).toRgbaString()) - .replace("\$C\$", bt) - } - JsBuilder().css(content).build() - } catch (e: FileNotFoundException) { - L.e(e) { "CssAssets file not found" } - JsInjector(JsActions.EMPTY.function) - } - - override fun inject(webView: WebView, prefs: Prefs) = - injector(webView.context, prefs).inject(webView, prefs) - - fun reset() { - injector = null - } - - companion object { - - // Ensures that all non themes and the selected theme are loaded - suspend fun load(context: Context, prefs: Prefs) { - withContext(Dispatchers.IO) { - val currentTheme = prefs.themeInjector as? CssAssets - val (themes, others) = values().partition { it.folder == THEME_FOLDER } - themes.filter { it != currentTheme }.forEach { it.reset() } - currentTheme?.injector(context, prefs) - others.forEach { it.injector(context, prefs) } - } - } - } -} - -private const val THEME_FOLDER = "themes" diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/ThemeProvider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/ThemeProvider.kt new file mode 100644 index 00000000..570f3719 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/ThemeProvider.kt @@ -0,0 +1,150 @@ +/* + * Copyright 2018 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.injectors + +import android.content.Context +import android.graphics.Color +import ca.allanwang.kau.utils.adjustAlpha +import ca.allanwang.kau.utils.colorToBackground +import ca.allanwang.kau.utils.colorToForeground +import ca.allanwang.kau.utils.isColorVisibleOn +import ca.allanwang.kau.utils.toRgbaString +import ca.allanwang.kau.utils.use +import ca.allanwang.kau.utils.withAlpha +import com.pitchedapps.frost.enums.FACEBOOK_BLUE +import com.pitchedapps.frost.enums.Theme +import com.pitchedapps.frost.enums.ThemeCategory +import com.pitchedapps.frost.prefs.Prefs +import com.pitchedapps.frost.utils.L +import java.io.BufferedReader +import java.io.FileNotFoundException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.koin.core.context.GlobalContext + +/** + * Provides [InjectorContract] for each [ThemeCategory]. + * Can be reloaded to take in changes from [Prefs] + */ +class ThemeProvider(private val context: Context, private val prefs: Prefs) { + + private var theme: Theme = Theme.values[prefs.theme] + + private val injectors: MutableMap<ThemeCategory, InjectorContract> = mutableMapOf() + + val textColor: Int + get() = theme.textColorGetter(prefs) + + val accentColor: Int + get() = theme.accentColorGetter(prefs) + + val accentColorForWhite: Int + get() = when { + accentColor.isColorVisibleOn(Color.WHITE) -> accentColor + textColor.isColorVisibleOn(Color.WHITE) -> textColor + else -> FACEBOOK_BLUE + } + + val nativeBgColor: Int + get() = bgColor.withAlpha(30) + + fun nativeBgColor(unread: Boolean) = bgColor + .colorToForeground(if (unread) 0.7f else 0.0f) + .withAlpha(30) + + val bgColor: Int + get() = theme.backgroundColorGetter(prefs) + + val headerColor: Int + get() = theme.headerColorGetter(prefs) + + val iconColor: Int + get() = theme.iconColorGetter(prefs) + + val isCustomTheme: Boolean + get() = theme == Theme.CUSTOM + + /** + * Note that while this can be loaded from any thread, it is typically done through [preload]] + */ + fun injector(category: ThemeCategory): InjectorContract = + injectors.getOrPut(category) { createInjector(category) } + + /** + * Note that while this can be loaded from any thread, it is typically done through [preload] + */ + private fun createInjector(category: ThemeCategory): InjectorContract { + val file = theme.file ?: return JsActions.EMPTY + try { + var content = + context.assets.open("css/${category.folder}/themes/$file").bufferedReader() + .use(BufferedReader::readText) + if (theme == Theme.CUSTOM) { + val bt = if (Color.alpha(bgColor) == 255) + bgColor.toRgbaString() + else + "transparent" + + val bb = bgColor.colorToForeground(0.35f) + + content = content + .replace("\$T\$", textColor.toRgbaString()) + .replace("\$TT\$", textColor.colorToBackground(0.05f).toRgbaString()) + .replace("\$TD\$", textColor.adjustAlpha(0.6f).toRgbaString()) + .replace("\$A\$", accentColor.toRgbaString()) + .replace("\$AT\$", iconColor.toRgbaString()) + .replace("\$B\$", bgColor.toRgbaString()) + .replace("\$BT\$", bt) + .replace("\$BBT\$", bb.withAlpha(51).toRgbaString()) + .replace("\$O\$", bgColor.withAlpha(255).toRgbaString()) + .replace("\$OO\$", bb.withAlpha(255).toRgbaString()) + .replace("\$D\$", textColor.adjustAlpha(0.3f).toRgbaString()) + .replace("\$TI\$", bb.withAlpha(60).toRgbaString()) + .replace("\$C\$", bt) + } + return JsBuilder().css(content).build() + } catch (e: FileNotFoundException) { + L.e(e) { "CssAssets file not found" } + return JsActions.EMPTY + } + } + + fun setTheme(id: Int) { + theme = Theme.values[id] + reset() + } + + fun reset() { + injectors.clear() + } + + suspend fun preload() { + withContext(Dispatchers.IO) { + reset() + ThemeCategory.values().forEach { injector(it) } + } + } + + companion object { + + fun get(): ThemeProvider = GlobalContext.get().get() + + fun module() = org.koin.dsl.module { + single { ThemeProvider(get(), get()) } + } + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt index 6e735e5b..486b09a9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt @@ -66,7 +66,7 @@ class IntroFragmentTheme : BaseIntroFragment(R.layout.intro_theme) { setOnClickListener { v -> prefs.theme = theme.ordinal (activity as IntroActivity).apply { - binding.ripple.ripple(prefs.bgColor, v.x + v.pivotX, v.y + v.pivotY) + binding.ripple.ripple(themeProvider.bgColor, v.x + v.pivotX, v.y + v.pivotY) theme() } themeList.forEach { it.animate().scaleXY(if (it == this) 1.6f else 0.8f).start() } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt index 429171dd..e27d4fee 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt @@ -57,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(themeProvider.textColor) + desc.setTextColor(themeProvider.textColor) + phone.tint(themeProvider.textColor) + screen.tint(themeProvider.bgColor) } fun themeImageComponent(color: Int, vararg id: Int) { @@ -96,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(themeProvider.iconColor, R.id.intro_phone_avatar_1, R.id.intro_phone_avatar_2) + themeImageComponent(themeProvider.bgColor.colorToForeground(), R.id.intro_phone_nav) + themeImageComponent(themeProvider.headerColor, R.id.intro_phone_header) } override fun onPageScrolledImpl(positionOffset: Float) { @@ -122,14 +122,14 @@ class IntroTabTouchFragment : BaseImageIntroFragment( override fun themeFragmentImpl() { super.themeFragmentImpl() themeImageComponent( - prefs.iconColor, + themeProvider.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(themeProvider.headerColor, R.id.intro_phone_tab) + themeImageComponent(themeProvider.textColor.withAlpha(80), R.id.intro_phone_icon_ripple) } } @@ -141,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(themeProvider.headerColor, R.id.intro_phone_toolbar) + themeImageComponent(themeProvider.bgColor.colorToForeground(0.1f), R.id.intro_phone_image) themeImageComponent( - prefs.bgColor.colorToForeground(0.2f), + themeProvider.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(themeProvider.bgColor.colorToForeground(0.3f), R.id.intro_phone_comment) themeImageComponent( - prefs.bgColor.colorToForeground(0.1f), + themeProvider.bgColor.colorToForeground(0.1f), R.id.intro_phone_card_1, R.id.intro_phone_card_2 ) themeImageComponent( - prefs.textColor, + themeProvider.textColor, R.id.intro_phone_image_indicator, R.id.intro_phone_comment_indicator, R.id.intro_phone_card_indicator diff --git a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroMainFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroMainFragments.kt index d160bee4..8003cb2e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroMainFragments.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroMainFragments.kt @@ -33,9 +33,11 @@ import ca.allanwang.kau.utils.bindViewResettable import ca.allanwang.kau.utils.setOnSingleTapListener import com.pitchedapps.frost.R import com.pitchedapps.frost.activities.IntroActivity +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import kotlin.math.abs import org.koin.android.ext.android.inject +import org.koin.core.component.inject /** * Created by Allan Wang on 2017-07-28. @@ -49,6 +51,7 @@ import org.koin.android.ext.android.inject abstract class BaseIntroFragment(val layoutRes: Int) : Fragment() { protected val prefs: Prefs by inject() + protected val themeProvider: ThemeProvider 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(themeProvider.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(themeProvider.textColor) } } @@ -146,7 +149,7 @@ class IntroFragmentEnd : BaseIntroFragment(R.layout.intro_end) { override fun themeFragmentImpl() { super.themeFragmentImpl() - image.imageTintList = ColorStateList.valueOf(prefs.textColor) + image.imageTintList = ColorStateList.valueOf(themeProvider.textColor) } @SuppressLint("ClickableViewAccessibility") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/prefs/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/prefs/Prefs.kt index 2714b930..d31be432 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/prefs/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/prefs/Prefs.kt @@ -28,7 +28,7 @@ import com.pitchedapps.frost.prefs.sections.ShowcasePrefs import com.pitchedapps.frost.prefs.sections.ShowcasePrefsImpl import com.pitchedapps.frost.prefs.sections.ThemePrefs import com.pitchedapps.frost.prefs.sections.ThemePrefsImpl -import org.koin.core.context.KoinContextHandler +import org.koin.core.context.GlobalContext import org.koin.dsl.module /** @@ -48,7 +48,7 @@ interface Prefs : ShowcasePrefs, PrefsBase { companion object { - fun get(): Prefs = KoinContextHandler.get().get() + fun get(): Prefs = GlobalContext.get().get() fun module() = module { single<BehaviourPrefs> { BehaviourPrefsImpl(factory = get()) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/prefs/sections/ThemePrefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/prefs/sections/ThemePrefs.kt index 84f64435..47496d6d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/prefs/sections/ThemePrefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/prefs/sections/ThemePrefs.kt @@ -16,17 +16,10 @@ */ package com.pitchedapps.frost.prefs.sections -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 import com.pitchedapps.frost.BuildConfig -import com.pitchedapps.frost.enums.FACEBOOK_BLUE -import com.pitchedapps.frost.enums.Theme -import com.pitchedapps.frost.injectors.InjectorContract +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.OldPrefs import com.pitchedapps.frost.prefs.PrefsBase import org.koin.core.component.KoinComponent @@ -45,26 +38,6 @@ interface ThemePrefs : PrefsBase { var customIconColor: Int - val textColor: Int - - val accentColor: Int - - val accentColorForWhite: Int - - val nativeBgColor: Int - - fun nativeBgColor(unread: Boolean): Int - - val bgColor: Int - - val headerColor: Int - - val iconColor: Int - - val themeInjector: InjectorContract - - val isCustomTheme: Boolean - var tintNavBar: Boolean } @@ -74,9 +47,10 @@ class ThemePrefsImpl( ThemePrefs, KoinComponent { private val oldPrefs: OldPrefs by inject() + private val themeProvider: ThemeProvider by inject() - override var theme: Int by kpref("theme", oldPrefs.theme /* 0 */) { _: Int -> - loader.invalidate() + override var theme: Int by kpref("theme", oldPrefs.theme /* 0 */) { + themeProvider.setTheme(it) } override var customTextColor: Int by kpref( @@ -104,44 +78,5 @@ class ThemePrefsImpl( oldPrefs.customIconColor /* 0xffeceff1.toInt() */ ) - private val loader = lazyResettable { Theme.values[theme] } - - private val t: Theme by loader - - override val textColor: Int - get() = t.textColorGetter(this) - - override val accentColor: Int - get() = t.accentColorGetter(this) - - override val accentColorForWhite: Int - get() = when { - accentColor.isColorVisibleOn(Color.WHITE) -> accentColor - textColor.isColorVisibleOn(Color.WHITE) -> textColor - else -> FACEBOOK_BLUE - } - - override val nativeBgColor: Int - get() = bgColor.withAlpha(30) - - override fun nativeBgColor(unread: Boolean) = bgColor - .colorToForeground(if (unread) 0.7f else 0.0f) - .withAlpha(30) - - override val bgColor: Int - get() = t.backgroundColorGetter(this) - - override val headerColor: Int - get() = t.headerColorGetter(this) - - override val iconColor: Int - get() = t.iconColorGetter(this) - - override val themeInjector: InjectorContract - get() = t.injector - - override val isCustomTheme: Boolean - get() = t == Theme.CUSTOM - override var tintNavBar: Boolean by kpref("tint_nav_bar", oldPrefs.tintNavBar /* true */) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt index 0a90895e..0bb9d254 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationUtils.kt @@ -31,6 +31,7 @@ import androidx.core.app.NotificationCompat import ca.allanwang.kau.utils.color import ca.allanwang.kau.utils.string import com.pitchedapps.frost.R +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.frostUri @@ -41,13 +42,13 @@ import com.pitchedapps.frost.utils.frostUri const val NOTIF_CHANNEL_GENERAL = "general" const val NOTIF_CHANNEL_MESSAGES = "messages" -fun setupNotificationChannels(c: Context, prefs: Prefs) { +fun setupNotificationChannels(c: Context, themeProvider: ThemeProvider) { 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, prefs) - manager.createNotificationChannel(NOTIF_CHANNEL_MESSAGES, "$appName: $msg", prefs) + manager.createNotificationChannel(NOTIF_CHANNEL_GENERAL, appName, themeProvider) + manager.createNotificationChannel(NOTIF_CHANNEL_MESSAGES, "$appName: $msg", themeProvider) manager.notificationChannels .filter { it.id != NOTIF_CHANNEL_GENERAL && @@ -61,14 +62,14 @@ fun setupNotificationChannels(c: Context, prefs: Prefs) { private fun NotificationManager.createNotificationChannel( id: String, name: String, - prefs: Prefs + themeProvider: ThemeProvider ): NotificationChannel { val channel = NotificationChannel( id, name, NotificationManager.IMPORTANCE_DEFAULT ) channel.enableLights(true) - channel.lightColor = prefs.accentColor + channel.lightColor = themeProvider.accentColor channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC createNotificationChannel(channel) return channel diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt index 65e24ab8..64d9dba2 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt @@ -27,7 +27,6 @@ import com.pitchedapps.frost.R 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.REQUEST_NAV import com.pitchedapps.frost.utils.REQUEST_TEXT_ZOOM import com.pitchedapps.frost.utils.frostEvent @@ -56,7 +55,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { item.pref = index shouldRestartMain() reload() - setFrostTheme(prefs, true) + setFrostTheme(themeProvider, true) themeExterior() invalidateOptionsMenu() frostEvent("Theme", "Count" to Theme(index).name) @@ -70,13 +69,13 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { } fun KPrefColorPicker.KPrefColorContract.dependsOnCustom() { - enabler = prefs::isCustomTheme + enabler = themeProvider::isCustomTheme onDisabledClick = { frostSnackbar(R.string.requires_custom_theme) } allowCustom = true } fun invalidateCustomTheme() { - CssAssets.CUSTOM.reset() + themeProvider.reset() } colorPicker(R.string.text_color, prefs::customTextColor, { @@ -103,7 +102,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { prefs.customBackgroundColor = it bgCanvas.ripple(it, duration = 500L) invalidateCustomTheme() - setFrostTheme(prefs, true) + setFrostTheme(themeProvider, true) shouldRestartMain() }) { dependsOnCustom() @@ -112,7 +111,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { colorPicker(R.string.header_color, prefs::customHeaderColor, { prefs.customHeaderColor = it - frostNavigationBar(prefs) + frostNavigationBar(prefs, themeProvider) toolbarCanvas.ripple(it, RippleCanvas.MIDDLE, RippleCanvas.END, duration = 500L) reload() shouldRestartMain() @@ -161,7 +160,7 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { checkbox(R.string.tint_nav, prefs::tintNavBar, { prefs.tintNavBar = it - frostNavigationBar(prefs) + frostNavigationBar(prefs, themeProvider) setFrostResult(REQUEST_NAV) }) { descRes = R.string.tint_nav_desc diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt index e7bc1542..13ce2920 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -35,6 +35,7 @@ import ca.allanwang.kau.email.sendEmail import ca.allanwang.kau.mediapicker.createMediaFile import ca.allanwang.kau.mediapicker.createPrivateMediaFile import ca.allanwang.kau.utils.colorToForeground +import ca.allanwang.kau.utils.ctxCoroutine import ca.allanwang.kau.utils.darken import ca.allanwang.kau.utils.isColorDark import ca.allanwang.kau.utils.navigationBarColor @@ -64,11 +65,12 @@ import com.pitchedapps.frost.facebook.FBCDN_NET import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.FbUrlFormatter.Companion.VIDEO_REDIRECT +import com.pitchedapps.frost.facebook.MESSENGER_COM import com.pitchedapps.frost.facebook.USER_AGENT import com.pitchedapps.frost.facebook.formattedFbUri import com.pitchedapps.frost.facebook.formattedFbUrl -import com.pitchedapps.frost.injectors.CssAssets import com.pitchedapps.frost.injectors.JsAssets +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import java.io.File import java.io.IOException @@ -76,14 +78,14 @@ import java.net.URLEncoder import java.nio.charset.StandardCharsets import java.util.ArrayList import java.util.Locale -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import org.apache.commons.text.StringEscapeUtils import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject /** * Created by Allan Wang on 2017-06-03. @@ -95,14 +97,6 @@ const val ARG_IMAGE_URL = "arg_image_url" const val ARG_TEXT = "arg_text" const val ARG_COOKIE = "arg_cookie" -/** - * Most context items implement [CoroutineScope] by default. - * We will add a fallback just in case. - * It is expected that the scope returned always has the Android main dispatcher as part of the context. - */ -internal inline val Context.ctxCoroutine: CoroutineScope - get() = this as? CoroutineScope ?: GlobalScope - inline fun <reified T : Activity> Context.launchNewTask( cookieList: ArrayList<CookieEntity> = arrayListOf(), clearStack: Boolean = false @@ -181,17 +175,22 @@ fun WebOverlayActivity.url(): String { return intent.getStringExtra(ARG_URL) ?: FbItem.FEED.url } -fun Activity.setFrostTheme(prefs: Prefs, forceTransparent: Boolean = false) { +fun Activity.setFrostTheme(themeProvider: ThemeProvider, forceTransparent: Boolean = false) { val isTransparent = - forceTransparent || (Color.alpha(prefs.bgColor) != 255) || (Color.alpha(prefs.headerColor) != 255) - if (prefs.bgColor.isColorDark) { + forceTransparent || (Color.alpha(themeProvider.bgColor) != 255) || (Color.alpha( + themeProvider.headerColor + ) != 255) + if (themeProvider.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(val prefs: Prefs) { +class ActivityThemeUtils : KoinComponent { + + private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private var toolbar: Toolbar? = null var themeWindow = true @@ -217,21 +216,21 @@ class ActivityThemeUtils(val prefs: Prefs) { 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 = themeProvider.headerColor.darken(0.1f).withAlpha(255) + if (prefs.tintNavBar) navigationBarColor = themeProvider.headerColor + if (themeWindow) window.setBackgroundDrawable(ColorDrawable(themeProvider.bgColor)) + toolbar?.setBackgroundColor(themeProvider.headerColor) + toolbar?.setTitleTextColor(themeProvider.iconColor) + toolbar?.overflowIcon?.setTint(themeProvider.iconColor) + texts.forEach { it.setTextColor(themeProvider.textColor) } + headers.forEach { it.setBackgroundColor(themeProvider.headerColor) } + backgrounds.forEach { it.setBackgroundColor(themeProvider.bgColor) } } } } -inline fun Activity.setFrostColors(prefs: Prefs, builder: ActivityThemeUtils.() -> Unit) { - val themer = ActivityThemeUtils(prefs) +inline fun Activity.setFrostColors(builder: ActivityThemeUtils.() -> Unit) { + val themer = ActivityThemeUtils() themer.builder() themer.theme(this) } @@ -258,19 +257,19 @@ 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() + val themeProvider = ThemeProvider.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(themeProvider.textColor) + actionView.setTextColor(themeProvider.accentColor) // only set if previous text colors are set - view.setBackgroundColor(prefs.bgColor.withAlpha(255).colorToForeground(0.1f)) + view.setBackgroundColor(themeProvider.bgColor.withAlpha(255).colorToForeground(0.1f)) } } -fun Activity.frostNavigationBar(prefs: Prefs) { - navigationBarColor = if (prefs.tintNavBar) prefs.headerColor else Color.BLACK +fun Activity.frostNavigationBar(prefs: Prefs, themeProvider: ThemeProvider) { + navigationBarColor = if (prefs.tintNavBar) themeProvider.headerColor else Color.BLACK } @Throws(IOException::class) @@ -306,6 +305,12 @@ fun Context.resolveActivityForUri(uri: Uri): Boolean { inline val String?.isFacebookUrl get() = this != null && (contains(FACEBOOK_COM) || contains(FBCDN_NET)) +inline val String?.isMessengerUrl + get() = this != null && contains(MESSENGER_COM) + +inline val String?.isFbCookie + get() = this != null && contains("c_user") + /** * [true] if url is a video and can be accepted by VideoViewer */ @@ -453,7 +458,7 @@ fun String.unescapeHtml(): String = .replace("\\u003C", "<") .replace("\\\"", "\"") -suspend fun Context.loadAssets(prefs: Prefs): Unit = coroutineScope { - CssAssets.load(this@loadAssets, prefs) +suspend fun Context.loadAssets(themeProvider: ThemeProvider): Unit = coroutineScope { + themeProvider.preload() JsAssets.load(this@loadAssets) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt index 6ba9ff4d..6eebd36f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt @@ -37,7 +37,7 @@ import com.pitchedapps.frost.db.CookieEntity import com.pitchedapps.frost.facebook.profilePictureUrl import com.pitchedapps.frost.glide.FrostGlide import com.pitchedapps.frost.glide.GlideApp -import com.pitchedapps.frost.prefs.Prefs +import com.pitchedapps.frost.injectors.ThemeProvider import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -48,13 +48,13 @@ 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() + private val themeProvider: ThemeProvider by inject() override fun bindView(holder: ViewHolder, payloads: List<Any>) { super.bindView(holder, payloads) with(holder) { text.invisible() - text.setTextColor(prefs.textColor) + text.setTextColor(themeProvider.textColor) if (cookie != null) { text.text = cookie.name GlideApp.with(itemView).load(profilePictureUrl(cookie.id)) @@ -86,7 +86,7 @@ class AccountItem(val cookie: CookieEntity?) : GoogleMaterial.Icon.gmd_add_circle_outline.toDrawable( itemView.context, 100, - prefs.textColor + themeProvider.textColor ) ) text.text = itemView.context.getString(R.string.kau_add_account) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/BadgedIcon.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/BadgedIcon.kt index ed94b59f..78af4edf 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/BadgedIcon.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/BadgedIcon.kt @@ -29,6 +29,7 @@ import ca.allanwang.kau.utils.visible import ca.allanwang.kau.utils.withAlpha import com.mikepenz.iconics.typeface.IIcon import com.pitchedapps.frost.databinding.ViewBadgedIconBinding +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -43,6 +44,7 @@ class BadgedIcon @JvmOverloads constructor( ) : ConstraintLayout(context, attrs, defStyleAttr), KoinComponent { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private val binding: ViewBadgedIconBinding = ViewBadgedIconBinding.inflate(LayoutInflater.from(context), this, true) @@ -52,7 +54,7 @@ class BadgedIcon @JvmOverloads constructor( private fun ViewBadgedIconBinding.init() { val badgeColor = - prefs.mainActivityLayout.backgroundColor(prefs).withAlpha(255).colorToForeground(0.2f) + prefs.mainActivityLayout.backgroundColor(themeProvider).withAlpha(255).colorToForeground(0.2f) val badgeBackground = GradientDrawable( GradientDrawable.Orientation.BOTTOM_TOP, @@ -60,7 +62,7 @@ class BadgedIcon @JvmOverloads constructor( ) badgeBackground.cornerRadius = 13.dpToPx.toFloat() badgeText.background = badgeBackground - badgeText.setTextColor(prefs.mainActivityLayout.iconColor(prefs)) + badgeText.setTextColor(prefs.mainActivityLayout.iconColor(themeProvider)) } var iicon: IIcon? = null @@ -70,13 +72,13 @@ class BadgedIcon @JvmOverloads constructor( value?.toDrawable( context, sizeDp = 20, - color = prefs.mainActivityLayout.iconColor(prefs) + color = prefs.mainActivityLayout.iconColor(themeProvider) ) ) } fun setAllAlpha(alpha: Float) { - // badgeTextView.setTextColor(prefs.textColor.withAlpha(alpha.toInt())) + // badgeTextView.setTextColor(themeProvider.textColor.withAlpha(alpha.toInt())) binding.badgeImage.drawable.alpha = alpha.toInt() } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt index 55f41807..177b8862 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostContentView.kt @@ -22,7 +22,6 @@ import android.util.AttributeSet import android.view.View import android.widget.FrameLayout import android.widget.ProgressBar -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import ca.allanwang.kau.utils.ContextHelper import ca.allanwang.kau.utils.bindView import ca.allanwang.kau.utils.circularReveal @@ -39,6 +38,7 @@ import com.pitchedapps.frost.contracts.FrostContentCore import com.pitchedapps.frost.contracts.FrostContentParent import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.WEB_LOAD_DELAY +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.kotlin.subscribeDuringJob import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L @@ -80,6 +80,7 @@ abstract class FrostContentView<out T> @JvmOverloads constructor( FrostContentParent, KoinComponent where T : View, T : FrostContentCore { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider 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) @@ -102,14 +103,26 @@ abstract class FrostContentView<out T> @JvmOverloads constructor( protected abstract val layoutRes: Int - override var swipeEnabled = true + @Volatile + override var swipeDisabledByAction = false set(value) { - if (field == value) - return field = value - refresh.post { refresh.isEnabled = value } + updateSwipeEnabler() } + @Volatile + override var swipeAllowedByPage: Boolean = true + set(value) { + field = value + updateSwipeEnabler() + } + + private fun updateSwipeEnabler() { + val swipeEnabled = swipeAllowedByPage && !swipeDisabledByAction + if (refresh.isEnabled == swipeEnabled) return + refresh.post { refresh.isEnabled = swipeEnabled } + } + /** * Sets up everything * Called by [bind] @@ -134,7 +147,6 @@ abstract class FrostContentView<out T> @JvmOverloads constructor( refreshChannel.subscribeDuringJob(scope, ContextHelper.coroutineContext) { r -> refresh.isRefreshing = r - refresh.isEnabled = true } progressChannel.subscribeDuringJob(scope, ContextHelper.coroutineContext) { p -> @@ -156,9 +168,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(themeProvider.textColor.withAlpha(180)) + refresh.setColorSchemeColors(themeProvider.iconColor) + refresh.setProgressBackgroundColorSchemeColor(themeProvider.headerColor.withAlpha(255)) } override fun reloadTextSizeSelf() { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt index 09be1184..a76aeea0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt @@ -25,6 +25,7 @@ import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewTreeObserver import android.widget.FrameLayout +import ca.allanwang.kau.utils.ctxCoroutine import ca.allanwang.kau.utils.fadeIn import ca.allanwang.kau.utils.fadeOut import ca.allanwang.kau.utils.gone @@ -43,9 +44,9 @@ import com.pitchedapps.frost.R import com.pitchedapps.frost.databinding.ViewVideoBinding import com.pitchedapps.frost.db.CookieDao import com.pitchedapps.frost.db.currentCookie +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L -import com.pitchedapps.frost.utils.ctxCoroutine import com.pitchedapps.frost.utils.frostDownload import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -88,6 +89,7 @@ class FrostVideoViewer @JvmOverloads constructor( } private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private val cookieDao: CookieDao by inject() private val binding: ViewVideoBinding = @@ -100,8 +102,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 && themeProvider.bgColor.isColorDark) + themeProvider.bgColor.withMinAlpha(200) else Color.BLACK ) @@ -109,7 +111,7 @@ class FrostVideoViewer @JvmOverloads constructor( video.pause() videoToolbar.inflateMenu(R.menu.menu_video) context.setMenuIcons( - videoToolbar.menu, prefs.iconColor, + videoToolbar.menu, themeProvider.iconColor, R.id.action_pip to GoogleMaterial.Icon.gmd_picture_in_picture_alt, R.id.action_download to GoogleMaterial.Icon.gmd_file_download ) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt index 34bb5204..ecd8c093 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt @@ -24,6 +24,7 @@ import android.util.AttributeSet import android.view.View import android.view.ViewGroup import ca.allanwang.kau.utils.AnimHolder +import ca.allanwang.kau.utils.ctxCoroutine import ca.allanwang.kau.utils.launchMain import com.pitchedapps.frost.contracts.FrostContentContainer import com.pitchedapps.frost.contracts.FrostContentCore @@ -34,9 +35,9 @@ 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.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L -import com.pitchedapps.frost.utils.ctxCoroutine import com.pitchedapps.frost.utils.frostDownload import com.pitchedapps.frost.web.FrostChromeClient import com.pitchedapps.frost.web.FrostJSI @@ -62,6 +63,7 @@ class FrostWebView @JvmOverloads constructor( val fbCookie: FbCookie by inject() val prefs: Prefs by inject() + val themeProvider: ThemeProvider by inject() val cookieDao: CookieDao by inject() override fun reload(animate: Boolean) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt index 0d53608b..c3d5a90e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt @@ -37,6 +37,7 @@ import com.mikepenz.fastadapter.listeners.ClickEventHook import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.pitchedapps.frost.R +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -51,6 +52,7 @@ class Keywords @JvmOverloads constructor( ) : ConstraintLayout(context, attrs, defStyleAttr), KoinComponent { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider 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) @@ -58,8 +60,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, prefs)) + editText.tint(themeProvider.textColor) + addIcon.setImageDrawable(GoogleMaterial.Icon.gmd_add.keywordDrawable(context, themeProvider)) addIcon.setOnClickListener { if (editText.text.isNullOrEmpty()) editText.error = context.string(R.string.empty_keyword) @@ -91,8 +93,8 @@ class Keywords @JvmOverloads constructor( } } -private fun IIcon.keywordDrawable(context: Context, prefs: Prefs): Drawable = - toDrawable(context, 20, prefs.textColor) +private fun IIcon.keywordDrawable(context: Context, themeProvider: ThemeProvider): Drawable = + toDrawable(context, 20, themeProvider.textColor) class KeywordItem(val keyword: String) : AbstractItem<KeywordItem.ViewHolder>() { @@ -116,16 +118,16 @@ class KeywordItem(val keyword: String) : AbstractItem<KeywordItem.ViewHolder>() class ViewHolder(v: View) : RecyclerView.ViewHolder(v), KoinComponent { - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider 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) + text.setTextColor(themeProvider.textColor) delete.setImageDrawable( GoogleMaterial.Icon.gmd_delete.keywordDrawable( itemView.context, - prefs + themeProvider ) ) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt index 9e9c2340..f02adcf0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/SwipeRefreshLayout.kt @@ -101,9 +101,4 @@ class SwipeRefreshLayout @JvmOverloads constructor(context: Context, attrs: Attr super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed) } } - - /** - * Alias for adding on refresh listener - */ - interface OnRefreshListener : SwipeRefreshLayout.OnRefreshListener } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt index 17ab77a7..fe85ab9c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt @@ -24,9 +24,11 @@ import android.util.AttributeSet import android.view.View import android.webkit.WebView import ca.allanwang.kau.utils.withAlpha +import com.pitchedapps.frost.enums.ThemeCategory import com.pitchedapps.frost.facebook.USER_AGENT +import com.pitchedapps.frost.injectors.CssAsset import com.pitchedapps.frost.injectors.CssHider -import com.pitchedapps.frost.injectors.CssSmallAssets +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L @@ -50,6 +52,7 @@ class DebugWebView @JvmOverloads constructor( ) : WebView(context, attrs, defStyleAttr), KoinComponent { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() var onPageFinished: (String?) -> Unit = {} init { @@ -97,7 +100,7 @@ class DebugWebView @JvmOverloads constructor( private fun injectBackgroundColor() { setBackgroundColor( - if (url.isFacebookUrl) prefs.bgColor.withAlpha(255) + if (url.isFacebookUrl) themeProvider.bgColor.withAlpha(255) else Color.WHITE ) } @@ -112,12 +115,12 @@ class DebugWebView @JvmOverloads constructor( CssHider.STORIES.maybe(!prefs.showStories), CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!prefs.showSuggestedFriends), CssHider.SUGGESTED_GROUPS.maybe(!prefs.showSuggestedGroups), - prefs.themeInjector, + themeProvider.injector(ThemeCategory.FACEBOOK), CssHider.NON_RECENT.maybe( (url?.contains("?sk=h_chr") ?: false) && prefs.aggressiveRecents ), - CssSmallAssets.FullSizeImage.maybe(prefs.fullSizeImage), + CssAsset.FullSizeImage.maybe(prefs.fullSizeImage), prefs = prefs ) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt index 40a048af..12e10e10 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -18,6 +18,7 @@ package com.pitchedapps.frost.web import android.content.Context import android.webkit.JavascriptInterface +import ca.allanwang.kau.utils.ctxCoroutine import com.pitchedapps.frost.activities.MainActivity import com.pitchedapps.frost.activities.WebOverlayActivityBase import com.pitchedapps.frost.contracts.MainActivityContract @@ -28,7 +29,6 @@ import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.WebContext import com.pitchedapps.frost.utils.cookies -import com.pitchedapps.frost.utils.ctxCoroutine import com.pitchedapps.frost.utils.isIndependent import com.pitchedapps.frost.utils.launchImageActivity import com.pitchedapps.frost.utils.showWebContextMenu @@ -97,7 +97,7 @@ class FrostJSI(val web: FrostWebView) { fun longClick(start: Boolean) { activity?.contentBinding?.viewpager?.enableSwipe = !start if (web.frostWebClient.urlSupportsRefresh) { - web.parent.swipeEnabled = !start + web.parent.swipeDisabledByAction = start } } @@ -109,7 +109,7 @@ class FrostJSI(val web: FrostWebView) { if (!web.frostWebClient.urlSupportsRefresh) { return } - web.parent.swipeEnabled = !disable + web.parent.swipeDisabledByAction = disable if (disable) { // locked onto an input field; ensure content is visible (context as? MainActivityContract)?.collapseAppBar() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt index 324af69b..1495b2e0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt @@ -22,27 +22,38 @@ import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import android.webkit.WebViewClient +import ca.allanwang.kau.utils.ctxCoroutine import ca.allanwang.kau.utils.withAlpha +import com.pitchedapps.frost.db.CookieDao +import com.pitchedapps.frost.db.currentCookie +import com.pitchedapps.frost.db.updateMessengerCookie +import com.pitchedapps.frost.enums.ThemeCategory import com.pitchedapps.frost.facebook.FACEBOOK_BASE_COM import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem +import com.pitchedapps.frost.facebook.HTTPS_MESSENGER_COM +import com.pitchedapps.frost.facebook.MESSENGER_THREAD_PREFIX import com.pitchedapps.frost.facebook.WWW_FACEBOOK_COM import com.pitchedapps.frost.facebook.formattedFbUrl +import com.pitchedapps.frost.injectors.CssAsset import com.pitchedapps.frost.injectors.CssHider -import com.pitchedapps.frost.injectors.CssSmallAssets import com.pitchedapps.frost.injectors.JsActions import com.pitchedapps.frost.injectors.JsAssets +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.isExplicitIntent import com.pitchedapps.frost.utils.isFacebookUrl +import com.pitchedapps.frost.utils.isFbCookie import com.pitchedapps.frost.utils.isImageUrl import com.pitchedapps.frost.utils.isIndirectImageUrl +import com.pitchedapps.frost.utils.isMessengerUrl import com.pitchedapps.frost.utils.launchImageActivity import com.pitchedapps.frost.utils.resolveActivityForUri import com.pitchedapps.frost.views.FrostWebView import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.launch /** * Created by Allan Wang on 2017-05-31. @@ -68,10 +79,11 @@ 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 + protected val fbCookie: FbCookie get() = web.fbCookie + protected val prefs: Prefs get() = web.prefs + protected val themeProvider: ThemeProvider get() = web.themeProvider + protected val refresh: SendChannel<Boolean> = web.parent.refreshChannel + protected val isMain = web.parent.baseEnum != null /** * True if current url supports refresh. See [doUpdateVisitedHistory] for updates @@ -81,7 +93,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { override fun doUpdateVisitedHistory(view: WebView, url: String?, isReload: Boolean) { super.doUpdateVisitedHistory(view, url, isReload) urlSupportsRefresh = urlSupportsRefresh(url) - web.parent.swipeEnabled = urlSupportsRefresh + web.parent.swipeAllowedByPage = urlSupportsRefresh view.jsInject( JsAssets.AUTO_RESIZE_TEXTAREA.maybe(prefs.autoExpandTextBox), prefs = prefs @@ -91,6 +103,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { private fun urlSupportsRefresh(url: String?): Boolean { if (url == null) return false + if (url.isMessengerUrl) return false if (!url.isFacebookUrl) return true if (url.contains("soft=composer")) return false if (url.contains("sharer.php") || url.contains("sharer-dialog.php")) return false @@ -110,7 +123,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { CssHider.STORIES.maybe(!prefs.showStories), CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!prefs.showSuggestedFriends), CssHider.SUGGESTED_GROUPS.maybe(!prefs.showSuggestedGroups), - prefs.themeInjector, + themeProvider.injector(ThemeCategory.FACEBOOK), CssHider.NON_RECENT.maybe( (web.url?.contains("?sk=h_chr") ?: false) && prefs.aggressiveRecents @@ -118,7 +131,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { CssHider.ADS.maybe(!prefs.showFacebookAds), CssHider.POST_ACTIONS.maybe(!prefs.showPostActions), CssHider.POST_REACTIONS.maybe(!prefs.showPostReactions), - CssSmallAssets.FullSizeImage.maybe(prefs.fullSizeImage), + CssAsset.FullSizeImage.maybe(prefs.fullSizeImage), JsAssets.DOCUMENT_WATCHER, JsAssets.HORIZONTAL_SCROLLING, JsAssets.AUTO_RESIZE_TEXTAREA.maybe(prefs.autoExpandTextBox), @@ -130,6 +143,13 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { ) } + private fun WebView.messengerJsInject() { + jsInject( + themeProvider.injector(ThemeCategory.MESSENGER), + prefs = prefs + ) + } + override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) if (url == null) return @@ -141,7 +161,7 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { web.setBackgroundColor( when { isMain -> Color.TRANSPARENT - web.url.isFacebookUrl -> prefs.bgColor.withAlpha(255) + web.url.isFacebookUrl -> themeProvider.bgColor.withAlpha(255) else -> Color.WHITE } ) @@ -150,18 +170,25 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { override fun onPageCommitVisible(view: WebView, url: String?) { super.onPageCommitVisible(view, url) injectBackgroundColor() - if (url.isFacebookUrl) { - v { "Page commit visible" } - view.facebookJsInject() - } else { - refresh.offer(false) + when { + url.isFacebookUrl -> { + v { "FB Page commit visible" } + view.facebookJsInject() + } + url.isMessengerUrl -> { + v { "Messenger Page commit visible" } + view.messengerJsInject() + } + else -> { + refresh.offer(false) + } } } override fun onPageFinished(view: WebView, url: String?) { url ?: return v { "finished $url" } - if (!url.isFacebookUrl) { + if (!url.isFacebookUrl && !url.isMessengerUrl) { refresh.offer(false) return } @@ -179,13 +206,20 @@ open class FrostWebViewClient(val web: FrostWebView) : BaseWebViewClient() { v { "page finished reveal" } refresh.offer(false) injectBackgroundColor() - web.jsInject( - JsActions.LOGIN_CHECK, - JsAssets.TEXTAREA_LISTENER, - JsAssets.HEADER_BADGES.maybe(isMain), - prefs = prefs - ) - web.facebookJsInject() + when { + web.url.isFacebookUrl -> { + web.jsInject( + JsActions.LOGIN_CHECK, + JsAssets.TEXTAREA_LISTENER, + JsAssets.HEADER_BADGES.maybe(isMain), + prefs = prefs + ) + web.facebookJsInject() + } + web.url.isMessengerUrl -> { + web.messengerJsInject() + } + } } open fun handleHtml(html: String?) { @@ -253,8 +287,6 @@ 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) { @@ -276,3 +308,38 @@ class FrostWebViewClientMenu(web: FrostWebView) : FrostWebViewClient(web) { // Skip } } + +class FrostWebViewClientMessenger(web: FrostWebView) : FrostWebViewClient(web) { + + override fun onPageFinished(view: WebView, url: String?) { + super.onPageFinished(view, url) + messengerCookieCheck(url!!) + } + + private val cookieDao: CookieDao get() = web.cookieDao + private var hasCookie = fbCookie.messengerCookie.isFbCookie + + /** + * Check cookie changes. Unlike fb checks, we will continuously poll for cookie changes during loading. + * There is no lifecycle association between messenger login and facebook login, + * so we'll try to be smart about when to check for state changes. + * + * From testing, it looks like this is called after redirects. + * We can therefore classify no login as pointing to messenger.com, + * and login as pointing to messenger.com/t/[thread id] + */ + private fun messengerCookieCheck(url: String?) { + if (url?.startsWith(HTTPS_MESSENGER_COM) != true) return + val shouldHaveCookie = url.startsWith(MESSENGER_THREAD_PREFIX) + L._d { "Messenger client: $url $shouldHaveCookie" } + if (shouldHaveCookie == hasCookie) return + hasCookie = shouldHaveCookie + web.context.ctxCoroutine.launch { + cookieDao.updateMessengerCookie( + prefs.userId, + if (shouldHaveCookie) fbCookie.messengerCookie else null + ) + L._d { "New cookie ${cookieDao.currentCookie(prefs)?.toSensitiveString()}" } + } + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt index 1a081d2e..fadbadf9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt @@ -30,12 +30,14 @@ import ca.allanwang.kau.utils.fadeIn import ca.allanwang.kau.utils.isVisible import ca.allanwang.kau.utils.launchMain import com.pitchedapps.frost.db.CookieEntity +import com.pitchedapps.frost.enums.ThemeCategory import com.pitchedapps.frost.facebook.FB_LOGIN_URL import com.pitchedapps.frost.facebook.FB_USER_MATCHER import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.USER_AGENT import com.pitchedapps.frost.facebook.get import com.pitchedapps.frost.injectors.CssHider +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.utils.L @@ -56,6 +58,7 @@ class LoginWebView @JvmOverloads constructor( private val fbCookie: FbCookie by inject() private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private val completable: CompletableDeferred<CookieEntity> = CompletableDeferred() private lateinit var progressCallback: (Int) -> Unit @@ -105,7 +108,7 @@ class LoginWebView @JvmOverloads constructor( if (url.isFacebookUrl) view.jsInject( CssHider.CORE, - prefs.themeInjector, + themeProvider.injector(ThemeCategory.FACEBOOK), prefs = prefs ) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt b/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt index aef3fada..3bf37f7d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/widgets/NotificationWidget.kt @@ -43,6 +43,7 @@ import com.pitchedapps.frost.db.NotificationDao import com.pitchedapps.frost.db.selectNotificationsSync import com.pitchedapps.frost.glide.FrostGlide import com.pitchedapps.frost.glide.GlideApp +import com.pitchedapps.frost.injectors.ThemeProvider import com.pitchedapps.frost.prefs.Prefs import com.pitchedapps.frost.services.NotificationContent import com.pitchedapps.frost.services.NotificationType @@ -53,6 +54,7 @@ import org.koin.core.component.inject class NotificationWidget : AppWidgetProvider(), KoinComponent { private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() override fun onUpdate( context: Context, @@ -66,14 +68,14 @@ class NotificationWidget : AppWidgetProvider(), KoinComponent { 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, themeProvider.headerColor) + views.setIcon(R.id.img_frost, context, R.drawable.frost_f_24, themeProvider.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, themeProvider.bgColor) views.setRemoteAdapter(R.id.widget_notification_list, intent) val pendingIntentTemplate = PendingIntent.getActivity( @@ -156,7 +158,7 @@ class NotificationWidgetDataProvider(val context: Context, val intent: Intent) : RemoteViewsService.RemoteViewsFactory, KoinComponent { - private val prefs: Prefs by inject() + private val themeProvider: ThemeProvider by inject() private val notifDao: NotificationDao by inject() @@ -192,10 +194,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, themeProvider.nativeBgColor(notif.unread)) + views.setTextColor(R.id.item_content, themeProvider.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, themeProvider.textColor.withAlpha(150)) views.setTextViewText(R.id.item_date, notif.timestamp.toReadableTime(context)) val avatar = glide.load(notif.profileUrl).transform(FrostGlide.circleCrop) diff --git a/app/src/main/play/en-US/whatsnew b/app/src/main/play/en-US/whatsnew index 5e14d894..dc12e80e 100644 --- a/app/src/main/play/en-US/whatsnew +++ b/app/src/main/play/en-US/whatsnew @@ -1,4 +1,5 @@ -v2.4.7 +v3.0.0 -* Fix theme not always sticking on refresh -* Disable long press menu from appearing immediately after scrolling
\ No newline at end of file +* Removed email support. Please use GitHub for all inquiries as I no longer have time to look through all emails +* Added initial support for messenger.com (settings > appearance > main activity tabs) +* Fix swipe to refresh not disabling for certain pages
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee79b811..860895e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,7 @@ <string name="events">Events</string> <string name="friends">Friends</string> <string name="messages">Messages</string> + <string name="messenger">Messenger</string> <string name="notifications">Notifications</string> <string name="activity_log">Activity Log</string> <string name="pages">Pages</string> diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 6a526ccb..bdece3ae 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -6,13 +6,18 @@ <item text="" /> --> - <version title="v2.4.7" /> - <item text="Fix theme not always sticking on refresh" /> - <item text="Disable long press menu from appearing immediately after scrolling" /> + <version title="v3.0.0" /> + <item text="Removed email support. Please use GitHub for all inquiries as I no longer have time to look through all emails" /> + <item text="Added initial support for messenger.com (settings > appearance > main activity tabs)" /> + <item text="Fix swipe to refresh not disabling for certain pages" /> <item text="" /> <item text="" /> <item text="" /> + <version title="v2.4.7" /> + <item text="Fix theme not always sticking on refresh" /> + <item text="Disable long press menu from appearing immediately after scrolling" /> + <version title="v2.4.6" /> <item text="Add option to hide likes and action bar in newsfeed" /> <item text="Fix textbox scroll position when typing multiple lines" /> diff --git a/app/src/schemas/com.pitchedapps.frost.db.FrostPrivateDatabase/2.json b/app/src/schemas/com.pitchedapps.frost.db.FrostPrivateDatabase/2.json new file mode 100644 index 00000000..066f4478 --- /dev/null +++ b/app/src/schemas/com.pitchedapps.frost.db.FrostPrivateDatabase/2.json @@ -0,0 +1,201 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "c7625cc0226e291f14c14f528a11e739", + "entities": [ + { + "tableName": "cookies", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cookie_id` INTEGER NOT NULL, `name` TEXT, `cookie` TEXT, `cookieMessenger` TEXT, PRIMARY KEY(`cookie_id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "cookie_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cookie", + "columnName": "cookie", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cookieMessenger", + "columnName": "cookieMessenger", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "cookie_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "notifications", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`notif_id` INTEGER NOT NULL, `userId` INTEGER NOT NULL, `href` TEXT NOT NULL, `title` TEXT, `text` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `profileUrl` TEXT, `type` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`notif_id`, `userId`), FOREIGN KEY(`userId`) REFERENCES `cookies`(`cookie_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "notif_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "href", + "columnName": "href", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "profileUrl", + "columnName": "profileUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "notif_id", + "userId" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_notifications_notif_id", + "unique": false, + "columnNames": [ + "notif_id" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notifications_notif_id` ON `${TABLE_NAME}` (`notif_id`)" + }, + { + "name": "index_notifications_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notifications_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "cookies", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "userId" + ], + "referencedColumns": [ + "cookie_id" + ] + } + ] + }, + { + "tableName": "frost_cache", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `type` TEXT NOT NULL, `lastUpdated` INTEGER NOT NULL, `contents` TEXT NOT NULL, PRIMARY KEY(`id`, `type`), FOREIGN KEY(`id`) REFERENCES `cookies`(`cookie_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdated", + "columnName": "lastUpdated", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contents", + "columnName": "contents", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id", + "type" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [ + { + "table": "cookies", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "id" + ], + "referencedColumns": [ + "cookie_id" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c7625cc0226e291f14c14f528a11e739')" + ] + } +}
\ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbConstTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbConstTest.kt new file mode 100644 index 00000000..83bce973 --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbConstTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2021 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.facebook + +import kotlin.test.Test +import kotlin.test.assertFalse + +class FbConstTest { + + private val constants = listOf( + FACEBOOK_COM, + MESSENGER_COM, + FBCDN_NET, + WWW_FACEBOOK_COM, + WWW_MESSENGER_COM, + HTTPS_FACEBOOK_COM, + HTTPS_MESSENGER_COM, + FACEBOOK_BASE_COM, + FB_URL_BASE, + FACEBOOK_MBASIC_COM, + FB_URL_MBASIC_BASE, + FB_LOGIN_URL, + FB_HOME_URL, + MESSENGER_THREAD_PREFIX + ) + + /** + * Make sure we don't have accidental double forward slashes after appending + */ + @Test + fun doubleForwardSlashTest() { + constants.forEach { + assertFalse( + it.replace("https://", "").contains("//"), + "Accidental forward slash for $it" + ) + } + } +} diff --git a/app/src/test/kotlin/com/pitchedapps/frost/injectors/CssAssetsTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/injectors/ThemeProviderTest.kt index 8894e5b6..e1ef6225 100644 --- a/app/src/test/kotlin/com/pitchedapps/frost/injectors/CssAssetsTest.kt +++ b/app/src/test/kotlin/com/pitchedapps/frost/injectors/ThemeProviderTest.kt @@ -16,17 +16,21 @@ */ package com.pitchedapps.frost.injectors +import com.pitchedapps.frost.enums.Theme +import com.pitchedapps.frost.enums.ThemeCategory import java.io.File import kotlin.test.Test import kotlin.test.assertTrue -class CssAssetsTest { +class ThemeProviderTest { @Test fun verifyAssetsExist() { - CssAssets.values().forEach { asset -> - val file = File("src/web/assets/css/${asset.folder}/${asset.file}").absoluteFile - assertTrue(file.exists(), "${asset.name} not found at ${file.path}") + ThemeCategory.values().forEach { category -> + Theme.values.filter { it != Theme.DEFAULT }.forEach { theme -> + val file = File("src/web/assets/css/${category.folder}/themes/${theme.file}").absoluteFile + assertTrue(file.exists(), "${theme.name} not found at ${file.path}") + } } } } diff --git a/app/src/web/scss/core/_base.scss b/app/src/web/scss/core/_base.scss index 472319fe..f6b5c903 100644 --- a/app/src/web/scss/core/_base.scss +++ b/app/src/web/scss/core/_base.scss @@ -1,4 +1,8 @@ @mixin placeholder { + ::placeholder { + @content; + } + ::-webkit-input-placeholder { @content; } @@ -14,6 +18,17 @@ :-ms-input-placeholder { @content; } + + ::-ms-input-placeholder { + @content; + } +} + +@mixin fill-available { + width: 100%; + max-width: -webkit-fill-available; + max-width: -moz-available; + max-width: fill-available; } @mixin keyframes($name) { @@ -38,7 +53,12 @@ @function str-replace($string, $search, $replace: "") { $index: str-index($string, $search); - @return if($index, str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace), $string); + @return if( + $index, + str-slice($string, 1, $index - 1) + $replace + + str-replace(str-slice($string, $index + str-length($search)), $search, $replace), + $string + ); } // https://css-tricks.com/probably-dont-base64-svg/ diff --git a/app/src/web/scss/core/_colors.scss b/app/src/web/scss/core/_colors.scss index 7610572c..ea75cc15 100644 --- a/app/src/web/scss/core/_colors.scss +++ b/app/src/web/scss/core/_colors.scss @@ -4,6 +4,7 @@ $bg_transparent: rgba(#f0f, 0.02) !default; //Our default colors are test colors; production files should always import the actual colors $text: #d7b0d7 !default; +$text_disabled: rgba($text, 0.6) !default; // must be visible with accent as the background $accent_text: #76d7c2 !default; $link: #9266d5 !default; diff --git a/app/src/web/scss/core/_core_bg.scss b/app/src/web/scss/facebook/core/_core_bg.scss index c5269ccf..c5269ccf 100644 --- a/app/src/web/scss/core/_core_bg.scss +++ b/app/src/web/scss/facebook/core/_core_bg.scss diff --git a/app/src/web/scss/core/_core_border.scss b/app/src/web/scss/facebook/core/_core_border.scss index 1929e546..1929e546 100644 --- a/app/src/web/scss/core/_core_border.scss +++ b/app/src/web/scss/facebook/core/_core_border.scss diff --git a/app/src/web/scss/facebook/core/_core_hider.scss b/app/src/web/scss/facebook/core/_core_hider.scss new file mode 100644 index 00000000..a166db3b --- /dev/null +++ b/app/src/web/scss/facebook/core/_core_hider.scss @@ -0,0 +1,4 @@ +[data-sigil=m_login_upsell], +[data-sigil="m-loading-indicator-animate m-loading-indicator-root"] { + display: none !important; +}
\ No newline at end of file diff --git a/app/src/web/scss/core/_core_messenger.scss b/app/src/web/scss/facebook/core/_core_messages.scss index 608fc23d..aa3c04eb 100644 --- a/app/src/web/scss/core/_core_messenger.scss +++ b/app/src/web/scss/facebook/core/_core_messages.scss @@ -1,4 +1,4 @@ -// Not all messenger related components are here; only the main ones. +// Not all message related components are here; only the main ones. // Borders for instance are merged into core_border // Other person's message bubble diff --git a/app/src/web/scss/core/_core_text.scss b/app/src/web/scss/facebook/core/_core_text.scss index 8ac66758..8ac66758 100644 --- a/app/src/web/scss/core/_core_text.scss +++ b/app/src/web/scss/facebook/core/_core_text.scss diff --git a/app/src/web/scss/core/_main.scss b/app/src/web/scss/facebook/core/_main.scss index 3e972f93..3e972f93 100644 --- a/app/src/web/scss/core/_main.scss +++ b/app/src/web/scss/facebook/core/_main.scss diff --git a/app/src/web/scss/core/_svg.scss b/app/src/web/scss/facebook/core/_svg.scss index 9ed25433..9ed25433 100644 --- a/app/src/web/scss/core/_svg.scss +++ b/app/src/web/scss/facebook/core/_svg.scss diff --git a/app/src/web/scss/core/core.scss b/app/src/web/scss/facebook/core/core.scss index 38086529..d7e790d1 100644 --- a/app/src/web/scss/core/core.scss +++ b/app/src/web/scss/facebook/core/core.scss @@ -1,9 +1,10 @@ -@import "colors"; -@import "base"; +@import "../../core/colors"; +@import "../../core/base"; @import "core_text"; @import "core_bg"; @import "core_border"; -@import "core_messenger"; +@import "core_messages"; +@import "core_hider"; //GLOBAL overrides; use with caution *, *::after, *::before { @@ -15,13 +16,8 @@ // box-shadow: none !important; // } -[data-sigil=m_login_upsell], -[data-sigil="m-loading-indicator-animate m-loading-indicator-root"] { - display: none !important; -} - @include placeholder { - color: $text !important; + color: $text_disabled !important; } .excessItem { diff --git a/app/src/web/scss/themes/.gitignore b/app/src/web/scss/facebook/themes/.gitignore index 4c46adff..4c46adff 100644 --- a/app/src/web/scss/themes/.gitignore +++ b/app/src/web/scss/facebook/themes/.gitignore diff --git a/app/src/web/scss/facebook/themes/custom.scss b/app/src/web/scss/facebook/themes/custom.scss new file mode 100644 index 00000000..ab3d0f34 --- /dev/null +++ b/app/src/web/scss/facebook/themes/custom.scss @@ -0,0 +1,2 @@ +@import "../../palette/custom"; +@import "../core/main"; diff --git a/app/src/web/scss/facebook/themes/default.scss b/app/src/web/scss/facebook/themes/default.scss new file mode 100644 index 00000000..92e352a7 --- /dev/null +++ b/app/src/web/scss/facebook/themes/default.scss @@ -0,0 +1 @@ +@import "../core/core_hider"; diff --git a/app/src/web/scss/facebook/themes/material_amoled.scss b/app/src/web/scss/facebook/themes/material_amoled.scss new file mode 100644 index 00000000..4b1b5d17 --- /dev/null +++ b/app/src/web/scss/facebook/themes/material_amoled.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_amoled"; +@import "../core/main"; diff --git a/app/src/web/scss/facebook/themes/material_dark.scss b/app/src/web/scss/facebook/themes/material_dark.scss new file mode 100644 index 00000000..badec66d --- /dev/null +++ b/app/src/web/scss/facebook/themes/material_dark.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_dark"; +@import "../core/main"; diff --git a/app/src/web/scss/facebook/themes/material_glass.scss b/app/src/web/scss/facebook/themes/material_glass.scss new file mode 100644 index 00000000..480def7f --- /dev/null +++ b/app/src/web/scss/facebook/themes/material_glass.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_glass"; +@import "../core/main"; diff --git a/app/src/web/scss/facebook/themes/material_light.scss b/app/src/web/scss/facebook/themes/material_light.scss new file mode 100644 index 00000000..2a84f449 --- /dev/null +++ b/app/src/web/scss/facebook/themes/material_light.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_light"; +@import "../core/main";
\ No newline at end of file diff --git a/app/src/web/scss/messenger/core/_core_bg.scss b/app/src/web/scss/messenger/core/_core_bg.scss new file mode 100644 index 00000000..92d3dd85 --- /dev/null +++ b/app/src/web/scss/messenger/core/_core_bg.scss @@ -0,0 +1,4 @@ +html, body, :root, #root, +[style*="background-color: #FFFFFF"], [style*="background-color: #E4E6EB"] { + background: $bg_transparent !important; +} diff --git a/app/src/web/scss/messenger/core/_core_border.scss b/app/src/web/scss/messenger/core/_core_border.scss new file mode 100644 index 00000000..10495a02 --- /dev/null +++ b/app/src/web/scss/messenger/core/_core_border.scss @@ -0,0 +1,3 @@ +[role="navigation"] { + border-right: 2px solid $bg_opaque2 !important; +}
\ No newline at end of file diff --git a/app/src/web/scss/messenger/core/_core_hider.scss b/app/src/web/scss/messenger/core/_core_hider.scss new file mode 100644 index 00000000..a2dbeb48 --- /dev/null +++ b/app/src/web/scss/messenger/core/_core_hider.scss @@ -0,0 +1,15 @@ +// Sizing adjustments +[role="navigation"] { + .rq0escxv.l9j0dhe7.du4w35lb.j83agx80.g5gj957u.rj1gh0hx.buofh1pr.hpfvmrgz.i1fnvgqd.bp9cbjyn.owycx6da.btwxx1t3.dflh9lhu.scb9dxdr.sj5x9vvc.cxgpxx05.sn0e7ne5.f6rbj1fe.l3ldwz01 /* New! Messenger app for windows */, + .rq0escxv.l9j0dhe7.du4w35lb.n851cfcs.aahdfvyu /* Search messenger */, + .wkznzc2l /* Top left chat + menu entry */ { + display: none !important; + } +} + +header[role="banner"] /* login banner */, +._90px._9gb7 /* login bottom banner */, +.rq0escxv.l9j0dhe7.du4w35lb.j83agx80.cbu4d94t.pfnyh3mw.d2edcug0.hpfvmrgz.p8fzw8mz.pcp91wgn.iuny7tx3.ipjc6fyt /* Top bar call video info icons */, +.kuivcneq /* Right sidebar */ { + display: none !important; +} diff --git a/app/src/web/scss/messenger/core/_core_text.scss b/app/src/web/scss/messenger/core/_core_text.scss new file mode 100644 index 00000000..7409cc4e --- /dev/null +++ b/app/src/web/scss/messenger/core/_core_text.scss @@ -0,0 +1,3 @@ +html, body, input { + color: $text !important; +}
\ No newline at end of file diff --git a/app/src/web/scss/messenger/core/_core_vars.scss b/app/src/web/scss/messenger/core/_core_vars.scss new file mode 100644 index 00000000..66795299 --- /dev/null +++ b/app/src/web/scss/messenger/core/_core_vars.scss @@ -0,0 +1,34 @@ +:root, .__fb-light-mode { + --attachment-footer-background: #{$bg_opaque} !important; + --card-background-flat: #{$bg_opaque} !important; + --card-background: #{$bg_opaque} !important; + --comment-background: #{$bg_opaque2} !important; + --comment-footer-background: #{$bg_opaque} !important; + --disabled-button-background: #{$bg_opaque} !important; + --disabled-icon: #{$text_disabled} !important; + --disabled-text: #{$text_disabled} !important; + --divider: #{$divider} !important; + --hero-banner-background: #{$bg_opaque} !important; + --highlight-bg: #{$bg_opaque2} !important; + --media-outer-border: #{$bg_opaque} !important; + --messenger-card-background: #{$bg_opaque} !important; + --messenger-card-background: #{$bg_opaque} !important; // Main background; needs to be opaque to hide gradient used for sender card + --messenger-reply-background: #{$bg_opaque2} !important; + --nav-bar-background-gradient-wash: #{$bg_opaque} !important; + --nav-bar-background-gradient: #{$bg_opaque} !important; + --nav-bar-background: #{$bg_opaque} !important; + --new-notification-background: #{$bg_opaque2} !important; + --placeholder-text: #{$text} !important; // Date + --primary-icon: #{$text} !important; + --primary-text: #{$text} !important; + --secondary-icon: #{$text} !important; + --secondary-text: #{$text} !important; + --surface-background: #{$bg_opaque2} !important; // Emoji background + --toggle-active-background: #{$bg_opaque2} !important; + --wash: #{$bg_opaque2} !important; + --web-wash: #{$bg_opaque2} !important; + + [role="navigation"] { + --surface-background: #{$bg_opaque} !important; // Nav background + } +} diff --git a/app/src/web/scss/messenger/core/_main.scss b/app/src/web/scss/messenger/core/_main.scss new file mode 100644 index 00000000..e5da5377 --- /dev/null +++ b/app/src/web/scss/messenger/core/_main.scss @@ -0,0 +1,3 @@ +@import "core"; + +//this file is used as the base for all messenger themes diff --git a/app/src/web/scss/messenger/core/core.scss b/app/src/web/scss/messenger/core/core.scss new file mode 100644 index 00000000..89199222 --- /dev/null +++ b/app/src/web/scss/messenger/core/core.scss @@ -0,0 +1,11 @@ +@import "../../core/colors"; +@import "../../core/base"; +@import "core_vars"; +@import "core_text"; +@import "core_bg"; +@import "core_border"; +@import "core_hider"; + +@include placeholder { + color: $text_disabled !important; +}
\ No newline at end of file diff --git a/app/src/web/scss/messenger/themes/.gitignore b/app/src/web/scss/messenger/themes/.gitignore new file mode 100644 index 00000000..4c46adff --- /dev/null +++ b/app/src/web/scss/messenger/themes/.gitignore @@ -0,0 +1 @@ +test.scss diff --git a/app/src/web/scss/messenger/themes/custom.scss b/app/src/web/scss/messenger/themes/custom.scss new file mode 100644 index 00000000..ab3d0f34 --- /dev/null +++ b/app/src/web/scss/messenger/themes/custom.scss @@ -0,0 +1,2 @@ +@import "../../palette/custom"; +@import "../core/main"; diff --git a/app/src/web/scss/messenger/themes/default.scss b/app/src/web/scss/messenger/themes/default.scss new file mode 100644 index 00000000..92e352a7 --- /dev/null +++ b/app/src/web/scss/messenger/themes/default.scss @@ -0,0 +1 @@ +@import "../core/core_hider"; diff --git a/app/src/web/scss/messenger/themes/material_amoled.scss b/app/src/web/scss/messenger/themes/material_amoled.scss new file mode 100644 index 00000000..4b1b5d17 --- /dev/null +++ b/app/src/web/scss/messenger/themes/material_amoled.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_amoled"; +@import "../core/main"; diff --git a/app/src/web/scss/messenger/themes/material_dark.scss b/app/src/web/scss/messenger/themes/material_dark.scss new file mode 100644 index 00000000..badec66d --- /dev/null +++ b/app/src/web/scss/messenger/themes/material_dark.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_dark"; +@import "../core/main"; diff --git a/app/src/web/scss/messenger/themes/material_glass.scss b/app/src/web/scss/messenger/themes/material_glass.scss new file mode 100644 index 00000000..480def7f --- /dev/null +++ b/app/src/web/scss/messenger/themes/material_glass.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_glass"; +@import "../core/main"; diff --git a/app/src/web/scss/messenger/themes/material_light.scss b/app/src/web/scss/messenger/themes/material_light.scss new file mode 100644 index 00000000..2a84f449 --- /dev/null +++ b/app/src/web/scss/messenger/themes/material_light.scss @@ -0,0 +1,2 @@ +@import "../../palette/material_light"; +@import "../core/main";
\ No newline at end of file diff --git a/app/src/web/scss/themes/custom.scss b/app/src/web/scss/palette/_custom.scss index 50c029fb..0adb7768 100644 --- a/app/src/web/scss/themes/custom.scss +++ b/app/src/web/scss/palette/_custom.scss @@ -1,5 +1,6 @@ $bg_transparent: unquote('$BT$'); $text: unquote('$T$'); +$text_disabled: unquote('$TD$'); $link: unquote('$TT$'); $accent: unquote('$A$'); $accent_text: unquote('$AT$'); @@ -10,5 +11,3 @@ $bg_opaque2: unquote('$OO$'); $divider: unquote('$D$'); $card: unquote('$C$'); $tint: unquote('$TI$'); - -@import "../core/main"; diff --git a/app/src/web/scss/themes/material_amoled.scss b/app/src/web/scss/palette/_material_amoled.scss index 19190126..3eca36bc 100644 --- a/app/src/web/scss/themes/material_amoled.scss +++ b/app/src/web/scss/palette/_material_amoled.scss @@ -7,5 +7,3 @@ $background2: rgba($background, 0.35); $bg_transparent: $background; $card: $background2; $tint: rgba(#fff, 0.2); - -@import "../core/main"; diff --git a/app/src/web/scss/themes/material_dark.scss b/app/src/web/scss/palette/_material_dark.scss index 18b8b461..a27627c0 100644 --- a/app/src/web/scss/themes/material_dark.scss +++ b/app/src/web/scss/palette/_material_dark.scss @@ -6,5 +6,3 @@ $background: #303030; $bg_transparent: $background; $card: #353535; $tint: rgba(#fff, 0.2); - -@import "../core/main"; diff --git a/app/src/web/scss/themes/material_glass.scss b/app/src/web/scss/palette/_material_glass.scss index 0c61a38c..c9e399e7 100644 --- a/app/src/web/scss/themes/material_glass.scss +++ b/app/src/web/scss/palette/_material_glass.scss @@ -5,6 +5,4 @@ $accent: #5d86dd; $background: rgba(#000, 0.1); $bg_transparent: transparent; $card: rgba(#000, 0.25); -$tint: rgba(#fff, 0.15); - -@import "../core/main"; +$tint: rgba(#fff, 0.15);
\ No newline at end of file diff --git a/app/src/web/scss/themes/material_light.scss b/app/src/web/scss/palette/_material_light.scss index 7ec58463..2a799180 100644 --- a/app/src/web/scss/themes/material_light.scss +++ b/app/src/web/scss/palette/_material_light.scss @@ -10,6 +10,4 @@ $background2: rgba(darken($background, 8%), 0.35); $bg_transparent: $background; $card: #fff; -$tint: #ddd; - -@import "../core/main";
\ No newline at end of file +$tint: #ddd;
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 6ed9d7b9..fc6aae95 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -16,7 +16,7 @@ object Versions { // https://square.github.io/okhttp/changelog/ const val okhttp = "4.9.0" // https://developer.android.com/jetpack/androidx/releases/room - const val room = "2.2.5" + const val room = "2.2.6" // http://robolectric.org/getting-started/ const val roboelectric = "4.4" // https://github.com/davemorrissey/subsampling-scale-image-view#quick-start diff --git a/docs/Changelog.md b/docs/Changelog.md index caf30a00..9f4712c5 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,5 +1,10 @@ # Changelog +## v3.0.0 +* Removed email support. Please use GitHub for all inquiries as I no longer have time to look through all emails +* Added initial support for messenger.com (settings > appearance > main activity tabs) +* Fix swipe to refresh not disabling for certain pages + ## v2.4.7 * Fix theme not always sticking on refresh * Disable long press menu from appearing immediately after scrolling |