Account Activity enhancements (#1196)
* use the "follow" button as an "unblock" button on the profiles of blocked users * use the "follow" button as an "unblock" button on the profiles of blocked users * add an icon to the profiles that can be clicked to mute/unmute the user * add an icon to the profiles that can be clicked to mute/unmute the user * Fix view issues * Fix view issues * Implement swipe to refresh for Account layout * Implement swipe to refresh handler at the account screen * Implement swipe to refresh * Correct account refresh * Show Progress Bar * Show Progress Bar * Move "itSelf" check into the viewModel * Change methods access level * Change TimelineFragment newInstance overload * Change avatarSize type to Float * Replace ImageButton with MaterialButton * Update account activity swipe to refresh colors * Refactor code * Refactor code * Fix crash on moved account refresh * Show moved account stats * Update mute button behaviour * Show tabs and content for moved accounts * Fix crash on tablet
This commit is contained in:
parent
2cd25b6ce0
commit
ae5d8b8633
13 changed files with 890 additions and 580 deletions
|
@ -64,7 +64,7 @@ import javax.inject.Inject
|
||||||
class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportFragmentInjector, LinkListener {
|
class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportFragmentInjector, LinkListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<androidx.fragment.app.Fragment>
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelFactory
|
lateinit var viewModelFactory: ViewModelFactory
|
||||||
|
|
||||||
|
@ -72,12 +72,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
|
|
||||||
private val accountFieldAdapter = AccountFieldAdapter(this)
|
private val accountFieldAdapter = AccountFieldAdapter(this)
|
||||||
|
|
||||||
private lateinit var accountId: String
|
|
||||||
private var followState: FollowState = FollowState.NOT_FOLLOWING
|
private var followState: FollowState = FollowState.NOT_FOLLOWING
|
||||||
private var blocking: Boolean = false
|
private var blocking: Boolean = false
|
||||||
private var muting: Boolean = false
|
private var muting: Boolean = false
|
||||||
private var showingReblogs: Boolean = false
|
private var showingReblogs: Boolean = false
|
||||||
private var isSelf: Boolean = false
|
|
||||||
private var loadedAccount: Account? = null
|
private var loadedAccount: Account? = null
|
||||||
|
|
||||||
// fields for scroll animation
|
// fields for scroll animation
|
||||||
|
@ -95,7 +93,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
private var textColorPrimary: Int = 0
|
private var textColorPrimary: Int = 0
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private var textColorSecondary: Int = 0
|
private var textColorSecondary: Int = 0
|
||||||
@Px
|
|
||||||
private var avatarSize: Float = 0f
|
private var avatarSize: Float = 0f
|
||||||
@Px
|
@Px
|
||||||
private var titleVisibleHeight: Int = 0
|
private var titleVisibleHeight: Int = 0
|
||||||
|
@ -106,46 +104,118 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
REQUESTED
|
REQUESTED
|
||||||
}
|
}
|
||||||
|
|
||||||
private var adapter: AccountPagerAdapter? = null
|
private lateinit var adapter: AccountPagerAdapter
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
makeNotificationBarTransparent()
|
||||||
|
setContentView(R.layout.activity_account)
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)[AccountViewModel::class.java]
|
viewModel = ViewModelProviders.of(this, viewModelFactory)[AccountViewModel::class.java]
|
||||||
|
|
||||||
viewModel.accountData.observe(this, Observer<Resource<Account>> {
|
// Obtain information to fill out the profile.
|
||||||
when (it) {
|
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID))
|
||||||
is Success -> onAccountChanged(it.data)
|
|
||||||
is Error -> {
|
if (viewModel.isSelf) {
|
||||||
Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
|
updateButtons()
|
||||||
.setAction(R.string.action_retry) { reload() }
|
}
|
||||||
.show()
|
|
||||||
|
hideFab = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fabHide", false)
|
||||||
|
|
||||||
|
loadResources()
|
||||||
|
setupToolbar()
|
||||||
|
setupTabs()
|
||||||
|
setupAccountViews()
|
||||||
|
setupRefreshLayout()
|
||||||
|
subscribeObservables()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load colors and dimensions from resources
|
||||||
|
*/
|
||||||
|
private fun loadResources() {
|
||||||
|
toolbarColor = ThemeUtils.getColor(this, R.attr.toolbar_background_color)
|
||||||
|
backgroundColor = ThemeUtils.getColor(this, android.R.attr.colorBackground)
|
||||||
|
statusBarColorTransparent = ContextCompat.getColor(this, R.color.header_background_filter)
|
||||||
|
statusBarColorOpaque = ThemeUtils.getColor(this, R.attr.colorPrimaryDark)
|
||||||
|
textColorPrimary = ThemeUtils.getColor(this, android.R.attr.textColorPrimary)
|
||||||
|
textColorSecondary = ThemeUtils.getColor(this, android.R.attr.textColorSecondary)
|
||||||
|
avatarSize = resources.getDimension(R.dimen.account_activity_avatar_size)
|
||||||
|
titleVisibleHeight = resources.getDimensionPixelSize(R.dimen.account_activity_scroll_title_visible_height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup account widgets visibility and actions
|
||||||
|
*/
|
||||||
|
private fun setupAccountViews() {
|
||||||
|
// Initialise the default UI states.
|
||||||
|
accountFloatingActionButton.hide()
|
||||||
|
accountFollowButton.hide()
|
||||||
|
accountMuteButton.hide()
|
||||||
|
accountFollowsYouTextView.hide()
|
||||||
|
|
||||||
|
|
||||||
|
// setup the RecyclerView for the account fields
|
||||||
|
accountFieldList.isNestedScrollingEnabled = false
|
||||||
|
accountFieldList.layoutManager = LinearLayoutManager(this)
|
||||||
|
accountFieldList.adapter = accountFieldAdapter
|
||||||
|
|
||||||
|
|
||||||
|
val accountListClickListener = { v: View ->
|
||||||
|
val type = when (v.id) {
|
||||||
|
R.id.accountFollowers -> AccountListActivity.Type.FOLLOWERS
|
||||||
|
R.id.accountFollowing -> AccountListActivity.Type.FOLLOWS
|
||||||
|
else -> throw AssertionError()
|
||||||
|
}
|
||||||
|
val accountListIntent = AccountListActivity.newIntent(this, type, viewModel.accountId)
|
||||||
|
startActivityWithSlideInAnimation(accountListIntent)
|
||||||
|
}
|
||||||
|
accountFollowers.setOnClickListener(accountListClickListener)
|
||||||
|
accountFollowing.setOnClickListener(accountListClickListener)
|
||||||
|
|
||||||
|
accountStatuses.setOnClickListener {
|
||||||
|
// Make nice ripple effect on tab
|
||||||
|
accountTabLayout.getTabAt(0)!!.select()
|
||||||
|
val poorTabView = (accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0)
|
||||||
|
poorTabView.isPressed = true
|
||||||
|
accountTabLayout.postDelayed({ poorTabView.isPressed = false }, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init timeline tabs
|
||||||
|
*/
|
||||||
|
private fun setupTabs() {
|
||||||
|
// Setup the tabs and timeline pager.
|
||||||
|
adapter = AccountPagerAdapter(supportFragmentManager, viewModel.accountId)
|
||||||
|
val pageTitles = arrayOf(getString(R.string.title_statuses), getString(R.string.title_statuses_with_replies), getString(R.string.title_statuses_pinned), getString(R.string.title_media))
|
||||||
|
adapter.setPageTitles(pageTitles)
|
||||||
|
accountFragmentViewPager.pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||||
|
val pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
|
||||||
|
R.drawable.tab_page_margin_dark)
|
||||||
|
accountFragmentViewPager.setPageMarginDrawable(pageMarginDrawable)
|
||||||
|
accountFragmentViewPager.adapter = adapter
|
||||||
|
accountFragmentViewPager.offscreenPageLimit = 2
|
||||||
|
accountTabLayout.setupWithViewPager(accountFragmentViewPager)
|
||||||
|
accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||||
|
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||||
|
tab?.position?.let { position ->
|
||||||
|
(adapter.getFragment(position) as? ReselectableFragment)?.onReselect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
viewModel.relationshipData.observe(this, Observer<Resource<Relationship>> {
|
|
||||||
val relation = it?.data
|
|
||||||
if (relation != null) {
|
|
||||||
onRelationshipChanged(relation)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it is Error) {
|
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||||
Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(R.string.action_retry) { reload() }
|
override fun onTabSelected(tab: TabLayout.Tab?) {}
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
val decorView = window.decorView
|
/**
|
||||||
decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
* Setup toolbar
|
||||||
window.statusBarColor = Color.TRANSPARENT
|
*/
|
||||||
|
private fun setupToolbar() {
|
||||||
setContentView(R.layout.activity_account)
|
|
||||||
|
|
||||||
val intent = intent
|
|
||||||
accountId = intent.getStringExtra(KEY_ACCOUNT_ID)
|
|
||||||
|
|
||||||
// set toolbar top margin according to system window insets
|
// set toolbar top margin according to system window insets
|
||||||
accountCoordinatorLayout.setOnApplyWindowInsetsListener { _, insets ->
|
accountCoordinatorLayout.setOnApplyWindowInsetsListener { _, insets ->
|
||||||
val top = insets.systemWindowInsetTop
|
val top = insets.systemWindowInsetTop
|
||||||
|
@ -162,17 +232,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
hideFab = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fabHide", false)
|
|
||||||
|
|
||||||
toolbarColor = ThemeUtils.getColor(this, R.attr.toolbar_background_color)
|
|
||||||
backgroundColor = ThemeUtils.getColor(this, android.R.attr.colorBackground)
|
|
||||||
statusBarColorTransparent = ContextCompat.getColor(this, R.color.header_background_filter)
|
|
||||||
statusBarColorOpaque = ThemeUtils.getColor(this, R.attr.colorPrimaryDark)
|
|
||||||
textColorPrimary = ThemeUtils.getColor(this, android.R.attr.textColorPrimary)
|
|
||||||
textColorSecondary = ThemeUtils.getColor(this, android.R.attr.textColorSecondary)
|
|
||||||
avatarSize = resources.getDimensionPixelSize(R.dimen.account_activity_avatar_size).toFloat()
|
|
||||||
titleVisibleHeight = resources.getDimensionPixelSize(R.dimen.account_activity_scroll_title_visible_height)
|
|
||||||
|
|
||||||
ThemeUtils.setDrawableTint(this, accountToolbar.navigationIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
|
ThemeUtils.setDrawableTint(this, accountToolbar.navigationIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
|
||||||
ThemeUtils.setDrawableTint(this, accountToolbar.overflowIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
|
ThemeUtils.setDrawableTint(this, accountToolbar.overflowIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
|
||||||
|
|
||||||
|
@ -201,7 +260,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
ThemeUtils.setDrawableTint(context, accountToolbar.overflowIcon, attribute)
|
ThemeUtils.setDrawableTint(context, accountToolbar.overflowIcon, attribute)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideFab && !isSelf && !blocking) {
|
if (hideFab && !viewModel.isSelf && !blocking) {
|
||||||
if (verticalOffset > oldOffset) {
|
if (verticalOffset > oldOffset) {
|
||||||
accountFloatingActionButton.show()
|
accountFloatingActionButton.show()
|
||||||
}
|
}
|
||||||
|
@ -228,99 +287,98 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
accountToolbar.setBackgroundColor(evaluatedToolbarColor)
|
accountToolbar.setBackgroundColor(evaluatedToolbarColor)
|
||||||
accountHeaderInfoContainer.setBackgroundColor(evaluatedTabBarColor)
|
accountHeaderInfoContainer.setBackgroundColor(evaluatedTabBarColor)
|
||||||
accountTabLayout.setBackgroundColor(evaluatedTabBarColor)
|
accountTabLayout.setBackgroundColor(evaluatedTabBarColor)
|
||||||
|
swipeToRefreshLayout.isEnabled = verticalOffset == 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Initialise the default UI states.
|
}
|
||||||
accountFloatingActionButton.hide()
|
|
||||||
accountFollowButton.hide()
|
|
||||||
accountFollowsYouTextView.hide()
|
|
||||||
|
|
||||||
// Obtain information to fill out the profile.
|
private fun makeNotificationBarTransparent() {
|
||||||
viewModel.obtainAccount(accountId)
|
val decorView = window.decorView
|
||||||
|
decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
window.statusBarColor = Color.TRANSPARENT
|
||||||
|
}
|
||||||
|
|
||||||
val activeAccount = accountManager.activeAccount
|
/**
|
||||||
|
* Subscribe to data loaded at the view model
|
||||||
if (accountId == activeAccount?.accountId) {
|
*/
|
||||||
isSelf = true
|
private fun subscribeObservables() {
|
||||||
updateButtons()
|
viewModel.accountData.observe(this, Observer<Resource<Account>> {
|
||||||
} else {
|
when (it) {
|
||||||
isSelf = false
|
is Success -> onAccountChanged(it.data)
|
||||||
viewModel.obtainRelationship(accountId)
|
is Error -> {
|
||||||
}
|
Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(R.string.action_retry) { viewModel.refresh() }
|
||||||
// setup the RecyclerView for the account fields
|
.show()
|
||||||
accountFieldList.isNestedScrollingEnabled = false
|
|
||||||
accountFieldList.layoutManager = LinearLayoutManager(this)
|
|
||||||
accountFieldList.adapter = accountFieldAdapter
|
|
||||||
|
|
||||||
// Setup the tabs and timeline pager.
|
|
||||||
adapter = AccountPagerAdapter(supportFragmentManager, accountId)
|
|
||||||
val pageTitles = arrayOf(getString(R.string.title_statuses), getString(R.string.title_statuses_with_replies), getString(R.string.title_statuses_pinned), getString(R.string.title_media))
|
|
||||||
adapter?.setPageTitles(pageTitles)
|
|
||||||
accountFragmentViewPager.pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
|
||||||
val pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
|
|
||||||
R.drawable.tab_page_margin_dark)
|
|
||||||
accountFragmentViewPager.setPageMarginDrawable(pageMarginDrawable)
|
|
||||||
accountFragmentViewPager.adapter = adapter
|
|
||||||
accountFragmentViewPager.offscreenPageLimit = 2
|
|
||||||
accountTabLayout.setupWithViewPager(accountFragmentViewPager)
|
|
||||||
accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
|
||||||
override fun onTabReselected(tab: TabLayout.Tab?) {
|
|
||||||
tab?.position?.let {
|
|
||||||
(adapter?.getFragment(tab.position) as? ReselectableFragment)?.onReselect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
viewModel.relationshipData.observe(this, Observer<Resource<Relationship>> {
|
||||||
|
val relation = it?.data
|
||||||
|
if (relation != null) {
|
||||||
|
onRelationshipChanged(relation)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
if (it is Error) {
|
||||||
|
Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
|
||||||
override fun onTabSelected(tab: TabLayout.Tab?) {}
|
.setAction(R.string.action_retry) { viewModel.refresh() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
val accountListClickListener = { v: View ->
|
}
|
||||||
val type = when (v.id) {
|
|
||||||
R.id.accountFollowers -> AccountListActivity.Type.FOLLOWERS
|
|
||||||
R.id.accountFollowing -> AccountListActivity.Type.FOLLOWS
|
|
||||||
else -> throw AssertionError()
|
|
||||||
}
|
|
||||||
val accountListIntent = AccountListActivity.newIntent(this, type, accountId)
|
|
||||||
startActivityWithSlideInAnimation(accountListIntent)
|
|
||||||
}
|
|
||||||
accountFollowers.setOnClickListener(accountListClickListener)
|
|
||||||
accountFollowing.setOnClickListener(accountListClickListener)
|
|
||||||
|
|
||||||
accountStatuses.setOnClickListener {
|
/**
|
||||||
// Make nice ripple effect on tab
|
* Setup swipe to refresh layout
|
||||||
accountTabLayout.getTabAt(0)!!.select()
|
*/
|
||||||
val poorTabView = (accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0)
|
private fun setupRefreshLayout() {
|
||||||
poorTabView.isPressed = true
|
swipeToRefreshLayout.setOnRefreshListener {
|
||||||
accountTabLayout.postDelayed({ poorTabView.isPressed = false }, 300)
|
viewModel.refresh()
|
||||||
|
adapter.refreshContent()
|
||||||
}
|
}
|
||||||
|
viewModel.isRefreshing.observe(this, Observer { isRefreshing ->
|
||||||
|
swipeToRefreshLayout.isRefreshing = isRefreshing == true
|
||||||
|
})
|
||||||
|
swipeToRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||||
|
swipeToRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(this,
|
||||||
|
android.R.attr.colorBackground))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onAccountChanged(account: Account?) {
|
private fun onAccountChanged(account: Account?) {
|
||||||
if (account != null) {
|
loadedAccount = account ?: return
|
||||||
loadedAccount = account
|
|
||||||
val usernameFormatted = getString(R.string.status_username_format, account.username)
|
|
||||||
accountUsernameTextView.text = usernameFormatted
|
|
||||||
accountDisplayNameTextView.text = CustomEmojiHelper.emojifyString(account.name, account.emojis, accountDisplayNameTextView)
|
|
||||||
if (supportActionBar != null) {
|
|
||||||
try {
|
|
||||||
supportActionBar?.title = EmojiCompat.get().process(account.name)
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
supportActionBar?.title = account.name
|
|
||||||
}
|
|
||||||
|
|
||||||
val subtitle = String.format(getString(R.string.status_username_format),
|
val usernameFormatted = getString(R.string.status_username_format, account.username)
|
||||||
account.username)
|
accountUsernameTextView.text = usernameFormatted
|
||||||
supportActionBar?.subtitle = subtitle
|
accountDisplayNameTextView.text = CustomEmojiHelper.emojifyString(account.name, account.emojis, accountDisplayNameTextView)
|
||||||
}
|
|
||||||
val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
|
|
||||||
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
|
||||||
|
|
||||||
accountLockedImageView.visible(account.locked)
|
val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
|
||||||
accountBadgeTextView.visible(account.bot)
|
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
|
||||||
|
|
||||||
|
accountFieldAdapter.fields = account.fields ?: emptyList()
|
||||||
|
accountFieldAdapter.emojis = account.emojis ?: emptyList()
|
||||||
|
accountFieldAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
|
||||||
|
accountLockedImageView.visible(account.locked)
|
||||||
|
accountBadgeTextView.visible(account.bot)
|
||||||
|
|
||||||
|
updateAccountAvatar()
|
||||||
|
updateToolbar()
|
||||||
|
updateMovedAccount()
|
||||||
|
updateRemoteAccount()
|
||||||
|
updateAccountStats()
|
||||||
|
|
||||||
|
accountMuteButton.setOnClickListener {
|
||||||
|
viewModel.changeMuteState()
|
||||||
|
updateMuteButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load account's avatar and header image
|
||||||
|
*/
|
||||||
|
private fun updateAccountAvatar() {
|
||||||
|
loadedAccount?.let { account ->
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.load(account.avatar)
|
.load(account.avatar)
|
||||||
.placeholder(R.drawable.avatar_default)
|
.placeholder(R.drawable.avatar_default)
|
||||||
|
@ -330,6 +388,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
.into(accountHeaderImageView)
|
.into(accountHeaderImageView)
|
||||||
|
|
||||||
|
|
||||||
accountAvatarImageView.setOnClickListener { avatarView ->
|
accountAvatarImageView.setOnClickListener { avatarView ->
|
||||||
val intent = ViewMediaActivity.newAvatarIntent(avatarView.context, account.avatar)
|
val intent = ViewMediaActivity.newAvatarIntent(avatarView.context, account.avatar)
|
||||||
|
|
||||||
|
@ -338,52 +397,75 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
|
|
||||||
startActivity(intent, options.toBundle())
|
startActivity(intent, options.toBundle())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
accountFieldAdapter.fields = account.fields ?: emptyList()
|
/**
|
||||||
accountFieldAdapter.emojis = account.emojis ?: emptyList()
|
* Update toolbar views for loaded account
|
||||||
accountFieldAdapter.notifyDataSetChanged()
|
*/
|
||||||
|
private fun updateToolbar() {
|
||||||
|
loadedAccount?.let { account ->
|
||||||
|
try {
|
||||||
|
supportActionBar?.title = EmojiCompat.get().process(account.name)
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
supportActionBar?.title = account.name
|
||||||
|
}
|
||||||
|
supportActionBar?.subtitle = String.format(getString(R.string.status_username_format), account.username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (account.moved != null) {
|
/**
|
||||||
val movedAccount = account.moved
|
* Update moved account info
|
||||||
|
*/
|
||||||
|
private fun updateMovedAccount() {
|
||||||
|
loadedAccount?.moved?.let { movedAccount ->
|
||||||
|
|
||||||
accountMovedView.show()
|
accountMovedView?.show()
|
||||||
|
|
||||||
// necessary because accountMovedView is now replaced in layout hierachy
|
// necessary because accountMovedView is now replaced in layout hierachy
|
||||||
findViewById<View>(R.id.accountMovedView).setOnClickListener {
|
findViewById<View>(R.id.accountMovedViewLayout).setOnClickListener {
|
||||||
onViewAccount(movedAccount.id)
|
onViewAccount(movedAccount.id)
|
||||||
}
|
|
||||||
|
|
||||||
accountMovedDisplayName.text = movedAccount.name
|
|
||||||
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
|
|
||||||
|
|
||||||
Glide.with(this)
|
|
||||||
.load(movedAccount.avatar)
|
|
||||||
.placeholder(R.drawable.avatar_default)
|
|
||||||
.into(accountMovedAvatar)
|
|
||||||
|
|
||||||
accountMovedText.text = getString(R.string.account_moved_description, movedAccount.displayName)
|
|
||||||
|
|
||||||
// this is necessary because API 19 can't handle vector compound drawables
|
|
||||||
val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
|
|
||||||
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
|
|
||||||
movedIcon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
|
||||||
|
|
||||||
accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
|
|
||||||
|
|
||||||
accountFollowers.hide()
|
|
||||||
accountFollowing.hide()
|
|
||||||
accountStatuses.hide()
|
|
||||||
accountTabLayout.hide()
|
|
||||||
accountFragmentViewPager.hide()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accountMovedDisplayName.text = movedAccount.name
|
||||||
|
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
|
||||||
|
|
||||||
|
Glide.with(this)
|
||||||
|
.load(movedAccount.avatar)
|
||||||
|
.placeholder(R.drawable.avatar_default)
|
||||||
|
.into(accountMovedAvatar)
|
||||||
|
|
||||||
|
accountMovedText.text = getString(R.string.account_moved_description, movedAccount.displayName)
|
||||||
|
|
||||||
|
// this is necessary because API 19 can't handle vector compound drawables
|
||||||
|
val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
|
||||||
|
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
|
||||||
|
movedIcon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
||||||
|
|
||||||
|
accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check is account remote and update info if so
|
||||||
|
*/
|
||||||
|
private fun updateRemoteAccount() {
|
||||||
|
loadedAccount?.let { account ->
|
||||||
if (account.isRemote()) {
|
if (account.isRemote()) {
|
||||||
accountRemoveView.show()
|
accountRemoveView.show()
|
||||||
accountRemoveView.setOnClickListener {
|
accountRemoveView.setOnClickListener {
|
||||||
LinkHelper.openLink(account.url, this)
|
LinkHelper.openLink(account.url, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update account stat info
|
||||||
|
*/
|
||||||
|
private fun updateAccountStats() {
|
||||||
|
loadedAccount?.let { account ->
|
||||||
val numberFormat = NumberFormat.getNumberInstance()
|
val numberFormat = NumberFormat.getNumberInstance()
|
||||||
accountFollowersTextView.text = numberFormat.format(account.followersCount)
|
accountFollowersTextView.text = numberFormat.format(account.followersCount)
|
||||||
accountFollowingTextView.text = numberFormat.format(account.followingCount)
|
accountFollowingTextView.text = numberFormat.format(account.followingCount)
|
||||||
|
@ -392,19 +474,25 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
accountFloatingActionButton.setOnClickListener { mention() }
|
accountFloatingActionButton.setOnClickListener { mention() }
|
||||||
|
|
||||||
accountFollowButton.setOnClickListener {
|
accountFollowButton.setOnClickListener {
|
||||||
if (isSelf) {
|
if (viewModel.isSelf) {
|
||||||
val intent = Intent(this@AccountActivity, EditProfileActivity::class.java)
|
val intent = Intent(this@AccountActivity, EditProfileActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (blocking) {
|
||||||
|
viewModel.changeBlockState()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
when (followState) {
|
when (followState) {
|
||||||
AccountActivity.FollowState.NOT_FOLLOWING -> {
|
FollowState.NOT_FOLLOWING -> {
|
||||||
viewModel.changeFollowState(accountId)
|
viewModel.changeFollowState()
|
||||||
}
|
}
|
||||||
AccountActivity.FollowState.REQUESTED -> {
|
FollowState.REQUESTED -> {
|
||||||
showFollowRequestPendingDialog()
|
showFollowRequestPendingDialog()
|
||||||
}
|
}
|
||||||
AccountActivity.FollowState.FOLLOWING -> {
|
FollowState.FOLLOWING -> {
|
||||||
showUnfollowWarningDialog()
|
showUnfollowWarningDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,11 +501,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
outState.putString(KEY_ACCOUNT_ID, accountId)
|
|
||||||
super.onSaveInstanceState(outState)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRelationshipChanged(relation: Relationship) {
|
private fun onRelationshipChanged(relation: Relationship) {
|
||||||
followState = when {
|
followState = when {
|
||||||
relation.following -> FollowState.FOLLOWING
|
relation.following -> FollowState.FOLLOWING
|
||||||
|
@ -433,53 +516,67 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
updateButtons()
|
updateButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reload() {
|
|
||||||
viewModel.obtainAccount(accountId, true)
|
|
||||||
viewModel.obtainRelationship(accountId)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateFollowButton() {
|
private fun updateFollowButton() {
|
||||||
if (isSelf) {
|
if (viewModel.isSelf) {
|
||||||
accountFollowButton.setText(R.string.action_edit_own_profile)
|
accountFollowButton.setText(R.string.action_edit_own_profile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (blocking) {
|
||||||
|
accountFollowButton.setText(R.string.action_unblock)
|
||||||
|
return
|
||||||
|
}
|
||||||
when (followState) {
|
when (followState) {
|
||||||
AccountActivity.FollowState.NOT_FOLLOWING -> {
|
FollowState.NOT_FOLLOWING -> {
|
||||||
accountFollowButton.setText(R.string.action_follow)
|
accountFollowButton.setText(R.string.action_follow)
|
||||||
}
|
}
|
||||||
AccountActivity.FollowState.REQUESTED -> {
|
FollowState.REQUESTED -> {
|
||||||
accountFollowButton.setText(R.string.state_follow_requested)
|
accountFollowButton.setText(R.string.state_follow_requested)
|
||||||
}
|
}
|
||||||
AccountActivity.FollowState.FOLLOWING -> {
|
FollowState.FOLLOWING -> {
|
||||||
accountFollowButton.setText(R.string.action_unfollow)
|
accountFollowButton.setText(R.string.action_unfollow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateMuteButton() {
|
||||||
|
if (muting) {
|
||||||
|
accountMuteButton.setIconResource(R.drawable.ic_unmute_24dp)
|
||||||
|
} else {
|
||||||
|
accountMuteButton.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateButtons() {
|
private fun updateButtons() {
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
|
|
||||||
if (!blocking && loadedAccount?.moved == null) {
|
if (loadedAccount?.moved == null) {
|
||||||
|
|
||||||
accountFollowButton.show()
|
accountFollowButton.show()
|
||||||
updateFollowButton()
|
updateFollowButton()
|
||||||
|
|
||||||
if (isSelf) {
|
if (blocking || viewModel.isSelf) {
|
||||||
accountFloatingActionButton.hide()
|
accountFloatingActionButton.hide()
|
||||||
|
accountMuteButton.hide()
|
||||||
} else {
|
} else {
|
||||||
accountFloatingActionButton.show()
|
accountFloatingActionButton.show()
|
||||||
|
if (muting)
|
||||||
|
accountMuteButton.show()
|
||||||
|
else
|
||||||
|
accountMuteButton.hide()
|
||||||
|
updateMuteButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
accountFloatingActionButton.hide()
|
accountFloatingActionButton.hide()
|
||||||
accountFollowButton.hide()
|
accountFollowButton.hide()
|
||||||
|
accountMuteButton.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.account_toolbar, menu)
|
menuInflater.inflate(R.menu.account_toolbar, menu)
|
||||||
|
|
||||||
if (!isSelf) {
|
if (!viewModel.isSelf) {
|
||||||
val follow = menu.findItem(R.id.action_follow)
|
val follow = menu.findItem(R.id.action_follow)
|
||||||
follow.title = if (followState == FollowState.NOT_FOLLOWING) {
|
follow.title = if (followState == FollowState.NOT_FOLLOWING) {
|
||||||
getString(R.string.action_follow)
|
getString(R.string.action_follow)
|
||||||
|
@ -529,7 +626,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
private fun showFollowRequestPendingDialog() {
|
private fun showFollowRequestPendingDialog() {
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setMessage(R.string.dialog_message_cancel_follow_request)
|
.setMessage(R.string.dialog_message_cancel_follow_request)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeFollowState(accountId) }
|
.setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeFollowState() }
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@ -537,7 +634,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
private fun showUnfollowWarningDialog() {
|
private fun showUnfollowWarningDialog() {
|
||||||
AlertDialog.Builder(this)
|
AlertDialog.Builder(this)
|
||||||
.setMessage(R.string.dialog_unfollow_warning)
|
.setMessage(R.string.dialog_unfollow_warning)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeFollowState(accountId) }
|
.setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeFollowState() }
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@ -585,20 +682,20 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_follow -> {
|
R.id.action_follow -> {
|
||||||
viewModel.changeFollowState(accountId)
|
viewModel.changeFollowState()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_block -> {
|
R.id.action_block -> {
|
||||||
viewModel.changeBlockState(accountId)
|
viewModel.changeBlockState()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_mute -> {
|
R.id.action_mute -> {
|
||||||
viewModel.changeMuteState(accountId)
|
viewModel.changeMuteState()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_show_reblogs -> {
|
R.id.action_show_reblogs -> {
|
||||||
viewModel.changeShowReblogsState(accountId)
|
viewModel.changeShowReblogsState()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -606,7 +703,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionButton(): FloatingActionButton? {
|
override fun getActionButton(): FloatingActionButton? {
|
||||||
return if (!isSelf && !blocking) {
|
return if (!viewModel.isSelf && !blocking) {
|
||||||
accountFloatingActionButton
|
accountFloatingActionButton
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||||
|
|
|
@ -30,6 +30,7 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasSu
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||||
|
|
|
@ -77,7 +77,7 @@ class NetworkModule {
|
||||||
.apply {
|
.apply {
|
||||||
addInterceptor(InstanceSwitchAuthInterceptor(accountManager))
|
addInterceptor(InstanceSwitchAuthInterceptor(accountManager))
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC))
|
addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.keylesspalace.tusky.ViewMediaActivity
|
||||||
import com.keylesspalace.tusky.di.Injectable
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.entity.Status
|
import com.keylesspalace.tusky.entity.Status
|
||||||
|
import com.keylesspalace.tusky.interfaces.RefreshableFragment
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils
|
import com.keylesspalace.tusky.util.ThemeUtils
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
|
@ -53,22 +54,26 @@ import javax.inject.Inject
|
||||||
* Fragment with multiple columns of media previews for the specified account.
|
* Fragment with multiple columns of media previews for the specified account.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AccountMediaFragment : BaseFragment(), Injectable {
|
class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(accountId: String): AccountMediaFragment {
|
fun newInstance(accountId: String, enableSwipeToRefresh:Boolean=true): AccountMediaFragment {
|
||||||
val fragment = AccountMediaFragment()
|
val fragment = AccountMediaFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putString(ACCOUNT_ID_ARG, accountId)
|
args.putString(ACCOUNT_ID_ARG, accountId)
|
||||||
|
args.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH,enableSwipeToRefresh)
|
||||||
fragment.arguments = args
|
fragment.arguments = args
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val ACCOUNT_ID_ARG = "account_id"
|
private const val ACCOUNT_ID_ARG = "account_id"
|
||||||
private const val TAG = "AccountMediaFragment"
|
private const val TAG = "AccountMediaFragment"
|
||||||
|
private const val ARG_ENABLE_SWIPE_TO_REFRESH = "arg.enable.swipe.to.refresh"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var isSwipeToRefreshEnabled: Boolean = true
|
||||||
|
private var needToRefresh = false
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var api: MastodonApi
|
lateinit var api: MastodonApi
|
||||||
|
|
||||||
|
@ -78,6 +83,8 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
private var fetchingStatus = FetchingStatus.NOT_FETCHING
|
private var fetchingStatus = FetchingStatus.NOT_FETCHING
|
||||||
private var isVisibleToUser: Boolean = false
|
private var isVisibleToUser: Boolean = false
|
||||||
|
|
||||||
|
private var accountId: String?=null
|
||||||
|
|
||||||
private val callback = object : Callback<List<Status>> {
|
private val callback = object : Callback<List<Status>> {
|
||||||
override fun onFailure(call: Call<List<Status>>?, t: Throwable?) {
|
override fun onFailure(call: Call<List<Status>>?, t: Throwable?) {
|
||||||
fetchingStatus = FetchingStatus.NOT_FETCHING
|
fetchingStatus = FetchingStatus.NOT_FETCHING
|
||||||
|
@ -85,6 +92,7 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
progressBar.visibility = View.GONE
|
progressBar.visibility = View.GONE
|
||||||
|
topProgressBar?.hide()
|
||||||
statusView.show()
|
statusView.show()
|
||||||
if (t is IOException) {
|
if (t is IOException) {
|
||||||
statusView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
statusView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||||
|
@ -105,6 +113,7 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
progressBar.visibility = View.GONE
|
progressBar.visibility = View.GONE
|
||||||
|
topProgressBar?.hide()
|
||||||
|
|
||||||
val body = response.body()
|
val body = response.body()
|
||||||
body?.let { fetched ->
|
body?.let { fetched ->
|
||||||
|
@ -115,6 +124,8 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
result.addAll(AttachmentViewData.list(status))
|
result.addAll(AttachmentViewData.list(status))
|
||||||
}
|
}
|
||||||
adapter.addTop(result)
|
adapter.addTop(result)
|
||||||
|
if (result.isNotEmpty())
|
||||||
|
recyclerView.scrollToPosition(0)
|
||||||
|
|
||||||
if (statuses.isEmpty()) {
|
if (statuses.isEmpty()) {
|
||||||
statusView.show()
|
statusView.show()
|
||||||
|
@ -152,6 +163,11 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
isSwipeToRefreshEnabled = arguments?.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH,true)==true
|
||||||
|
accountId = arguments?.getString(ACCOUNT_ID_ARG)
|
||||||
|
}
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?): View? {
|
savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_timeline, container, false)
|
return inflater.inflate(R.layout.fragment_timeline, container, false)
|
||||||
|
@ -171,24 +187,15 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
val accountId = arguments?.getString(ACCOUNT_ID_ARG)
|
|
||||||
|
|
||||||
swipeRefreshLayout.setOnRefreshListener {
|
|
||||||
statusView.hide()
|
if (isSwipeToRefreshEnabled) {
|
||||||
if (fetchingStatus != FetchingStatus.NOT_FETCHING) return@setOnRefreshListener
|
swipeRefreshLayout.setOnRefreshListener {
|
||||||
currentCall = if (statuses.isEmpty()) {
|
refresh()
|
||||||
fetchingStatus = FetchingStatus.INITIAL_FETCHING
|
|
||||||
api.accountStatuses(accountId, null, null, null, null, true, null)
|
|
||||||
} else {
|
|
||||||
fetchingStatus = FetchingStatus.REFRESHING
|
|
||||||
api.accountStatuses(accountId, null, statuses[0].id, null, null, true, null)
|
|
||||||
}
|
}
|
||||||
currentCall?.enqueue(callback)
|
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
||||||
|
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(view.context, android.R.attr.colorBackground))
|
||||||
}
|
}
|
||||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
|
|
||||||
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(view.context, android.R.attr.colorBackground))
|
|
||||||
|
|
||||||
statusView.visibility = View.GONE
|
statusView.visibility = View.GONE
|
||||||
|
|
||||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
@ -212,6 +219,22 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
if (isVisibleToUser) doInitialLoadingIfNeeded()
|
if (isVisibleToUser) doInitialLoadingIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refresh() {
|
||||||
|
statusView.hide()
|
||||||
|
if (fetchingStatus != FetchingStatus.NOT_FETCHING) return
|
||||||
|
currentCall = if (statuses.isEmpty()) {
|
||||||
|
fetchingStatus = FetchingStatus.INITIAL_FETCHING
|
||||||
|
api.accountStatuses(accountId, null, null, null, null, true, null)
|
||||||
|
} else {
|
||||||
|
fetchingStatus = FetchingStatus.REFRESHING
|
||||||
|
api.accountStatuses(accountId, null, statuses[0].id, null, null, true, null)
|
||||||
|
}
|
||||||
|
currentCall?.enqueue(callback)
|
||||||
|
|
||||||
|
if (!isSwipeToRefreshEnabled)
|
||||||
|
topProgressBar?.show()
|
||||||
|
}
|
||||||
|
|
||||||
// That's sort of an optimization to only load media once user has opened the tab
|
// That's sort of an optimization to only load media once user has opened the tab
|
||||||
// Attention: can be called before *any* lifecycle method!
|
// Attention: can be called before *any* lifecycle method!
|
||||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||||
|
@ -224,12 +247,14 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
statusView.hide()
|
statusView.hide()
|
||||||
}
|
}
|
||||||
val accountId = arguments?.getString(ACCOUNT_ID_ARG)
|
|
||||||
if (fetchingStatus == FetchingStatus.NOT_FETCHING && statuses.isEmpty()) {
|
if (fetchingStatus == FetchingStatus.NOT_FETCHING && statuses.isEmpty()) {
|
||||||
fetchingStatus = FetchingStatus.INITIAL_FETCHING
|
fetchingStatus = FetchingStatus.INITIAL_FETCHING
|
||||||
currentCall = api.accountStatuses(accountId, null, null, null, null, true, null)
|
currentCall = api.accountStatuses(accountId, null, null, null, null, true, null)
|
||||||
currentCall?.enqueue(callback)
|
currentCall?.enqueue(callback)
|
||||||
}
|
}
|
||||||
|
else if (needToRefresh)
|
||||||
|
refresh()
|
||||||
|
needToRefresh = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun viewMedia(items: List<AttachmentViewData>, currentIndex: Int, view: View?) {
|
private fun viewMedia(items: List<AttachmentViewData>, currentIndex: Int, view: View?) {
|
||||||
|
@ -321,4 +346,13 @@ class AccountMediaFragment : BaseFragment(), Injectable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun refreshContent() {
|
||||||
|
if (isAdded)
|
||||||
|
refresh()
|
||||||
|
else
|
||||||
|
needToRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -48,6 +48,7 @@ import com.keylesspalace.tusky.entity.Filter;
|
||||||
import com.keylesspalace.tusky.entity.Poll;
|
import com.keylesspalace.tusky.entity.Poll;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
|
import com.keylesspalace.tusky.interfaces.RefreshableFragment;
|
||||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
import com.keylesspalace.tusky.network.MastodonApi;
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
|
@ -82,6 +83,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.arch.core.util.Function;
|
import androidx.arch.core.util.Function;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
import androidx.core.widget.ContentLoadingProgressBar;
|
||||||
import androidx.lifecycle.Lifecycle;
|
import androidx.lifecycle.Lifecycle;
|
||||||
import androidx.recyclerview.widget.AsyncDifferConfig;
|
import androidx.recyclerview.widget.AsyncDifferConfig;
|
||||||
import androidx.recyclerview.widget.AsyncListDiffer;
|
import androidx.recyclerview.widget.AsyncListDiffer;
|
||||||
|
@ -92,6 +94,7 @@ import androidx.recyclerview.widget.ListUpdateCallback;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import at.connyduck.sparkbutton.helpers.Utils;
|
import at.connyduck.sparkbutton.helpers.Utils;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
@ -108,12 +111,15 @@ import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvid
|
||||||
public class TimelineFragment extends SFragment implements
|
public class TimelineFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener,
|
SwipeRefreshLayout.OnRefreshListener,
|
||||||
StatusActionListener,
|
StatusActionListener,
|
||||||
Injectable, ReselectableFragment {
|
Injectable, ReselectableFragment, RefreshableFragment {
|
||||||
private static final String TAG = "TimelineF"; // logging tag
|
private static final String TAG = "TimelineF"; // logging tag
|
||||||
private static final String KIND_ARG = "kind";
|
private static final String KIND_ARG = "kind";
|
||||||
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id";
|
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id";
|
||||||
|
private static final String ARG_ENABLE_SWIPE_TO_REFRESH = "arg.enable.swipe.to.refresh";
|
||||||
|
|
||||||
private static final int LOAD_AT_ONCE = 30;
|
private static final int LOAD_AT_ONCE = 30;
|
||||||
|
private boolean isSwipeToRefreshEnabled = true;
|
||||||
|
private boolean isNeedRefresh;
|
||||||
|
|
||||||
public enum Kind {
|
public enum Kind {
|
||||||
HOME,
|
HOME,
|
||||||
|
@ -146,6 +152,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
private SwipeRefreshLayout swipeRefreshLayout;
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
private ContentLoadingProgressBar topProgressBar;
|
||||||
private BackgroundMessageView statusView;
|
private BackgroundMessageView statusView;
|
||||||
|
|
||||||
private TimelineAdapter adapter;
|
private TimelineAdapter adapter;
|
||||||
|
@ -182,18 +189,19 @@ public class TimelineFragment extends SFragment implements
|
||||||
});
|
});
|
||||||
|
|
||||||
public static TimelineFragment newInstance(Kind kind) {
|
public static TimelineFragment newInstance(Kind kind) {
|
||||||
TimelineFragment fragment = new TimelineFragment();
|
return newInstance(kind, null);
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
arguments.putString(KIND_ARG, kind.name());
|
|
||||||
fragment.setArguments(arguments);
|
|
||||||
return fragment;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TimelineFragment newInstance(Kind kind, String hashtagOrId) {
|
public static TimelineFragment newInstance(Kind kind, @Nullable String hashtagOrId) {
|
||||||
|
return newInstance(kind, hashtagOrId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimelineFragment newInstance(Kind kind, @Nullable String hashtagOrId, boolean enableSwipeToRefresh) {
|
||||||
TimelineFragment fragment = new TimelineFragment();
|
TimelineFragment fragment = new TimelineFragment();
|
||||||
Bundle arguments = new Bundle();
|
Bundle arguments = new Bundle();
|
||||||
arguments.putString(KIND_ARG, kind.name());
|
arguments.putString(KIND_ARG, kind.name());
|
||||||
arguments.putString(HASHTAG_OR_ID_ARG, hashtagOrId);
|
arguments.putString(HASHTAG_OR_ID_ARG, hashtagOrId);
|
||||||
|
arguments.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, enableSwipeToRefresh);
|
||||||
fragment.setArguments(arguments);
|
fragment.setArguments(arguments);
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
@ -213,6 +221,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
|
|
||||||
adapter = new TimelineAdapter(dataSource, this);
|
adapter = new TimelineAdapter(dataSource, this);
|
||||||
|
|
||||||
|
isSwipeToRefreshEnabled = arguments.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -224,6 +234,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
swipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout);
|
||||||
progressBar = rootView.findViewById(R.id.progressBar);
|
progressBar = rootView.findViewById(R.id.progressBar);
|
||||||
statusView = rootView.findViewById(R.id.statusView);
|
statusView = rootView.findViewById(R.id.statusView);
|
||||||
|
topProgressBar = rootView.findViewById(R.id.topProgressBar);
|
||||||
|
|
||||||
setupSwipeRefreshLayout();
|
setupSwipeRefreshLayout();
|
||||||
setupRecyclerView();
|
setupRecyclerView();
|
||||||
|
@ -236,6 +247,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
this.sendInitialRequest();
|
this.sendInitialRequest();
|
||||||
} else {
|
} else {
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
|
if (isNeedRefresh)
|
||||||
|
onRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootView;
|
return rootView;
|
||||||
|
@ -388,11 +401,14 @@ public class TimelineFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupSwipeRefreshLayout() {
|
private void setupSwipeRefreshLayout() {
|
||||||
Context context = swipeRefreshLayout.getContext();
|
swipeRefreshLayout.setEnabled(isSwipeToRefreshEnabled);
|
||||||
swipeRefreshLayout.setOnRefreshListener(this);
|
if (isSwipeToRefreshEnabled) {
|
||||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue);
|
Context context = swipeRefreshLayout.getContext();
|
||||||
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(context,
|
swipeRefreshLayout.setOnRefreshListener(this);
|
||||||
android.R.attr.colorBackground));
|
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue);
|
||||||
|
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(ThemeUtils.getColor(context,
|
||||||
|
android.R.attr.colorBackground));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRecyclerView() {
|
private void setupRecyclerView() {
|
||||||
|
@ -524,8 +540,10 @@ public class TimelineFragment extends SFragment implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
swipeRefreshLayout.setEnabled(true);
|
if (isSwipeToRefreshEnabled)
|
||||||
|
swipeRefreshLayout.setEnabled(true);
|
||||||
this.statusView.setVisibility(View.GONE);
|
this.statusView.setVisibility(View.GONE);
|
||||||
|
isNeedRefresh = false;
|
||||||
if (this.initialUpdateFailed) {
|
if (this.initialUpdateFailed) {
|
||||||
updateCurrent();
|
updateCurrent();
|
||||||
} else {
|
} else {
|
||||||
|
@ -936,6 +954,9 @@ public class TimelineFragment extends SFragment implements
|
||||||
private void sendFetchTimelineRequest(@Nullable String maxId, @Nullable String sinceId,
|
private void sendFetchTimelineRequest(@Nullable String maxId, @Nullable String sinceId,
|
||||||
@Nullable String sinceIdMinusOne,
|
@Nullable String sinceIdMinusOne,
|
||||||
final FetchEnd fetchEnd, final int pos) {
|
final FetchEnd fetchEnd, final int pos) {
|
||||||
|
if (isAdded() && (fetchEnd == FetchEnd.TOP || fetchEnd == FetchEnd.BOTTOM && maxId == null && progressBar.getVisibility() != View.VISIBLE) && !isSwipeToRefreshEnabled)
|
||||||
|
topProgressBar.show();
|
||||||
|
|
||||||
if (kind == Kind.HOME) {
|
if (kind == Kind.HOME) {
|
||||||
TimelineRequestMode mode;
|
TimelineRequestMode mode;
|
||||||
// allow getting old statuses/fallbacks for network only for for bottom loading
|
// allow getting old statuses/fallbacks for network only for for bottom loading
|
||||||
|
@ -1015,20 +1036,24 @@ public class TimelineFragment extends SFragment implements
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateBottomLoadingState(fetchEnd);
|
if (isAdded()) {
|
||||||
progressBar.setVisibility(View.GONE);
|
topProgressBar.hide();
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
updateBottomLoadingState(fetchEnd);
|
||||||
swipeRefreshLayout.setEnabled(true);
|
progressBar.setVisibility(View.GONE);
|
||||||
if (this.statuses.size() == 0) {
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
this.showNothing();
|
swipeRefreshLayout.setEnabled(true);
|
||||||
} else {
|
if (this.statuses.size() == 0) {
|
||||||
this.statusView.setVisibility(View.GONE);
|
this.showNothing();
|
||||||
|
} else {
|
||||||
|
this.statusView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFetchTimelineFailure(Exception exception, FetchEnd fetchEnd, int position) {
|
private void onFetchTimelineFailure(Exception exception, FetchEnd fetchEnd, int position) {
|
||||||
if (isAdded()) {
|
if (isAdded()) {
|
||||||
swipeRefreshLayout.setRefreshing(false);
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
topProgressBar.hide();
|
||||||
|
|
||||||
if (fetchEnd == FetchEnd.MIDDLE && !statuses.get(position).isRight()) {
|
if (fetchEnd == FetchEnd.MIDDLE && !statuses.get(position).isRight()) {
|
||||||
Placeholder placeholder = statuses.get(position).asLeftOrNull();
|
Placeholder placeholder = statuses.get(position).asLeftOrNull();
|
||||||
|
@ -1267,7 +1292,10 @@ public class TimelineFragment extends SFragment implements
|
||||||
adapter.notifyItemRangeInserted(position, count);
|
adapter.notifyItemRangeInserted(position, count);
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
if (position == 0 && context != null) {
|
if (position == 0 && context != null) {
|
||||||
recyclerView.scrollBy(0, Utils.dpToPx(context, -30));
|
if (isSwipeToRefreshEnabled)
|
||||||
|
recyclerView.scrollBy(0, Utils.dpToPx(context, -30));
|
||||||
|
else
|
||||||
|
recyclerView.scrollToPosition(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1362,4 +1390,12 @@ public class TimelineFragment extends SFragment implements
|
||||||
public void onReselect() {
|
public void onReselect() {
|
||||||
jumpToTop();
|
jumpToTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshContent() {
|
||||||
|
if (isAdded())
|
||||||
|
onRefresh();
|
||||||
|
else
|
||||||
|
isNeedRefresh = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.keylesspalace.tusky.interfaces
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by pandasoft (joelpyska1@gmail.com) on 04/04/2019.
|
||||||
|
*/
|
||||||
|
interface RefreshableFragment {
|
||||||
|
/**
|
||||||
|
* Call this method to refresh fragment content
|
||||||
|
*/
|
||||||
|
fun refreshContent()
|
||||||
|
}
|
|
@ -20,6 +20,10 @@ import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.AccountMediaFragment;
|
import com.keylesspalace.tusky.fragment.AccountMediaFragment;
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||||
|
import com.keylesspalace.tusky.interfaces.RefreshableFragment;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
@ -34,6 +38,8 @@ public class AccountPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
private SparseArray<Fragment> fragments = new SparseArray<>(TAB_COUNT);
|
private SparseArray<Fragment> fragments = new SparseArray<>(TAB_COUNT);
|
||||||
|
|
||||||
|
private final Set<Integer> pagesToRefresh = new HashSet<>();
|
||||||
|
|
||||||
public AccountPagerAdapter(FragmentManager manager, String accountId) {
|
public AccountPagerAdapter(FragmentManager manager, String accountId) {
|
||||||
super(manager);
|
super(manager);
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
|
@ -48,16 +54,16 @@ public class AccountPagerAdapter extends FragmentPagerAdapter {
|
||||||
public Fragment getItem(int position) {
|
public Fragment getItem(int position) {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 0: {
|
case 0: {
|
||||||
return TimelineFragment.newInstance(TimelineFragment.Kind.USER, accountId);
|
return TimelineFragment.newInstance(TimelineFragment.Kind.USER, accountId,false);
|
||||||
}
|
}
|
||||||
case 1: {
|
case 1: {
|
||||||
return TimelineFragment.newInstance(TimelineFragment.Kind.USER_WITH_REPLIES, accountId);
|
return TimelineFragment.newInstance(TimelineFragment.Kind.USER_WITH_REPLIES, accountId,false);
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
return TimelineFragment.newInstance(TimelineFragment.Kind.USER_PINNED, accountId);
|
return TimelineFragment.newInstance(TimelineFragment.Kind.USER_PINNED, accountId,false);
|
||||||
}
|
}
|
||||||
case 3: {
|
case 3: {
|
||||||
return AccountMediaFragment.newInstance(accountId);
|
return AccountMediaFragment.newInstance(accountId,false);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new AssertionError("Page " + position + " is out of AccountPagerAdapter bounds");
|
throw new AssertionError("Page " + position + " is out of AccountPagerAdapter bounds");
|
||||||
|
@ -76,6 +82,11 @@ public class AccountPagerAdapter extends FragmentPagerAdapter {
|
||||||
Object fragment = super.instantiateItem(container, position);
|
Object fragment = super.instantiateItem(container, position);
|
||||||
if (fragment instanceof Fragment)
|
if (fragment instanceof Fragment)
|
||||||
fragments.put(position, (Fragment) fragment);
|
fragments.put(position, (Fragment) fragment);
|
||||||
|
if (pagesToRefresh.contains(position)) {
|
||||||
|
if (fragment instanceof RefreshableFragment)
|
||||||
|
((RefreshableFragment) fragment).refreshContent();
|
||||||
|
pagesToRefresh.remove(position);
|
||||||
|
}
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,4 +105,16 @@ public class AccountPagerAdapter extends FragmentPagerAdapter {
|
||||||
public Fragment getFragment(int position) {
|
public Fragment getFragment(int position) {
|
||||||
return fragments.get(position);
|
return fragments.get(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refreshContent(){
|
||||||
|
for (int i=0;i<getCount();i++){
|
||||||
|
Fragment fragment = getFragment(i);
|
||||||
|
if (fragment instanceof RefreshableFragment){
|
||||||
|
((RefreshableFragment) fragment).refreshContent();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pagesToRefresh.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.keylesspalace.tusky.viewmodel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.keylesspalace.tusky.appstore.*
|
import com.keylesspalace.tusky.appstore.*
|
||||||
|
import com.keylesspalace.tusky.db.AccountManager
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
import com.keylesspalace.tusky.entity.Relationship
|
import com.keylesspalace.tusky.entity.Relationship
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
@ -16,30 +17,36 @@ import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AccountViewModel @Inject constructor(
|
class AccountViewModel @Inject constructor(
|
||||||
private val mastodonApi: MastodonApi,
|
private val mastodonApi: MastodonApi,
|
||||||
private val eventHub: EventHub
|
private val eventHub: EventHub,
|
||||||
): ViewModel() {
|
private val accountManager: AccountManager
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
val accountData = MutableLiveData<Resource<Account>>()
|
val accountData = MutableLiveData<Resource<Account>>()
|
||||||
val relationshipData = MutableLiveData<Resource<Relationship>>()
|
val relationshipData = MutableLiveData<Resource<Relationship>>()
|
||||||
|
|
||||||
private val callList: MutableList<Call<*>> = mutableListOf()
|
private val callList: MutableList<Call<*>> = mutableListOf()
|
||||||
private val disposable: Disposable = eventHub.events
|
private val disposable: Disposable = eventHub.events
|
||||||
.subscribe { event ->
|
.subscribe { event ->
|
||||||
if (event is ProfileEditedEvent && event.newProfileData.id == accountData.value?.data?.id) {
|
if (event is ProfileEditedEvent && event.newProfileData.id == accountData.value?.data?.id) {
|
||||||
accountData.postValue(Success(event.newProfileData))
|
accountData.postValue(Success(event.newProfileData))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isRefreshing = MutableLiveData<Boolean>().apply { value = false }
|
||||||
|
private var isDataLoading = false
|
||||||
|
|
||||||
fun obtainAccount(accountId: String, reload: Boolean = false) {
|
lateinit var accountId: String
|
||||||
if(accountData.value == null || reload) {
|
var isSelf = false
|
||||||
|
|
||||||
|
private fun obtainAccount(reload: Boolean = false) {
|
||||||
|
if (accountData.value == null || reload) {
|
||||||
|
isDataLoading = true
|
||||||
accountData.postValue(Loading())
|
accountData.postValue(Loading())
|
||||||
|
|
||||||
val call = mastodonApi.account(accountId)
|
val call = mastodonApi.account(accountId)
|
||||||
call.enqueue(object : Callback<Account> {
|
call.enqueue(object : Callback<Account> {
|
||||||
override fun onResponse(call: Call<Account>,
|
override fun onResponse(call: Call<Account>,
|
||||||
response: Response<Account>) {
|
response: Response<Account>) {
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
|
@ -47,10 +54,14 @@ class AccountViewModel @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
accountData.postValue(Error())
|
accountData.postValue(Error())
|
||||||
}
|
}
|
||||||
|
isDataLoading = false
|
||||||
|
isRefreshing.postValue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||||
accountData.postValue(Error())
|
accountData.postValue(Error())
|
||||||
|
isDataLoading = false
|
||||||
|
isRefreshing.postValue(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -58,14 +69,14 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun obtainRelationship(accountId: String, reload: Boolean = false) {
|
private fun obtainRelationship(reload: Boolean = false) {
|
||||||
if(relationshipData.value == null || reload) {
|
if (relationshipData.value == null || reload) {
|
||||||
|
|
||||||
relationshipData.postValue(Loading())
|
relationshipData.postValue(Loading())
|
||||||
|
|
||||||
val ids = listOf(accountId)
|
val ids = listOf(accountId)
|
||||||
val call = mastodonApi.relationships(ids)
|
val call = mastodonApi.relationships(ids)
|
||||||
call.enqueue(object : Callback<List<Relationship>> {
|
call.enqueue(object : Callback<List<Relationship>> {
|
||||||
override fun onResponse(call: Call<List<Relationship>>,
|
override fun onResponse(call: Call<List<Relationship>>,
|
||||||
response: Response<List<Relationship>>) {
|
response: Response<List<Relationship>>) {
|
||||||
val relationships = response.body()
|
val relationships = response.body()
|
||||||
|
@ -86,47 +97,47 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeFollowState(id: String) {
|
fun changeFollowState() {
|
||||||
val relationship = relationshipData.value?.data
|
val relationship = relationshipData.value?.data
|
||||||
if (relationship?.following == true || relationship?.requested == true) {
|
if (relationship?.following == true || relationship?.requested == true) {
|
||||||
changeRelationship(RelationShipAction.UNFOLLOW, id)
|
changeRelationship(RelationShipAction.UNFOLLOW)
|
||||||
} else {
|
} else {
|
||||||
changeRelationship(RelationShipAction.FOLLOW, id)
|
changeRelationship(RelationShipAction.FOLLOW)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeBlockState(id: String) {
|
fun changeBlockState() {
|
||||||
if (relationshipData.value?.data?.blocking == true) {
|
if (relationshipData.value?.data?.blocking == true) {
|
||||||
changeRelationship(RelationShipAction.UNBLOCK, id)
|
changeRelationship(RelationShipAction.UNBLOCK)
|
||||||
} else {
|
} else {
|
||||||
changeRelationship(RelationShipAction.BLOCK, id)
|
changeRelationship(RelationShipAction.BLOCK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeMuteState(id: String) {
|
fun changeMuteState() {
|
||||||
if (relationshipData.value?.data?.muting == true) {
|
if (relationshipData.value?.data?.muting == true) {
|
||||||
changeRelationship(RelationShipAction.UNMUTE, id)
|
changeRelationship(RelationShipAction.UNMUTE)
|
||||||
} else {
|
} else {
|
||||||
changeRelationship(RelationShipAction.MUTE, id)
|
changeRelationship(RelationShipAction.MUTE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeShowReblogsState(id: String) {
|
fun changeShowReblogsState() {
|
||||||
if (relationshipData.value?.data?.showingReblogs == true) {
|
if (relationshipData.value?.data?.showingReblogs == true) {
|
||||||
changeRelationship(RelationShipAction.FOLLOW, id, false)
|
changeRelationship(RelationShipAction.FOLLOW, false)
|
||||||
} else {
|
} else {
|
||||||
changeRelationship(RelationShipAction.FOLLOW, id, true)
|
changeRelationship(RelationShipAction.FOLLOW, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeRelationship(relationshipAction: RelationShipAction, id: String, showReblogs: Boolean = true) {
|
private fun changeRelationship(relationshipAction: RelationShipAction, showReblogs: Boolean = true) {
|
||||||
val relation = relationshipData.value?.data
|
val relation = relationshipData.value?.data
|
||||||
val account = accountData.value?.data
|
val account = accountData.value?.data
|
||||||
|
|
||||||
if(relation != null && account != null) {
|
if (relation != null && account != null) {
|
||||||
// optimistically post new state for faster response
|
// optimistically post new state for faster response
|
||||||
|
|
||||||
val newRelation = when(relationshipAction) {
|
val newRelation = when (relationshipAction) {
|
||||||
RelationShipAction.FOLLOW -> {
|
RelationShipAction.FOLLOW -> {
|
||||||
if (account.locked) {
|
if (account.locked) {
|
||||||
relation.copy(requested = true)
|
relation.copy(requested = true)
|
||||||
|
@ -134,11 +145,11 @@ class AccountViewModel @Inject constructor(
|
||||||
relation.copy(following = true)
|
relation.copy(following = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RelationShipAction.UNFOLLOW -> relation.copy(following = false)
|
RelationShipAction.UNFOLLOW -> relation.copy(following = false)
|
||||||
RelationShipAction.BLOCK -> relation.copy(blocking = true)
|
RelationShipAction.BLOCK -> relation.copy(blocking = true)
|
||||||
RelationShipAction.UNBLOCK -> relation.copy(blocking = false)
|
RelationShipAction.UNBLOCK -> relation.copy(blocking = false)
|
||||||
RelationShipAction.MUTE -> relation.copy(muting = true)
|
RelationShipAction.MUTE -> relation.copy(muting = true)
|
||||||
RelationShipAction.UNMUTE -> relation.copy(muting = false)
|
RelationShipAction.UNMUTE -> relation.copy(muting = false)
|
||||||
}
|
}
|
||||||
relationshipData.postValue(Loading(newRelation))
|
relationshipData.postValue(Loading(newRelation))
|
||||||
}
|
}
|
||||||
|
@ -151,10 +162,11 @@ class AccountViewModel @Inject constructor(
|
||||||
relationshipData.postValue(Success(relationship))
|
relationshipData.postValue(Success(relationship))
|
||||||
|
|
||||||
when (relationshipAction) {
|
when (relationshipAction) {
|
||||||
RelationShipAction.UNFOLLOW -> eventHub.dispatch(UnfollowEvent(id))
|
RelationShipAction.UNFOLLOW -> eventHub.dispatch(UnfollowEvent(accountId))
|
||||||
RelationShipAction.BLOCK -> eventHub.dispatch(BlockEvent(id))
|
RelationShipAction.BLOCK -> eventHub.dispatch(BlockEvent(accountId))
|
||||||
RelationShipAction.MUTE -> eventHub.dispatch(MuteEvent(id))
|
RelationShipAction.MUTE -> eventHub.dispatch(MuteEvent(accountId))
|
||||||
else -> {}
|
else -> {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -168,13 +180,13 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val call = when(relationshipAction) {
|
val call = when (relationshipAction) {
|
||||||
RelationShipAction.FOLLOW -> mastodonApi.followAccount(id, showReblogs)
|
RelationShipAction.FOLLOW -> mastodonApi.followAccount(accountId, showReblogs)
|
||||||
RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(id)
|
RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(accountId)
|
||||||
RelationShipAction.BLOCK -> mastodonApi.blockAccount(id)
|
RelationShipAction.BLOCK -> mastodonApi.blockAccount(accountId)
|
||||||
RelationShipAction.UNBLOCK -> mastodonApi.unblockAccount(id)
|
RelationShipAction.UNBLOCK -> mastodonApi.unblockAccount(accountId)
|
||||||
RelationShipAction.MUTE -> mastodonApi.muteAccount(id)
|
RelationShipAction.MUTE -> mastodonApi.muteAccount(accountId)
|
||||||
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(id)
|
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
|
||||||
}
|
}
|
||||||
|
|
||||||
call.enqueue(callback)
|
call.enqueue(callback)
|
||||||
|
@ -189,6 +201,27 @@ class AccountViewModel @Inject constructor(
|
||||||
disposable.dispose()
|
disposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
reload(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reload(isReload: Boolean = false) {
|
||||||
|
if (isDataLoading)
|
||||||
|
return
|
||||||
|
accountId.let {
|
||||||
|
obtainAccount(isReload)
|
||||||
|
if (!isSelf)
|
||||||
|
obtainRelationship(isReload)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAccountInfo(accountId: String) {
|
||||||
|
this.accountId = accountId
|
||||||
|
this.isSelf = accountManager.activeAccount?.accountId == accountId
|
||||||
|
reload(false)
|
||||||
|
}
|
||||||
|
|
||||||
enum class RelationShipAction {
|
enum class RelationShipAction {
|
||||||
FOLLOW, UNFOLLOW, BLOCK, UNBLOCK, MUTE, UNMUTE
|
FOLLOW, UNFOLLOW, BLOCK, UNBLOCK, MUTE, UNMUTE
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,5 +45,16 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@drawable/elephant_error"
|
tools:src="@drawable/elephant_error"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
android:id="@+id/topProgressBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:indeterminate="true"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="parent"
|
||||||
|
android:visibility="gone"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
|
@ -1,316 +1,367 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/accountCoordinatorLayout"
|
android:id="@+id/swipeToRefreshLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:textDirection="anyRtl"
|
|
||||||
android:fillViewport="true">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/accountAppBarLayout"
|
android:id="@+id/accountCoordinatorLayout"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:elevation="@dimen/actionbar_elevation">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
|
||||||
android:id="@+id/collapsingToolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:contentScrim="?attr/toolbar_background_color"
|
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
|
||||||
app:statusBarScrim="?android:attr/colorBackground"
|
|
||||||
app:titleEnabled="false">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/accountHeaderImageView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="180dp"
|
|
||||||
android:layout_alignTop="@+id/account_header_info"
|
|
||||||
android:background="?attr/account_header_background_color"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_collapseMode="parallax"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:background="#000" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/accountHeaderInfoContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="180dp"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="16dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/accountFollowButton"
|
|
||||||
style="@style/TuskyButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:textSize="?attr/status_text_medium"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="Follow" />
|
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
|
||||||
android:id="@+id/accountDisplayNameTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="62dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="?attr/status_text_large"
|
|
||||||
android:textStyle="normal|bold"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="Tusky Mastodon Client " />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountUsernameTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
|
||||||
android:textSize="?attr/status_text_medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountDisplayNameTextView"
|
|
||||||
tools:text="\@Tusky" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/accountLockedImageView"
|
|
||||||
android:layout_width="16sp"
|
|
||||||
android:layout_height="16sp"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:contentDescription="@string/description_account_locked"
|
|
||||||
android:tint="?android:textColorSecondary"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/accountUsernameTextView"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/accountUsernameTextView"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/accountUsernameTextView"
|
|
||||||
app:srcCompat="@drawable/reblog_private_light"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountFollowsYouTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:background="@drawable/profile_badge_background"
|
|
||||||
android:text="@string/follows_you"
|
|
||||||
android:textSize="?attr/status_text_small"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountUsernameTextView"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountBadgeTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:background="@drawable/profile_badge_background"
|
|
||||||
android:text="@string/profile_badge_bot_text"
|
|
||||||
android:textSize="?attr/status_text_small"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/accountFollowsYouTextView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountUsernameTextView"
|
|
||||||
app:layout_goneMarginStart="0dp"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
android:id="@+id/labelBarrier"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:barrierDirection="bottom"
|
|
||||||
app:constraint_referenced_ids="accountFollowsYouTextView,accountBadgeTextView" />
|
|
||||||
|
|
||||||
<androidx.emoji.widget.EmojiTextView
|
|
||||||
android:id="@+id/accountNoteTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:paddingTop="10dp"
|
|
||||||
android:textColor="?android:textColorTertiary"
|
|
||||||
android:textSize="?attr/status_text_medium"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/labelBarrier"
|
|
||||||
tools:text="This is a test description. Descriptions can be quite looooong." />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/accountFieldList"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountNoteTextView"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:listitem="@layout/item_account_field" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/accountMovedView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inflatedId="@+id/accountMovedView"
|
|
||||||
android:layout="@layout/view_account_moved"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountFieldList" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountRemoveView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:text="@string/label_remote_account"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountMovedView"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/accountStatuses"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/accountFollowing"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountRemoveView">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountStatusesTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:textColor="@color/account_tab_font_color"
|
|
||||||
android:textSize="?attr/status_text_medium"
|
|
||||||
tools:text="3000" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingBottom="6dp"
|
|
||||||
android:text="@string/title_statuses"
|
|
||||||
android:textColor="@color/account_tab_font_color"
|
|
||||||
android:textSize="?attr/status_text_medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/accountFollowing"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/accountFollowers"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/accountStatuses"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountRemoveView">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountFollowingTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:textColor="@color/account_tab_font_color"
|
|
||||||
android:textSize="?attr/status_text_medium"
|
|
||||||
tools:text="500" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingBottom="6dp"
|
|
||||||
android:text="@string/title_follows"
|
|
||||||
android:textColor="@color/account_tab_font_color"
|
|
||||||
android:textSize="?attr/status_text_medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/accountFollowers"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/accountFollowing"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountRemoveView">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountFollowersTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:textColor="@color/account_tab_font_color"
|
|
||||||
android:textSize="?attr/status_text_medium"
|
|
||||||
tools:text="1234" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingBottom="6dp"
|
|
||||||
android:text="@string/title_followers"
|
|
||||||
android:textColor="@color/account_tab_font_color"
|
|
||||||
android:textSize="?attr/status_text_medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<!-- top margin equal to statusbar size will be set programmatically -->
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/accountToolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:layout_gravity="top"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
app:layout_collapseMode="pin"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways" />
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
|
||||||
android:id="@+id/accountTabLayout"
|
|
||||||
style="@style/TuskyTabAppearance"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?android:colorBackground"
|
|
||||||
app:tabGravity="center"
|
|
||||||
app:tabMode="scrollable"
|
|
||||||
app:tabTextAppearance="@style/TuskyTabAppearance" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
|
||||||
android:id="@+id/accountFragmentViewPager"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
android:fillViewport="true"
|
||||||
|
android:textDirection="anyRtl">
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/accountFloatingActionButton"
|
android:id="@+id/accountAppBarLayout"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:elevation="@dimen/actionbar_elevation">
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:contentDescription="@string/action_mention"
|
|
||||||
app:srcCompat="@drawable/ic_create_24dp" />
|
|
||||||
|
|
||||||
<include layout="@layout/item_status_bottom_sheet" />
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:id="@+id/collapsingToolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:contentScrim="?attr/toolbar_background_color"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||||
|
app:statusBarScrim="?android:attr/colorBackground"
|
||||||
|
app:titleEnabled="false">
|
||||||
|
|
||||||
<com.keylesspalace.tusky.view.RoundedImageView
|
<ImageView
|
||||||
android:id="@+id/accountAvatarImageView"
|
android:id="@+id/accountHeaderImageView"
|
||||||
android:layout_width="@dimen/account_activity_avatar_size"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/account_activity_avatar_size"
|
android:layout_height="180dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_alignTop="@+id/account_header_info"
|
||||||
android:background="@drawable/avatar_background"
|
android:background="?attr/account_header_background_color"
|
||||||
android:padding="3dp"
|
android:scaleType="centerCrop"
|
||||||
app:layout_anchor="@+id/accountHeaderInfoContainer"
|
app:layout_collapseMode="parallax"
|
||||||
app:layout_anchorGravity="top"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_scrollFlags="scroll"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/avatar_default" />
|
tools:background="#000" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/accountHeaderInfoContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="180dp"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideAvatar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_begin="@dimen/account_activity_avatar_size" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/accountFollowButton"
|
||||||
|
style="@style/TuskyButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textSize="?attr/status_text_medium"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="1"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/accountMuteButton"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Follow Requested" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/accountMuteButton"
|
||||||
|
style="@style/TuskyButton.Outlined"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
app:icon="@drawable/ic_unmute_24dp"
|
||||||
|
app:layout_constrainedHeight="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/accountFollowButton"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/accountFollowButton"
|
||||||
|
app:layout_constraintHorizontal_bias="1"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/guideAvatar"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/accountFollowButton" />
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiTextView
|
||||||
|
android:id="@+id/accountDisplayNameTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="62dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
android:textSize="?attr/status_text_large"
|
||||||
|
android:textStyle="normal|bold"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Tusky Mastodon Client " />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountUsernameTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:textSize="?attr/status_text_medium"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountDisplayNameTextView"
|
||||||
|
tools:text="\@Tusky" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/accountLockedImageView"
|
||||||
|
android:layout_width="16sp"
|
||||||
|
android:layout_height="16sp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:contentDescription="@string/description_account_locked"
|
||||||
|
android:tint="?android:textColorSecondary"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/accountUsernameTextView"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/accountUsernameTextView"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/accountUsernameTextView"
|
||||||
|
app:srcCompat="@drawable/reblog_private_light"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountFollowsYouTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:background="@drawable/profile_badge_background"
|
||||||
|
android:text="@string/follows_you"
|
||||||
|
android:textSize="?attr/status_text_small"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountUsernameTextView"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountBadgeTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:background="@drawable/profile_badge_background"
|
||||||
|
android:text="@string/profile_badge_bot_text"
|
||||||
|
android:textSize="?attr/status_text_small"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/accountFollowsYouTextView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountUsernameTextView"
|
||||||
|
app:layout_goneMarginStart="0dp"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/labelBarrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:constraint_referenced_ids="accountFollowsYouTextView,accountBadgeTextView" />
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiTextView
|
||||||
|
android:id="@+id/accountNoteTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:textColor="?android:textColorTertiary"
|
||||||
|
android:textSize="?attr/status_text_medium"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/labelBarrier"
|
||||||
|
tools:text="This is a test description. Descriptions can be quite looooong." />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/accountFieldList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountNoteTextView"
|
||||||
|
tools:itemCount="2"
|
||||||
|
tools:listitem="@layout/item_account_field" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountRemoveView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:text="@string/label_remote_account"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountFieldList"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/accountMovedView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inflatedId="@+id/accountMovedViewLayout"
|
||||||
|
android:layout="@layout/view_account_moved"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountRemoveView" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/barrierRemote"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:constraint_referenced_ids="accountMovedView,accountMovedViewLayout" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/accountStatuses"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/accountFollowing"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/barrierRemote">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountStatusesTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:textColor="@color/account_tab_font_color"
|
||||||
|
android:textSize="?attr/status_text_medium"
|
||||||
|
tools:text="3000" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:text="@string/title_statuses"
|
||||||
|
android:textColor="@color/account_tab_font_color"
|
||||||
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/accountFollowing"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/accountFollowers"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/accountStatuses"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/barrierRemote">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountFollowingTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:textColor="@color/account_tab_font_color"
|
||||||
|
android:textSize="?attr/status_text_medium"
|
||||||
|
tools:text="500" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:text="@string/title_follows"
|
||||||
|
android:textColor="@color/account_tab_font_color"
|
||||||
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/accountFollowers"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/accountFollowing"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/barrierRemote">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountFollowersTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:textColor="@color/account_tab_font_color"
|
||||||
|
android:textSize="?attr/status_text_medium"
|
||||||
|
tools:text="1234" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:text="@string/title_followers"
|
||||||
|
android:textColor="@color/account_tab_font_color"
|
||||||
|
android:textSize="?attr/status_text_medium" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<!-- top margin equal to statusbar size will be set programmatically -->
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/accountToolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways" />
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/accountTabLayout"
|
||||||
|
style="@style/TuskyTabAppearance"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
app:tabGravity="center"
|
||||||
|
app:tabMode="scrollable"
|
||||||
|
app:tabTextAppearance="@style/TuskyTabAppearance" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager.widget.ViewPager
|
||||||
|
android:id="@+id/accountFragmentViewPager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/accountFloatingActionButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/action_mention"
|
||||||
|
app:srcCompat="@drawable/ic_create_24dp" />
|
||||||
|
|
||||||
|
<include layout="@layout/item_status_bottom_sheet" />
|
||||||
|
|
||||||
|
<com.keylesspalace.tusky.view.RoundedImageView
|
||||||
|
android:id="@+id/accountAvatarImageView"
|
||||||
|
android:layout_width="@dimen/account_activity_avatar_size"
|
||||||
|
android:layout_height="@dimen/account_activity_avatar_size"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:background="@drawable/avatar_background"
|
||||||
|
android:padding="3dp"
|
||||||
|
app:layout_anchor="@+id/accountHeaderInfoContainer"
|
||||||
|
app:layout_anchorGravity="top"
|
||||||
|
app:layout_scrollFlags="scroll"
|
||||||
|
app:srcCompat="@drawable/avatar_default" />
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
|
@ -4,7 +4,6 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipeRefreshLayout"
|
android:id="@+id/swipeRefreshLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -38,5 +37,15 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:src="@drawable/elephant_error"
|
tools:src="@drawable/elephant_error"
|
||||||
tools:visibility="visible"/>
|
tools:visibility="visible"/>
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
android:id="@+id/topProgressBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:indeterminate="true"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="parent"
|
||||||
|
android:visibility="gone"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in a new issue