diff options
Diffstat (limited to 'app/src/main/kotlin')
8 files changed, 466 insertions, 245 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index ea0de778..b33f00e2 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -18,20 +18,12 @@ package com.pitchedapps.frost import android.app.Activity import android.app.Application -import android.graphics.drawable.Drawable -import android.net.Uri import android.os.Bundle -import android.widget.ImageView import ca.allanwang.kau.logging.KL import ca.allanwang.kau.utils.buildIsLollipopAndUp import com.bugsnag.android.Bugsnag import com.bugsnag.android.Configuration -import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.signature.ApplicationVersionSignature -import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader -import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.pitchedapps.frost.db.FrostDatabase -import com.pitchedapps.frost.glide.GlideApp import com.pitchedapps.frost.services.scheduleNotificationsFromPrefs import com.pitchedapps.frost.services.setupNotificationChannels import com.pitchedapps.frost.utils.BuildUtils @@ -75,22 +67,6 @@ class FrostApp : Application() { scheduleNotificationsFromPrefs() - /** - * Drawer profile loading logic - * Reload the image on every version update - */ - DrawerImageLoader.init(object : AbstractDrawerImageLoader() { - override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) { - val c = imageView.context - val request = GlideApp.with(c) - val old = request.load(uri).apply(RequestOptions().placeholder(placeholder)) - request.load(uri).apply( - RequestOptions() - .signature(ApplicationVersionSignature.obtain(c)) - ) - .thumbnail(old).into(imageView) - } - }) if (BuildConfig.DEBUG) { registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityPaused(activity: Activity) {} 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 0ef70bcd..17b475da 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt @@ -74,7 +74,6 @@ class AboutActivity : AboutActivityBase(null, { "kau", "kotterknife", "materialdialogs", - "materialdrawer", "subsamplingscaleimageview" ) 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 858f8590..62138b19 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt @@ -18,28 +18,41 @@ package com.pitchedapps.frost.activities import android.annotation.SuppressLint import android.app.ActivityOptions +import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.graphics.PointF -import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.RippleDrawable import android.net.Uri import android.os.Bundle import android.view.Menu import android.view.MenuItem +import android.view.View import android.webkit.ValueCallback import android.webkit.WebChromeClient import android.webkit.WebView import android.widget.FrameLayout -import androidx.annotation.StringRes -import androidx.coordinatorlayout.widget.CoordinatorLayout +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.ActionBarDrawerToggle +import androidx.appcompat.widget.Toolbar +import androidx.core.view.updateLayoutParams +import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentPagerAdapter import ca.allanwang.kau.searchview.SearchItem import ca.allanwang.kau.searchview.SearchView import ca.allanwang.kau.searchview.SearchViewHolder import ca.allanwang.kau.searchview.bindSearchView -import ca.allanwang.kau.utils.bindView +import ca.allanwang.kau.ui.ProgressAnimator +import ca.allanwang.kau.utils.adjustAlpha +import ca.allanwang.kau.utils.colorToForeground +import ca.allanwang.kau.utils.dimenPixelSize +import ca.allanwang.kau.utils.drawable import ca.allanwang.kau.utils.fadeScaleTransition +import ca.allanwang.kau.utils.gone +import ca.allanwang.kau.utils.invisible import ca.allanwang.kau.utils.materialDialog import ca.allanwang.kau.utils.restart import ca.allanwang.kau.utils.setIcon @@ -47,33 +60,31 @@ import ca.allanwang.kau.utils.setMenuIcons import ca.allanwang.kau.utils.showIf import ca.allanwang.kau.utils.string import ca.allanwang.kau.utils.tint +import ca.allanwang.kau.utils.toDrawable import ca.allanwang.kau.utils.toast +import ca.allanwang.kau.utils.visible import ca.allanwang.kau.utils.withMinAlpha -import co.zsmb.materialdrawerkt.builders.Builder -import co.zsmb.materialdrawerkt.builders.accountHeader -import co.zsmb.materialdrawerkt.builders.drawer -import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem -import co.zsmb.materialdrawerkt.draweritems.badgeable.secondaryItem -import co.zsmb.materialdrawerkt.draweritems.divider -import co.zsmb.materialdrawerkt.draweritems.profile.profile -import co.zsmb.materialdrawerkt.draweritems.profile.profileSetting import com.afollestad.materialdialogs.checkbox.checkBoxPrompt +import com.bumptech.glide.Glide +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.tabs.TabLayout -import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial -import com.mikepenz.iconics.utils.colorInt -import com.mikepenz.iconics.utils.paddingDp -import com.mikepenz.materialdrawer.AccountHeader -import com.mikepenz.materialdrawer.Drawer import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.R import com.pitchedapps.frost.contracts.FileChooserContract import com.pitchedapps.frost.contracts.FileChooserDelegate import com.pitchedapps.frost.contracts.MainActivityContract import com.pitchedapps.frost.contracts.VideoViewHolder +import com.pitchedapps.frost.databinding.ActivityMainBinding +import com.pitchedapps.frost.databinding.ActivityMainBottomTabsBinding +import com.pitchedapps.frost.databinding.ActivityMainDrawerWrapperBinding +import com.pitchedapps.frost.databinding.ViewNavHeaderBinding import com.pitchedapps.frost.db.CookieDao +import com.pitchedapps.frost.db.CookieEntity import com.pitchedapps.frost.db.GenericDao import com.pitchedapps.frost.db.currentCookie import com.pitchedapps.frost.db.getTabs @@ -85,6 +96,8 @@ import com.pitchedapps.frost.facebook.parsers.SearchParser import com.pitchedapps.frost.facebook.profilePictureUrl import com.pitchedapps.frost.fragments.BaseFragment import com.pitchedapps.frost.fragments.WebFragment +import com.pitchedapps.frost.glide.FrostGlide +import com.pitchedapps.frost.glide.GlideApp import com.pitchedapps.frost.services.scheduleNotificationsFromPrefs import com.pitchedapps.frost.utils.ACTIVITY_SETTINGS import com.pitchedapps.frost.utils.BiometricUtils @@ -112,10 +125,6 @@ import com.pitchedapps.frost.views.BadgedIcon import com.pitchedapps.frost.views.FrostVideoViewer import com.pitchedapps.frost.views.FrostViewPager import com.pitchedapps.frost.widgets.NotificationWidget -import kotlinx.android.synthetic.main.activity_frame_wrapper.* -import kotlinx.android.synthetic.main.view_main_fab.* -import kotlinx.android.synthetic.main.view_main_toolbar.* -import kotlinx.android.synthetic.main.view_main_viewpager.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import org.koin.android.ext.android.inject @@ -135,23 +144,24 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, * Note that tabs themselves are initialized through a coroutine during onCreate */ protected val adapter: SectionsPagerAdapter = SectionsPagerAdapter() - override val frameWrapper: FrameLayout get() = frame_wrapper - val viewPager: FrostViewPager get() = container + override val frameWrapper: FrameLayout get() = drawerWrapperBinding.mainContainer + lateinit var drawerWrapperBinding: ActivityMainDrawerWrapperBinding + lateinit var contentBinding: ActivityMainContentBinding val cookieDao: CookieDao by inject() val genericDao: GenericDao by inject() - /* - * Components with the same id in multiple layout files - */ - val tabs: TabLayout by bindView(R.id.tabs) - val appBar: AppBarLayout by bindView(R.id.appbar) - val coordinator: CoordinatorLayout by bindView(R.id.main_content) + interface ActivityMainContentBinding { + val root: View + val toolbar: Toolbar + val viewpager: FrostViewPager + val tabs: TabLayout + val appbar: AppBarLayout + val fab: FloatingActionButton + } protected var lastPosition = -1 override var videoViewer: FrostVideoViewer? = null - private lateinit var drawer: Drawer - private lateinit var drawerHeader: AccountHeader private var lastAccessTime = -1L override var searchView: SearchView? = null @@ -161,16 +171,45 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, final override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val start = System.currentTimeMillis() - setFrameContentView(Prefs.mainActivityLayout.layoutRes) - setFrostColors { - toolbar(toolbar) - themeWindow = false - header(appBar) - background(viewPager) + drawerWrapperBinding = ActivityMainDrawerWrapperBinding.inflate(layoutInflater) + setContentView(drawerWrapperBinding.root) + contentBinding = when (Prefs.mainActivityLayout) { + MainActivityLayout.TOP_BAR -> { + val binding = ActivityMainBinding.inflate(layoutInflater) + object : ActivityMainContentBinding { + override val root: View = binding.root + override val toolbar: Toolbar = binding.toolbar + override val viewpager: FrostViewPager = binding.viewpager + override val tabs: TabLayout = binding.tabs + override val appbar: AppBarLayout = binding.appbar + override val fab: FloatingActionButton = binding.fab + } + } + MainActivityLayout.BOTTOM_BAR -> { + val binding = ActivityMainBottomTabsBinding.inflate(layoutInflater) + object : ActivityMainContentBinding { + override val root: View = binding.root + override val toolbar: Toolbar = binding.toolbar + override val viewpager: FrostViewPager = binding.viewpager + override val tabs: TabLayout = binding.tabs + override val appbar: AppBarLayout = binding.appbar + override val fab: FloatingActionButton = binding.fab + } + } + } + drawerWrapperBinding.mainContainer.addView(contentBinding.root) + with(contentBinding) { + setFrostColors { + toolbar(toolbar) + themeWindow = false + header(appbar) + background(viewpager) + } + setSupportActionBar(toolbar) + viewpager.adapter = adapter + tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor()) + } - setSupportActionBar(toolbar) - viewPager.adapter = adapter - tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor()) onNestedCreate(savedInstanceState) L.i { "Main finished loading UI in ${System.currentTimeMillis() - start} ms" } launch { @@ -192,9 +231,10 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, ) } } - setupDrawer(savedInstanceState) +// setupDrawer(savedInstanceState) L.i { "Main started in ${System.currentTimeMillis() - start} ms" } - initFab() + drawerWrapperBinding.initDrawer() + contentBinding.initFab() lastAccessTime = System.currentTimeMillis() } @@ -206,182 +246,378 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, private var hasFab = false private var shouldShow = false - private fun initFab() { + private class FrostMenuBuilder(private val context: Context, private val menu: Menu) { + private var order: Int = 0 + private var groupId: Int = 13 + private val items: MutableList<Menu> = mutableListOf() + + fun primaryFrostItem(fbItem: FbItem) { + val item = menu.add(groupId, fbItem.ordinal, order++, context.string(fbItem.titleId)) + item.icon = fbItem.icon.toDrawable(context, 18) + } + + fun divider() { + groupId++ + } + + fun secondaryFrostItem(fbItem: FbItem) { + menu.add(groupId, fbItem.ordinal, order++, context.string(fbItem.titleId)) + } + } + + private fun createNavDrawable(): RippleDrawable { + val drawable = drawable(R.drawable.nav_item_background) as RippleDrawable + drawable.setColor( + ColorStateList( + arrayOf(intArrayOf()), + intArrayOf(Prefs.accentColor.adjustAlpha(0.16f)) + ) + ) + return drawable + } + + private fun ActivityMainDrawerWrapperBinding.initDrawer() { + + val toggle = ActionBarDrawerToggle( + this@BaseMainActivity, drawer, contentBinding.toolbar, + R.string.open, + R.string.close + ) + toggle.isDrawerSlideAnimationEnabled = false + drawer.addDrawerListener(toggle) + toggle.syncState() + + val foregroundColor = ColorStateList.valueOf(Prefs.textColor) + + with(navigation) { + FrostMenuBuilder(this@BaseMainActivity, menu).apply { + primaryFrostItem(FbItem.FEED_MOST_RECENT) + primaryFrostItem(FbItem.FEED_TOP_STORIES) + primaryFrostItem(FbItem.ACTIVITY_LOG) + divider() + primaryFrostItem(FbItem.PHOTOS) + primaryFrostItem(FbItem.GROUPS) + primaryFrostItem(FbItem.FRIENDS) + primaryFrostItem(FbItem.CHAT) + primaryFrostItem(FbItem.PAGES) + divider() + primaryFrostItem(FbItem.EVENTS) + primaryFrostItem(FbItem.BIRTHDAYS) + primaryFrostItem(FbItem.ON_THIS_DAY) + divider() + primaryFrostItem(FbItem.NOTES) + primaryFrostItem(FbItem.SAVED) + primaryFrostItem(FbItem.MARKETPLACE) + } + setNavigationItemSelectedListener { + val item = FbItem.values[it.itemId] + frostEvent("Drawer Tab", "name" to item.name) + drawer.closeDrawer(navigation) + launchWebOverlay(item.url) + false + } + val navBg = Prefs.bgColor.withMinAlpha(200) + setBackgroundColor(navBg) + itemBackground = createNavDrawable() + itemTextColor = foregroundColor + itemIconTintList = foregroundColor + + val header = NavHeader() + addHeaderView(header.root) + } + } + + private fun ActivityMainContentBinding.initFab() { hasFab = false shouldShow = false fab.backgroundTintList = ColorStateList.valueOf(Prefs.headerColor.withMinAlpha(200)) fab.hide() - appBar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> + appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> if (!hasFab) return@OnOffsetChangedListener val percent = abs(verticalOffset.toFloat() / appBarLayout.totalScrollRange) val shouldShow = percent < 0.2 - if (this.shouldShow != shouldShow) { - this.shouldShow = shouldShow + if (this@BaseMainActivity.shouldShow != shouldShow) { + this@BaseMainActivity.shouldShow = shouldShow fab.showIf(shouldShow) } }) } override fun showFab(iicon: IIcon, clickEvent: () -> Unit) { - hasFab = true - fab.setOnClickListener { clickEvent() } - if (shouldShow) { - if (fab.isShown) { - fab.fadeScaleTransition { - setIcon(iicon, Prefs.iconColor) + with(contentBinding) { + hasFab = true + fab.setOnClickListener { clickEvent() } + if (shouldShow) { + if (fab.isShown) { + fab.fadeScaleTransition { + setIcon(iicon, Prefs.iconColor) + } + return } - return } + fab.setIcon(iicon, Prefs.iconColor) + fab.showIf(shouldShow) } - fab.setIcon(iicon, Prefs.iconColor) - fab.showIf(shouldShow) } override fun hideFab() { - hasFab = false - fab.setOnClickListener(null) - fab.hide() + with(contentBinding) { + hasFab = false + fab.setOnClickListener(null) + fab.hide() + } } fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) { - (0 until tabs.tabCount).asSequence().forEach { i -> - action(i, tabs.getTabAt(i)!!.customView as BadgedIcon) + with(contentBinding) { + (0 until tabs.tabCount).asSequence().forEach { i -> + action(i, tabs.getTabAt(i)!!.customView as BadgedIcon) + } } } - private fun setupDrawer(savedInstanceState: Bundle?) { - val navBg = Prefs.bgColor.withMinAlpha(200).toLong() - val navHeader = Prefs.headerColor.withMinAlpha(200) - drawer = drawer { - toolbar = this@BaseMainActivity.toolbar - savedInstance = savedInstanceState - translucentStatusBar = false - sliderBackgroundColor = navBg - drawerHeader = accountHeader { - textColor = Prefs.iconColor.toLong() - backgroundDrawable = ColorDrawable(navHeader) - selectionSecondLineShown = false - cookies().forEach { (id, name) -> - profile(name = name ?: "") { - iconUrl = profilePictureUrl(id) - textColor = Prefs.textColor.toLong() - selectedTextColor = Prefs.textColor.toLong() - selectedColor = 0x00000001.toLong() - identifier = id + private inner class NavHeader { + + private var orderedAccounts: List<CookieEntity> = cookies() + private var pendingUpdate: Boolean = false + private val binding = ViewNavHeaderBinding.inflate(layoutInflater) + val root: View get() = binding.root + + init { + setPrimary(Prefs.userId) + binding.updateAccounts() + with(drawerWrapperBinding) { + drawer.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() { + override fun onDrawerClosed(drawerView: View) { + if (drawer !== navigation) return + if (!pendingUpdate) return + pendingUpdate = false + binding.updateAccounts() } + }) + } + with(binding) { + optionsContainer.setBackgroundColor( + Prefs.bgColor.withMinAlpha(200).colorToForeground( + 0.1f + ) + ) + var showOptions = false + val animator: ProgressAnimator = ProgressAnimator.ofFloat { } + background.setOnClickListener { + animator.reset() + if (showOptions) { + animator.apply { + withAnimator(optionsContainer.height.toFloat(), 0f) { + optionsContainer.updateLayoutParams { + height = it.toInt() + } + } + withAnimator(arrow.rotation, 0f) { + arrow.rotation = it + } + withEndAction { + optionsContainer.gone() + } + } + } else { + animator.apply { + optionsContainer.measure( + View.MeasureSpec.UNSPECIFIED, + View.MeasureSpec.UNSPECIFIED + ) + withAnimator( + optionsContainer.height.toFloat(), + optionsContainer.measuredHeight.toFloat() + ) { + optionsContainer.updateLayoutParams { + height = it.toInt() + } + } + withAnimator(arrow.rotation, 180f) { + arrow.rotation = it + } + withStartAction { + optionsContainer.visible() + } + } + } + showOptions = !showOptions + animator.start() } - profileSetting(nameRes = R.string.kau_logout) { - iicon = GoogleMaterial.Icon.gmd_exit_to_app - iconColor = Prefs.textColor.toLong() - textColor = Prefs.textColor.toLong() - identifier = -2L - } - profileSetting(nameRes = R.string.kau_add_account) { - iconDrawable = - IconicsDrawable( - this@BaseMainActivity, - GoogleMaterial.Icon.gmd_add - ).actionBar().paddingDp(5) - .colorInt(Prefs.textColor) - textColor = Prefs.textColor.toLong() - identifier = -3L - } - profileSetting(nameRes = R.string.kau_manage_account) { - iicon = GoogleMaterial.Icon.gmd_settings - iconColor = Prefs.textColor.toLong() - textColor = Prefs.textColor.toLong() - identifier = -4L + + val textColor = Prefs.textColor + + fun TextView.setOptionsIcon(iicon: IIcon) { + setCompoundDrawablesRelativeWithIntrinsicBounds( + iicon.toDrawable(this@BaseMainActivity, color = textColor, sizeDp = 20), + null, + null, + null + ) + setTextColor(textColor) + background = createNavDrawable() } - onProfileChanged { _, profile, current -> - if (current) launchWebOverlay(FbItem.PROFILE.url) - else when (profile.identifier) { - -2L -> { - // TODO no backpressure support - this@BaseMainActivity.launch { - val currentCookie = cookieDao.currentCookie() - if (currentCookie == null) { - toast(R.string.account_not_found) - FbCookie.reset() - launchLogin(cookies(), true) - } else { - materialDialog { - title(R.string.kau_logout) - message( - text = - String.format( - string(R.string.kau_logout_confirm_as_x), - currentCookie.name ?: Prefs.userId.toString() - ) + + with(optionsLogout) { + setOptionsIcon(GoogleMaterial.Icon.gmd_exit_to_app) + setOnClickListener { + launch { + val currentCookie = cookieDao.currentCookie() + if (currentCookie == null) { + toast(R.string.account_not_found) + FbCookie.reset() + launchLogin(cookies(), true) + } else { + materialDialog { + title(R.string.kau_logout) + message( + text = + String.format( + string(R.string.kau_logout_confirm_as_x), + currentCookie.name ?: Prefs.userId.toString() ) - positiveButton(R.string.kau_yes) { - this@BaseMainActivity.launch { - FbCookie.logout(this@BaseMainActivity) - } + ) + positiveButton(R.string.kau_yes) { + this@BaseMainActivity.launch { + FbCookie.logout(this@BaseMainActivity) } - negativeButton(R.string.kau_no) } + negativeButton(R.string.kau_no) } } } - -3L -> launchNewTask<LoginActivity>(clearStack = false) - -4L -> launchNewTask<SelectorActivity>(cookies(), false) - else -> { - this@BaseMainActivity.launch { - FbCookie.switchUser(profile.identifier) - tabsForEachView { _, view -> view.badgeText = null } - refreshAll() - } - } } - false } + with(optionsAddAccount) { + setOptionsIcon(GoogleMaterial.Icon.gmd_add) + setOnClickListener { + launchNewTask<LoginActivity>(clearStack = false) + } + } + with(optionsManageAccount) { + setOptionsIcon(GoogleMaterial.Icon.gmd_settings) + setOnClickListener { + launchNewTask<SelectorActivity>(cookies(), false) + } + } + arrow.setImageDrawable( + GoogleMaterial.Icon.gmd_arrow_drop_down.toDrawable( + this@BaseMainActivity, + color = Prefs.textColor + ) + ) } - drawerHeader.setActiveProfile(Prefs.userId) - primaryFrostItem(FbItem.FEED_MOST_RECENT) - primaryFrostItem(FbItem.FEED_TOP_STORIES) - primaryFrostItem(FbItem.ACTIVITY_LOG) - divider() - primaryFrostItem(FbItem.PHOTOS) - primaryFrostItem(FbItem.GROUPS) - primaryFrostItem(FbItem.FRIENDS) - primaryFrostItem(FbItem.CHAT) - primaryFrostItem(FbItem.PAGES) - divider() - primaryFrostItem(FbItem.EVENTS) - primaryFrostItem(FbItem.BIRTHDAYS) - primaryFrostItem(FbItem.ON_THIS_DAY) - divider() - primaryFrostItem(FbItem.NOTES) - primaryFrostItem(FbItem.SAVED) - primaryFrostItem(FbItem.MARKETPLACE) } - } - private fun Builder.primaryFrostItem(item: FbItem) = this.primaryItem(item.titleId) { - iicon = item.icon - iconColor = Prefs.textColor.toLong() - textColor = Prefs.textColor.toLong() - selectedIconColor = Prefs.textColor.toLong() - selectedTextColor = Prefs.textColor.toLong() - selectedColor = 0x00000001.toLong() - identifier = item.titleId.toLong() - onClick { _ -> - frostEvent("Drawer Tab", "name" to item.name) - launchWebOverlay(item.url) - false + private fun setPrimary(id: Long) { + val (primaries, others) = orderedAccounts.partition { it.id == id } + if (primaries.size != 1) { + L._e(null) { "Updating account primaries, could not find specified id" } + } + orderedAccounts = primaries + others + } + + /** + * Syncs UI to match [orderedAccounts]. + * + * We keep this separate as we usually only want to update when the drawer is hidden. + */ + private fun ViewNavHeaderBinding.updateAccounts() { + avatarPrimary.setAccount(orderedAccounts.getOrNull(0), true) + avatarSecondary.setAccount(orderedAccounts.getOrNull(1), false) + avatarTertiary.setAccount(orderedAccounts.getOrNull(2), false) + optionsAccountsContainer.removeAllViews() + name.text = orderedAccounts.getOrNull(0)?.name + name.setTextColor(Prefs.textColor) + val glide = Glide.with(root) + val accountSize = dimenPixelSize(R.dimen.drawer_account_avatar_size) + val textColor = Prefs.textColor + val navBackground = createNavDrawable() + orderedAccounts.forEach { cookie -> + val tv = + TextView( + this@BaseMainActivity, + null, + 0, + R.style.Main_DrawerAccountUserOptions + ) + glide.load(profilePictureUrl(cookie.id)).transform(FrostGlide.circleCrop) + .into(object : CustomTarget<Drawable>(accountSize, accountSize) { + override fun onLoadCleared(placeholder: Drawable?) { + tv.setCompoundDrawablesRelativeWithIntrinsicBounds( + placeholder, + null, + null, + null + ) + } + + override fun onResourceReady( + resource: Drawable, + transition: Transition<in Drawable>? + ) { + tv.setCompoundDrawablesRelativeWithIntrinsicBounds( + resource, + null, + null, + null + ) + } + }) + tv.text = cookie.name + tv.setTextColor(textColor) + tv.background = navBackground + tv.setOnClickListener { + switchAccount(cookie.id) + } + optionsAccountsContainer.addView(tv) + } + } + + private fun closeDrawer() { + with(drawerWrapperBinding) { + drawer.closeDrawer(navigation) + } } - } - private fun Builder.secondaryFrostItem(@StringRes title: Int, onClick: () -> Unit) = - this.secondaryItem(title) { - textColor = Prefs.textColor.toLong() - selectedIconColor = Prefs.textColor.toLong() - selectedTextColor = Prefs.textColor.toLong() - selectedColor = 0x00000001.toLong() - identifier = title.toLong() - onClick { _ -> onClick(); false } + private fun ImageView.setAccount( + cookie: CookieEntity?, + primary: Boolean + ) { + if (cookie == null) { + invisible() + setOnClickListener(null) + } else { + visible() + GlideApp.with(this) + .load(profilePictureUrl(cookie.id)) + .transform(FrostGlide.circleCrop) + .into(this) + setOnClickListener { + if (primary) { + launchWebOverlay(FbItem.PROFILE.url) + } else { + switchAccount(cookie.id) + } + closeDrawer() + } + } } + private fun switchAccount(id: Long) { + if (Prefs.userId == id) return + setPrimary(id) + pendingUpdate = true + closeDrawer() + launch { + FbCookie.switchUser(id) + tabsForEachView { _, view -> view.badgeText = null } + refreshAll() + } + } + } + private fun refreshAll() { L.d { "Refresh all" } fragmentChannel.offer(REQUEST_REFRESH) @@ -389,7 +625,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_main, menu) - toolbar.tint(Prefs.iconColor) + contentBinding.toolbar.tint(Prefs.iconColor) setMenuIcons( menu, Prefs.iconColor, R.id.action_settings to GoogleMaterial.Icon.gmd_settings, @@ -504,8 +740,6 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, } } - private val STATE_FORCE_FALLBACK = "frost_state_force_fallback" - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) adapter.saveInstanceState(outState) @@ -545,13 +779,17 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, } override fun collapseAppBar() { - appBar.post { appBar.setExpanded(false) } + with(contentBinding) { + appbar.post { appbar.setExpanded(false) } + } } override fun backConsumer(): Boolean { - if (drawer.isDrawerOpen) { - drawer.closeDrawer() - return true + with(drawerWrapperBinding) { + if (drawer.isDrawerOpen(navigation)) { + drawer.closeDrawer(navigation) + return true + } } if (currentFragment.onBackPressed()) return true if (Prefs.exitConfirmation) { @@ -570,7 +808,7 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, } inline val currentFragment - get() = supportFragmentManager.findFragmentByTag("android:switcher:${R.id.container}:${viewPager.currentItem}") as BaseFragment + get() = supportFragmentManager.findFragmentByTag("android:switcher:${R.id.container}:${contentBinding.viewpager.currentItem}") as BaseFragment override fun reloadFragment(fragment: BaseFragment) { runOnUiThread { adapter.reloadFragment(fragment) } @@ -589,25 +827,27 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, this.pages.clear() this.pages.addAll(pages) notifyDataSetChanged() - tabs.removeAllTabs() - this.pages.forEachIndexed { index, fbItem -> - tabs.addTab( - tabs.newTab() - .setCustomView(BadgedIcon(this@BaseMainActivity).apply { - iicon = fbItem.icon - }.also { - it.setAllAlpha(if (index == 0) SELECTED_TAB_ALPHA else UNSELECTED_TAB_ALPHA) - }) - ) - } - lastPosition = 0 - viewPager.setCurrentItem(0, false) - viewPager.offscreenPageLimit = pages.size - viewPager.post { - if (!fragmentChannel.isClosedForSend) { - fragmentChannel.offer(0) + with(contentBinding) { + tabs.removeAllTabs() + this@SectionsPagerAdapter.pages.forEachIndexed { index, fbItem -> + tabs.addTab( + tabs.newTab() + .setCustomView(BadgedIcon(this@BaseMainActivity).apply { + iicon = fbItem.icon + }.also { + it.setAllAlpha(if (index == 0) SELECTED_TAB_ALPHA else UNSELECTED_TAB_ALPHA) + }) + ) } - } // trigger hook so title is set + lastPosition = 0 + viewpager.setCurrentItem(0, false) + viewpager.offscreenPageLimit = pages.size + viewpager.post { + if (!fragmentChannel.isClosedForSend) { + fragmentChannel.offer(0) + } + } // trigger hook so title is set + } } fun saveInstanceState(outState: Bundle) { @@ -652,14 +892,19 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract, } } + private val lowerVideoPaddingPointF = PointF() + override val lowerVideoPadding: PointF - get() = + get() { if (Prefs.mainActivityLayout == MainActivityLayout.BOTTOM_BAR) - PointF(0f, toolbar.height.toFloat()) + lowerVideoPaddingPointF.set(0f, contentBinding.toolbar.height.toFloat()) else - PointF(0f, 0f) + lowerVideoPaddingPointF.set(0f, 0f) + return lowerVideoPaddingPointF + } companion object { + private const val STATE_FORCE_FALLBACK = "frost_state_force_fallback" const val SELECTED_TAB_ALPHA = 255f const val UNSELECTED_TAB_ALPHA = 128f } 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 e0dc409b..c8177604 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt @@ -34,7 +34,6 @@ import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.createFreshDir import com.pitchedapps.frost.utils.setFrostColors import kotlinx.android.synthetic.main.activity_debug.* -import kotlinx.android.synthetic.main.view_main_fab.* import kotlinx.coroutines.CoroutineExceptionHandler import java.io.File import kotlin.coroutines.resume diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index fc9eca37..e0053402 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -37,12 +37,14 @@ class MainActivity : BaseMainActivity() { override val headerBadgeChannel = BroadcastChannel<String>(Channel.CONFLATED) override fun onNestedCreate(savedInstanceState: Bundle?) { - setupTabs() - setupViewPager() + with(contentBinding) { + setupTabs() + setupViewPager() + } } - private fun setupViewPager() { - viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { + private fun ActivityMainContentBinding.setupViewPager() { + viewpager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { super.onPageSelected(position) if (lastPosition == position) { @@ -75,9 +77,9 @@ class MainActivity : BaseMainActivity() { }) } - private fun setupTabs() { - viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs)) - tabs.addOnTabSelectedListener(object : TabLayout.ViewPagerOnTabSelectedListener(viewPager) { + private fun ActivityMainContentBinding.setupTabs() { + viewpager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs)) + tabs.addOnTabSelectedListener(object : TabLayout.ViewPagerOnTabSelectedListener(viewpager) { override fun onTabReselected(tab: TabLayout.Tab) { super.onTabReselected(tab) currentFragment.onTabClick() @@ -88,7 +90,7 @@ class MainActivity : BaseMainActivity() { (tab.customView as BadgedIcon).badgeText = null } }) - headerBadgeChannel.subscribeDuringJob(this, Dispatchers.IO) { html -> + headerBadgeChannel.subscribeDuringJob(this@MainActivity, Dispatchers.IO) { html -> try { val doc = Jsoup.parse(html) if (doc.select("[data-sigil=count]").isEmpty()) 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 a3a4c1a4..92aa93ab 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/enums/MainActivityLayout.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/enums/MainActivityLayout.kt @@ -24,23 +24,20 @@ import com.pitchedapps.frost.utils.Prefs */ enum class MainActivityLayout( val titleRes: Int, - val layoutRes: Int, val backgroundColor: () -> Int, val iconColor: () -> Int ) { TOP_BAR(R.string.top_bar, - R.layout.activity_main, { Prefs.headerColor }, { Prefs.iconColor }), BOTTOM_BAR(R.string.bottom_bar, - R.layout.activity_main_bottom_tabs, { Prefs.bgColor }, { Prefs.textColor }); companion object { val values = values() // save one instance - operator fun invoke(index: Int) = values[index] + operator fun invoke(index: Int) = values.getOrElse(index) { TOP_BAR } } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt index 9089d891..becc9f02 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt @@ -47,7 +47,10 @@ fun Context.showWebContextMenu(wc: WebContext) { } onDismiss { // showing the dialog interrupts the touch down event, so we must ensure that the viewpager's swipe is enabled - (this@showWebContextMenu as? MainActivity)?.viewPager?.enableSwipe = true + (this@showWebContextMenu as? MainActivity) + ?.contentBinding + ?.viewpager + ?.enableSwipe = true } } } 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 0988e331..c8e551a3 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt @@ -88,7 +88,7 @@ class FrostJSI(val web: FrostWebView) { */ @JavascriptInterface fun longClick(start: Boolean) { - activity?.viewPager?.enableSwipe = !start + activity?.contentBinding?.viewpager?.enableSwipe = !start if (web.frostWebClient.urlSupportsRefresh) { web.parent.swipeEnabled = !start } @@ -151,7 +151,7 @@ class FrostJSI(val web: FrostWebView) { @JavascriptInterface fun allowHorizontalScrolling(enable: Boolean) { - activity?.viewPager?.enableSwipe = enable + activity?.contentBinding?.viewpager?.enableSwipe = enable (context as? WebOverlayActivityBase)?.swipeBack?.disallowIntercept = !enable } } |