Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AccountsDashboardFragment.kt

772 lines
33 KiB
Kotlin

/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* 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 org.mariotaku.twidere.fragment
import android.accounts.AccountManager
import android.accounts.OnAccountsUpdateListener
import android.animation.Animator
import android.animation.Animator.AnimatorListener
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.database.ContentObserver
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.view.*
import android.view.View.OnClickListener
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.widget.ActionMenuView.OnMenuItemClickListener
import androidx.core.content.ContextCompat
import androidx.core.view.MenuItemCompat
import androidx.loader.app.LoaderManager.LoaderCallbacks
import androidx.loader.content.FixedAsyncTaskLoader
import androidx.loader.content.Loader
import androidx.viewpager.widget.ViewPager
import com.google.android.material.navigation.NavigationView
import kotlinx.android.synthetic.main.header_drawer_account_selector.view.*
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.kpreferences.get
import org.mariotaku.kpreferences.set
import org.mariotaku.ktextension.*
import org.mariotaku.twidere.Constants.EXTRA_FEATURES_NOTICE_VERSION
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.ComposeActivity
import org.mariotaku.twidere.activity.HomeActivity
import org.mariotaku.twidere.activity.PremiumDashboardActivity
import org.mariotaku.twidere.activity.QuickSearchBarActivity
import org.mariotaku.twidere.adapter.AccountSelectorAdapter
import org.mariotaku.twidere.adapter.RecyclerPagerAdapter
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.annotation.ProfileImageSize
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.*
import org.mariotaku.twidere.constant.extraFeaturesNoticeVersionKey
import org.mariotaku.twidere.constant.iWantMyStarsBackKey
import org.mariotaku.twidere.constant.newDocumentApiKey
import org.mariotaku.twidere.constant.profileImageStyleKey
import org.mariotaku.twidere.extension.loadProfileBanner
import org.mariotaku.twidere.extension.loadProfileImage
import org.mariotaku.twidere.extension.model.setActivated
import org.mariotaku.twidere.extension.queryCount
import org.mariotaku.twidere.fragment.AccountsDashboardFragment.AccountsInfo
import org.mariotaku.twidere.graphic.BadgeDrawable
import org.mariotaku.twidere.menu.AccountToggleProvider
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback
import org.mariotaku.twidere.view.holder.AccountProfileImageViewHolder
import org.mariotaku.twidere.view.transformer.AccountsSelectorTransformer
import java.lang.ref.WeakReference
class AccountsDashboardFragment : BaseFragment(), LoaderCallbacks<AccountsInfo>,
OnSharedPreferenceChangeListener, OnClickListener, KeyboardShortcutCallback,
NavigationView.OnNavigationItemSelectedListener, AccountSelectorAdapter.Listener {
private val systemWindowsInsets = Rect()
private lateinit var accountsAdapter: AccountSelectorAdapter
private val navigationView by lazy { view as NavigationView }
private val accountsHeader by lazy { navigationView.getHeaderView(0) }
private val hasNextAccountIndicator by lazy { accountsHeader.hasNextAccountIndicator }
private val hasPrevAccountIndicator by lazy { accountsHeader.hasPrevAccountIndicator }
private val accountsSelector by lazy { accountsHeader.otherAccountsList }
private val accountProfileBanner by lazy { accountsHeader.accountProfileBanner }
private val floatingProfileImageSnapshot by lazy { accountsHeader.floatingProfileImageSnapshot }
private val accountProfileImageView by lazy { accountsHeader.profileImage }
private val accountProfileNameView by lazy { accountsHeader.name }
private val accountProfileScreenNameView by lazy { accountsHeader.screenName }
private val accountDashboardMenu by lazy { accountsHeader.accountDashboardMenu }
private val profileContainer by lazy { accountsHeader.profileContainer }
private val noAccountContainer by lazy { accountsHeader.noAccountContainer }
private lateinit var accountActionProvider: AccountToggleProvider
private var switchAccountAnimationPlaying: Boolean = false
private var useStarsForLikes: Boolean = false
private var loaderInitialized: Boolean = false
@SuppressLint("RestrictedApi")
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
accountsAdapter = AccountSelectorAdapter(layoutInflater, preferences, requestManager).also {
it.listener = this
}
accountsSelector.adapter = accountsAdapter
accountsSelector.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
val adapter = accountsAdapter
val pagePosition = position + positionOffset
val pageCount = adapter.count
val visiblePages = 1 / adapter.getPageWidth(position)
if (pageCount < visiblePages) {
hasPrevAccountIndicator.alpha = 0f
hasNextAccountIndicator.alpha = 0f
} else {
hasPrevAccountIndicator.alpha = pagePosition.coerceIn(0f, 1f)
hasNextAccountIndicator.alpha = (pageCount - (pagePosition + visiblePages))
.coerceIn(0f, 1f)
}
}
})
accountsSelector.setPageTransformer(false, AccountsSelectorTransformer)
hasPrevAccountIndicator.alpha = 0f
hasNextAccountIndicator.alpha = 0f
val profileImageStyle = preferences[profileImageStyleKey]
floatingProfileImageSnapshot.style = profileImageStyle
accountProfileImageView.style = profileImageStyle
SupportMenuInflater(context).inflate(R.menu.action_dashboard_timeline_toggle,
accountDashboardMenu.menu)
accountActionProvider = MenuItemCompat.getActionProvider(
accountDashboardMenu.menu.findItem(R.id.select_account)) as AccountToggleProvider
accountDashboardMenu.setOnMenuItemClickListener(OnMenuItemClickListener { item ->
if (item.groupId == AccountToggleProvider.MENU_GROUP) {
val accounts = accountActionProvider.accounts
val account = accounts[item.order]
val newActivated = !account.activated
accountActionProvider.setAccountActivated(account.key, newActivated)
account.account.setActivated(AccountManager.get(context), newActivated)
return@OnMenuItemClickListener true
} else {
when (item.itemId) {
R.id.compose -> {
val account = accountsAdapter.selectedAccount ?: return@OnMenuItemClickListener true
val composeIntent = Intent(INTENT_ACTION_COMPOSE)
activity?.let { composeIntent.setClass(it, ComposeActivity::class.java) }
composeIntent.putExtra(EXTRA_ACCOUNT_KEY, account.key)
startActivity(composeIntent)
return@OnMenuItemClickListener true
}
}
}
return@OnMenuItemClickListener false
})
profileContainer.setOnClickListener(this)
accountProfileBanner.setInAnimation(context, android.R.anim.fade_in)
accountProfileBanner.setOutAnimation(context, android.R.anim.fade_out)
accountProfileBanner.setFactory {
layoutInflater.inflate(R.layout.layout_account_dashboard_profile_banner,
accountProfileBanner, false)
}
navigationView.setNavigationItemSelectedListener(this)
preferences.registerOnSharedPreferenceChangeListener(this)
updateSystemWindowsInsets()
}
override fun onStart() {
super.onStart()
loadAccounts()
}
override fun onResume() {
super.onResume()
updateDefaultAccountState()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_accounts_dashboard, container, false)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_SETTINGS -> {
if (data == null) return
if (data.getBooleanExtra(EXTRA_SHOULD_RESTART, false)) {
Utils.restartActivity(activity)
} else if (data.getBooleanExtra(EXTRA_SHOULD_RECREATE, false)) {
activity?.recreate()
}
return
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun handleKeyboardShortcutSingle(handler: KeyboardShortcutsHandler,
keyCode: Int, event: KeyEvent, metaState: Int): Boolean {
return false
}
override fun isKeyboardShortcutHandled(handler: KeyboardShortcutsHandler, keyCode: Int, event: KeyEvent, metaState: Int): Boolean {
val action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event, metaState)
return ACTION_NAVIGATION_PREVIOUS == action || ACTION_NAVIGATION_NEXT == action
}
override fun handleKeyboardShortcutRepeat(handler: KeyboardShortcutsHandler, keyCode: Int,
repeatCount: Int, event: KeyEvent, metaState: Int): Boolean {
return true
}
override fun onClick(v: View) {
when (v.id) {
R.id.profileContainer -> {
val account = accountsAdapter.selectedAccount ?: return
val activity = activity
if (account.user != null) {
activity?.let {
IntentUtils.openUserProfile(it, account.user!!,
preferences[newDocumentApiKey], null)
}
} else {
activity?.let {
IntentUtils.openUserProfile(it, account.key, account.key,
account.user.screen_name, null, preferences[newDocumentApiKey],
null)
}
}
}
}
}
override fun onCreateLoader(id: Int, args: Bundle?): Loader<AccountsInfo> {
return AccountsInfoLoader(requireActivity(), accountsAdapter.accounts == null)
}
override fun onLoadFinished(loader: Loader<AccountsInfo>, data: AccountsInfo) {
if (context == null || isDetached || (activity?.isFinishing != false)) return
updateAccountProviderData(data)
}
override fun onLoaderReset(loader: Loader<AccountsInfo>) {
}
override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) {
if (KEY_DEFAULT_ACCOUNT_KEY == key) {
updateDefaultAccountState()
}
}
override fun onApplySystemWindowInsets(insets: Rect) {
view?.findViewById<View?>(com.google.android.material.R.id.design_navigation_view)?.
setPadding(0, 0, 0, insets.bottom)
systemWindowsInsets.set(insets)
updateSystemWindowsInsets()
}
fun loadAccounts() {
if (!loaderInitialized) {
loaderInitialized = true
loaderManager.initLoader(0, null, this)
} else {
loaderManager.restartLoader(0, null, this)
}
}
private fun updateAccountProviderData(data: AccountsInfo) {
val accounts = data.accounts
if (accounts.isNotEmpty()) {
noAccountContainer.visibility = View.GONE
profileContainer.visibility = View.VISIBLE
} else {
noAccountContainer.visibility = View.VISIBLE
profileContainer.visibility = View.INVISIBLE
}
useStarsForLikes = preferences[iWantMyStarsBackKey]
accountsAdapter.accounts = accounts
val defaultKey = preferences.getString(KEY_DEFAULT_ACCOUNT_KEY, null)?.let(UserKey::valueOf)
?: accounts.firstOrNull { it.activated }?.key
val defaultAccount = accounts.firstOrNull { it.key.maybeEquals(defaultKey) }
accountsAdapter.selectedAccount = defaultAccount
accountActionProvider.isExclusive = false
accountActionProvider.accounts = accounts
updateAccountActions()
val currentAccount = accountsAdapter.selectedAccount
if (currentAccount != null) {
displayAccountBanner(currentAccount)
displayCurrentAccount(null)
}
updateDefaultAccountState()
if (data.draftsCount > 0) {
navigationView.menu.findItem(R.id.drafts).title = "${getString(R.string.title_drafts)} (${data.draftsCount})"
} else {
navigationView.menu.findItem(R.id.drafts).title = getString(R.string.title_drafts)
}
}
private fun updateSystemWindowsInsets() {
profileContainer?.setPadding(0, systemWindowsInsets.top, 0, 0)
}
internal fun updateAccountActions() {
val activity = activity as? HomeActivity ?: return
val tabs = activity.tabs
val account = accountsAdapter.selectedAccount ?: return
var hasDmTab = false
var hasInteractionsTab = false
var hasPublicTimelineTab = false
var hasNetworkPublicTimelineTab = false
for (tab in tabs) {
when (tab.type) {
CustomTabType.DIRECT_MESSAGES -> {
if (!hasDmTab) {
hasDmTab = hasAccountInTab(tab, account.key, account.activated)
}
}
CustomTabType.NOTIFICATIONS_TIMELINE -> {
if (!hasInteractionsTab) {
hasInteractionsTab = hasAccountInTab(tab, account.key, account.activated)
}
}
CustomTabType.PUBLIC_TIMELINE -> {
if (!hasPublicTimelineTab) {
hasPublicTimelineTab = hasAccountInTab(tab, account.key, true)
}
}
CustomTabType.NETWORK_PUBLIC_TIMELINE -> {
if (!hasNetworkPublicTimelineTab) {
hasNetworkPublicTimelineTab = hasAccountInTab(tab, account.key, true)
}
}
}
}
val menu = navigationView.menu
menu.setItemAvailability(R.id.interactions, !hasInteractionsTab)
menu.setItemAvailability(R.id.favorites, useStarsForLikes)
menu.setItemAvailability(R.id.likes, !useStarsForLikes)
menu.setItemAvailability(R.id.premium_features, extraFeaturesService.isSupported())
if (preferences[extraFeaturesNoticeVersionKey] < EXTRA_FEATURES_NOTICE_VERSION) {
val icon = context?.let { ContextCompat.getDrawable(it, R.drawable.ic_action_infinity) }
val color = context?.let { ContextCompat.getColor(it, R.color.material_red) }
val size = resources.getDimensionPixelSize(R.dimen.element_spacing_msmall)
if (icon != null && color != null) {
menu.setItemIcon(R.id.premium_features, BadgeDrawable(icon, color, size))
}
} else {
menu.setItemIcon(R.id.premium_features, R.drawable.ic_action_infinity)
}
var hasLists = false
var hasGroups = false
var hasPublicTimeline = false
var hasNetworkPublicTimeline = false
var hasDirectMessages = false
when (account.type) {
AccountType.TWITTER -> {
hasDirectMessages = !hasDmTab
hasLists = true
}
AccountType.STATUSNET -> {
hasDirectMessages = !hasDmTab
hasGroups = true
hasPublicTimeline = !hasPublicTimelineTab
hasNetworkPublicTimeline = !hasNetworkPublicTimelineTab
}
AccountType.FANFOU -> {
hasDirectMessages = !hasDmTab
hasPublicTimeline = !hasPublicTimelineTab
}
AccountType.MASTODON -> {
hasPublicTimeline = !hasPublicTimelineTab
hasNetworkPublicTimeline = !hasNetworkPublicTimelineTab
}
}
menu.setItemAvailability(R.id.messages, hasDirectMessages)
menu.setItemAvailability(R.id.groups, hasGroups)
menu.setItemAvailability(R.id.lists, hasLists)
menu.setItemAvailability(R.id.public_timeline, hasPublicTimeline)
menu.setItemAvailability(R.id.network_public_timeline, hasNetworkPublicTimeline)
}
private fun hasAccountInTab(tab: SupportTabSpec, accountKey: UserKey, isActivated: Boolean): Boolean {
if (tab.args == null) return false
val accountKeys = context?.let { Utils.getAccountKeys(it, tab.args) } ?: return isActivated
return accountKey in accountKeys
}
private fun closeAccountsDrawer() {
val activity = activity as? HomeActivity ?: return
activity.closeAccountsDrawer()
}
private fun getLocationOnScreen(view: View, rectF: RectF) {
val location = IntArray(2)
view.getLocationOnScreen(location)
rectF.set(location[0].toFloat(), location[1].toFloat(), (location[0] + view.width).toFloat(), (location[1] + view.height).toFloat())
}
override fun onAccountSelected(holder: AccountProfileImageViewHolder, details: AccountDetails) {
if (switchAccountAnimationPlaying) return
val snapshotView = floatingProfileImageSnapshot
val profileImageView = accountProfileImageView
val clickedImageView = holder.iconView
// Reset snapshot view position
snapshotView.pivotX = 0f
snapshotView.pivotY = 0f
snapshotView.translationX = 0f
snapshotView.translationY = 0f
val matrix = Matrix()
val sourceBounds = RectF()
val destBounds = RectF()
val snapshotBounds = RectF()
getLocationOnScreen(clickedImageView, sourceBounds)
getLocationOnScreen(profileImageView, destBounds)
getLocationOnScreen(snapshotView, snapshotBounds)
val finalScale = destBounds.width() / sourceBounds.width()
val snapshotBitmap = TransitionUtils.createViewBitmap(clickedImageView, matrix,
RectF(0f, 0f, sourceBounds.width(), sourceBounds.height()))
val lp = snapshotView.layoutParams
lp.width = clickedImageView.width
lp.height = clickedImageView.height
snapshotView.layoutParams = lp
// Copied from MaterialNavigationDrawer: https://github.com/madcyph3r/AdvancedMaterialDrawer/
val set = AnimatorSet()
set.play(ObjectAnimator.ofFloat(snapshotView, View.TRANSLATION_X, sourceBounds.left - snapshotBounds.left, destBounds.left - snapshotBounds.left))
.with(ObjectAnimator.ofFloat(snapshotView, View.TRANSLATION_Y, sourceBounds.top - snapshotBounds.top, destBounds.top - snapshotBounds.top))
.with(ObjectAnimator.ofFloat<View>(snapshotView, View.SCALE_X, 1f, finalScale))
.with(ObjectAnimator.ofFloat<View>(snapshotView, View.SCALE_Y, 1f, finalScale))
.with(ObjectAnimator.ofFloat<View>(profileImageView, View.ALPHA, 1f, 0f))
.with(ObjectAnimator.ofFloat<View>(clickedImageView, View.SCALE_X, 0f, 1f))
.with(ObjectAnimator.ofFloat<View>(clickedImageView, View.SCALE_Y, 0f, 1f))
val animationTransition: Long = 400
set.duration = animationTransition
set.interpolator = DecelerateInterpolator()
set.addListener(object : AnimatorListener {
private var clickedDrawable: Drawable? = null
private var clickedColors: IntArray? = null
override fun onAnimationStart(animation: Animator) {
if (context == null || isDetached || (activity?.isFinishing != false)) return
snapshotView.visibility = View.VISIBLE
snapshotView.setImageBitmap(snapshotBitmap)
val profileDrawable = profileImageView.drawable
clickedDrawable = clickedImageView.drawable
//TODO complete border color
clickedColors = clickedImageView.borderColors
val oldSelectedAccount = accountsAdapter.selectedAccount ?: return
val profileImageStyle = preferences[profileImageStyleKey]
requestManager.loadProfileImage(context!!, oldSelectedAccount,
profileImageStyle, clickedImageView.cornerRadius, clickedImageView.cornerRadiusRatio)
.into(clickedImageView).onLoadStarted(profileDrawable)
//TODO complete border color
clickedImageView.setBorderColors(*profileImageView.borderColors)
displayAccountBanner(details)
switchAccountAnimationPlaying = true
}
override fun onAnimationEnd(animation: Animator) {
finishAnimation()
}
override fun onAnimationCancel(animation: Animator) {
finishAnimation()
}
override fun onAnimationRepeat(animation: Animator) {
}
private fun finishAnimation() {
preferences.edit()
.putString(KEY_DEFAULT_ACCOUNT_KEY, details.key.toString())
.apply()
accountsAdapter.selectedAccount = details
updateAccountActions()
displayCurrentAccount(clickedDrawable)
snapshotView.visibility = View.INVISIBLE
snapshotView.setImageDrawable(null)
profileImageView.setImageDrawable(clickedDrawable)
//TODO complete border color
//profileImageView.setBorderColors(*clickedColors!!)
profileImageView.alpha = 1f
clickedImageView.scaleX = 1f
clickedImageView.scaleY = 1f
clickedImageView.alpha = 1f
switchAccountAnimationPlaying = false
}
})
set.start()
}
private fun displayAccountBanner(account: AccountDetails) {
if (context == null || isDetached || (activity?.isFinishing != false)) return
val bannerWidth = accountProfileBanner.width
val res = resources
val defWidth = res.displayMetrics.widthPixels
val width = if (bannerWidth > 0) bannerWidth else defWidth
val bannerView = accountProfileBanner.nextView as ImageView
val user = account.user
val fallbackBanner = when {
user.link_color != 0 -> {
ColorDrawable(user.link_color)
}
user.account_color != 0 -> {
ColorDrawable(user.account_color)
}
else -> {
ColorDrawable(Chameleon.getOverrideTheme(requireActivity(), activity).colorPrimary)
}
}
requestManager.loadProfileBanner(requireContext(), account.user, width).fallback(fallbackBanner)
.into(bannerView)
}
private fun displayCurrentAccount(profileImageSnapshot: Drawable?) {
if (context == null || isDetached || (activity?.isFinishing != false)) return
val account = accountsAdapter.selectedAccount ?: return
accountProfileNameView.spannable = account.user.name
val showType = accountsAdapter.accounts?.groupBy { it.type }?.count()?.let {
it > 1
} ?: false
accountProfileScreenNameView.spannable = if (account.type == AccountType.MASTODON || account.type == AccountType.STATUSNET) {
account.account.name
} else {
"${if (showType) account.type else ""}@${account.user.screen_name}"
}
requestManager.loadProfileImage(requireContext(), account, preferences[profileImageStyleKey],
accountProfileImageView.cornerRadius, accountProfileImageView.cornerRadiusRatio,
ProfileImageSize.REASONABLY_SMALL).placeholder(profileImageSnapshot).into(accountProfileImageView)
//TODO complete border color
accountProfileImageView.setBorderColors(account.color)
accountProfileBanner.showNext()
}
private fun updateDefaultAccountState() {
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
val account = accountsAdapter.selectedAccount ?: return false
val currentActivity = activity ?: return false
when (item.itemId) {
R.id.search -> {
val intent = Intent(currentActivity, QuickSearchBarActivity::class.java)
intent.putExtra(EXTRA_ACCOUNT_KEY, account.key)
startActivity(intent)
closeAccountsDrawer()
}
R.id.compose -> {
val composeIntent = Intent(INTENT_ACTION_COMPOSE)
composeIntent.setClass(currentActivity, ComposeActivity::class.java)
composeIntent.putExtra(EXTRA_ACCOUNT_KEY, account.key)
startActivity(composeIntent)
}
R.id.likes, R.id.favorites -> {
IntentUtils.openUserFavorites(currentActivity, account.key, account.key,
account.user.screen_name)
}
R.id.lists -> {
IntentUtils.openUserLists(currentActivity, account.key,
account.key, account.user.screen_name)
}
R.id.groups -> {
IntentUtils.openUserGroups(currentActivity, account.key,
account.key, account.user.screen_name)
}
R.id.public_timeline -> {
IntentUtils.openPublicTimeline(currentActivity, account.key)
}
R.id.network_public_timeline -> {
IntentUtils.openNetworkPublicTimeline(currentActivity, account.key)
}
R.id.messages -> {
IntentUtils.openDirectMessages(currentActivity, account.key)
}
R.id.interactions -> {
IntentUtils.openInteractions(currentActivity, account.key)
}
R.id.edit -> {
IntentUtils.openProfileEditor(currentActivity, account.key)
}
R.id.accounts -> {
IntentUtils.openAccountsManager(currentActivity)
closeAccountsDrawer()
}
R.id.drafts -> {
IntentUtils.openDrafts(currentActivity)
closeAccountsDrawer()
}
R.id.filters -> {
IntentUtils.openFilters(currentActivity)
closeAccountsDrawer()
}
R.id.premium_features -> {
val intent = Intent(currentActivity, PremiumDashboardActivity::class.java)
startActivity(intent)
preferences[extraFeaturesNoticeVersionKey] = EXTRA_FEATURES_NOTICE_VERSION
closeAccountsDrawer()
}
R.id.settings -> {
val intent = IntentUtils.settings()
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivityForResult(intent, REQUEST_SETTINGS)
closeAccountsDrawer()
}
}
return false
}
fun setStatusBarHeight(height: Int) {
val top = activity?.let { Utils.getInsetsTopWithoutActionBarHeight(it, height) }
if (top != null) {
profileContainer.setPadding(0, top, 0, 0)
}
}
fun shouldDisableDrawerSlide(e: MotionEvent): Boolean {
if (accountsSelector == null) return false
return TwidereViewUtils.hitView(e, accountsSelector)
}
internal class AccountSpaceViewHolder(itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView)
data class AccountsInfo(
val accounts: Array<AccountDetails>,
val draftsCount: Int
)
class AccountsInfoLoader(
context: Context,
val firsSyncLoad: Boolean
) : FixedAsyncTaskLoader<AccountsInfo>(context) {
private var contentObserver: ContentObserver? = null
set(value) {
field?.let {
context.contentResolver.unregisterContentObserver(it)
}
if (value != null) {
context.contentResolver.registerContentObserver(Drafts.CONTENT_URI, true, value)
}
}
private var accountListener: OnAccountsUpdateListener? = null
set(value) {
val am = AccountManager.get(context)
field?.let {
am.removeOnAccountsUpdatedListenerSafe(it)
}
if (value != null) {
am.addOnAccountsUpdatedListenerSafe(value, updateImmediately = true)
}
}
private var firstLoad: Boolean
init {
firstLoad = true
}
override fun loadInBackground(): AccountsInfo {
return loadAccountsInfo(true)
}
override fun onForceLoad() {
if (firsSyncLoad && firstLoad) {
deliverResult(loadAccountsInfo(false))
return
}
super.onForceLoad()
}
/**
* Handles a request to completely reset the Loader.
*/
override fun onReset() {
super.onReset()
// Ensure the loader is stopped
onStopLoading()
// Stop monitoring for changes.
contentObserver = null
accountListener = null
}
/**
* Handles a request to start the Loader.
*/
override fun onStartLoading() {
val weakLoader = WeakReference(this)
// Start watching for changes in the app data.
if (contentObserver == null) {
contentObserver = object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
weakLoader.get()?.onContentChanged()
}
override fun onChange(selfChange: Boolean, uri: Uri?) {
weakLoader.get()?.onContentChanged()
}
}
}
if (accountListener == null) {
accountListener = OnAccountsUpdateListener {
weakLoader.get()?.onContentChanged()
}
}
if (takeContentChanged() || firstLoad) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad()
firstLoad = false
}
}
/**
* Handles a request to stop the Loader.
*/
override fun onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad()
}
private fun loadAccountsInfo(loadFromDb: Boolean): AccountsInfo {
val accounts = AccountUtils.getAllAccountDetails(AccountManager.get(context), true)
val draftsCount = if (loadFromDb) {
context.contentResolver.queryCount(Drafts.CONTENT_URI_UNSENT, null,
null)
} else {
-1
}
return AccountsInfo(accounts, draftsCount)
}
}
}