migrating to ViewBinding part 2: Activities (#2093)

This commit is contained in:
Konrad Pozniak 2021-03-07 19:05:51 +01:00 committed by GitHub
parent 5167b8578e
commit ff69a2ad0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 741 additions and 729 deletions

View file

@ -9,19 +9,20 @@ import android.text.method.LinkMovementMethod
import android.text.style.URLSpan
import android.text.util.Linkify
import android.widget.TextView
import com.keylesspalace.tusky.databinding.ActivityAboutBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.util.CustomURLSpan
import com.keylesspalace.tusky.util.hide
import kotlinx.android.synthetic.main.activity_about.*
import kotlinx.android.synthetic.main.toolbar_basic.*
class AboutActivity : BottomSheetActivity(), Injectable {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_about)
setSupportActionBar(toolbar)
val binding = ActivityAboutBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
@ -29,26 +30,24 @@ class AboutActivity : BottomSheetActivity(), Injectable {
setTitle(R.string.about_title_activity)
versionTextView.text = getString(R.string.about_app_version, getString(R.string.app_name), BuildConfig.VERSION_NAME)
binding.versionTextView.text = getString(R.string.about_app_version, getString(R.string.app_name), BuildConfig.VERSION_NAME)
if(BuildConfig.CUSTOM_INSTANCE.isBlank()) {
aboutPoweredByTusky.hide()
binding.aboutPoweredByTusky.hide()
}
aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_tusky_license)
aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site)
aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site)
binding.aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_tusky_license)
binding.aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site)
binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site)
tuskyProfileButton.setOnClickListener {
binding.tuskyProfileButton.setOnClickListener {
viewUrl(BuildConfig.SUPPORT_ACCOUNT_URL)
}
aboutLicensesButton.setOnClickListener {
binding.aboutLicensesButton.setOnClickListener {
startActivityWithSlideInAnimation(Intent(this, LicenseActivity::class.java))
}
}
}
private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
@ -73,5 +72,4 @@ private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
setText(builder)
linksClickable = true
movementMethod = LinkMovementMethod.getInstance()
}

View file

@ -50,6 +50,7 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.keylesspalace.tusky.adapter.AccountFieldAdapter
import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.report.ReportActivity
import com.keylesspalace.tusky.databinding.ActivityAccountBinding
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Relationship
@ -63,8 +64,6 @@ import com.keylesspalace.tusky.view.showMuteAccountDialog
import com.keylesspalace.tusky.viewmodel.AccountViewModel
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.activity_account.*
import kotlinx.android.synthetic.main.view_account_moved.*
import java.text.NumberFormat
import javax.inject.Inject
import kotlin.math.abs
@ -78,6 +77,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private val viewModel: AccountViewModel by viewModels { viewModelFactory }
private val binding: ActivityAccountBinding by viewBinding(ActivityAccountBinding::inflate)
private lateinit var accountFieldAdapter : AccountFieldAdapter
private var followState: FollowState = FollowState.NOT_FOLLOWING
@ -118,7 +119,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
super.onCreate(savedInstanceState)
loadResources()
makeNotificationBarTransparent()
setContentView(R.layout.activity_account)
setContentView(binding.root)
// Obtain information to fill out the profile.
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
@ -136,9 +137,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (viewModel.isSelf) {
updateButtons()
saveNoteInfo.hide()
binding.saveNoteInfo.hide()
} else {
saveNoteInfo.visibility = View.INVISIBLE
binding.saveNoteInfo.visibility = View.INVISIBLE
}
}
@ -158,16 +159,16 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
*/
private fun setupAccountViews() {
// Initialise the default UI states.
accountFloatingActionButton.hide()
accountFollowButton.hide()
accountMuteButton.hide()
accountFollowsYouTextView.hide()
binding.accountFloatingActionButton.hide()
binding.accountFollowButton.hide()
binding.accountMuteButton.hide()
binding.accountFollowsYouTextView.hide()
// setup the RecyclerView for the account fields
accountFieldAdapter = AccountFieldAdapter(this, animateEmojis)
accountFieldList.isNestedScrollingEnabled = false
accountFieldList.layoutManager = LinearLayoutManager(this)
accountFieldList.adapter = accountFieldAdapter
binding.accountFieldList.isNestedScrollingEnabled = false
binding.accountFieldList.layoutManager = LinearLayoutManager(this)
binding.accountFieldList.adapter = accountFieldAdapter
val accountListClickListener = { v: View ->
@ -179,15 +180,15 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val accountListIntent = AccountListActivity.newIntent(this, type, viewModel.accountId)
startActivityWithSlideInAnimation(accountListIntent)
}
accountFollowers.setOnClickListener(accountListClickListener)
accountFollowing.setOnClickListener(accountListClickListener)
binding.accountFollowers.setOnClickListener(accountListClickListener)
binding.accountFollowing.setOnClickListener(accountListClickListener)
accountStatuses.setOnClickListener {
binding.accountStatuses.setOnClickListener {
// Make nice ripple effect on tab
accountTabLayout.getTabAt(0)!!.select()
val poorTabView = (accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0)
binding.accountTabLayout.getTabAt(0)!!.select()
val poorTabView = (binding.accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0)
poorTabView.isPressed = true
accountTabLayout.postDelayed({ poorTabView.isPressed = false }, 300)
binding.accountTabLayout.postDelayed({ poorTabView.isPressed = false }, 300)
}
// If wellbeing mode is enabled, follow stats and posts count should be hidden
@ -195,11 +196,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_PROFILE, false)
if (wellbeingEnabled) {
accountStatuses.hide()
accountFollowers.hide()
accountFollowing.hide()
binding.accountStatuses.hide()
binding.accountFollowers.hide()
binding.accountFollowing.hide()
}
}
/**
@ -209,19 +209,19 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
// Setup the tabs and timeline pager.
adapter = AccountPagerAdapter(this, viewModel.accountId)
accountFragmentViewPager.adapter = adapter
accountFragmentViewPager.offscreenPageLimit = 2
binding.accountFragmentViewPager.adapter = adapter
binding.accountFragmentViewPager.offscreenPageLimit = 2
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))
TabLayoutMediator(accountTabLayout, accountFragmentViewPager) { tab, position ->
TabLayoutMediator(binding.accountTabLayout, binding.accountFragmentViewPager) { tab, position ->
tab.text = pageTitles[position]
}.attach()
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
accountFragmentViewPager.setPageTransformer(MarginPageTransformer(pageMargin))
binding.accountFragmentViewPager.setPageTransformer(MarginPageTransformer(pageMargin))
accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
binding.accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
tab?.position?.let { position ->
(adapter.getFragment(position) as? ReselectableFragment)?.onReselect()
@ -237,17 +237,17 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun setupToolbar() {
// set toolbar top margin according to system window insets
accountCoordinatorLayout.setOnApplyWindowInsetsListener { _, insets ->
binding.accountCoordinatorLayout.setOnApplyWindowInsetsListener { _, insets ->
val top = insets.systemWindowInsetTop
val toolbarParams = accountToolbar.layoutParams as CollapsingToolbarLayout.LayoutParams
val toolbarParams = binding.accountToolbar.layoutParams as CollapsingToolbarLayout.LayoutParams
toolbarParams.topMargin = top
insets.consumeSystemWindowInsets()
}
// Setup the toolbar.
setSupportActionBar(accountToolbar)
setSupportActionBar(binding.accountToolbar)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
@ -258,9 +258,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation)
toolbarBackground.fillColor = ColorStateList.valueOf(Color.TRANSPARENT)
accountToolbar.background = toolbarBackground
binding.accountToolbar.background = toolbarBackground
accountHeaderInfoContainer.background = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation)
binding.accountHeaderInfoContainer.background = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation)
val avatarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation).apply {
fillColor = ColorStateList.valueOf(toolbarColor)
@ -269,10 +269,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
.setAllCornerSizes(resources.getDimension(R.dimen.account_avatar_background_radius))
.build()
}
accountAvatarImageView.background = avatarBackground
binding.accountAvatarImageView.background = avatarBackground
// Add a listener to change the toolbar icon color when it enters/exits its collapsed state.
accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
binding.accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
@ -289,19 +289,19 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (hideFab && !viewModel.isSelf && !blocking) {
if (verticalOffset > oldOffset) {
accountFloatingActionButton.show()
binding.accountFloatingActionButton.show()
}
if (verticalOffset < oldOffset) {
accountFloatingActionButton.hide()
binding.accountFloatingActionButton.hide()
}
}
val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize
accountAvatarImageView.scaleX = scaledAvatarSize
accountAvatarImageView.scaleY = scaledAvatarSize
binding.accountAvatarImageView.scaleX = scaledAvatarSize
binding.accountAvatarImageView.scaleY = scaledAvatarSize
accountAvatarImageView.visible(scaledAvatarSize > 0)
binding.accountAvatarImageView.visible(scaledAvatarSize > 0)
val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost(1f)
@ -311,7 +311,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
toolbarBackground.fillColor = ColorStateList.valueOf(evaluatedToolbarColor)
swipeToRefreshLayout.isEnabled = verticalOffset == 0
binding.swipeToRefreshLayout.isEnabled = verticalOffset == 0
}
})
@ -331,7 +331,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
when (it) {
is Success -> onAccountChanged(it.data)
is Error -> {
Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
Snackbar.make(binding.accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
.setAction(R.string.action_retry) { viewModel.refresh() }
.show()
}
@ -344,7 +344,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
}
if (it is Error) {
Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
Snackbar.make(binding.accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
.setAction(R.string.action_retry) { viewModel.refresh() }
.show()
}
@ -355,7 +355,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
accountFieldAdapter.notifyDataSetChanged()
})
viewModel.noteSaved.observe(this) {
saveNoteInfo.visible(it, View.INVISIBLE)
binding.saveNoteInfo.visible(it, View.INVISIBLE)
}
}
@ -363,32 +363,32 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
* Setup swipe to refresh layout
*/
private fun setupRefreshLayout() {
swipeToRefreshLayout.setOnRefreshListener {
binding.swipeToRefreshLayout.setOnRefreshListener {
viewModel.refresh()
adapter.refreshContent()
}
viewModel.isRefreshing.observe(this, { isRefreshing ->
swipeToRefreshLayout.isRefreshing = isRefreshing == true
binding.swipeToRefreshLayout.isRefreshing = isRefreshing == true
})
swipeToRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.swipeToRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
}
private fun onAccountChanged(account: Account?) {
loadedAccount = account ?: return
val usernameFormatted = getString(R.string.status_username_format, account.username)
accountUsernameTextView.text = usernameFormatted
accountDisplayNameTextView.text = account.name.emojify(account.emojis, accountDisplayNameTextView, animateEmojis)
binding.accountUsernameTextView.text = usernameFormatted
binding.accountDisplayNameTextView.text = account.name.emojify(account.emojis, binding.accountDisplayNameTextView, animateEmojis)
val emojifiedNote = account.note.emojify(account.emojis, accountNoteTextView, animateEmojis)
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)
val emojifiedNote = account.note.emojify(account.emojis, binding.accountNoteTextView, animateEmojis)
LinkHelper.setClickableText(binding.accountNoteTextView, emojifiedNote, null, this)
// accountFieldAdapter.fields = account.fields ?: emptyList()
accountFieldAdapter.emojis = account.emojis ?: emptyList()
accountFieldAdapter.notifyDataSetChanged()
accountLockedImageView.visible(account.locked)
accountBadgeTextView.visible(account.bot)
binding.accountLockedImageView.visible(account.locked)
binding.accountBadgeTextView.visible(account.bot)
updateAccountAvatar()
updateToolbar()
@ -397,7 +397,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
updateAccountStats()
invalidateOptionsMenu()
accountMuteButton.setOnClickListener {
binding.accountMuteButton.setOnClickListener {
viewModel.unmuteAccount()
updateMuteButton()
}
@ -411,7 +411,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
loadAvatar(
account.avatar,
accountAvatarImageView,
binding.accountAvatarImageView,
resources.getDimensionPixelSize(R.dimen.avatar_radius_94dp),
animateAvatar
)
@ -420,10 +420,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
.asBitmap()
.load(account.header)
.centerCrop()
.into(accountHeaderImageView)
.into(binding.accountHeaderImageView)
accountAvatarImageView.setOnClickListener { avatarView ->
binding.accountAvatarImageView.setOnClickListener { avatarView ->
val intent = ViewMediaActivity.newSingleImageIntent(avatarView.context, account.avatar)
avatarView.transitionName = account.avatar
@ -440,7 +440,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateToolbar() {
loadedAccount?.let { account ->
val emojifiedName = account.name.emojify(account.emojis, accountToolbar, animateEmojis)
val emojifiedName = account.name.emojify(account.emojis, binding.accountToolbar, animateEmojis)
try {
supportActionBar?.title = EmojiCompat.get().process(emojifiedName)
@ -457,28 +457,27 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateMovedAccount() {
loadedAccount?.moved?.let { movedAccount ->
accountMovedView?.show()
binding.accountMovedView.show()
// necessary because accountMovedView is now replaced in layout hierachy
findViewById<View>(R.id.accountMovedViewLayout).setOnClickListener {
binding.accountMovedView.setOnClickListener {
onViewAccount(movedAccount.id)
}
accountMovedDisplayName.text = movedAccount.name
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
binding.accountMovedDisplayName.text = movedAccount.name
binding.accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
val avatarRadius = resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp)
loadAvatar(movedAccount.avatar, accountMovedAvatar, avatarRadius, animateAvatar)
loadAvatar(movedAccount.avatar, binding.accountMovedAvatar, avatarRadius, animateAvatar)
accountMovedText.text = getString(R.string.account_moved_description, movedAccount.name)
binding.accountMovedText.text = getString(R.string.account_moved_description, movedAccount.name)
// 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?.colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
binding.accountMovedText.setCompoundDrawablesRelativeWithIntrinsicBounds(movedIcon, null, null, null)
}
}
@ -489,8 +488,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateRemoteAccount() {
loadedAccount?.let { account ->
if (account.isRemote()) {
accountRemoveView.show()
accountRemoveView.setOnClickListener {
binding.accountRemoveView.show()
binding.accountRemoveView.setOnClickListener {
LinkHelper.openLink(account.url, this)
}
}
@ -503,13 +502,13 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateAccountStats() {
loadedAccount?.let { account ->
val numberFormat = NumberFormat.getNumberInstance()
accountFollowersTextView.text = numberFormat.format(account.followersCount)
accountFollowingTextView.text = numberFormat.format(account.followingCount)
accountStatusesTextView.text = numberFormat.format(account.statusesCount)
binding.accountFollowersTextView.text = numberFormat.format(account.followersCount)
binding.accountFollowingTextView.text = numberFormat.format(account.followingCount)
binding.accountStatusesTextView.text = numberFormat.format(account.statusesCount)
accountFloatingActionButton.setOnClickListener { mention() }
binding.accountFloatingActionButton.setOnClickListener { mention() }
accountFollowButton.setOnClickListener {
binding.accountFollowButton.setOnClickListener {
if (viewModel.isSelf) {
val intent = Intent(this@AccountActivity, EditProfileActivity::class.java)
startActivity(intent)
@ -552,14 +551,14 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_PROFILE, false)
accountFollowsYouTextView.visible(relation.followedBy && !wellbeingEnabled)
binding.accountFollowsYouTextView.visible(relation.followedBy && !wellbeingEnabled)
// because subscribing is Pleroma extension, enable it __only__ when we have non-null subscribing field
// it's also now supported in Mastodon 3.3.0rc but called notifying and use different API call
if(!viewModel.isSelf && followState == FollowState.FOLLOWING
&& (relation.subscribing != null || relation.notifying != null)) {
accountSubscribeButton.show()
accountSubscribeButton.setOnClickListener {
binding.accountSubscribeButton.show()
binding.accountSubscribeButton.setOnClickListener {
viewModel.changeSubscribingState()
}
if(relation.notifying != null)
@ -569,12 +568,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
}
// remove the listener so it doesn't fire on non-user changes
accountNoteTextInputLayout.editText?.removeTextChangedListener(noteWatcher)
binding.accountNoteTextInputLayout.editText?.removeTextChangedListener(noteWatcher)
accountNoteTextInputLayout.visible(relation.note != null)
accountNoteTextInputLayout.editText?.setText(relation.note)
binding.accountNoteTextInputLayout.visible(relation.note != null)
binding.accountNoteTextInputLayout.editText?.setText(relation.note)
accountNoteTextInputLayout.editText?.addTextChangedListener(noteWatcher)
binding.accountNoteTextInputLayout.editText?.addTextChangedListener(noteWatcher)
updateButtons()
}
@ -587,22 +586,22 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateFollowButton() {
if (viewModel.isSelf) {
accountFollowButton.setText(R.string.action_edit_own_profile)
binding.accountFollowButton.setText(R.string.action_edit_own_profile)
return
}
if (blocking) {
accountFollowButton.setText(R.string.action_unblock)
binding.accountFollowButton.setText(R.string.action_unblock)
return
}
when (followState) {
FollowState.NOT_FOLLOWING -> {
accountFollowButton.setText(R.string.action_follow)
binding.accountFollowButton.setText(R.string.action_follow)
}
FollowState.REQUESTED -> {
accountFollowButton.setText(R.string.state_follow_requested)
binding.accountFollowButton.setText(R.string.state_follow_requested)
}
FollowState.FOLLOWING -> {
accountFollowButton.setText(R.string.action_unfollow)
binding.accountFollowButton.setText(R.string.action_unfollow)
}
}
updateSubscribeButton()
@ -610,23 +609,23 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateMuteButton() {
if (muting) {
accountMuteButton.setIconResource(R.drawable.ic_unmute_24dp)
binding.accountMuteButton.setIconResource(R.drawable.ic_unmute_24dp)
} else {
accountMuteButton.hide()
binding.accountMuteButton.hide()
}
}
private fun updateSubscribeButton() {
if(followState != FollowState.FOLLOWING) {
accountSubscribeButton.hide()
binding.accountSubscribeButton.hide()
}
if(subscribing) {
accountSubscribeButton.setIconResource(R.drawable.ic_notifications_active_24dp)
accountSubscribeButton.contentDescription = getString(R.string.action_unsubscribe_account)
binding.accountSubscribeButton.setIconResource(R.drawable.ic_notifications_active_24dp)
binding.accountSubscribeButton.contentDescription = getString(R.string.action_unsubscribe_account)
} else {
accountSubscribeButton.setIconResource(R.drawable.ic_notifications_24dp)
accountSubscribeButton.contentDescription = getString(R.string.action_subscribe_account)
binding.accountSubscribeButton.setIconResource(R.drawable.ic_notifications_24dp)
binding.accountSubscribeButton.contentDescription = getString(R.string.action_subscribe_account)
}
}
@ -635,27 +634,27 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (loadedAccount?.moved == null) {
accountFollowButton.show()
binding.accountFollowButton.show()
updateFollowButton()
if (blocking || viewModel.isSelf) {
accountFloatingActionButton.hide()
accountMuteButton.hide()
accountSubscribeButton.hide()
binding.accountFloatingActionButton.hide()
binding.accountMuteButton.hide()
binding.accountSubscribeButton.hide()
} else {
accountFloatingActionButton.show()
binding.accountFloatingActionButton.show()
if (muting)
accountMuteButton.show()
binding.accountMuteButton.show()
else
accountMuteButton.hide()
binding.accountMuteButton.hide()
updateMuteButton()
}
} else {
accountFloatingActionButton.hide()
accountFollowButton.hide()
accountMuteButton.hide()
accountSubscribeButton.hide()
binding.accountFloatingActionButton.hide()
binding.accountFollowButton.hide()
binding.accountMuteButton.hide()
binding.accountSubscribeButton.hide()
}
}
@ -833,7 +832,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
override fun getActionButton(): FloatingActionButton? {
return if (!viewModel.isSelf && !blocking) {
accountFloatingActionButton
binding.accountFloatingActionButton
} else null
}

View file

@ -18,10 +18,10 @@ package com.keylesspalace.tusky
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.keylesspalace.tusky.databinding.ActivityAccountListBinding
import com.keylesspalace.tusky.fragment.AccountListFragment
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class AccountListActivity : BaseActivity(), HasAndroidInjector {
@ -41,12 +41,13 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_account_list)
val binding = ActivityAccountListBinding.inflate(layoutInflater)
setContentView(binding.root)
val type = intent.getSerializableExtra(EXTRA_TYPE) as Type
val id: String? = intent.getStringExtra(EXTRA_ID)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
when (type) {
Type.BLOCKS -> setTitle(R.string.title_blocks)

View file

@ -38,6 +38,7 @@ import com.bumptech.glide.load.resource.bitmap.FitCenter
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.adapter.AccountFieldEditAdapter
import com.keylesspalace.tusky.databinding.ActivityEditProfileBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.util.*
@ -47,8 +48,6 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import com.theartofdev.edmodo.cropper.CropImage
import kotlinx.android.synthetic.main.activity_edit_profile.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class EditProfileActivity : BaseActivity(), Injectable {
@ -71,6 +70,8 @@ class EditProfileActivity : BaseActivity(), Injectable {
private val viewModel: EditProfileViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityEditProfileBinding::inflate)
private var currentlyPicking: PickType = PickType.NOTHING
private val accountFieldEditAdapter = AccountFieldEditAdapter()
@ -88,33 +89,33 @@ class EditProfileActivity : BaseActivity(), Injectable {
currentlyPicking = PickType.valueOf(it)
}
setContentView(R.layout.activity_edit_profile)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run {
setTitle(R.string.title_edit_profile)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
avatarButton.setOnClickListener { onMediaPick(PickType.AVATAR) }
headerButton.setOnClickListener { onMediaPick(PickType.HEADER) }
binding.avatarButton.setOnClickListener { onMediaPick(PickType.AVATAR) }
binding.headerButton.setOnClickListener { onMediaPick(PickType.HEADER) }
fieldList.layoutManager = LinearLayoutManager(this)
fieldList.adapter = accountFieldEditAdapter
binding.fieldList.layoutManager = LinearLayoutManager(this)
binding.fieldList.adapter = accountFieldEditAdapter
val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).apply { sizeDp = 12; colorInt = Color.WHITE }
addFieldButton.setCompoundDrawablesRelativeWithIntrinsicBounds(plusDrawable, null, null, null)
binding.addFieldButton.setCompoundDrawablesRelativeWithIntrinsicBounds(plusDrawable, null, null, null)
addFieldButton.setOnClickListener {
binding.addFieldButton.setOnClickListener {
accountFieldEditAdapter.addField()
if(accountFieldEditAdapter.itemCount >= MAX_ACCOUNT_FIELDS) {
it.isVisible = false
}
scrollView.post{
scrollView.smoothScrollTo(0, it.bottom)
binding.scrollView.post{
binding.scrollView.smoothScrollTo(0, it.bottom)
}
}
@ -126,12 +127,12 @@ class EditProfileActivity : BaseActivity(), Injectable {
val me = profileRes.data
if (me != null) {
displayNameEditText.setText(me.displayName)
noteEditText.setText(me.source?.note)
lockedCheckBox.isChecked = me.locked
binding.displayNameEditText.setText(me.displayName)
binding.noteEditText.setText(me.source?.note)
binding.lockedCheckBox.isChecked = me.locked
accountFieldEditAdapter.setFields(me.source?.fields ?: emptyList())
addFieldButton.isEnabled = me.source?.fields?.size ?: 0 < MAX_ACCOUNT_FIELDS
binding.addFieldButton.isEnabled = me.source?.fields?.size ?: 0 < MAX_ACCOUNT_FIELDS
if(viewModel.avatarData.value == null) {
Glide.with(this)
@ -141,19 +142,19 @@ class EditProfileActivity : BaseActivity(), Injectable {
FitCenter(),
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
)
.into(avatarPreview)
.into(binding.avatarPreview)
}
if(viewModel.headerData.value == null) {
Glide.with(this)
.load(me.header)
.into(headerPreview)
.into(binding.headerPreview)
}
}
}
is Error -> {
val snackbar = Snackbar.make(avatarButton, R.string.error_generic, Snackbar.LENGTH_LONG)
val snackbar = Snackbar.make(binding.avatarButton, R.string.error_generic, Snackbar.LENGTH_LONG)
snackbar.setAction(R.string.action_retry) {
viewModel.obtainProfile()
}
@ -169,14 +170,14 @@ class EditProfileActivity : BaseActivity(), Injectable {
is Success -> {
val instance = result.data
if (instance?.maxBioChars != null && instance.maxBioChars > 0) {
noteEditTextLayout.counterMaxLength = instance.maxBioChars
binding.noteEditTextLayout.counterMaxLength = instance.maxBioChars
}
}
}
}
observeImage(viewModel.avatarData, avatarPreview, avatarProgressBar, true)
observeImage(viewModel.headerData, headerPreview, headerProgressBar, false)
observeImage(viewModel.avatarData, binding.avatarPreview, binding.avatarProgressBar, true)
observeImage(viewModel.headerData, binding.headerPreview, binding.headerProgressBar, false)
viewModel.saveData.observe(this, {
when(it) {
@ -184,7 +185,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
finish()
}
is Loading -> {
saveProgressBar.visibility = View.VISIBLE
binding.saveProgressBar.visibility = View.VISIBLE
}
is Error -> {
onSaveFailure(it.errorMessage)
@ -202,9 +203,9 @@ class EditProfileActivity : BaseActivity(), Injectable {
override fun onStop() {
super.onStop()
if(!isFinishing) {
viewModel.updateProfile(displayNameEditText.text.toString(),
noteEditText.text.toString(),
lockedCheckBox.isChecked,
viewModel.updateProfile(binding.displayNameEditText.text.toString(),
binding.noteEditText.text.toString(),
binding.lockedCheckBox.isChecked,
accountFieldEditAdapter.getFieldData())
}
}
@ -268,7 +269,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
initiateMediaPicking()
} else {
endMediaPicking()
Snackbar.make(avatarButton, R.string.error_media_upload_permission, Snackbar.LENGTH_LONG).show()
Snackbar.make(binding.avatarButton, R.string.error_media_upload_permission, Snackbar.LENGTH_LONG).show()
}
}
}
@ -309,39 +310,38 @@ class EditProfileActivity : BaseActivity(), Injectable {
return
}
viewModel.save(displayNameEditText.text.toString(),
noteEditText.text.toString(),
lockedCheckBox.isChecked,
viewModel.save(binding.displayNameEditText.text.toString(),
binding.noteEditText.text.toString(),
binding.lockedCheckBox.isChecked,
accountFieldEditAdapter.getFieldData(),
this)
}
private fun onSaveFailure(msg: String?) {
val errorMsg = msg ?: getString(R.string.error_media_upload_sending)
Snackbar.make(avatarButton, errorMsg, Snackbar.LENGTH_LONG).show()
saveProgressBar.visibility = View.GONE
Snackbar.make(binding.avatarButton, errorMsg, Snackbar.LENGTH_LONG).show()
binding.saveProgressBar.visibility = View.GONE
}
private fun beginMediaPicking() {
when (currentlyPicking) {
PickType.AVATAR -> {
avatarProgressBar.visibility = View.VISIBLE
avatarPreview.visibility = View.INVISIBLE
avatarButton.setImageDrawable(null)
binding.avatarProgressBar.visibility = View.VISIBLE
binding.avatarPreview.visibility = View.INVISIBLE
binding.avatarButton.setImageDrawable(null)
}
PickType.HEADER -> {
headerProgressBar.visibility = View.VISIBLE
headerPreview.visibility = View.INVISIBLE
headerButton.setImageDrawable(null)
binding.headerProgressBar.visibility = View.VISIBLE
binding.headerPreview.visibility = View.INVISIBLE
binding.headerButton.setImageDrawable(null)
}
PickType.NOTHING -> { /* do nothing */ }
}
}
private fun endMediaPicking() {
avatarProgressBar.visibility = View.GONE
headerProgressBar.visibility = View.GONE
binding.avatarProgressBar.visibility = View.GONE
binding.headerProgressBar.visibility = View.GONE
currentlyPicking = PickType.NOTHING
}
@ -402,7 +402,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
}
private fun onResizeFailure() {
Snackbar.make(avatarButton, R.string.error_media_upload_sending, Snackbar.LENGTH_LONG).show()
Snackbar.make(binding.avatarButton, R.string.error_media_upload_sending, Snackbar.LENGTH_LONG).show()
endMediaPicking()
}

View file

@ -7,13 +7,13 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.databinding.ActivityFiltersBinding
import com.keylesspalace.tusky.databinding.DialogFilterBinding
import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import kotlinx.android.synthetic.main.activity_filters.*
import kotlinx.android.synthetic.main.dialog_filter.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import com.keylesspalace.tusky.util.viewBinding
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
@ -28,13 +28,28 @@ class FiltersActivity: BaseActivity() {
@Inject
lateinit var eventHub: EventHub
private val binding by viewBinding(ActivityFiltersBinding::inflate)
private lateinit var context : String
private lateinit var filters: MutableList<Filter>
private lateinit var dialog: AlertDialog
companion object {
const val FILTERS_CONTEXT = "filters_context"
const val FILTERS_TITLE = "filters_title"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run {
// Back button
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
binding.addFilterButton.setOnClickListener {
showAddFilterDialog()
}
title = intent?.getStringExtra(FILTERS_TITLE)
context = intent?.getStringExtra(FILTERS_CONTEXT)!!
loadFilters()
}
private fun updateFilter(filter: Filter, itemIndex: Int) {
@ -101,52 +116,51 @@ class FiltersActivity: BaseActivity() {
}
private fun showAddFilterDialog() {
dialog = AlertDialog.Builder(this@FiltersActivity)
val binding = DialogFilterBinding.inflate(layoutInflater)
binding.phraseWholeWord.isChecked = true
AlertDialog.Builder(this@FiltersActivity)
.setTitle(R.string.filter_addition_dialog_title)
.setView(R.layout.dialog_filter)
.setView(binding.root)
.setPositiveButton(android.R.string.ok){ _, _ ->
createFilter(dialog.phraseEditText.text.toString(), dialog.phraseWholeWord.isChecked)
createFilter(binding.phraseEditText.text.toString(), binding.phraseWholeWord.isChecked)
}
.setNeutralButton(android.R.string.cancel, null)
.create()
dialog.show()
dialog.phraseWholeWord.isChecked = true
.show()
}
private fun setupEditDialogForItem(itemIndex: Int) {
dialog = AlertDialog.Builder(this@FiltersActivity)
val binding = DialogFilterBinding.inflate(layoutInflater)
val filter = filters[itemIndex]
binding.phraseEditText.setText(filter.phrase)
binding.phraseWholeWord.isChecked = filter.wholeWord
AlertDialog.Builder(this@FiltersActivity)
.setTitle(R.string.filter_edit_dialog_title)
.setView(R.layout.dialog_filter)
.setView(binding.root)
.setPositiveButton(R.string.filter_dialog_update_button) { _, _ ->
val oldFilter = filters[itemIndex]
val newFilter = Filter(oldFilter.id, dialog.phraseEditText.text.toString(), oldFilter.context,
oldFilter.expiresAt, oldFilter.irreversible, dialog.phraseWholeWord.isChecked)
val newFilter = Filter(oldFilter.id, binding.phraseEditText.text.toString(), oldFilter.context,
oldFilter.expiresAt, oldFilter.irreversible, binding.phraseWholeWord.isChecked)
updateFilter(newFilter, itemIndex)
}
.setNegativeButton(R.string.filter_dialog_remove_button) { _, _ ->
deleteFilter(itemIndex)
}
.setNeutralButton(android.R.string.cancel, null)
.create()
dialog.show()
// Need to show the dialog before referencing any elements from its view
val filter = filters[itemIndex]
dialog.phraseEditText.setText(filter.phrase)
dialog.phraseWholeWord.isChecked = filter.wholeWord
.show()
}
private fun refreshFilterDisplay() {
filtersView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, filters.map { filter -> filter.phrase })
filtersView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> setupEditDialogForItem(position) }
binding.filtersView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, filters.map { filter -> filter.phrase })
binding.filtersView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> setupEditDialogForItem(position) }
}
private fun loadFilters() {
filterMessageView.hide()
filtersView.hide()
addFilterButton.hide()
filterProgressBar.show()
binding.filterMessageView.hide()
binding.filtersView.hide()
binding.addFilterButton.hide()
binding.filterProgressBar.show()
api.getFilters().enqueue(object : Callback<List<Filter>> {
override fun onResponse(call: Call<List<Filter>>, response: Response<List<Filter>>) {
@ -156,52 +170,33 @@ class FiltersActivity: BaseActivity() {
filters = filterResponse.filter { filter -> filter.context.contains(context) }.toMutableList()
refreshFilterDisplay()
filtersView.show()
addFilterButton.show()
filterProgressBar.hide()
binding.filtersView.show()
binding.addFilterButton.show()
binding.filterProgressBar.hide()
} else {
filterProgressBar.hide()
filterMessageView.show()
filterMessageView.setup(R.drawable.elephant_error,
binding.filterProgressBar.hide()
binding.filterMessageView.show()
binding.filterMessageView.setup(R.drawable.elephant_error,
R.string.error_generic) { loadFilters() }
}
}
override fun onFailure(call: Call<List<Filter>>, t: Throwable) {
filterProgressBar.hide()
filterMessageView.show()
binding.filterProgressBar.hide()
binding.filterMessageView.show()
if (t is IOException) {
filterMessageView.setup(R.drawable.elephant_offline,
binding.filterMessageView.setup(R.drawable.elephant_offline,
R.string.error_network) { loadFilters() }
} else {
filterMessageView.setup(R.drawable.elephant_error,
binding.filterMessageView.setup(R.drawable.elephant_error,
R.string.error_generic) { loadFilters() }
}
}
})
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_filters)
setupToolbarBackArrow()
addFilterButton.setOnClickListener {
showAddFilterDialog()
}
title = intent?.getStringExtra(FILTERS_TITLE)
context = intent?.getStringExtra(FILTERS_CONTEXT)!!
loadFilters()
companion object {
const val FILTERS_CONTEXT = "filters_context"
const val FILTERS_TITLE = "filters_title"
}
private fun setupToolbarBackArrow() {
setSupportActionBar(toolbar)
supportActionBar?.run {
// Back button
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
}
}

View file

@ -19,23 +19,20 @@ import android.os.Bundle
import androidx.annotation.RawRes
import android.util.Log
import android.widget.TextView
import com.keylesspalace.tusky.databinding.ActivityLicenseBinding
import com.keylesspalace.tusky.util.IOUtils
import kotlinx.android.extensions.CacheImplementation
import kotlinx.android.extensions.ContainerOptions
import kotlinx.android.synthetic.main.activity_license.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
class LicenseActivity : BaseActivity() {
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_license)
val binding = ActivityLicenseBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
@ -43,7 +40,7 @@ class LicenseActivity : BaseActivity() {
setTitle(R.string.title_licenses)
loadFileIntoTextView(R.raw.apache, licenseApacheTextView)
loadFileIntoTextView(R.raw.apache, binding.licenseApacheTextView)
}
@ -67,7 +64,5 @@ class LicenseActivity : BaseActivity() {
IOUtils.closeQuietly(br)
textView.text = sb.toString()
}
}

View file

@ -24,12 +24,14 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.*
import androidx.recyclerview.widget.ListAdapter
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.databinding.ActivityListsBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.MastoList
@ -47,8 +49,6 @@ import com.uber.autodispose.autoDispose
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.activity_lists.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
/**
@ -57,47 +57,42 @@ import javax.inject.Inject
class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
companion object {
@JvmStatic
fun newIntent(context: Context): Intent {
return Intent(context, ListsActivity::class.java)
}
}
@Inject
lateinit var viewModelFactory: ViewModelFactory
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
private lateinit var viewModel: ListsViewModel
private val viewModel: ListsViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityListsBinding::inflate)
private val adapter = ListsAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lists)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
title = getString(R.string.title_lists)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
listsRecycler.adapter = adapter
listsRecycler.layoutManager = LinearLayoutManager(this)
listsRecycler.addItemDecoration(
binding.listsRecycler.adapter = adapter
binding.listsRecycler.layoutManager = LinearLayoutManager(this)
binding.listsRecycler.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
viewModel = viewModelFactory.create(ListsViewModel::class.java)
viewModel.state
.observeOn(AndroidSchedulers.mainThread())
.autoDispose(from(this))
.subscribe(this::update)
viewModel.retryLoading()
addListButton.setOnClickListener {
binding.addListButton.setOnClickListener {
showlistNameDialog(null)
}
@ -153,37 +148,36 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
private fun update(state: ListsViewModel.State) {
adapter.submitList(state.lists)
progressBar.visible(state.loadingState == LOADING)
binding.progressBar.visible(state.loadingState == LOADING)
when (state.loadingState) {
INITIAL, LOADING -> messageView.hide()
INITIAL, LOADING -> binding.messageView.hide()
ERROR_NETWORK -> {
messageView.show()
messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
binding.messageView.show()
binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
viewModel.retryLoading()
}
}
ERROR_OTHER -> {
messageView.show()
messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
binding.messageView.show()
binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
viewModel.retryLoading()
}
}
LOADED ->
if (state.lists.isEmpty()) {
messageView.show()
messageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty,
binding.messageView.show()
binding.messageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty,
null)
} else {
messageView.hide()
binding.messageView.hide()
}
}
}
private fun showMessage(@StringRes messageId: Int) {
Snackbar.make(
listsRecycler, messageId, Snackbar.LENGTH_SHORT
binding.listsRecycler, messageId, Snackbar.LENGTH_SHORT
).show()
}
private fun onListSelected(listId: String) {
@ -215,8 +209,6 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
}
}
override fun androidInjector() = dispatchingAndroidInjector
private object ListsDiffer : DiffUtil.ItemCallback<MastoList>() {
override fun areItemsTheSame(oldItem: MastoList, newItem: MastoList): Boolean {
return oldItem.id == newItem.id
@ -273,4 +265,10 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
viewModel.renameList(listId, name.toString())
}
}
override fun androidInjector() = dispatchingAndroidInjector
companion object {
fun newIntent(context: Context) = Intent(context, ListsActivity::class.java)
}
}

View file

@ -29,15 +29,12 @@ import androidx.appcompat.app.AlertDialog
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.databinding.ActivityLoginBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.AccessToken
import com.keylesspalace.tusky.entity.AppCredentials
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.getNonNullString
import com.keylesspalace.tusky.util.rickRoll
import com.keylesspalace.tusky.util.shouldRickRoll
import kotlinx.android.synthetic.main.activity_login.*
import com.keylesspalace.tusky.util.*
import okhttp3.HttpUrl
import retrofit2.Call
import retrofit2.Callback
@ -49,6 +46,8 @@ class LoginActivity : BaseActivity(), Injectable {
@Inject
lateinit var mastodonApi: MastodonApi
private val binding by viewBinding(ActivityLoginBinding::inflate)
private lateinit var preferences: SharedPreferences
private val oauthRedirectUri: String
@ -61,26 +60,26 @@ class LoginActivity : BaseActivity(), Injectable {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
setContentView(binding.root)
if(savedInstanceState == null && BuildConfig.CUSTOM_INSTANCE.isNotBlank() && !isAdditionalLogin()) {
domainEditText.setText(BuildConfig.CUSTOM_INSTANCE)
domainEditText.setSelection(BuildConfig.CUSTOM_INSTANCE.length)
binding.domainEditText.setText(BuildConfig.CUSTOM_INSTANCE)
binding.domainEditText.setSelection(BuildConfig.CUSTOM_INSTANCE.length)
}
if(BuildConfig.CUSTOM_LOGO_URL.isNotBlank()) {
Glide.with(loginLogo)
Glide.with(binding.loginLogo)
.load(BuildConfig.CUSTOM_LOGO_URL)
.placeholder(null)
.into(loginLogo)
.into(binding.loginLogo)
}
preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE)
loginButton.setOnClickListener { onButtonClick() }
binding.loginButton.setOnClickListener { onButtonClick() }
whatsAnInstanceTextView.setOnClickListener {
binding.whatsAnInstanceTextView.setOnClickListener {
val dialog = AlertDialog.Builder(this)
.setMessage(R.string.dialog_whats_an_instance)
.setPositiveButton(R.string.action_close, null)
@ -90,11 +89,11 @@ class LoginActivity : BaseActivity(), Injectable {
}
if (isAdditionalLogin()) {
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowTitleEnabled(false)
} else {
toolbar.visibility = View.GONE
binding.toolbar.visibility = View.GONE
}
}
@ -117,15 +116,15 @@ class LoginActivity : BaseActivity(), Injectable {
*/
private fun onButtonClick() {
loginButton.isEnabled = false
binding.loginButton.isEnabled = false
val domain = canonicalizeDomain(domainEditText.text.toString())
val domain = canonicalizeDomain(binding.domainEditText.text.toString())
try {
HttpUrl.Builder().host(domain).scheme("https").build()
} catch (e: IllegalArgumentException) {
setLoading(false)
domainTextInputLayout.error = getString(R.string.error_invalid_domain)
binding.domainTextInputLayout.error = getString(R.string.error_invalid_domain)
return
}
@ -138,8 +137,8 @@ class LoginActivity : BaseActivity(), Injectable {
override fun onResponse(call: Call<AppCredentials>,
response: Response<AppCredentials>) {
if (!response.isSuccessful) {
loginButton.isEnabled = true
domainTextInputLayout.error = getString(R.string.error_failed_app_registration)
binding.loginButton.isEnabled = true
binding.domainTextInputLayout.error = getString(R.string.error_failed_app_registration)
setLoading(false)
Log.e(TAG, "App authentication failed. " + response.message())
return
@ -158,8 +157,8 @@ class LoginActivity : BaseActivity(), Injectable {
}
override fun onFailure(call: Call<AppCredentials>, t: Throwable) {
loginButton.isEnabled = true
domainTextInputLayout.error = getString(R.string.error_failed_app_registration)
binding.loginButton.isEnabled = true
binding.domainTextInputLayout.error = getString(R.string.error_failed_app_registration)
setLoading(false)
Log.e(TAG, Log.getStackTraceString(t))
}
@ -190,7 +189,7 @@ class LoginActivity : BaseActivity(), Injectable {
if (viewIntent.resolveActivity(packageManager) != null) {
startActivity(viewIntent)
} else {
domainEditText.error = getString(R.string.error_no_web_browser_found)
binding.domainEditText.error = getString(R.string.error_no_web_browser_found)
setLoading(false)
}
}
@ -224,7 +223,7 @@ class LoginActivity : BaseActivity(), Injectable {
onLoginSuccess(response.body()!!.accessToken, domain)
} else {
setLoading(false)
domainTextInputLayout.error = getString(R.string.error_retrieving_oauth_token)
binding.domainTextInputLayout.error = getString(R.string.error_retrieving_oauth_token)
Log.e(TAG, String.format("%s %s",
getString(R.string.error_retrieving_oauth_token),
response.message()))
@ -233,7 +232,7 @@ class LoginActivity : BaseActivity(), Injectable {
override fun onFailure(call: Call<AccessToken>, t: Throwable) {
setLoading(false)
domainTextInputLayout.error = getString(R.string.error_retrieving_oauth_token)
binding.domainTextInputLayout.error = getString(R.string.error_retrieving_oauth_token)
Log.e(TAG, String.format("%s %s",
getString(R.string.error_retrieving_oauth_token),
t.message))
@ -246,14 +245,14 @@ class LoginActivity : BaseActivity(), Injectable {
/* Authorization failed. Put the error response where the user can read it and they
* can try again. */
setLoading(false)
domainTextInputLayout.error = getString(R.string.error_authorization_denied)
binding.domainTextInputLayout.error = getString(R.string.error_authorization_denied)
Log.e(TAG, String.format("%s %s",
getString(R.string.error_authorization_denied),
error))
} else {
// This case means a junk response was received somehow.
setLoading(false)
domainTextInputLayout.error = getString(R.string.error_authorization_unknown)
binding.domainTextInputLayout.error = getString(R.string.error_authorization_unknown)
}
} else {
// first show or user cancelled login
@ -263,12 +262,12 @@ class LoginActivity : BaseActivity(), Injectable {
private fun setLoading(loadingState: Boolean) {
if (loadingState) {
loginLoadingLayout.visibility = View.VISIBLE
loginInputLayout.visibility = View.GONE
binding.loginLoadingLayout.visibility = View.VISIBLE
binding.loginInputLayout.visibility = View.GONE
} else {
loginLoadingLayout.visibility = View.GONE
loginInputLayout.visibility = View.VISIBLE
loginButton.isEnabled = true
binding.loginLoadingLayout.visibility = View.GONE
binding.loginInputLayout.visibility = View.VISIBLE
binding.loginButton.isEnabled = true
}
}

View file

@ -59,6 +59,7 @@ import com.keylesspalace.tusky.components.notifications.NotificationHelper
import com.keylesspalace.tusky.components.preference.PreferencesActivity
import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity
import com.keylesspalace.tusky.components.search.SearchActivity
import com.keylesspalace.tusky.databinding.ActivityMainBinding
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.entity.Account
@ -86,7 +87,6 @@ import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
@ -108,6 +108,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
@Inject
lateinit var draftHelper: DraftHelper
private val binding by viewBinding(ActivityMainBinding::inflate)
private lateinit var header: AccountHeaderView
private var notificationTabPosition = 0
@ -179,21 +181,21 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
}
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
setContentView(R.layout.activity_main)
setContentView(binding.root)
glide = Glide.with(this)
composeButton.setOnClickListener {
binding.composeButton.setOnClickListener {
val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
startActivity(composeIntent)
}
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
mainToolbar.visible(!hideTopToolbar)
binding.mainToolbar.visible(!hideTopToolbar)
loadDrawerAvatar(activeAccount.profilePictureUrl, true)
mainToolbar.menu.add(R.string.action_search).apply {
binding.mainToolbar.menu.add(R.string.action_search).apply {
setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply {
sizeDp = 20
@ -249,11 +251,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onBackPressed() {
when {
mainDrawerLayout.isOpen -> {
mainDrawerLayout.close()
binding.mainDrawerLayout.isOpen -> {
binding.mainDrawerLayout.close()
}
viewPager.currentItem != 0 -> {
viewPager.currentItem = 0
binding.viewPager.currentItem != 0 -> {
binding.viewPager.currentItem = 0
}
else -> {
super.onBackPressed()
@ -264,10 +266,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_MENU -> {
if (mainDrawerLayout.isOpen) {
mainDrawerLayout.close()
if (binding.mainDrawerLayout.isOpen) {
binding.mainDrawerLayout.close()
} else {
mainDrawerLayout.open()
binding.mainDrawerLayout.open()
}
return true
}
@ -319,8 +321,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
private fun setupDrawer(savedInstanceState: Bundle?, addSearchButton: Boolean) {
mainToolbar.setNavigationOnClickListener {
mainDrawerLayout.open()
binding.mainToolbar.setNavigationOnClickListener {
binding.mainDrawerLayout.open()
}
header = AccountHeaderView(this).apply {
@ -333,7 +335,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
descriptionRes = R.string.add_account_description
iconicsIcon = GoogleMaterial.Icon.gmd_add
}, 0)
attachToSliderView(mainDrawer)
attachToSliderView(binding.mainDrawer)
dividerBelowHeader = false
closeDrawerOnProfileListClick = true
}
@ -369,7 +371,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
})
mainDrawer.apply {
binding.mainDrawer.apply {
tintStatusBar = true
addItems(
primaryDrawerItem {
@ -464,7 +466,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
)
if (addSearchButton) {
mainDrawer.addItemsAtPosition(4,
binding.mainDrawer.addItemsAtPosition(4,
primaryDrawerItem {
nameRes = R.string.action_search
iconicsIcon = GoogleMaterial.Icon.gmd_search
@ -478,7 +480,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
if (BuildConfig.DEBUG) {
mainDrawer.addItems(
binding.mainDrawer.addItems(
secondaryDrawerItem {
nameText = "debug"
isEnabled = false
@ -490,7 +492,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(mainDrawer.saveInstanceState(outState))
super.onSaveInstanceState(binding.mainDrawer.saveInstanceState(outState))
}
private fun setupTabs(selectNotificationTab: Boolean) {
@ -498,21 +500,21 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
val activeTabLayout = if (preferences.getString("mainNavPosition", "top") == "bottom") {
val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize)
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
(composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin
tabLayout.hide()
bottomTabLayout
(binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin
binding.tabLayout.hide()
binding.bottomTabLayout
} else {
bottomNav.hide()
(viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
(composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager
tabLayout
binding.bottomNav.hide()
(binding.viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
(binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager
binding.tabLayout
}
val tabs = accountManager.activeAccount!!.tabPreferences
val adapter = MainPagerAdapter(tabs, this)
viewPager.adapter = adapter
TabLayoutMediator(activeTabLayout, viewPager) { _: TabLayout.Tab?, _: Int -> }.attach()
binding.viewPager.adapter = adapter
TabLayoutMediator(activeTabLayout, binding.viewPager) { _: TabLayout.Tab?, _: Int -> }.attach()
activeTabLayout.removeAllTabs()
for (i in tabs.indices) {
val tab = activeTabLayout.newTab()
@ -533,10 +535,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
binding.viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
val enableSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true)
viewPager.isUserInputEnabled = enableSwipeForTabs
binding.viewPager.isUserInputEnabled = enableSwipeForTabs
onTabSelectedListener?.let {
activeTabLayout.removeOnTabSelectedListener(it)
@ -548,7 +550,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager)
}
mainToolbar.title = tabs[tab.position].title(this@MainActivity)
binding.mainToolbar.title = tabs[tab.position].title(this@MainActivity)
}
override fun onTabUnselected(tab: TabLayout.Tab) {}
@ -564,8 +566,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
val activeTabPosition = if (selectNotificationTab) notificationTabPosition else 0
mainToolbar.title = tabs[activeTabPosition].title(this@MainActivity)
mainToolbar.setOnClickListener {
binding.mainToolbar.title = tabs[activeTabPosition].title(this@MainActivity)
binding.mainToolbar.setOnClickListener {
(adapter.getFragment(activeTabLayout.selectedTabPosition) as? ReselectableFragment)?.onReselect()
}
@ -659,7 +661,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this)
// Show follow requests in the menu, if this is a locked account.
if (me.locked && mainDrawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) {
if (me.locked && binding.mainDrawer.getDrawerItem(DRAWER_ITEM_FOLLOW_REQUESTS) == null) {
val followRequestsItem = primaryDrawerItem {
identifier = DRAWER_ITEM_FOLLOW_REQUESTS
nameRes = R.string.action_view_follow_requests
@ -670,9 +672,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
startActivityWithSlideInAnimation(intent)
}
}
mainDrawer.addItemAtPosition(4, followRequestsItem)
binding.mainDrawer.addItemAtPosition(4, followRequestsItem)
} else if (!me.locked) {
mainDrawer.removeItems(DRAWER_ITEM_FOLLOW_REQUESTS)
binding.mainDrawer.removeItems(DRAWER_ITEM_FOLLOW_REQUESTS)
}
updateProfiles()
updateShortcut(this, accountManager.activeAccount!!)
@ -695,16 +697,16 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onLoadStarted(placeholder: Drawable?) {
if (placeholder != null) {
mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
mainToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
binding.mainToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
}
override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) {
mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
binding.mainToolbar.navigationIcon = FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
}
})
@ -726,7 +728,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
private fun updateAnnouncementsBadge() {
mainDrawer.updateBadge(DRAWER_ITEM_ANNOUNCEMENTS, StringHolder(if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString()))
binding.mainDrawer.updateBadge(DRAWER_ITEM_ANNOUNCEMENTS, StringHolder(if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString()))
}
private fun updateProfiles() {
@ -779,7 +781,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
override fun getActionButton(): FloatingActionButton? = composeButton
override fun getActionButton(): FloatingActionButton? = binding.composeButton
override fun androidInjector() = androidInjector

View file

@ -4,43 +4,28 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.keylesspalace.tusky.databinding.ActivityModalTimelineBinding
import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
companion object {
private const val ARG_KIND = "kind"
private const val ARG_ARG = "arg"
@JvmStatic
fun newIntent(context: Context, kind: TimelineFragment.Kind,
argument: String?): Intent {
val intent = Intent(context, ModalTimelineActivity::class.java)
intent.putExtra(ARG_KIND, kind)
intent.putExtra(ARG_ARG, argument)
return intent
}
}
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_modal_timeline)
val binding = ActivityModalTimelineBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(toolbar)
val bar = supportActionBar
if (bar != null) {
bar.title = getString(R.string.title_list_timeline)
bar.setDisplayHomeAsUpEnabled(true)
bar.setDisplayShowHomeEnabled(true)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
title = getString(R.string.title_list_timeline)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
if (supportFragmentManager.findFragmentById(R.id.contentFrame) == null) {
@ -57,4 +42,18 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn
override fun androidInjector() = dispatchingAndroidInjector
companion object {
private const val ARG_KIND = "kind"
private const val ARG_ARG = "arg"
@JvmStatic
fun newIntent(context: Context, kind: TimelineFragment.Kind,
argument: String?): Intent {
val intent = Intent(context, ModalTimelineActivity::class.java)
intent.putExtra(ARG_KIND, kind)
intent.putExtra(ARG_ARG, argument)
return intent
}
}
}

View file

@ -19,6 +19,7 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.commit
import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding
import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.fragment.TimelineFragment.Kind
@ -27,9 +28,6 @@ import javax.inject.Inject
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.extensions.CacheImplementation
import kotlinx.android.extensions.ContainerOptions
import kotlinx.android.synthetic.main.toolbar_basic.*
class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
@ -39,12 +37,12 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
private val kind: Kind
get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!)
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_statuslist)
val binding = ActivityStatuslistBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
val title = if(kind == Kind.FAVOURITES) {
R.string.title_favourites

View file

@ -38,17 +38,17 @@ import com.keylesspalace.tusky.adapter.ListSelectionAdapter
import com.keylesspalace.tusky.adapter.TabAdapter
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent
import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.onTextChanged
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
import com.uber.autodispose.autoDispose
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_tab_preference.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import java.util.regex.Pattern
import javax.inject.Inject
@ -59,6 +59,8 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
@Inject
lateinit var eventHub: EventHub
private val binding by viewBinding(ActivityTabPreferenceBinding::inflate)
private lateinit var currentTabs: MutableList<TabData>
private lateinit var currentTabsAdapter: TabAdapter
private lateinit var touchHelper: ItemTouchHelper
@ -73,9 +75,9 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tab_preference)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
setTitle(R.string.title_tab_preferences)
@ -85,13 +87,13 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().toMutableList()
currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT)
currentTabsRecyclerView.adapter = currentTabsAdapter
currentTabsRecyclerView.layoutManager = LinearLayoutManager(this)
currentTabsRecyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
binding.currentTabsRecyclerView.adapter = currentTabsAdapter
binding.currentTabsRecyclerView.layoutManager = LinearLayoutManager(this)
binding.currentTabsRecyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
addTabAdapter = TabAdapter(listOf(createTabDataFromId(DIRECT)), true, this)
addTabRecyclerView.adapter = addTabAdapter
addTabRecyclerView.layoutManager = LinearLayoutManager(this)
binding.addTabRecyclerView.adapter = addTabAdapter
binding.addTabRecyclerView.layoutManager = LinearLayoutManager(this)
touchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
@ -132,17 +134,17 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
}
})
touchHelper.attachToRecyclerView(currentTabsRecyclerView)
touchHelper.attachToRecyclerView(binding.currentTabsRecyclerView)
actionButton.setOnClickListener {
binding.actionButton.setOnClickListener {
toggleFab(true)
}
scrim.setOnClickListener {
binding.scrim.setOnClickListener {
toggleFab(false)
}
maxTabsInfo.text = getString(R.string.max_tab_number_reached, MAX_TAB_COUNT)
binding.maxTabsInfo.text = getString(R.string.max_tab_number_reached, MAX_TAB_COUNT)
updateAvailableTabs()
}
@ -193,18 +195,18 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
private fun toggleFab(expand: Boolean) {
val transition = MaterialContainerTransform().apply {
startView = if (expand) actionButton else sheet
val endView: View = if (expand) sheet else actionButton
startView = if (expand) binding.actionButton else binding.sheet
val endView: View = if (expand) binding.sheet else binding.actionButton
this.endView = endView
addTarget(endView)
scrimColor = Color.TRANSPARENT
setPathMotion(MaterialArcMotion())
}
TransitionManager.beginDelayedTransition(tabPreferenceContainer, transition)
actionButton.visible(!expand)
sheet.visible(expand)
scrim.visible(expand)
TransitionManager.beginDelayedTransition(binding.root, transition)
binding.actionButton.visible(!expand)
binding.sheet.visible(expand)
binding.scrim.visible(expand)
}
private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) {
@ -310,7 +312,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
addTabAdapter.updateData(addableTabs)
maxTabsInfo.visible(addableTabs.size == 0 || currentTabs.size >= MAX_TAB_COUNT)
binding.maxTabsInfo.visible(addableTabs.size == 0 || currentTabs.size >= MAX_TAB_COUNT)
currentTabsAdapter.setRemoveButtonVisible(currentTabs.size > MIN_TAB_COUNT)
}
@ -337,7 +339,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
}
override fun onBackPressed() {
if (actionButton.isVisible) {
if (binding.actionButton.isVisible) {
super.onBackPressed()
} else {
toggleFab(false)

View file

@ -44,18 +44,19 @@ import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import com.bumptech.glide.request.FutureTarget
import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID
import com.keylesspalace.tusky.databinding.ActivityViewMediaBinding
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.fragment.ViewImageFragment
import com.keylesspalace.tusky.pager.SingleImagePagerAdapter
import com.keylesspalace.tusky.pager.ImagePagerAdapter
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider
import com.uber.autodispose.autoDispose
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_view_media.*
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
@ -65,27 +66,8 @@ import java.util.*
typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener {
companion object {
private const val EXTRA_ATTACHMENTS = "attachments"
private const val EXTRA_ATTACHMENT_INDEX = "index"
private const val EXTRA_SINGLE_IMAGE_URL = "single_image"
private const val TAG = "ViewMediaActivity"
@JvmStatic
fun newIntent(context: Context?, attachments: List<AttachmentViewData>, index: Int): Intent {
val intent = Intent(context, ViewMediaActivity::class.java)
intent.putParcelableArrayListExtra(EXTRA_ATTACHMENTS, ArrayList(attachments))
intent.putExtra(EXTRA_ATTACHMENT_INDEX, index)
return intent
}
@JvmStatic
fun newSingleImageIntent(context: Context, url: String): Intent {
val intent = Intent(context, ViewMediaActivity::class.java)
intent.putExtra(EXTRA_SINGLE_IMAGE_URL, url)
return intent
}
}
private val binding by viewBinding(ActivityViewMediaBinding::inflate)
var isToolbarVisible = true
private set
@ -102,7 +84,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_media)
setContentView(binding.root)
supportPostponeEnterTransition()
@ -125,24 +107,24 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
SingleImagePagerAdapter(this, imageUrl!!)
}
viewPager.adapter = adapter
viewPager.setCurrentItem(initialPosition, false)
viewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
binding.viewPager.adapter = adapter
binding.viewPager.setCurrentItem(initialPosition, false)
binding.viewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
toolbar.title = getPageTitle(position)
binding.toolbar.title = getPageTitle(position)
}
})
// Setup the toolbar.
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbar)
val actionBar = supportActionBar
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowHomeEnabled(true)
actionBar.title = getPageTitle(initialPosition)
}
toolbar.setNavigationOnClickListener { supportFinishAfterTransition() }
toolbar.setOnMenuItemClickListener { item: MenuItem ->
binding.toolbar.setNavigationOnClickListener { supportFinishAfterTransition() }
binding.toolbar.setOnMenuItemClickListener { item: MenuItem ->
when (item.itemId) {
R.id.action_download -> requestDownloadMedia()
R.id.action_open_status -> onOpenStatus()
@ -156,7 +138,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
window.statusBarColor = Color.BLACK
window.sharedElementEnterTransition.addListener(object : NoopTransitionListener {
override fun onTransitionEnd(transition: Transition) {
adapter.onTransitionEnd(viewPager.currentItem)
adapter.onTransitionEnd(binding.viewPager.currentItem)
window.sharedElementEnterTransition.removeListener(this)
}
})
@ -165,7 +147,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.view_media_toolbar, menu)
// We don't support 'open status' from single image views
menu?.findItem(R.id.action_open_status)?.isVisible = (attachments != null)
menu.findItem(R.id.action_open_status)?.isVisible = (attachments != null)
return true
}
@ -192,14 +174,14 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
val alpha = if (isToolbarVisible) 1.0f else 0.0f
if (isToolbarVisible) {
// If to be visible, need to make visible immediately and animate alpha
toolbar.alpha = 0.0f
toolbar.visibility = visibility
binding.toolbar.alpha = 0.0f
binding.toolbar.visibility = visibility
}
toolbar.animate().alpha(alpha)
binding.toolbar.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
toolbar.visibility = visibility
binding.toolbar.visibility = visibility
animation.removeListener(this)
}
})
@ -214,7 +196,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
}
private fun downloadMedia() {
val url = imageUrl ?: attachments!![viewPager.currentItem].attachment.url
val url = imageUrl ?: attachments!![binding.viewPager.currentItem].attachment.url
val filename = Uri.parse(url).lastPathSegment
Toast.makeText(applicationContext, resources.getString(R.string.download_image, filename), Toast.LENGTH_SHORT).show()
@ -230,18 +212,18 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadMedia()
} else {
showErrorDialog(toolbar, R.string.error_media_download_permission, R.string.action_retry) { requestDownloadMedia() }
showErrorDialog(binding.toolbar, R.string.error_media_download_permission, R.string.action_retry) { requestDownloadMedia() }
}
}
}
private fun onOpenStatus() {
val attach = attachments!![viewPager.currentItem]
val attach = attachments!![binding.viewPager.currentItem]
startActivityWithSlideInAnimation(ViewThreadActivity.startIntent(this, attach.statusId, attach.statusUrl))
}
private fun copyLink() {
val url = imageUrl ?: attachments!![viewPager.currentItem].attachment.url
val url = imageUrl ?: attachments!![binding.viewPager.currentItem].attachment.url
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText(null, url))
}
@ -256,7 +238,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
if (imageUrl != null) {
shareImage(directory, imageUrl!!)
} else {
val attachment = attachments!![viewPager.currentItem].attachment
val attachment = attachments!![binding.viewPager.currentItem].attachment
when (attachment.type) {
Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
Attachment.Type.AUDIO,
@ -280,7 +262,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
private fun shareImage(directory: File, url: String) {
isCreating = true
progressBarShare.visibility = View.VISIBLE
binding.progressBarShare.visibility = View.VISIBLE
invalidateOptionsMenu()
val file = File(directory, getTemporaryMediaFilename("png"))
val futureTask: FutureTarget<Bitmap> =
@ -312,14 +294,14 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
Log.d(TAG, "Download image result: $result")
isCreating = false
invalidateOptionsMenu()
progressBarShare.visibility = View.GONE
binding.progressBarShare.visibility = View.GONE
if (result)
shareFile(file, "image/png")
},
{ error ->
isCreating = false
invalidateOptionsMenu()
progressBarShare.visibility = View.GONE
binding.progressBarShare.visibility = View.GONE
Log.e(TAG, "Failed to download image", error)
}
)
@ -342,6 +324,28 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
shareFile(file, mimeType)
}
companion object {
private const val EXTRA_ATTACHMENTS = "attachments"
private const val EXTRA_ATTACHMENT_INDEX = "index"
private const val EXTRA_SINGLE_IMAGE_URL = "single_image"
private const val TAG = "ViewMediaActivity"
@JvmStatic
fun newIntent(context: Context?, attachments: List<AttachmentViewData>, index: Int): Intent {
val intent = Intent(context, ViewMediaActivity::class.java)
intent.putParcelableArrayListExtra(EXTRA_ATTACHMENTS, ArrayList(attachments))
intent.putExtra(EXTRA_ATTACHMENT_INDEX, index)
return intent
}
@JvmStatic
fun newSingleImageIntent(context: Context, url: String): Intent {
val intent = Intent(context, ViewMediaActivity::class.java)
intent.putExtra(EXTRA_SINGLE_IMAGE_URL, url)
return intent
}
}
}
abstract class ViewMediaAdapter(activity: FragmentActivity): FragmentStateAdapter(activity) {

View file

@ -30,13 +30,12 @@ import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewTagActivity
import com.keylesspalace.tusky.adapter.EmojiAdapter
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
import com.keylesspalace.tusky.databinding.ActivityAnnouncementsBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.view.EmojiPicker
import kotlinx.android.synthetic.main.activity_announcements.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, OnEmojiSelectedListener, Injectable {
@ -46,6 +45,8 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
private val viewModel: AnnouncementsViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityAnnouncementsBinding::inflate)
private lateinit var adapter: AnnouncementAdapter
private val picker by lazy { EmojiPicker(this) }
@ -63,22 +64,22 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_announcements)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
title = getString(R.string.title_announcements)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements)
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
announcementsList.setHasFixedSize(true)
announcementsList.layoutManager = LinearLayoutManager(this)
binding.announcementsList.setHasFixedSize(true)
binding.announcementsList.layoutManager = LinearLayoutManager(this)
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
announcementsList.addItemDecoration(divider)
binding.announcementsList.addItemDecoration(divider)
val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
@ -86,31 +87,31 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled, animateEmojis)
announcementsList.adapter = adapter
binding.announcementsList.adapter = adapter
viewModel.announcements.observe(this) {
when (it) {
is Success -> {
progressBar.hide()
swipeRefreshLayout.isRefreshing = false
binding.progressBar.hide()
binding.swipeRefreshLayout.isRefreshing = false
if (it.data.isNullOrEmpty()) {
errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_announcements)
errorMessageView.show()
binding.errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_announcements)
binding.errorMessageView.show()
} else {
errorMessageView.hide()
binding.errorMessageView.hide()
}
adapter.updateList(it.data ?: listOf())
}
is Loading -> {
errorMessageView.hide()
binding.errorMessageView.hide()
}
is Error -> {
progressBar.hide()
swipeRefreshLayout.isRefreshing = false
errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) {
binding.progressBar.hide()
binding.swipeRefreshLayout.isRefreshing = false
binding.errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) {
refreshAnnouncements()
}
errorMessageView.show()
binding.errorMessageView.show()
}
}
}
@ -120,12 +121,12 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
}
viewModel.load()
progressBar.show()
binding.progressBar.show()
}
private fun refreshAnnouncements() {
viewModel.load()
swipeRefreshLayout.isRefreshing = true
binding.swipeRefreshLayout.isRefreshing = true
}
override fun openReactionPicker(announcementId: String, target: View) {

View file

@ -61,6 +61,7 @@ import com.keylesspalace.tusky.components.compose.dialog.makeCaptionDialog
import com.keylesspalace.tusky.components.compose.dialog.showAddPollDialog
import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener
import com.keylesspalace.tusky.components.compose.view.ComposeScheduleView
import com.keylesspalace.tusky.databinding.ActivityComposeBinding
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.db.DraftAttachment
import com.keylesspalace.tusky.di.Injectable
@ -76,7 +77,6 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_compose.*
import java.io.File
import java.io.IOException
import java.util.*
@ -109,17 +109,20 @@ class ComposeActivity : BaseActivity(),
private val viewModel: ComposeViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityComposeBinding::inflate)
private val maxUploadMediaNumber = 4
private var mediaCount = 0
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val preferences = PreferenceManager.getDefaultSharedPreferences(this)
val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
if (theme == "black") {
setTheme(R.style.TuskyDialogActivityBlackTheme)
}
setContentView(R.layout.activity_compose)
setContentView(binding.root)
setupActionBar()
// do not do anything when not logged in, activity will be finished in super.onCreate() anyway
@ -135,10 +138,10 @@ class ComposeActivity : BaseActivity(),
},
onRemove = this::removeMediaFromQueue
)
composeMediaPreviewBar.layoutManager =
binding.composeMediaPreviewBar.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
composeMediaPreviewBar.adapter = mediaAdapter
composeMediaPreviewBar.itemAnimator = null
binding.composeMediaPreviewBar.adapter = mediaAdapter
binding.composeMediaPreviewBar.itemAnimator = null
subscribeToUpdates(mediaAdapter)
setupButtons()
@ -154,11 +157,11 @@ class ComposeActivity : BaseActivity(),
setupReplyViews(composeOptions?.replyingStatusAuthor, composeOptions?.replyingStatusContent)
val tootText = composeOptions?.tootText
if (!tootText.isNullOrEmpty()) {
composeEditField.setText(tootText)
binding.composeEditField.setText(tootText)
}
if (!composeOptions?.scheduledAt.isNullOrEmpty()) {
composeScheduleView.setDateTime(composeOptions?.scheduledAt)
binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt)
}
setupComposeField(preferences, viewModel.startingText)
@ -198,14 +201,14 @@ class ComposeActivity : BaseActivity(),
}
if (shareBody.isNotBlank()) {
val start = composeEditField.selectionStart.coerceAtLeast(0)
val end = composeEditField.selectionEnd.coerceAtLeast(0)
val start = binding.composeEditField.selectionStart.coerceAtLeast(0)
val end = binding.composeEditField.selectionEnd.coerceAtLeast(0)
val left = min(start, end)
val right = max(start, end)
composeEditField.text.replace(left, right, shareBody, 0, shareBody.length)
binding.composeEditField.text.replace(left, right, shareBody, 0, shareBody.length)
// move edittext cursor to first when shareBody parsed
composeEditField.text.insert(0, "\n")
composeEditField.setSelection(0)
binding.composeEditField.text.insert(0, "\n")
binding.composeEditField.setSelection(0)
}
}
}
@ -214,58 +217,58 @@ class ComposeActivity : BaseActivity(),
private fun setupReplyViews(replyingStatusAuthor: String?, replyingStatusContent: String?) {
if (replyingStatusAuthor != null) {
composeReplyView.show()
composeReplyView.text = getString(R.string.replying_to, replyingStatusAuthor)
binding.composeReplyView.show()
binding.composeReplyView.text = getString(R.string.replying_to, replyingStatusAuthor)
val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).apply { sizeDp = 12 }
ThemeUtils.setDrawableTint(this, arrowDownIcon, android.R.attr.textColorTertiary)
composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null)
binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null)
composeReplyView.setOnClickListener {
TransitionManager.beginDelayedTransition(composeReplyContentView.parent as ViewGroup)
binding.composeReplyView.setOnClickListener {
TransitionManager.beginDelayedTransition(binding.composeReplyContentView.parent as ViewGroup)
if (composeReplyContentView.isVisible) {
composeReplyContentView.hide()
composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null)
if (binding.composeReplyContentView.isVisible) {
binding.composeReplyContentView.hide()
binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null)
} else {
composeReplyContentView.show()
binding.composeReplyContentView.show()
val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).apply { sizeDp = 12 }
ThemeUtils.setDrawableTint(this, arrowUpIcon, android.R.attr.textColorTertiary)
composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowUpIcon, null)
binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowUpIcon, null)
}
}
}
replyingStatusContent?.let { composeReplyContentView.text = it }
replyingStatusContent?.let { binding.composeReplyContentView.text = it }
}
private fun setupContentWarningField(startingContentWarning: String?) {
if (startingContentWarning != null) {
composeContentWarningField.setText(startingContentWarning)
binding.composeContentWarningField.setText(startingContentWarning)
}
composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
binding.composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
}
private fun setupComposeField(preferences: SharedPreferences, startingText: String?) {
composeEditField.setOnCommitContentListener(this)
binding.composeEditField.setOnCommitContentListener(this)
composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
composeEditField.setAdapter(
binding.composeEditField.setAdapter(
ComposeAutoCompleteAdapter(
this,
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
)
)
composeEditField.setTokenizer(ComposeTokenizer())
binding.composeEditField.setTokenizer(ComposeTokenizer())
composeEditField.setText(startingText)
composeEditField.setSelection(composeEditField.length())
binding.composeEditField.setText(startingText)
binding.composeEditField.setSelection(binding.composeEditField.length())
val mentionColour = composeEditField.linkTextColors.defaultColor
highlightSpans(composeEditField.text, mentionColour)
composeEditField.afterTextChanged { editable ->
val mentionColour = binding.composeEditField.linkTextColors.defaultColor
highlightSpans(binding.composeEditField.text, mentionColour)
binding.composeEditField.afterTextChanged { editable ->
highlightSpans(editable, mentionColour)
updateVisibleCharactersLeft()
}
@ -273,7 +276,7 @@ class ComposeActivity : BaseActivity(),
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) {
composeEditField.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
binding.composeEditField.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
}
@ -282,7 +285,7 @@ class ComposeActivity : BaseActivity(),
viewModel.instanceParams.observe { instanceData ->
maximumTootCharacters = instanceData.maxChars
updateVisibleCharactersLeft()
composeScheduleButton.visible(instanceData.supportsScheduled)
binding.composeScheduleButton.visible(instanceData.supportsScheduled)
}
viewModel.emoji.observe { emoji -> setEmojiList(emoji) }
combineLiveData(viewModel.markMediaAsSensitive, viewModel.showContentWarning) { markSensitive, showContentWarning ->
@ -296,19 +299,19 @@ class ComposeActivity : BaseActivity(),
mediaAdapter.submitList(media)
if (media.size != mediaCount) {
mediaCount = media.size
composeMediaPreviewBar.visible(media.isNotEmpty())
binding.composeMediaPreviewBar.visible(media.isNotEmpty())
updateSensitiveMediaToggle(viewModel.markMediaAsSensitive.value != false, viewModel.showContentWarning.value != false)
}
}
viewModel.poll.observe { poll ->
pollPreview.visible(poll != null)
poll?.let(pollPreview::setPoll)
binding.pollPreview.visible(poll != null)
poll?.let(binding.pollPreview::setPoll)
}
viewModel.scheduledAt.observe { scheduledAt ->
if (scheduledAt == null) {
composeScheduleView.resetSchedule()
binding.composeScheduleView.resetSchedule()
} else {
composeScheduleView.setDateTime(scheduledAt)
binding.composeScheduleView.setDateTime(scheduledAt)
}
updateScheduleButton()
}
@ -316,7 +319,7 @@ class ComposeActivity : BaseActivity(),
val active = poll == null
&& media!!.size != 4
&& (media.isEmpty() || media.first().type == QueuedMedia.Type.IMAGE)
enableButton(composeAddMediaButton, active, active)
enableButton(binding.composeAddMediaButton, active, active)
enablePollButton(media.isNullOrEmpty())
}.subscribe()
viewModel.uploadError.observe {
@ -324,52 +327,52 @@ class ComposeActivity : BaseActivity(),
}
viewModel.setupComplete.observe {
// Focus may have changed during view model setup, ensure initial focus is on the edit field
composeEditField.requestFocus()
binding.composeEditField.requestFocus()
}
}
}
private fun setupButtons() {
composeOptionsBottomSheet.listener = this
binding.composeOptionsBottomSheet.listener = this
composeOptionsBehavior = BottomSheetBehavior.from(composeOptionsBottomSheet)
addMediaBehavior = BottomSheetBehavior.from(addMediaBottomSheet)
scheduleBehavior = BottomSheetBehavior.from(composeScheduleView)
emojiBehavior = BottomSheetBehavior.from(emojiView)
composeOptionsBehavior = BottomSheetBehavior.from(binding.composeOptionsBottomSheet)
addMediaBehavior = BottomSheetBehavior.from(binding.addMediaBottomSheet)
scheduleBehavior = BottomSheetBehavior.from(binding.composeScheduleView)
emojiBehavior = BottomSheetBehavior.from(binding.emojiView)
enableButton(composeEmojiButton, clickable = false, colorActive = false)
enableButton(binding.composeEmojiButton, clickable = false, colorActive = false)
// Setup the interface buttons.
composeTootButton.setOnClickListener { onSendClicked() }
composeAddMediaButton.setOnClickListener { openPickDialog() }
composeToggleVisibilityButton.setOnClickListener { showComposeOptions() }
composeContentWarningButton.setOnClickListener { onContentWarningChanged() }
composeEmojiButton.setOnClickListener { showEmojis() }
composeHideMediaButton.setOnClickListener { toggleHideMedia() }
composeScheduleButton.setOnClickListener { onScheduleClick() }
composeScheduleView.setResetOnClickListener { resetSchedule() }
composeScheduleView.setListener(this)
atButton.setOnClickListener { atButtonClicked() }
hashButton.setOnClickListener { hashButtonClicked() }
binding.composeTootButton.setOnClickListener { onSendClicked() }
binding.composeAddMediaButton.setOnClickListener { openPickDialog() }
binding.composeToggleVisibilityButton.setOnClickListener { showComposeOptions() }
binding.composeContentWarningButton.setOnClickListener { onContentWarningChanged() }
binding.composeEmojiButton.setOnClickListener { showEmojis() }
binding.composeHideMediaButton.setOnClickListener { toggleHideMedia() }
binding.composeScheduleButton.setOnClickListener { onScheduleClick() }
binding.composeScheduleView.setResetOnClickListener { resetSchedule() }
binding.composeScheduleView.setListener(this)
binding.atButton.setOnClickListener { atButtonClicked() }
binding.hashButton.setOnClickListener { hashButtonClicked() }
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply { colorInt = textColor; sizeDp = 18 }
actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds(cameraIcon, null, null, null)
binding.actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds(cameraIcon, null, null, null)
val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply { colorInt = textColor; sizeDp = 18 }
actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds(imageIcon, null, null, null)
binding.actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds(imageIcon, null, null, null)
val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply { colorInt = textColor; sizeDp = 18 }
addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(pollIcon, null, null, null)
binding.addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(pollIcon, null, null, null)
actionPhotoTake.setOnClickListener { initiateCameraApp() }
actionPhotoPick.setOnClickListener { onMediaPick() }
addPollTextActionTextView.setOnClickListener { openPollDialog() }
binding.actionPhotoTake.setOnClickListener { initiateCameraApp() }
binding.actionPhotoPick.setOnClickListener { onMediaPick() }
binding.addPollTextActionTextView.setOnClickListener { openPollDialog() }
}
private fun setupActionBar() {
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbar)
supportActionBar?.run {
title = null
setDisplayHomeAsUpEnabled(true)
@ -388,40 +391,40 @@ class ComposeActivity : BaseActivity(),
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
loadAvatar(
activeAccount.profilePictureUrl,
composeAvatar,
binding.composeAvatar,
avatarSize / 8,
animateAvatars
)
composeAvatar.contentDescription = getString(R.string.compose_active_account_description,
binding.composeAvatar.contentDescription = getString(R.string.compose_active_account_description,
activeAccount.fullName)
}
private fun replaceTextAtCaret(text: CharSequence) {
// If you select "backward" in an editable, you get SelectionStart > SelectionEnd
val start = composeEditField.selectionStart.coerceAtMost(composeEditField.selectionEnd)
val end = composeEditField.selectionStart.coerceAtLeast(composeEditField.selectionEnd)
val textToInsert = if (start > 0 && !composeEditField.text[start - 1].isWhitespace()) {
val start = binding.composeEditField.selectionStart.coerceAtMost(binding.composeEditField.selectionEnd)
val end = binding.composeEditField.selectionStart.coerceAtLeast(binding.composeEditField.selectionEnd)
val textToInsert = if (start > 0 && !binding.composeEditField.text[start - 1].isWhitespace()) {
" $text"
} else {
text
}
composeEditField.text.replace(start, end, textToInsert)
binding.composeEditField.text.replace(start, end, textToInsert)
// Set the cursor after the inserted text
composeEditField.setSelection(start + text.length)
binding.composeEditField.setSelection(start + text.length)
}
fun prependSelectedWordsWith(text: CharSequence) {
// If you select "backward" in an editable, you get SelectionStart > SelectionEnd
val start = composeEditField.selectionStart.coerceAtMost(composeEditField.selectionEnd)
val end = composeEditField.selectionStart.coerceAtLeast(composeEditField.selectionEnd)
val editorText = composeEditField.text
val start = binding.composeEditField.selectionStart.coerceAtMost(binding.composeEditField.selectionEnd)
val end = binding.composeEditField.selectionStart.coerceAtLeast(binding.composeEditField.selectionEnd)
val editorText = binding.composeEditField.text
if (start == end) {
// No selection, just insert text at caret
editorText.insert(start, text)
// Set the cursor after the inserted text
composeEditField.setSelection(start + text.length)
binding.composeEditField.setSelection(start + text.length)
} else {
var wasWord: Boolean
var isWord = end < editorText.length && !Character.isWhitespace(editorText[end])
@ -447,7 +450,7 @@ class ComposeActivity : BaseActivity(),
}
// Keep the same text (including insertions) selected
composeEditField.setSelection(start, newEnd)
binding.composeEditField.setSelection(start, newEnd)
}
}
@ -466,7 +469,7 @@ class ComposeActivity : BaseActivity(),
}
private fun displayTransientError(@StringRes stringId: Int) {
val bar = Snackbar.make(activityCompose, stringId, Snackbar.LENGTH_LONG)
val bar = Snackbar.make(binding.activityCompose, stringId, Snackbar.LENGTH_LONG)
//necessary so snackbar is shown over everything
bar.view.elevation = resources.getDimension(R.dimen.compose_activity_snackbar_elevation)
bar.show()
@ -478,49 +481,49 @@ class ComposeActivity : BaseActivity(),
private fun updateSensitiveMediaToggle(markMediaSensitive: Boolean, contentWarningShown: Boolean) {
if (viewModel.media.value.isNullOrEmpty()) {
composeHideMediaButton.hide()
binding.composeHideMediaButton.hide()
} else {
composeHideMediaButton.show()
binding.composeHideMediaButton.show()
@ColorInt val color = if (contentWarningShown) {
composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
composeHideMediaButton.isClickable = false
binding.composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
binding.composeHideMediaButton.isClickable = false
ContextCompat.getColor(this, R.color.transparent_tusky_blue)
} else {
composeHideMediaButton.isClickable = true
binding.composeHideMediaButton.isClickable = true
if (markMediaSensitive) {
composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
binding.composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
ContextCompat.getColor(this, R.color.tusky_blue)
} else {
composeHideMediaButton.setImageResource(R.drawable.ic_eye_24dp)
binding.composeHideMediaButton.setImageResource(R.drawable.ic_eye_24dp)
ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
}
}
composeHideMediaButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.composeHideMediaButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
}
}
private fun updateScheduleButton() {
@ColorInt val color = if (composeScheduleView.time == null) {
@ColorInt val color = if (binding.composeScheduleView.time == null) {
ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
} else {
ContextCompat.getColor(this, R.color.tusky_blue)
}
composeScheduleButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.composeScheduleButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
}
private fun enableButtons(enable: Boolean) {
composeAddMediaButton.isClickable = enable
composeToggleVisibilityButton.isClickable = enable
composeEmojiButton.isClickable = enable
composeHideMediaButton.isClickable = enable
composeScheduleButton.isClickable = enable
composeTootButton.isEnabled = enable
binding.composeAddMediaButton.isClickable = enable
binding.composeToggleVisibilityButton.isClickable = enable
binding.composeEmojiButton.isClickable = enable
binding.composeHideMediaButton.isClickable = enable
binding.composeScheduleButton.isClickable = enable
binding.composeTootButton.isEnabled = enable
}
private fun setStatusVisibility(visibility: Status.Visibility) {
composeOptionsBottomSheet.setStatusVisibility(visibility)
composeTootButton.setStatusVisibility(visibility)
binding.composeOptionsBottomSheet.setStatusVisibility(visibility)
binding.composeTootButton.setStatusVisibility(visibility)
val iconRes = when (visibility) {
Status.Visibility.PUBLIC -> R.drawable.ic_public_24dp
@ -529,7 +532,7 @@ class ComposeActivity : BaseActivity(),
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
else -> R.drawable.ic_lock_open_24dp
}
composeToggleVisibilityButton.setImageResource(iconRes)
binding.composeToggleVisibilityButton.setImageResource(iconRes)
}
private fun showComposeOptions() {
@ -545,7 +548,7 @@ class ComposeActivity : BaseActivity(),
private fun onScheduleClick() {
if (viewModel.scheduledAt.value == null) {
composeScheduleView.openPickDateDialog()
binding.composeScheduleView.openPickDateDialog()
} else {
showScheduleView()
}
@ -563,7 +566,7 @@ class ComposeActivity : BaseActivity(),
}
private fun showEmojis() {
emojiView.adapter?.let {
binding.emojiView.adapter?.let {
if (it.itemCount == 0) {
val errorMessage = getString(R.string.error_no_custom_emojis, accountManager.activeAccount!!.domain)
Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show()
@ -626,10 +629,10 @@ class ComposeActivity : BaseActivity(),
val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
layoutParams.setMargins(margin, margin, margin, marginBottom)
pollPreview.layoutParams = layoutParams
binding.pollPreview.layoutParams = layoutParams
pollPreview.setOnClickListener {
val popup = PopupMenu(this, pollPreview)
binding.pollPreview.setOnClickListener {
val popup = PopupMenu(this, binding.pollPreview)
val editId = 1
val removeId = 2
popup.menu.add(0, editId, 0, R.string.edit_poll)
@ -647,7 +650,7 @@ class ComposeActivity : BaseActivity(),
private fun removePoll() {
viewModel.poll.value = null
pollPreview.hide()
binding.pollPreview.hide()
}
override fun onVisibilityChanged(visibility: Status.Visibility) {
@ -658,39 +661,39 @@ class ComposeActivity : BaseActivity(),
@VisibleForTesting
fun calculateTextLength(): Int {
var offset = 0
val urlSpans = composeEditField.urls
val urlSpans = binding.composeEditField.urls
if (urlSpans != null) {
for (span in urlSpans) {
offset += max(0, span.url.length - MAXIMUM_URL_LENGTH)
}
}
var length = composeEditField.length() - offset
var length = binding.composeEditField.length() - offset
if (viewModel.showContentWarning.value!!) {
length += composeContentWarningField.length()
length += binding.composeContentWarningField.length()
}
return length
}
private fun updateVisibleCharactersLeft() {
val remainingLength = maximumTootCharacters - calculateTextLength()
composeCharactersLeftView.text = String.format(Locale.getDefault(), "%d", remainingLength)
binding.composeCharactersLeftView.text = String.format(Locale.getDefault(), "%d", remainingLength)
val textColor = if (remainingLength < 0) {
ContextCompat.getColor(this, R.color.tusky_red)
} else {
ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
}
composeCharactersLeftView.setTextColor(textColor)
binding.composeCharactersLeftView.setTextColor(textColor)
}
private fun onContentWarningChanged() {
val showWarning = composeContentWarningBar.isGone
val showWarning = binding.composeContentWarningBar.isGone
viewModel.contentWarningChanged(showWarning)
updateVisibleCharactersLeft()
}
private fun verifyScheduledTime(): Boolean {
return composeScheduleView.verifyScheduledTime(composeScheduleView.getDateTime(viewModel.scheduledAt.value))
return binding.composeScheduleView.verifyScheduledTime(binding.composeScheduleView.getDateTime(viewModel.scheduledAt.value))
}
private fun onSendClicked() {
@ -725,14 +728,14 @@ class ComposeActivity : BaseActivity(),
private fun sendStatus() {
enableButtons(false)
val contentText = composeEditField.text.toString()
val contentText = binding.composeEditField.text.toString()
var spoilerText = ""
if (viewModel.showContentWarning.value!!) {
spoilerText = composeContentWarningField.text.toString()
spoilerText = binding.composeContentWarningField.text.toString()
}
val characterCount = calculateTextLength()
if ((characterCount <= 0 || contentText.isBlank()) && viewModel.media.value!!.isEmpty()) {
composeEditField.error = getString(R.string.error_empty)
binding.composeEditField.error = getString(R.string.error_empty)
enableButtons(true)
} else if (characterCount <= maximumTootCharacters) {
if (viewModel.media.value!!.isNotEmpty()) {
@ -747,7 +750,7 @@ class ComposeActivity : BaseActivity(),
})
} else {
composeEditField.error = getString(R.string.error_compose_character_limit)
binding.composeEditField.error = getString(R.string.error_compose_character_limit)
enableButtons(true)
}
}
@ -758,7 +761,7 @@ class ComposeActivity : BaseActivity(),
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initiateMediaPicking()
} else {
val bar = Snackbar.make(activityCompose, R.string.error_media_upload_permission,
val bar = Snackbar.make(binding.activityCompose, R.string.error_media_upload_permission,
Snackbar.LENGTH_SHORT).apply {
}
@ -813,12 +816,12 @@ class ComposeActivity : BaseActivity(),
}
private fun enablePollButton(enable: Boolean) {
addPollTextActionTextView.isEnabled = enable
binding.addPollTextActionTextView.isEnabled = enable
val textColor = ThemeUtils.getColor(this,
if (enable) android.R.attr.textColorTertiary
else R.attr.textColorDisabled)
addPollTextActionTextView.setTextColor(textColor)
addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
binding.addPollTextActionTextView.setTextColor(textColor)
binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
}
private fun removeMediaFromQueue(item: QueuedMedia) {
@ -879,19 +882,18 @@ class ComposeActivity : BaseActivity(),
}
private fun showContentWarning(show: Boolean) {
TransitionManager.beginDelayedTransition(composeContentWarningBar.parent as ViewGroup)
TransitionManager.beginDelayedTransition(binding.composeContentWarningBar.parent as ViewGroup)
@ColorInt val color = if (show) {
composeContentWarningBar.show()
composeContentWarningField.setSelection(composeContentWarningField.text.length)
composeContentWarningField.requestFocus()
binding.composeContentWarningBar.show()
binding.composeContentWarningField.setSelection(binding.composeContentWarningField.text.length)
binding.composeContentWarningField.requestFocus()
ContextCompat.getColor(this, R.color.tusky_blue)
} else {
composeContentWarningBar.hide()
composeEditField.requestFocus()
binding.composeContentWarningBar.hide()
binding.composeEditField.requestFocus()
ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
}
composeContentWarningButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.composeContentWarningButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -939,8 +941,8 @@ class ComposeActivity : BaseActivity(),
}
private fun handleCloseButton() {
val contentText = composeEditField.text.toString()
val contentWarning = composeContentWarningField.text.toString()
val contentText = binding.composeEditField.text.toString()
val contentWarning = binding.composeContentWarningField.text.toString()
if (viewModel.didChange(contentText, contentWarning)) {
AlertDialog.Builder(this)
.setMessage(R.string.compose_save_draft)
@ -974,8 +976,8 @@ class ComposeActivity : BaseActivity(),
private fun setEmojiList(emojiList: List<Emoji>?) {
if (emojiList != null) {
emojiView.adapter = EmojiAdapter(emojiList, this@ComposeActivity)
enableButton(composeEmojiButton, true, emojiList.isNotEmpty())
binding.emojiView.adapter = EmojiAdapter(emojiList, this@ComposeActivity)
enableButton(binding.composeEmojiButton, true, emojiList.isNotEmpty())
}
}

View file

@ -4,10 +4,10 @@ import android.os.Bundle
import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.instancemute.fragment.InstanceListFragment
import com.keylesspalace.tusky.databinding.ActivityAccountListBinding
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import javax.inject.Inject
import kotlinx.android.synthetic.main.toolbar_basic.*
class InstanceListActivity: BaseActivity(), HasAndroidInjector {
@ -16,9 +16,10 @@ class InstanceListActivity: BaseActivity(), HasAndroidInjector {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityAccountListBinding.inflate(layoutInflater)
setContentView(R.layout.activity_account_list)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
setTitle(R.string.title_domain_mutes)
setDisplayHomeAsUpEnabled(true)

View file

@ -28,12 +28,12 @@ import com.keylesspalace.tusky.MainActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.databinding.ActivityPreferencesBinding
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.getNonNullString
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener,
@ -48,12 +48,12 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
private var restartActivitiesOnExit: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_preferences)
val binding = ActivityPreferencesBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)

View file

@ -22,11 +22,11 @@ import androidx.activity.viewModels
import com.keylesspalace.tusky.BottomSheetActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.report.adapter.ReportPagerAdapter
import com.keylesspalace.tusky.databinding.ActivityReportBinding
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.util.viewBinding
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.activity_report.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class ReportActivity : BottomSheetActivity(), HasAndroidInjector {
@ -39,6 +39,8 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector {
private val viewModel: ReportViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityReportBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val accountId = intent?.getStringExtra(ACCOUNT_ID)
@ -50,9 +52,9 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector {
viewModel.init(accountId, accountUserName, intent?.getStringExtra(STATUS_ID))
setContentView(R.layout.activity_report)
setContentView(binding.root)
setSupportActionBar(toolbar)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
title = getString(R.string.report_username_format, viewModel.accountUserName)
@ -69,8 +71,8 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector {
}
private fun initViewPager() {
wizard.isUserInputEnabled = false
wizard.adapter = ReportPagerAdapter(this)
binding.wizard.isUserInputEnabled = false
binding.wizard.adapter = ReportPagerAdapter(this)
}
private fun subscribeObservables() {
@ -96,18 +98,18 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector {
}
private fun showPreviousScreen() {
when (wizard.currentItem) {
when (binding.wizard.currentItem) {
0 -> closeScreen()
1 -> showStatusesPage()
}
}
private fun showDonePage() {
wizard.currentItem = 2
binding.wizard.currentItem = 2
}
private fun showNotesPage() {
wizard.currentItem = 1
binding.wizard.currentItem = 1
}
private fun closeScreen() {
@ -115,7 +117,7 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector {
}
private fun showStatusesPage() {
wizard.currentItem = 0
binding.wizard.currentItem = 0
}
companion object {

View file

@ -18,20 +18,19 @@ package com.keylesspalace.tusky.components.scheduled
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.activity.viewModels
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.databinding.ActivityScheduledTootBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.ScheduledStatus
import com.keylesspalace.tusky.util.Status
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import kotlinx.android.synthetic.main.activity_scheduled_toot.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject
class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injectable {
@ -39,31 +38,31 @@ class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injec
@Inject
lateinit var viewModelFactory: ViewModelFactory
lateinit var viewModel: ScheduledTootViewModel
private val viewModel: ScheduledTootViewModel by viewModels { viewModelFactory }
private val adapter = ScheduledTootAdapter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scheduled_toot)
setSupportActionBar(toolbar)
val binding = ActivityScheduledTootBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run {
title = getString(R.string.title_scheduled_toot)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses)
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
binding.swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses)
binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
scheduledTootList.setHasFixedSize(true)
scheduledTootList.layoutManager = LinearLayoutManager(this)
binding.scheduledTootList.setHasFixedSize(true)
binding.scheduledTootList.layoutManager = LinearLayoutManager(this)
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
scheduledTootList.addItemDecoration(divider)
scheduledTootList.adapter = adapter
viewModel = ViewModelProvider(this, viewModelFactory)[ScheduledTootViewModel::class.java]
binding.scheduledTootList.addItemDecoration(divider)
binding.scheduledTootList.adapter = adapter
viewModel.data.observe(this) {
adapter.submitList(it)
@ -72,31 +71,31 @@ class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injec
viewModel.networkState.observe(this) { (status) ->
when(status) {
Status.SUCCESS -> {
progressBar.hide()
swipeRefreshLayout.isRefreshing = false
binding.progressBar.hide()
binding.swipeRefreshLayout.isRefreshing = false
if(viewModel.data.value?.loadedCount == 0) {
errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_scheduled_status)
errorMessageView.show()
binding.errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_scheduled_status)
binding.errorMessageView.show()
} else {
errorMessageView.hide()
binding.errorMessageView.hide()
}
}
Status.RUNNING -> {
errorMessageView.hide()
binding.errorMessageView.hide()
if(viewModel.data.value?.loadedCount ?: 0 > 0) {
swipeRefreshLayout.isRefreshing = true
binding.swipeRefreshLayout.isRefreshing = true
} else {
progressBar.show()
binding.progressBar.show()
}
}
Status.FAILED -> {
if(viewModel.data.value?.loadedCount ?: 0 >= 0) {
progressBar.hide()
swipeRefreshLayout.isRefreshing = false
errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) {
binding.progressBar.hide()
binding.swipeRefreshLayout.isRefreshing = false
binding.errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) {
refreshStatuses()
}
errorMessageView.show()
binding.errorMessageView.show()
}
}
}

View file

@ -26,10 +26,11 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.keylesspalace.tusky.BottomSheetActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter
import com.keylesspalace.tusky.databinding.ActivitySearchBinding
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.util.viewBinding
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.activity_search.*
import javax.inject.Inject
class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
@ -41,10 +42,12 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
private val viewModel: SearchViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivitySearchBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
setSupportActionBar(toolbar)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
@ -55,9 +58,9 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
}
private fun setupPages() {
pages.adapter = SearchPagerAdapter(this)
binding.pages.adapter = SearchPagerAdapter(this)
TabLayoutMediator(tabs, pages) {
TabLayoutMediator(binding.tabs, binding.pages) {
tab, position ->
tab.text = getPageTitle(position)
}.attach()

View file

@ -0,0 +1,15 @@
package com.keylesspalace.tusky.util
import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
/**
* https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c
*/
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
crossinline bindingInflater: (LayoutInflater) -> T
) = lazy(LazyThreadSafetyMode.NONE) {
bindingInflater(layoutInflater)
}

View file

@ -6,7 +6,9 @@
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.AboutActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<FrameLayout
android:layout_width="match_parent"

View file

@ -71,7 +71,7 @@
app:layout_constraintStart_toEndOf="@id/accountMuteButton"
app:layout_constraintTop_toTopOf="parent"
tools:text="Follow Requested" />
<com.google.android.material.button.MaterialButton
android:id="@+id/accountSubscribeButton"
style="@style/TuskyButton.Outlined"
@ -248,20 +248,63 @@
app:layout_constraintTop_toBottomOf="@id/accountFieldList"
tools:visibility="visible" />
<ViewStub
<androidx.constraintlayout.widget.Group
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" />
android:visibility="gone"
app:constraint_referenced_ids="accountMovedText,accountMovedAvatar,accountMovedDisplayName,accountMovedUsername" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrierRemote"
<androidx.emoji.widget.EmojiTextView
android:id="@+id/accountMovedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="accountMovedView,accountMovedViewLayout" />
android:layout_marginTop="12dp"
android:drawablePadding="6dp"
android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/accountRemoveView"
tools:text="Account has moved" />
<ImageView
android:id="@+id/accountMovedAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/accountMovedText"
tools:src="@drawable/avatar_default" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/accountMovedDisplayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorPrimary"
android:textSize="?attr/status_text_large"
android:textStyle="normal|bold"
app:layout_constraintBottom_toTopOf="@id/accountMovedUsername"
app:layout_constraintStart_toEndOf="@id/accountMovedAvatar"
app:layout_constraintTop_toTopOf="@id/accountMovedAvatar"
tools:text="Display name" />
<TextView
android:id="@+id/accountMovedUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="?attr/status_text_medium"
app:layout_constraintBottom_toBottomOf="@id/accountMovedAvatar"
app:layout_constraintStart_toEndOf="@id/accountMovedAvatar"
app:layout_constraintTop_toBottomOf="@id/accountMovedDisplayName"
tools:text="\@username" />
<LinearLayout
android:id="@+id/accountStatuses"
@ -272,7 +315,7 @@
android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@id/accountFollowing"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrierRemote">
app:layout_constraintTop_toBottomOf="@id/accountMovedAvatar">
<TextView
android:id="@+id/accountStatusesTextView"
@ -303,7 +346,7 @@
android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@id/accountFollowers"
app:layout_constraintStart_toEndOf="@id/accountStatuses"
app:layout_constraintTop_toBottomOf="@id/barrierRemote">
app:layout_constraintTop_toBottomOf="@id/accountMovedAvatar">
<TextView
android:id="@+id/accountFollowingTextView"
@ -333,7 +376,7 @@
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/accountFollowing"
app:layout_constraintTop_toBottomOf="@id/barrierRemote">
app:layout_constraintTop_toBottomOf="@id/accountMovedAvatar">
<TextView
android:id="@+id/accountFollowersTextView"

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.AccountListActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"

View file

@ -5,7 +5,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<ProgressBar
android:id="@+id/progressBar"

View file

@ -6,7 +6,9 @@
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.EditProfileActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activityFilters"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.FiltersActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<ListView
android:id="@+id/filtersView"

View file

@ -5,9 +5,11 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.AboutActivity">
tools:context=".LicenseActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<ScrollView
android:layout_width="match_parent"

View file

@ -9,7 +9,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listsRecycler"
@ -18,7 +20,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar" />
app:layout_constraintTop_toBottomOf="@id/includedToolbar" />
<ProgressBar
android:id="@+id/progressBar"
@ -39,7 +41,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar"
app:layout_constraintTop_toBottomOf="@id/includedToolbar"
tools:visibility="visible"
app:layout_constrainedHeight="true" />

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.ModalTimelineActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/contentFrame"

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.components.preference.PreferencesActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".components.report.ReportActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/wizard"

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activityScheduledToot"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.AccountListActivity">
tools:context=".components.scheduled.ScheduledTootActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"

View file

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.StatusListActivity">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"

View file

@ -2,11 +2,12 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tabPreferenceContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_basic" />
<include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/currentTabsRecyclerView"

View file

@ -2,7 +2,6 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.ViewTagActivity">

View file

@ -1,61 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="12dp"
android:paddingBottom="12dp">
<androidx.emoji.widget.EmojiTextView
android:id="@+id/accountMovedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="6dp"
android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Account has moved" />
<ImageView
android:id="@+id/accountMovedAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/accountMovedText"
tools:src="@drawable/avatar_default" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/accountMovedDisplayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorPrimary"
android:textSize="?attr/status_text_large"
android:textStyle="normal|bold"
app:layout_constraintBottom_toTopOf="@id/accountMovedUsername"
app:layout_constraintStart_toEndOf="@id/accountMovedAvatar"
app:layout_constraintTop_toTopOf="@id/accountMovedAvatar"
tools:text="Display name" />
<TextView
android:id="@+id/accountMovedUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="?attr/status_text_medium"
app:layout_constraintBottom_toBottomOf="@id/accountMovedAvatar"
app:layout_constraintStart_toEndOf="@id/accountMovedAvatar"
app:layout_constraintTop_toBottomOf="@id/accountMovedDisplayName"
tools:text="\@username" />
</androidx.constraintlayout.widget.ConstraintLayout>