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.style.URLSpan
import android.text.util.Linkify import android.text.util.Linkify
import android.widget.TextView import android.widget.TextView
import com.keylesspalace.tusky.databinding.ActivityAboutBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.util.CustomURLSpan import com.keylesspalace.tusky.util.CustomURLSpan
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import kotlinx.android.synthetic.main.activity_about.*
import kotlinx.android.synthetic.main.toolbar_basic.*
class AboutActivity : BottomSheetActivity(), Injectable { class AboutActivity : BottomSheetActivity(), Injectable {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_about)
setSupportActionBar(toolbar) val binding = ActivityAboutBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run { supportActionBar?.run {
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
@ -29,26 +30,24 @@ class AboutActivity : BottomSheetActivity(), Injectable {
setTitle(R.string.about_title_activity) 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()) { if(BuildConfig.CUSTOM_INSTANCE.isBlank()) {
aboutPoweredByTusky.hide() binding.aboutPoweredByTusky.hide()
} }
aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_tusky_license) binding.aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_tusky_license)
aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site) binding.aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site)
aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site) binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site)
tuskyProfileButton.setOnClickListener { binding.tuskyProfileButton.setOnClickListener {
viewUrl(BuildConfig.SUPPORT_ACCOUNT_URL) viewUrl(BuildConfig.SUPPORT_ACCOUNT_URL)
} }
aboutLicensesButton.setOnClickListener { binding.aboutLicensesButton.setOnClickListener {
startActivityWithSlideInAnimation(Intent(this, LicenseActivity::class.java)) startActivityWithSlideInAnimation(Intent(this, LicenseActivity::class.java))
} }
} }
} }
private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) { private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
@ -73,5 +72,4 @@ private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
setText(builder) setText(builder)
linksClickable = true linksClickable = true
movementMethod = LinkMovementMethod.getInstance() 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.adapter.AccountFieldAdapter
import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.report.ReportActivity import com.keylesspalace.tusky.components.report.ReportActivity
import com.keylesspalace.tusky.databinding.ActivityAccountBinding
import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Relationship import com.keylesspalace.tusky.entity.Relationship
@ -63,8 +64,6 @@ import com.keylesspalace.tusky.view.showMuteAccountDialog
import com.keylesspalace.tusky.viewmodel.AccountViewModel import com.keylesspalace.tusky.viewmodel.AccountViewModel
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.activity_account.*
import kotlinx.android.synthetic.main.view_account_moved.*
import java.text.NumberFormat import java.text.NumberFormat
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -78,6 +77,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private val viewModel: AccountViewModel by viewModels { viewModelFactory } private val viewModel: AccountViewModel by viewModels { viewModelFactory }
private val binding: ActivityAccountBinding by viewBinding(ActivityAccountBinding::inflate)
private lateinit var accountFieldAdapter : AccountFieldAdapter private lateinit var accountFieldAdapter : AccountFieldAdapter
private var followState: FollowState = FollowState.NOT_FOLLOWING private var followState: FollowState = FollowState.NOT_FOLLOWING
@ -118,7 +119,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
loadResources() loadResources()
makeNotificationBarTransparent() makeNotificationBarTransparent()
setContentView(R.layout.activity_account) setContentView(binding.root)
// Obtain information to fill out the profile. // Obtain information to fill out the profile.
viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!) viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
@ -136,9 +137,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (viewModel.isSelf) { if (viewModel.isSelf) {
updateButtons() updateButtons()
saveNoteInfo.hide() binding.saveNoteInfo.hide()
} else { } else {
saveNoteInfo.visibility = View.INVISIBLE binding.saveNoteInfo.visibility = View.INVISIBLE
} }
} }
@ -158,16 +159,16 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
*/ */
private fun setupAccountViews() { private fun setupAccountViews() {
// Initialise the default UI states. // Initialise the default UI states.
accountFloatingActionButton.hide() binding.accountFloatingActionButton.hide()
accountFollowButton.hide() binding.accountFollowButton.hide()
accountMuteButton.hide() binding.accountMuteButton.hide()
accountFollowsYouTextView.hide() binding.accountFollowsYouTextView.hide()
// setup the RecyclerView for the account fields // setup the RecyclerView for the account fields
accountFieldAdapter = AccountFieldAdapter(this, animateEmojis) accountFieldAdapter = AccountFieldAdapter(this, animateEmojis)
accountFieldList.isNestedScrollingEnabled = false binding.accountFieldList.isNestedScrollingEnabled = false
accountFieldList.layoutManager = LinearLayoutManager(this) binding.accountFieldList.layoutManager = LinearLayoutManager(this)
accountFieldList.adapter = accountFieldAdapter binding.accountFieldList.adapter = accountFieldAdapter
val accountListClickListener = { v: View -> val accountListClickListener = { v: View ->
@ -179,15 +180,15 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val accountListIntent = AccountListActivity.newIntent(this, type, viewModel.accountId) val accountListIntent = AccountListActivity.newIntent(this, type, viewModel.accountId)
startActivityWithSlideInAnimation(accountListIntent) startActivityWithSlideInAnimation(accountListIntent)
} }
accountFollowers.setOnClickListener(accountListClickListener) binding.accountFollowers.setOnClickListener(accountListClickListener)
accountFollowing.setOnClickListener(accountListClickListener) binding.accountFollowing.setOnClickListener(accountListClickListener)
accountStatuses.setOnClickListener { binding.accountStatuses.setOnClickListener {
// Make nice ripple effect on tab // Make nice ripple effect on tab
accountTabLayout.getTabAt(0)!!.select() binding.accountTabLayout.getTabAt(0)!!.select()
val poorTabView = (accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0) val poorTabView = (binding.accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0)
poorTabView.isPressed = true 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 // 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) val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_PROFILE, false)
if (wellbeingEnabled) { if (wellbeingEnabled) {
accountStatuses.hide() binding.accountStatuses.hide()
accountFollowers.hide() binding.accountFollowers.hide()
accountFollowing.hide() binding.accountFollowing.hide()
} }
} }
/** /**
@ -209,19 +209,19 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
// Setup the tabs and timeline pager. // Setup the tabs and timeline pager.
adapter = AccountPagerAdapter(this, viewModel.accountId) adapter = AccountPagerAdapter(this, viewModel.accountId)
accountFragmentViewPager.adapter = adapter binding.accountFragmentViewPager.adapter = adapter
accountFragmentViewPager.offscreenPageLimit = 2 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)) 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] tab.text = pageTitles[position]
}.attach() }.attach()
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin) 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?) { override fun onTabReselected(tab: TabLayout.Tab?) {
tab?.position?.let { position -> tab?.position?.let { position ->
(adapter.getFragment(position) as? ReselectableFragment)?.onReselect() (adapter.getFragment(position) as? ReselectableFragment)?.onReselect()
@ -237,17 +237,17 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun setupToolbar() { private fun setupToolbar() {
// set toolbar top margin according to system window insets // set toolbar top margin according to system window insets
accountCoordinatorLayout.setOnApplyWindowInsetsListener { _, insets -> binding.accountCoordinatorLayout.setOnApplyWindowInsetsListener { _, insets ->
val top = insets.systemWindowInsetTop val top = insets.systemWindowInsetTop
val toolbarParams = accountToolbar.layoutParams as CollapsingToolbarLayout.LayoutParams val toolbarParams = binding.accountToolbar.layoutParams as CollapsingToolbarLayout.LayoutParams
toolbarParams.topMargin = top toolbarParams.topMargin = top
insets.consumeSystemWindowInsets() insets.consumeSystemWindowInsets()
} }
// Setup the toolbar. // Setup the toolbar.
setSupportActionBar(accountToolbar) setSupportActionBar(binding.accountToolbar)
supportActionBar?.run { supportActionBar?.run {
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
@ -258,9 +258,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation) val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation)
toolbarBackground.fillColor = ColorStateList.valueOf(Color.TRANSPARENT) 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 { val avatarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation).apply {
fillColor = ColorStateList.valueOf(toolbarColor) fillColor = ColorStateList.valueOf(toolbarColor)
@ -269,10 +269,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
.setAllCornerSizes(resources.getDimension(R.dimen.account_avatar_background_radius)) .setAllCornerSizes(resources.getDimension(R.dimen.account_avatar_background_radius))
.build() .build()
} }
accountAvatarImageView.background = avatarBackground binding.accountAvatarImageView.background = avatarBackground
// Add a listener to change the toolbar icon color when it enters/exits its collapsed state. // 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) { override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
@ -289,19 +289,19 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (hideFab && !viewModel.isSelf && !blocking) { if (hideFab && !viewModel.isSelf && !blocking) {
if (verticalOffset > oldOffset) { if (verticalOffset > oldOffset) {
accountFloatingActionButton.show() binding.accountFloatingActionButton.show()
} }
if (verticalOffset < oldOffset) { if (verticalOffset < oldOffset) {
accountFloatingActionButton.hide() binding.accountFloatingActionButton.hide()
} }
} }
val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize
accountAvatarImageView.scaleX = scaledAvatarSize binding.accountAvatarImageView.scaleX = scaledAvatarSize
accountAvatarImageView.scaleY = scaledAvatarSize binding.accountAvatarImageView.scaleY = scaledAvatarSize
accountAvatarImageView.visible(scaledAvatarSize > 0) binding.accountAvatarImageView.visible(scaledAvatarSize > 0)
val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost(1f) val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost(1f)
@ -311,7 +311,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
toolbarBackground.fillColor = ColorStateList.valueOf(evaluatedToolbarColor) 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) { when (it) {
is Success -> onAccountChanged(it.data) is Success -> onAccountChanged(it.data)
is Error -> { 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() } .setAction(R.string.action_retry) { viewModel.refresh() }
.show() .show()
} }
@ -344,7 +344,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
} }
if (it is Error) { 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() } .setAction(R.string.action_retry) { viewModel.refresh() }
.show() .show()
} }
@ -355,7 +355,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
accountFieldAdapter.notifyDataSetChanged() accountFieldAdapter.notifyDataSetChanged()
}) })
viewModel.noteSaved.observe(this) { 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 * Setup swipe to refresh layout
*/ */
private fun setupRefreshLayout() { private fun setupRefreshLayout() {
swipeToRefreshLayout.setOnRefreshListener { binding.swipeToRefreshLayout.setOnRefreshListener {
viewModel.refresh() viewModel.refresh()
adapter.refreshContent() adapter.refreshContent()
} }
viewModel.isRefreshing.observe(this, { isRefreshing -> 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?) { private fun onAccountChanged(account: Account?) {
loadedAccount = account ?: return loadedAccount = account ?: return
val usernameFormatted = getString(R.string.status_username_format, account.username) val usernameFormatted = getString(R.string.status_username_format, account.username)
accountUsernameTextView.text = usernameFormatted binding.accountUsernameTextView.text = usernameFormatted
accountDisplayNameTextView.text = account.name.emojify(account.emojis, accountDisplayNameTextView, animateEmojis) binding.accountDisplayNameTextView.text = account.name.emojify(account.emojis, binding.accountDisplayNameTextView, animateEmojis)
val emojifiedNote = account.note.emojify(account.emojis, accountNoteTextView, animateEmojis) val emojifiedNote = account.note.emojify(account.emojis, binding.accountNoteTextView, animateEmojis)
LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this) LinkHelper.setClickableText(binding.accountNoteTextView, emojifiedNote, null, this)
// accountFieldAdapter.fields = account.fields ?: emptyList() // accountFieldAdapter.fields = account.fields ?: emptyList()
accountFieldAdapter.emojis = account.emojis ?: emptyList() accountFieldAdapter.emojis = account.emojis ?: emptyList()
accountFieldAdapter.notifyDataSetChanged() accountFieldAdapter.notifyDataSetChanged()
accountLockedImageView.visible(account.locked) binding.accountLockedImageView.visible(account.locked)
accountBadgeTextView.visible(account.bot) binding.accountBadgeTextView.visible(account.bot)
updateAccountAvatar() updateAccountAvatar()
updateToolbar() updateToolbar()
@ -397,7 +397,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
updateAccountStats() updateAccountStats()
invalidateOptionsMenu() invalidateOptionsMenu()
accountMuteButton.setOnClickListener { binding.accountMuteButton.setOnClickListener {
viewModel.unmuteAccount() viewModel.unmuteAccount()
updateMuteButton() updateMuteButton()
} }
@ -411,7 +411,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
loadAvatar( loadAvatar(
account.avatar, account.avatar,
accountAvatarImageView, binding.accountAvatarImageView,
resources.getDimensionPixelSize(R.dimen.avatar_radius_94dp), resources.getDimensionPixelSize(R.dimen.avatar_radius_94dp),
animateAvatar animateAvatar
) )
@ -420,10 +420,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
.asBitmap() .asBitmap()
.load(account.header) .load(account.header)
.centerCrop() .centerCrop()
.into(accountHeaderImageView) .into(binding.accountHeaderImageView)
accountAvatarImageView.setOnClickListener { avatarView -> binding.accountAvatarImageView.setOnClickListener { avatarView ->
val intent = ViewMediaActivity.newSingleImageIntent(avatarView.context, account.avatar) val intent = ViewMediaActivity.newSingleImageIntent(avatarView.context, account.avatar)
avatarView.transitionName = account.avatar avatarView.transitionName = account.avatar
@ -440,7 +440,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateToolbar() { private fun updateToolbar() {
loadedAccount?.let { account -> loadedAccount?.let { account ->
val emojifiedName = account.name.emojify(account.emojis, accountToolbar, animateEmojis) val emojifiedName = account.name.emojify(account.emojis, binding.accountToolbar, animateEmojis)
try { try {
supportActionBar?.title = EmojiCompat.get().process(emojifiedName) supportActionBar?.title = EmojiCompat.get().process(emojifiedName)
@ -457,28 +457,27 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateMovedAccount() { private fun updateMovedAccount() {
loadedAccount?.moved?.let { movedAccount -> loadedAccount?.moved?.let { movedAccount ->
accountMovedView?.show() binding.accountMovedView.show()
// necessary because accountMovedView is now replaced in layout hierachy binding.accountMovedView.setOnClickListener {
findViewById<View>(R.id.accountMovedViewLayout).setOnClickListener {
onViewAccount(movedAccount.id) onViewAccount(movedAccount.id)
} }
accountMovedDisplayName.text = movedAccount.name binding.accountMovedDisplayName.text = movedAccount.name
accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username) binding.accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)
val avatarRadius = resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) 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 // this is necessary because API 19 can't handle vector compound drawables
val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate() val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary) val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
movedIcon?.colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN) 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() { private fun updateRemoteAccount() {
loadedAccount?.let { account -> loadedAccount?.let { account ->
if (account.isRemote()) { if (account.isRemote()) {
accountRemoveView.show() binding.accountRemoveView.show()
accountRemoveView.setOnClickListener { binding.accountRemoveView.setOnClickListener {
LinkHelper.openLink(account.url, this) LinkHelper.openLink(account.url, this)
} }
} }
@ -503,13 +502,13 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateAccountStats() { private fun updateAccountStats() {
loadedAccount?.let { account -> loadedAccount?.let { account ->
val numberFormat = NumberFormat.getNumberInstance() val numberFormat = NumberFormat.getNumberInstance()
accountFollowersTextView.text = numberFormat.format(account.followersCount) binding.accountFollowersTextView.text = numberFormat.format(account.followersCount)
accountFollowingTextView.text = numberFormat.format(account.followingCount) binding.accountFollowingTextView.text = numberFormat.format(account.followingCount)
accountStatusesTextView.text = numberFormat.format(account.statusesCount) binding.accountStatusesTextView.text = numberFormat.format(account.statusesCount)
accountFloatingActionButton.setOnClickListener { mention() } binding.accountFloatingActionButton.setOnClickListener { mention() }
accountFollowButton.setOnClickListener { binding.accountFollowButton.setOnClickListener {
if (viewModel.isSelf) { if (viewModel.isSelf) {
val intent = Intent(this@AccountActivity, EditProfileActivity::class.java) val intent = Intent(this@AccountActivity, EditProfileActivity::class.java)
startActivity(intent) startActivity(intent)
@ -552,14 +551,14 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
val preferences = PreferenceManager.getDefaultSharedPreferences(this) val preferences = PreferenceManager.getDefaultSharedPreferences(this)
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_PROFILE, false) 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 // 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 // it's also now supported in Mastodon 3.3.0rc but called notifying and use different API call
if(!viewModel.isSelf && followState == FollowState.FOLLOWING if(!viewModel.isSelf && followState == FollowState.FOLLOWING
&& (relation.subscribing != null || relation.notifying != null)) { && (relation.subscribing != null || relation.notifying != null)) {
accountSubscribeButton.show() binding.accountSubscribeButton.show()
accountSubscribeButton.setOnClickListener { binding.accountSubscribeButton.setOnClickListener {
viewModel.changeSubscribingState() viewModel.changeSubscribingState()
} }
if(relation.notifying != null) 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 // 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) binding.accountNoteTextInputLayout.visible(relation.note != null)
accountNoteTextInputLayout.editText?.setText(relation.note) binding.accountNoteTextInputLayout.editText?.setText(relation.note)
accountNoteTextInputLayout.editText?.addTextChangedListener(noteWatcher) binding.accountNoteTextInputLayout.editText?.addTextChangedListener(noteWatcher)
updateButtons() updateButtons()
} }
@ -587,22 +586,22 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateFollowButton() { private fun updateFollowButton() {
if (viewModel.isSelf) { if (viewModel.isSelf) {
accountFollowButton.setText(R.string.action_edit_own_profile) binding.accountFollowButton.setText(R.string.action_edit_own_profile)
return return
} }
if (blocking) { if (blocking) {
accountFollowButton.setText(R.string.action_unblock) binding.accountFollowButton.setText(R.string.action_unblock)
return return
} }
when (followState) { when (followState) {
FollowState.NOT_FOLLOWING -> { FollowState.NOT_FOLLOWING -> {
accountFollowButton.setText(R.string.action_follow) binding.accountFollowButton.setText(R.string.action_follow)
} }
FollowState.REQUESTED -> { FollowState.REQUESTED -> {
accountFollowButton.setText(R.string.state_follow_requested) binding.accountFollowButton.setText(R.string.state_follow_requested)
} }
FollowState.FOLLOWING -> { FollowState.FOLLOWING -> {
accountFollowButton.setText(R.string.action_unfollow) binding.accountFollowButton.setText(R.string.action_unfollow)
} }
} }
updateSubscribeButton() updateSubscribeButton()
@ -610,23 +609,23 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
private fun updateMuteButton() { private fun updateMuteButton() {
if (muting) { if (muting) {
accountMuteButton.setIconResource(R.drawable.ic_unmute_24dp) binding.accountMuteButton.setIconResource(R.drawable.ic_unmute_24dp)
} else { } else {
accountMuteButton.hide() binding.accountMuteButton.hide()
} }
} }
private fun updateSubscribeButton() { private fun updateSubscribeButton() {
if(followState != FollowState.FOLLOWING) { if(followState != FollowState.FOLLOWING) {
accountSubscribeButton.hide() binding.accountSubscribeButton.hide()
} }
if(subscribing) { if(subscribing) {
accountSubscribeButton.setIconResource(R.drawable.ic_notifications_active_24dp) binding.accountSubscribeButton.setIconResource(R.drawable.ic_notifications_active_24dp)
accountSubscribeButton.contentDescription = getString(R.string.action_unsubscribe_account) binding.accountSubscribeButton.contentDescription = getString(R.string.action_unsubscribe_account)
} else { } else {
accountSubscribeButton.setIconResource(R.drawable.ic_notifications_24dp) binding.accountSubscribeButton.setIconResource(R.drawable.ic_notifications_24dp)
accountSubscribeButton.contentDescription = getString(R.string.action_subscribe_account) binding.accountSubscribeButton.contentDescription = getString(R.string.action_subscribe_account)
} }
} }
@ -635,27 +634,27 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
if (loadedAccount?.moved == null) { if (loadedAccount?.moved == null) {
accountFollowButton.show() binding.accountFollowButton.show()
updateFollowButton() updateFollowButton()
if (blocking || viewModel.isSelf) { if (blocking || viewModel.isSelf) {
accountFloatingActionButton.hide() binding.accountFloatingActionButton.hide()
accountMuteButton.hide() binding.accountMuteButton.hide()
accountSubscribeButton.hide() binding.accountSubscribeButton.hide()
} else { } else {
accountFloatingActionButton.show() binding.accountFloatingActionButton.show()
if (muting) if (muting)
accountMuteButton.show() binding.accountMuteButton.show()
else else
accountMuteButton.hide() binding.accountMuteButton.hide()
updateMuteButton() updateMuteButton()
} }
} else { } else {
accountFloatingActionButton.hide() binding.accountFloatingActionButton.hide()
accountFollowButton.hide() binding.accountFollowButton.hide()
accountMuteButton.hide() binding.accountMuteButton.hide()
accountSubscribeButton.hide() binding.accountSubscribeButton.hide()
} }
} }
@ -833,7 +832,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
override fun getActionButton(): FloatingActionButton? { override fun getActionButton(): FloatingActionButton? {
return if (!viewModel.isSelf && !blocking) { return if (!viewModel.isSelf && !blocking) {
accountFloatingActionButton binding.accountFloatingActionButton
} else null } else null
} }

View File

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

View File

@ -7,13 +7,13 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent 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.entity.Filter
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import kotlinx.android.synthetic.main.activity_filters.* import com.keylesspalace.tusky.util.viewBinding
import kotlinx.android.synthetic.main.dialog_filter.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
@ -28,13 +28,28 @@ class FiltersActivity: BaseActivity() {
@Inject @Inject
lateinit var eventHub: EventHub lateinit var eventHub: EventHub
private val binding by viewBinding(ActivityFiltersBinding::inflate)
private lateinit var context : String private lateinit var context : String
private lateinit var filters: MutableList<Filter> private lateinit var filters: MutableList<Filter>
private lateinit var dialog: AlertDialog
companion object { override fun onCreate(savedInstanceState: Bundle?) {
const val FILTERS_CONTEXT = "filters_context" super.onCreate(savedInstanceState)
const val FILTERS_TITLE = "filters_title"
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) { private fun updateFilter(filter: Filter, itemIndex: Int) {
@ -101,52 +116,51 @@ class FiltersActivity: BaseActivity() {
} }
private fun showAddFilterDialog() { 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) .setTitle(R.string.filter_addition_dialog_title)
.setView(R.layout.dialog_filter) .setView(binding.root)
.setPositiveButton(android.R.string.ok){ _, _ -> .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) .setNeutralButton(android.R.string.cancel, null)
.create() .show()
dialog.show()
dialog.phraseWholeWord.isChecked = true
} }
private fun setupEditDialogForItem(itemIndex: Int) { 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) .setTitle(R.string.filter_edit_dialog_title)
.setView(R.layout.dialog_filter) .setView(binding.root)
.setPositiveButton(R.string.filter_dialog_update_button) { _, _ -> .setPositiveButton(R.string.filter_dialog_update_button) { _, _ ->
val oldFilter = filters[itemIndex] val oldFilter = filters[itemIndex]
val newFilter = Filter(oldFilter.id, dialog.phraseEditText.text.toString(), oldFilter.context, val newFilter = Filter(oldFilter.id, binding.phraseEditText.text.toString(), oldFilter.context,
oldFilter.expiresAt, oldFilter.irreversible, dialog.phraseWholeWord.isChecked) oldFilter.expiresAt, oldFilter.irreversible, binding.phraseWholeWord.isChecked)
updateFilter(newFilter, itemIndex) updateFilter(newFilter, itemIndex)
} }
.setNegativeButton(R.string.filter_dialog_remove_button) { _, _ -> .setNegativeButton(R.string.filter_dialog_remove_button) { _, _ ->
deleteFilter(itemIndex) deleteFilter(itemIndex)
} }
.setNeutralButton(android.R.string.cancel, null) .setNeutralButton(android.R.string.cancel, null)
.create() .show()
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
} }
private fun refreshFilterDisplay() { private fun refreshFilterDisplay() {
filtersView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, filters.map { filter -> filter.phrase }) binding.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.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> setupEditDialogForItem(position) }
} }
private fun loadFilters() { private fun loadFilters() {
filterMessageView.hide() binding.filterMessageView.hide()
filtersView.hide() binding.filtersView.hide()
addFilterButton.hide() binding.addFilterButton.hide()
filterProgressBar.show() binding.filterProgressBar.show()
api.getFilters().enqueue(object : Callback<List<Filter>> { api.getFilters().enqueue(object : Callback<List<Filter>> {
override fun onResponse(call: Call<List<Filter>>, response: Response<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() filters = filterResponse.filter { filter -> filter.context.contains(context) }.toMutableList()
refreshFilterDisplay() refreshFilterDisplay()
filtersView.show() binding.filtersView.show()
addFilterButton.show() binding.addFilterButton.show()
filterProgressBar.hide() binding.filterProgressBar.hide()
} else { } else {
filterProgressBar.hide() binding.filterProgressBar.hide()
filterMessageView.show() binding.filterMessageView.show()
filterMessageView.setup(R.drawable.elephant_error, binding.filterMessageView.setup(R.drawable.elephant_error,
R.string.error_generic) { loadFilters() } R.string.error_generic) { loadFilters() }
} }
} }
override fun onFailure(call: Call<List<Filter>>, t: Throwable) { override fun onFailure(call: Call<List<Filter>>, t: Throwable) {
filterProgressBar.hide() binding.filterProgressBar.hide()
filterMessageView.show() binding.filterMessageView.show()
if (t is IOException) { if (t is IOException) {
filterMessageView.setup(R.drawable.elephant_offline, binding.filterMessageView.setup(R.drawable.elephant_offline,
R.string.error_network) { loadFilters() } R.string.error_network) { loadFilters() }
} else { } else {
filterMessageView.setup(R.drawable.elephant_error, binding.filterMessageView.setup(R.drawable.elephant_error,
R.string.error_generic) { loadFilters() } R.string.error_generic) { loadFilters() }
} }
} }
}) })
} }
override fun onCreate(savedInstanceState: Bundle?) { companion object {
super.onCreate(savedInstanceState) const val FILTERS_CONTEXT = "filters_context"
const val FILTERS_TITLE = "filters_title"
setContentView(R.layout.activity_filters)
setupToolbarBackArrow()
addFilterButton.setOnClickListener {
showAddFilterDialog()
}
title = intent?.getStringExtra(FILTERS_TITLE)
context = intent?.getStringExtra(FILTERS_CONTEXT)!!
loadFilters()
} }
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 androidx.annotation.RawRes
import android.util.Log import android.util.Log
import android.widget.TextView import android.widget.TextView
import com.keylesspalace.tusky.databinding.ActivityLicenseBinding
import com.keylesspalace.tusky.util.IOUtils 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.BufferedReader
import java.io.IOException import java.io.IOException
import java.io.InputStreamReader import java.io.InputStreamReader
class LicenseActivity : BaseActivity() { class LicenseActivity : BaseActivity() {
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_license) val binding = ActivityLicenseBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.run { supportActionBar?.run {
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
@ -43,7 +40,7 @@ class LicenseActivity : BaseActivity() {
setTitle(R.string.title_licenses) 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) IOUtils.closeQuietly(br)
textView.text = sb.toString() textView.text = sb.toString()
} }
} }

View File

@ -24,12 +24,14 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.activity.viewModels
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.* import androidx.recyclerview.widget.*
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import at.connyduck.sparkbutton.helpers.Utils import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.databinding.ActivityListsBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.entity.MastoList
@ -47,8 +49,6 @@ import com.uber.autodispose.autoDispose
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.activity_lists.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -57,47 +57,42 @@ import javax.inject.Inject
class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector { class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
companion object {
@JvmStatic
fun newIntent(context: Context): Intent {
return Intent(context, ListsActivity::class.java)
}
}
@Inject @Inject
lateinit var viewModelFactory: ViewModelFactory lateinit var viewModelFactory: ViewModelFactory
@Inject @Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> 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() private val adapter = ListsAdapter()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_lists)
setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply { supportActionBar?.apply {
title = getString(R.string.title_lists) title = getString(R.string.title_lists)
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
listsRecycler.adapter = adapter binding.listsRecycler.adapter = adapter
listsRecycler.layoutManager = LinearLayoutManager(this) binding.listsRecycler.layoutManager = LinearLayoutManager(this)
listsRecycler.addItemDecoration( binding.listsRecycler.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
viewModel = viewModelFactory.create(ListsViewModel::class.java)
viewModel.state viewModel.state
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.autoDispose(from(this)) .autoDispose(from(this))
.subscribe(this::update) .subscribe(this::update)
viewModel.retryLoading() viewModel.retryLoading()
addListButton.setOnClickListener { binding.addListButton.setOnClickListener {
showlistNameDialog(null) showlistNameDialog(null)
} }
@ -153,37 +148,36 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
private fun update(state: ListsViewModel.State) { private fun update(state: ListsViewModel.State) {
adapter.submitList(state.lists) adapter.submitList(state.lists)
progressBar.visible(state.loadingState == LOADING) binding.progressBar.visible(state.loadingState == LOADING)
when (state.loadingState) { when (state.loadingState) {
INITIAL, LOADING -> messageView.hide() INITIAL, LOADING -> binding.messageView.hide()
ERROR_NETWORK -> { ERROR_NETWORK -> {
messageView.show() binding.messageView.show()
messageView.setup(R.drawable.elephant_offline, R.string.error_network) { binding.messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
viewModel.retryLoading() viewModel.retryLoading()
} }
} }
ERROR_OTHER -> { ERROR_OTHER -> {
messageView.show() binding.messageView.show()
messageView.setup(R.drawable.elephant_error, R.string.error_generic) { binding.messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
viewModel.retryLoading() viewModel.retryLoading()
} }
} }
LOADED -> LOADED ->
if (state.lists.isEmpty()) { if (state.lists.isEmpty()) {
messageView.show() binding.messageView.show()
messageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, binding.messageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty,
null) null)
} else { } else {
messageView.hide() binding.messageView.hide()
} }
} }
} }
private fun showMessage(@StringRes messageId: Int) { private fun showMessage(@StringRes messageId: Int) {
Snackbar.make( Snackbar.make(
listsRecycler, messageId, Snackbar.LENGTH_SHORT binding.listsRecycler, messageId, Snackbar.LENGTH_SHORT
).show() ).show()
} }
private fun onListSelected(listId: String) { private fun onListSelected(listId: String) {
@ -215,8 +209,6 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
} }
} }
override fun androidInjector() = dispatchingAndroidInjector
private object ListsDiffer : DiffUtil.ItemCallback<MastoList>() { private object ListsDiffer : DiffUtil.ItemCallback<MastoList>() {
override fun areItemsTheSame(oldItem: MastoList, newItem: MastoList): Boolean { override fun areItemsTheSame(oldItem: MastoList, newItem: MastoList): Boolean {
return oldItem.id == newItem.id return oldItem.id == newItem.id
@ -273,4 +265,10 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
viewModel.renameList(listId, name.toString()) 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.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.keylesspalace.tusky.databinding.ActivityLoginBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.AccessToken import com.keylesspalace.tusky.entity.AccessToken
import com.keylesspalace.tusky.entity.AppCredentials import com.keylesspalace.tusky.entity.AppCredentials
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.*
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 okhttp3.HttpUrl import okhttp3.HttpUrl
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
@ -49,6 +46,8 @@ class LoginActivity : BaseActivity(), Injectable {
@Inject @Inject
lateinit var mastodonApi: MastodonApi lateinit var mastodonApi: MastodonApi
private val binding by viewBinding(ActivityLoginBinding::inflate)
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
private val oauthRedirectUri: String private val oauthRedirectUri: String
@ -61,26 +60,26 @@ class LoginActivity : BaseActivity(), Injectable {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login) setContentView(binding.root)
if(savedInstanceState == null && BuildConfig.CUSTOM_INSTANCE.isNotBlank() && !isAdditionalLogin()) { if(savedInstanceState == null && BuildConfig.CUSTOM_INSTANCE.isNotBlank() && !isAdditionalLogin()) {
domainEditText.setText(BuildConfig.CUSTOM_INSTANCE) binding.domainEditText.setText(BuildConfig.CUSTOM_INSTANCE)
domainEditText.setSelection(BuildConfig.CUSTOM_INSTANCE.length) binding.domainEditText.setSelection(BuildConfig.CUSTOM_INSTANCE.length)
} }
if(BuildConfig.CUSTOM_LOGO_URL.isNotBlank()) { if(BuildConfig.CUSTOM_LOGO_URL.isNotBlank()) {
Glide.with(loginLogo) Glide.with(binding.loginLogo)
.load(BuildConfig.CUSTOM_LOGO_URL) .load(BuildConfig.CUSTOM_LOGO_URL)
.placeholder(null) .placeholder(null)
.into(loginLogo) .into(binding.loginLogo)
} }
preferences = getSharedPreferences( preferences = getSharedPreferences(
getString(R.string.preferences_file_key), Context.MODE_PRIVATE) 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) val dialog = AlertDialog.Builder(this)
.setMessage(R.string.dialog_whats_an_instance) .setMessage(R.string.dialog_whats_an_instance)
.setPositiveButton(R.string.action_close, null) .setPositiveButton(R.string.action_close, null)
@ -90,11 +89,11 @@ class LoginActivity : BaseActivity(), Injectable {
} }
if (isAdditionalLogin()) { if (isAdditionalLogin()) {
setSupportActionBar(toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowTitleEnabled(false) supportActionBar?.setDisplayShowTitleEnabled(false)
} else { } else {
toolbar.visibility = View.GONE binding.toolbar.visibility = View.GONE
} }
} }
@ -117,15 +116,15 @@ class LoginActivity : BaseActivity(), Injectable {
*/ */
private fun onButtonClick() { private fun onButtonClick() {
loginButton.isEnabled = false binding.loginButton.isEnabled = false
val domain = canonicalizeDomain(domainEditText.text.toString()) val domain = canonicalizeDomain(binding.domainEditText.text.toString())
try { try {
HttpUrl.Builder().host(domain).scheme("https").build() HttpUrl.Builder().host(domain).scheme("https").build()
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
setLoading(false) setLoading(false)
domainTextInputLayout.error = getString(R.string.error_invalid_domain) binding.domainTextInputLayout.error = getString(R.string.error_invalid_domain)
return return
} }
@ -138,8 +137,8 @@ class LoginActivity : BaseActivity(), Injectable {
override fun onResponse(call: Call<AppCredentials>, override fun onResponse(call: Call<AppCredentials>,
response: Response<AppCredentials>) { response: Response<AppCredentials>) {
if (!response.isSuccessful) { if (!response.isSuccessful) {
loginButton.isEnabled = true binding.loginButton.isEnabled = true
domainTextInputLayout.error = getString(R.string.error_failed_app_registration) binding.domainTextInputLayout.error = getString(R.string.error_failed_app_registration)
setLoading(false) setLoading(false)
Log.e(TAG, "App authentication failed. " + response.message()) Log.e(TAG, "App authentication failed. " + response.message())
return return
@ -158,8 +157,8 @@ class LoginActivity : BaseActivity(), Injectable {
} }
override fun onFailure(call: Call<AppCredentials>, t: Throwable) { override fun onFailure(call: Call<AppCredentials>, t: Throwable) {
loginButton.isEnabled = true binding.loginButton.isEnabled = true
domainTextInputLayout.error = getString(R.string.error_failed_app_registration) binding.domainTextInputLayout.error = getString(R.string.error_failed_app_registration)
setLoading(false) setLoading(false)
Log.e(TAG, Log.getStackTraceString(t)) Log.e(TAG, Log.getStackTraceString(t))
} }
@ -190,7 +189,7 @@ class LoginActivity : BaseActivity(), Injectable {
if (viewIntent.resolveActivity(packageManager) != null) { if (viewIntent.resolveActivity(packageManager) != null) {
startActivity(viewIntent) startActivity(viewIntent)
} else { } else {
domainEditText.error = getString(R.string.error_no_web_browser_found) binding.domainEditText.error = getString(R.string.error_no_web_browser_found)
setLoading(false) setLoading(false)
} }
} }
@ -224,7 +223,7 @@ class LoginActivity : BaseActivity(), Injectable {
onLoginSuccess(response.body()!!.accessToken, domain) onLoginSuccess(response.body()!!.accessToken, domain)
} else { } else {
setLoading(false) 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", Log.e(TAG, String.format("%s %s",
getString(R.string.error_retrieving_oauth_token), getString(R.string.error_retrieving_oauth_token),
response.message())) response.message()))
@ -233,7 +232,7 @@ class LoginActivity : BaseActivity(), Injectable {
override fun onFailure(call: Call<AccessToken>, t: Throwable) { override fun onFailure(call: Call<AccessToken>, t: Throwable) {
setLoading(false) 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", Log.e(TAG, String.format("%s %s",
getString(R.string.error_retrieving_oauth_token), getString(R.string.error_retrieving_oauth_token),
t.message)) t.message))
@ -246,14 +245,14 @@ class LoginActivity : BaseActivity(), Injectable {
/* Authorization failed. Put the error response where the user can read it and they /* Authorization failed. Put the error response where the user can read it and they
* can try again. */ * can try again. */
setLoading(false) 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", Log.e(TAG, String.format("%s %s",
getString(R.string.error_authorization_denied), getString(R.string.error_authorization_denied),
error)) error))
} else { } else {
// This case means a junk response was received somehow. // This case means a junk response was received somehow.
setLoading(false) setLoading(false)
domainTextInputLayout.error = getString(R.string.error_authorization_unknown) binding.domainTextInputLayout.error = getString(R.string.error_authorization_unknown)
} }
} else { } else {
// first show or user cancelled login // first show or user cancelled login
@ -263,12 +262,12 @@ class LoginActivity : BaseActivity(), Injectable {
private fun setLoading(loadingState: Boolean) { private fun setLoading(loadingState: Boolean) {
if (loadingState) { if (loadingState) {
loginLoadingLayout.visibility = View.VISIBLE binding.loginLoadingLayout.visibility = View.VISIBLE
loginInputLayout.visibility = View.GONE binding.loginInputLayout.visibility = View.GONE
} else { } else {
loginLoadingLayout.visibility = View.GONE binding.loginLoadingLayout.visibility = View.GONE
loginInputLayout.visibility = View.VISIBLE binding.loginInputLayout.visibility = View.VISIBLE
loginButton.isEnabled = true 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.preference.PreferencesActivity
import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity
import com.keylesspalace.tusky.components.search.SearchActivity import com.keylesspalace.tusky.components.search.SearchActivity
import com.keylesspalace.tusky.databinding.ActivityMainBinding
import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Account
@ -86,7 +87,6 @@ import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject import javax.inject.Inject
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector { class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
@ -108,6 +108,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
@Inject @Inject
lateinit var draftHelper: DraftHelper lateinit var draftHelper: DraftHelper
private val binding by viewBinding(ActivityMainBinding::inflate)
private lateinit var header: AccountHeaderView private lateinit var header: AccountHeaderView
private var notificationTabPosition = 0 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 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) glide = Glide.with(this)
composeButton.setOnClickListener { binding.composeButton.setOnClickListener {
val composeIntent = Intent(applicationContext, ComposeActivity::class.java) val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
startActivity(composeIntent) startActivity(composeIntent)
} }
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false) val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
mainToolbar.visible(!hideTopToolbar) binding.mainToolbar.visible(!hideTopToolbar)
loadDrawerAvatar(activeAccount.profilePictureUrl, true) 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) setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply { icon = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_search).apply {
sizeDp = 20 sizeDp = 20
@ -249,11 +251,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onBackPressed() { override fun onBackPressed() {
when { when {
mainDrawerLayout.isOpen -> { binding.mainDrawerLayout.isOpen -> {
mainDrawerLayout.close() binding.mainDrawerLayout.close()
} }
viewPager.currentItem != 0 -> { binding.viewPager.currentItem != 0 -> {
viewPager.currentItem = 0 binding.viewPager.currentItem = 0
} }
else -> { else -> {
super.onBackPressed() super.onBackPressed()
@ -264,10 +266,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_MENU -> {
if (mainDrawerLayout.isOpen) { if (binding.mainDrawerLayout.isOpen) {
mainDrawerLayout.close() binding.mainDrawerLayout.close()
} else { } else {
mainDrawerLayout.open() binding.mainDrawerLayout.open()
} }
return true return true
} }
@ -319,8 +321,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
private fun setupDrawer(savedInstanceState: Bundle?, addSearchButton: Boolean) { private fun setupDrawer(savedInstanceState: Bundle?, addSearchButton: Boolean) {
mainToolbar.setNavigationOnClickListener { binding.mainToolbar.setNavigationOnClickListener {
mainDrawerLayout.open() binding.mainDrawerLayout.open()
} }
header = AccountHeaderView(this).apply { header = AccountHeaderView(this).apply {
@ -333,7 +335,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
descriptionRes = R.string.add_account_description descriptionRes = R.string.add_account_description
iconicsIcon = GoogleMaterial.Icon.gmd_add iconicsIcon = GoogleMaterial.Icon.gmd_add
}, 0) }, 0)
attachToSliderView(mainDrawer) attachToSliderView(binding.mainDrawer)
dividerBelowHeader = false dividerBelowHeader = false
closeDrawerOnProfileListClick = true closeDrawerOnProfileListClick = true
} }
@ -369,7 +371,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
} }
}) })
mainDrawer.apply { binding.mainDrawer.apply {
tintStatusBar = true tintStatusBar = true
addItems( addItems(
primaryDrawerItem { primaryDrawerItem {
@ -464,7 +466,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
) )
if (addSearchButton) { if (addSearchButton) {
mainDrawer.addItemsAtPosition(4, binding.mainDrawer.addItemsAtPosition(4,
primaryDrawerItem { primaryDrawerItem {
nameRes = R.string.action_search nameRes = R.string.action_search
iconicsIcon = GoogleMaterial.Icon.gmd_search iconicsIcon = GoogleMaterial.Icon.gmd_search
@ -478,7 +480,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
} }
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
mainDrawer.addItems( binding.mainDrawer.addItems(
secondaryDrawerItem { secondaryDrawerItem {
nameText = "debug" nameText = "debug"
isEnabled = false isEnabled = false
@ -490,7 +492,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(mainDrawer.saveInstanceState(outState)) super.onSaveInstanceState(binding.mainDrawer.saveInstanceState(outState))
} }
private fun setupTabs(selectNotificationTab: Boolean) { private fun setupTabs(selectNotificationTab: Boolean) {
@ -498,21 +500,21 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
val activeTabLayout = if (preferences.getString("mainNavPosition", "top") == "bottom") { val activeTabLayout = if (preferences.getString("mainNavPosition", "top") == "bottom") {
val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize) val actionBarSize = ThemeUtils.getDimension(this, R.attr.actionBarSize)
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin) val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
(composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin (binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin
tabLayout.hide() binding.tabLayout.hide()
bottomTabLayout binding.bottomTabLayout
} else { } else {
bottomNav.hide() binding.bottomNav.hide()
(viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0 (binding.viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
(composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager (binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager
tabLayout binding.tabLayout
} }
val tabs = accountManager.activeAccount!!.tabPreferences val tabs = accountManager.activeAccount!!.tabPreferences
val adapter = MainPagerAdapter(tabs, this) val adapter = MainPagerAdapter(tabs, this)
viewPager.adapter = adapter binding.viewPager.adapter = adapter
TabLayoutMediator(activeTabLayout, viewPager) { _: TabLayout.Tab?, _: Int -> }.attach() TabLayoutMediator(activeTabLayout, binding.viewPager) { _: TabLayout.Tab?, _: Int -> }.attach()
activeTabLayout.removeAllTabs() activeTabLayout.removeAllTabs()
for (i in tabs.indices) { for (i in tabs.indices) {
val tab = activeTabLayout.newTab() val tab = activeTabLayout.newTab()
@ -533,10 +535,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
} }
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin) val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
viewPager.setPageTransformer(MarginPageTransformer(pageMargin)) binding.viewPager.setPageTransformer(MarginPageTransformer(pageMargin))
val enableSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true) val enableSwipeForTabs = preferences.getBoolean("enableSwipeForTabs", true)
viewPager.isUserInputEnabled = enableSwipeForTabs binding.viewPager.isUserInputEnabled = enableSwipeForTabs
onTabSelectedListener?.let { onTabSelectedListener?.let {
activeTabLayout.removeOnTabSelectedListener(it) activeTabLayout.removeOnTabSelectedListener(it)
@ -548,7 +550,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
NotificationHelper.clearNotificationsForActiveAccount(this@MainActivity, accountManager) 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) {} override fun onTabUnselected(tab: TabLayout.Tab) {}
@ -564,8 +566,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
} }
val activeTabPosition = if (selectNotificationTab) notificationTabPosition else 0 val activeTabPosition = if (selectNotificationTab) notificationTabPosition else 0
mainToolbar.title = tabs[activeTabPosition].title(this@MainActivity) binding.mainToolbar.title = tabs[activeTabPosition].title(this@MainActivity)
mainToolbar.setOnClickListener { binding.mainToolbar.setOnClickListener {
(adapter.getFragment(activeTabLayout.selectedTabPosition) as? ReselectableFragment)?.onReselect() (adapter.getFragment(activeTabLayout.selectedTabPosition) as? ReselectableFragment)?.onReselect()
} }
@ -659,7 +661,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this) NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this)
// Show follow requests in the menu, if this is a locked account. // 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 { val followRequestsItem = primaryDrawerItem {
identifier = DRAWER_ITEM_FOLLOW_REQUESTS identifier = DRAWER_ITEM_FOLLOW_REQUESTS
nameRes = R.string.action_view_follow_requests nameRes = R.string.action_view_follow_requests
@ -670,9 +672,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
startActivityWithSlideInAnimation(intent) startActivityWithSlideInAnimation(intent)
} }
} }
mainDrawer.addItemAtPosition(4, followRequestsItem) binding.mainDrawer.addItemAtPosition(4, followRequestsItem)
} else if (!me.locked) { } else if (!me.locked) {
mainDrawer.removeItems(DRAWER_ITEM_FOLLOW_REQUESTS) binding.mainDrawer.removeItems(DRAWER_ITEM_FOLLOW_REQUESTS)
} }
updateProfiles() updateProfiles()
updateShortcut(this, accountManager.activeAccount!!) updateShortcut(this, accountManager.activeAccount!!)
@ -695,16 +697,16 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
override fun onLoadStarted(placeholder: Drawable?) { override fun onLoadStarted(placeholder: Drawable?) {
if (placeholder != null) { 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>?) { 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?) { override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) { 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() { 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() { 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 override fun androidInjector() = androidInjector

View File

@ -4,43 +4,28 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.keylesspalace.tusky.databinding.ActivityModalTimelineBinding
import com.keylesspalace.tusky.fragment.TimelineFragment import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.ActionButtonActivity
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject import javax.inject.Inject
class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector { 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 @Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_modal_timeline) val binding = ActivityModalTimelineBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.includedToolbar.toolbar)
val bar = supportActionBar supportActionBar?.apply {
if (bar != null) { title = getString(R.string.title_list_timeline)
bar.title = getString(R.string.title_list_timeline) setDisplayHomeAsUpEnabled(true)
bar.setDisplayHomeAsUpEnabled(true) setDisplayShowHomeEnabled(true)
bar.setDisplayShowHomeEnabled(true)
} }
if (supportFragmentManager.findFragmentById(R.id.contentFrame) == null) { if (supportFragmentManager.findFragmentById(R.id.contentFrame) == null) {
@ -57,4 +42,18 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn
override fun androidInjector() = dispatchingAndroidInjector 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.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.commit import androidx.fragment.app.commit
import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding
import com.keylesspalace.tusky.fragment.TimelineFragment import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.fragment.TimelineFragment.Kind import com.keylesspalace.tusky.fragment.TimelineFragment.Kind
@ -27,9 +28,6 @@ import javax.inject.Inject
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector 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 { class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
@ -39,12 +37,12 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
private val kind: Kind private val kind: Kind
get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!) get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!)
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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) { val title = if(kind == Kind.FAVOURITES) {
R.string.title_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.adapter.TabAdapter
import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent import com.keylesspalace.tusky.appstore.MainTabsChangedEvent
import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.onTextChanged import com.keylesspalace.tusky.util.onTextChanged
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
import com.uber.autodispose.autoDispose import com.uber.autodispose.autoDispose
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers 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 java.util.regex.Pattern
import javax.inject.Inject import javax.inject.Inject
@ -59,6 +59,8 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
@Inject @Inject
lateinit var eventHub: EventHub lateinit var eventHub: EventHub
private val binding by viewBinding(ActivityTabPreferenceBinding::inflate)
private lateinit var currentTabs: MutableList<TabData> private lateinit var currentTabs: MutableList<TabData>
private lateinit var currentTabsAdapter: TabAdapter private lateinit var currentTabsAdapter: TabAdapter
private lateinit var touchHelper: ItemTouchHelper private lateinit var touchHelper: ItemTouchHelper
@ -73,9 +75,9 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tab_preference) setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply { supportActionBar?.apply {
setTitle(R.string.title_tab_preferences) setTitle(R.string.title_tab_preferences)
@ -85,13 +87,13 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().toMutableList() currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().toMutableList()
currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT) currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT)
currentTabsRecyclerView.adapter = currentTabsAdapter binding.currentTabsRecyclerView.adapter = currentTabsAdapter
currentTabsRecyclerView.layoutManager = LinearLayoutManager(this) binding.currentTabsRecyclerView.layoutManager = LinearLayoutManager(this)
currentTabsRecyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) binding.currentTabsRecyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
addTabAdapter = TabAdapter(listOf(createTabDataFromId(DIRECT)), true, this) addTabAdapter = TabAdapter(listOf(createTabDataFromId(DIRECT)), true, this)
addTabRecyclerView.adapter = addTabAdapter binding.addTabRecyclerView.adapter = addTabAdapter
addTabRecyclerView.layoutManager = LinearLayoutManager(this) binding.addTabRecyclerView.layoutManager = LinearLayoutManager(this)
touchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { touchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { 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) toggleFab(true)
} }
scrim.setOnClickListener { binding.scrim.setOnClickListener {
toggleFab(false) 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() updateAvailableTabs()
} }
@ -193,18 +195,18 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
private fun toggleFab(expand: Boolean) { private fun toggleFab(expand: Boolean) {
val transition = MaterialContainerTransform().apply { val transition = MaterialContainerTransform().apply {
startView = if (expand) actionButton else sheet startView = if (expand) binding.actionButton else binding.sheet
val endView: View = if (expand) sheet else actionButton val endView: View = if (expand) binding.sheet else binding.actionButton
this.endView = endView this.endView = endView
addTarget(endView) addTarget(endView)
scrimColor = Color.TRANSPARENT scrimColor = Color.TRANSPARENT
setPathMotion(MaterialArcMotion()) setPathMotion(MaterialArcMotion())
} }
TransitionManager.beginDelayedTransition(tabPreferenceContainer, transition) TransitionManager.beginDelayedTransition(binding.root, transition)
actionButton.visible(!expand) binding.actionButton.visible(!expand)
sheet.visible(expand) binding.sheet.visible(expand)
scrim.visible(expand) binding.scrim.visible(expand)
} }
private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) { private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) {
@ -310,7 +312,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
addTabAdapter.updateData(addableTabs) 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) currentTabsAdapter.setRemoveButtonVisible(currentTabs.size > MIN_TAB_COUNT)
} }
@ -337,7 +339,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
} }
override fun onBackPressed() { override fun onBackPressed() {
if (actionButton.isVisible) { if (binding.actionButton.isVisible) {
super.onBackPressed() super.onBackPressed()
} else { } else {
toggleFab(false) toggleFab(false)

View File

@ -44,18 +44,19 @@ import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.FutureTarget import com.bumptech.glide.request.FutureTarget
import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID
import com.keylesspalace.tusky.databinding.ActivityViewMediaBinding
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.fragment.ViewImageFragment import com.keylesspalace.tusky.fragment.ViewImageFragment
import com.keylesspalace.tusky.pager.SingleImagePagerAdapter import com.keylesspalace.tusky.pager.SingleImagePagerAdapter
import com.keylesspalace.tusky.pager.ImagePagerAdapter import com.keylesspalace.tusky.pager.ImagePagerAdapter
import com.keylesspalace.tusky.util.getTemporaryMediaFilename import com.keylesspalace.tusky.util.getTemporaryMediaFilename
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.viewdata.AttachmentViewData import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider
import com.uber.autodispose.autoDispose import com.uber.autodispose.autoDispose
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_view_media.*
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.FileOutputStream import java.io.FileOutputStream
@ -65,27 +66,8 @@ import java.util.*
typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener { 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 private val binding by viewBinding(ActivityViewMediaBinding::inflate)
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
}
}
var isToolbarVisible = true var isToolbarVisible = true
private set private set
@ -102,7 +84,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_media) setContentView(binding.root)
supportPostponeEnterTransition() supportPostponeEnterTransition()
@ -125,24 +107,24 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
SingleImagePagerAdapter(this, imageUrl!!) SingleImagePagerAdapter(this, imageUrl!!)
} }
viewPager.adapter = adapter binding.viewPager.adapter = adapter
viewPager.setCurrentItem(initialPosition, false) binding.viewPager.setCurrentItem(initialPosition, false)
viewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() { binding.viewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
toolbar.title = getPageTitle(position) binding.toolbar.title = getPageTitle(position)
} }
}) })
// Setup the toolbar. // Setup the toolbar.
setSupportActionBar(toolbar) setSupportActionBar(binding.toolbar)
val actionBar = supportActionBar val actionBar = supportActionBar
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true) actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowHomeEnabled(true) actionBar.setDisplayShowHomeEnabled(true)
actionBar.title = getPageTitle(initialPosition) actionBar.title = getPageTitle(initialPosition)
} }
toolbar.setNavigationOnClickListener { supportFinishAfterTransition() } binding.toolbar.setNavigationOnClickListener { supportFinishAfterTransition() }
toolbar.setOnMenuItemClickListener { item: MenuItem -> binding.toolbar.setOnMenuItemClickListener { item: MenuItem ->
when (item.itemId) { when (item.itemId) {
R.id.action_download -> requestDownloadMedia() R.id.action_download -> requestDownloadMedia()
R.id.action_open_status -> onOpenStatus() R.id.action_open_status -> onOpenStatus()
@ -156,7 +138,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
window.statusBarColor = Color.BLACK window.statusBarColor = Color.BLACK
window.sharedElementEnterTransition.addListener(object : NoopTransitionListener { window.sharedElementEnterTransition.addListener(object : NoopTransitionListener {
override fun onTransitionEnd(transition: Transition) { override fun onTransitionEnd(transition: Transition) {
adapter.onTransitionEnd(viewPager.currentItem) adapter.onTransitionEnd(binding.viewPager.currentItem)
window.sharedElementEnterTransition.removeListener(this) window.sharedElementEnterTransition.removeListener(this)
} }
}) })
@ -165,7 +147,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.view_media_toolbar, menu) menuInflater.inflate(R.menu.view_media_toolbar, menu)
// We don't support 'open status' from single image views // 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 return true
} }
@ -192,14 +174,14 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
val alpha = if (isToolbarVisible) 1.0f else 0.0f val alpha = if (isToolbarVisible) 1.0f else 0.0f
if (isToolbarVisible) { if (isToolbarVisible) {
// If to be visible, need to make visible immediately and animate alpha // If to be visible, need to make visible immediately and animate alpha
toolbar.alpha = 0.0f binding.toolbar.alpha = 0.0f
toolbar.visibility = visibility binding.toolbar.visibility = visibility
} }
toolbar.animate().alpha(alpha) binding.toolbar.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() { .setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
toolbar.visibility = visibility binding.toolbar.visibility = visibility
animation.removeListener(this) animation.removeListener(this)
} }
}) })
@ -214,7 +196,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
} }
private fun downloadMedia() { 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 val filename = Uri.parse(url).lastPathSegment
Toast.makeText(applicationContext, resources.getString(R.string.download_image, filename), Toast.LENGTH_SHORT).show() 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) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadMedia() downloadMedia()
} else { } 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() { private fun onOpenStatus() {
val attach = attachments!![viewPager.currentItem] val attach = attachments!![binding.viewPager.currentItem]
startActivityWithSlideInAnimation(ViewThreadActivity.startIntent(this, attach.statusId, attach.statusUrl)) startActivityWithSlideInAnimation(ViewThreadActivity.startIntent(this, attach.statusId, attach.statusUrl))
} }
private fun copyLink() { 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 val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText(null, url)) clipboard.setPrimaryClip(ClipData.newPlainText(null, url))
} }
@ -256,7 +238,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
if (imageUrl != null) { if (imageUrl != null) {
shareImage(directory, imageUrl!!) shareImage(directory, imageUrl!!)
} else { } else {
val attachment = attachments!![viewPager.currentItem].attachment val attachment = attachments!![binding.viewPager.currentItem].attachment
when (attachment.type) { when (attachment.type) {
Attachment.Type.IMAGE -> shareImage(directory, attachment.url) Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
Attachment.Type.AUDIO, Attachment.Type.AUDIO,
@ -280,7 +262,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
private fun shareImage(directory: File, url: String) { private fun shareImage(directory: File, url: String) {
isCreating = true isCreating = true
progressBarShare.visibility = View.VISIBLE binding.progressBarShare.visibility = View.VISIBLE
invalidateOptionsMenu() invalidateOptionsMenu()
val file = File(directory, getTemporaryMediaFilename("png")) val file = File(directory, getTemporaryMediaFilename("png"))
val futureTask: FutureTarget<Bitmap> = val futureTask: FutureTarget<Bitmap> =
@ -312,14 +294,14 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
Log.d(TAG, "Download image result: $result") Log.d(TAG, "Download image result: $result")
isCreating = false isCreating = false
invalidateOptionsMenu() invalidateOptionsMenu()
progressBarShare.visibility = View.GONE binding.progressBarShare.visibility = View.GONE
if (result) if (result)
shareFile(file, "image/png") shareFile(file, "image/png")
}, },
{ error -> { error ->
isCreating = false isCreating = false
invalidateOptionsMenu() invalidateOptionsMenu()
progressBarShare.visibility = View.GONE binding.progressBarShare.visibility = View.GONE
Log.e(TAG, "Failed to download image", error) Log.e(TAG, "Failed to download image", error)
} }
) )
@ -342,6 +324,28 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
shareFile(file, mimeType) 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) { 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.ViewTagActivity
import com.keylesspalace.tusky.adapter.EmojiAdapter import com.keylesspalace.tusky.adapter.EmojiAdapter
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
import com.keylesspalace.tusky.databinding.ActivityAnnouncementsBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.view.EmojiPicker import com.keylesspalace.tusky.view.EmojiPicker
import kotlinx.android.synthetic.main.activity_announcements.*
import kotlinx.android.synthetic.main.toolbar_basic.*
import javax.inject.Inject import javax.inject.Inject
class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, OnEmojiSelectedListener, Injectable { class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener, OnEmojiSelectedListener, Injectable {
@ -46,6 +45,8 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
private val viewModel: AnnouncementsViewModel by viewModels { viewModelFactory } private val viewModel: AnnouncementsViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityAnnouncementsBinding::inflate)
private lateinit var adapter: AnnouncementAdapter private lateinit var adapter: AnnouncementAdapter
private val picker by lazy { EmojiPicker(this) } private val picker by lazy { EmojiPicker(this) }
@ -63,22 +64,22 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_announcements) setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply { supportActionBar?.apply {
title = getString(R.string.title_announcements) title = getString(R.string.title_announcements)
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements) binding.swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements)
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue) binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
announcementsList.setHasFixedSize(true) binding.announcementsList.setHasFixedSize(true)
announcementsList.layoutManager = LinearLayoutManager(this) binding.announcementsList.layoutManager = LinearLayoutManager(this)
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
announcementsList.addItemDecoration(divider) binding.announcementsList.addItemDecoration(divider)
val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false) val wellbeingEnabled = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false)
@ -86,31 +87,31 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled, animateEmojis) adapter = AnnouncementAdapter(emptyList(), this, wellbeingEnabled, animateEmojis)
announcementsList.adapter = adapter binding.announcementsList.adapter = adapter
viewModel.announcements.observe(this) { viewModel.announcements.observe(this) {
when (it) { when (it) {
is Success -> { is Success -> {
progressBar.hide() binding.progressBar.hide()
swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
if (it.data.isNullOrEmpty()) { if (it.data.isNullOrEmpty()) {
errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_announcements) binding.errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_announcements)
errorMessageView.show() binding.errorMessageView.show()
} else { } else {
errorMessageView.hide() binding.errorMessageView.hide()
} }
adapter.updateList(it.data ?: listOf()) adapter.updateList(it.data ?: listOf())
} }
is Loading -> { is Loading -> {
errorMessageView.hide() binding.errorMessageView.hide()
} }
is Error -> { is Error -> {
progressBar.hide() binding.progressBar.hide()
swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { binding.errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) {
refreshAnnouncements() refreshAnnouncements()
} }
errorMessageView.show() binding.errorMessageView.show()
} }
} }
} }
@ -120,12 +121,12 @@ class AnnouncementsActivity : BottomSheetActivity(), AnnouncementActionListener,
} }
viewModel.load() viewModel.load()
progressBar.show() binding.progressBar.show()
} }
private fun refreshAnnouncements() { private fun refreshAnnouncements() {
viewModel.load() viewModel.load()
swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
} }
override fun openReactionPicker(announcementId: String, target: View) { 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.dialog.showAddPollDialog
import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener import com.keylesspalace.tusky.components.compose.view.ComposeOptionsListener
import com.keylesspalace.tusky.components.compose.view.ComposeScheduleView 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.AccountEntity
import com.keylesspalace.tusky.db.DraftAttachment import com.keylesspalace.tusky.db.DraftAttachment
import com.keylesspalace.tusky.di.Injectable 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.colorInt
import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.sizeDp
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_compose.*
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
@ -109,17 +109,20 @@ class ComposeActivity : BaseActivity(),
private val viewModel: ComposeViewModel by viewModels { viewModelFactory } private val viewModel: ComposeViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivityComposeBinding::inflate)
private val maxUploadMediaNumber = 4 private val maxUploadMediaNumber = 4
private var mediaCount = 0 private var mediaCount = 0
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val preferences = PreferenceManager.getDefaultSharedPreferences(this) val preferences = PreferenceManager.getDefaultSharedPreferences(this)
val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT) val theme = preferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
if (theme == "black") { if (theme == "black") {
setTheme(R.style.TuskyDialogActivityBlackTheme) setTheme(R.style.TuskyDialogActivityBlackTheme)
} }
setContentView(R.layout.activity_compose) setContentView(binding.root)
setupActionBar() setupActionBar()
// do not do anything when not logged in, activity will be finished in super.onCreate() anyway // 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 onRemove = this::removeMediaFromQueue
) )
composeMediaPreviewBar.layoutManager = binding.composeMediaPreviewBar.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
composeMediaPreviewBar.adapter = mediaAdapter binding.composeMediaPreviewBar.adapter = mediaAdapter
composeMediaPreviewBar.itemAnimator = null binding.composeMediaPreviewBar.itemAnimator = null
subscribeToUpdates(mediaAdapter) subscribeToUpdates(mediaAdapter)
setupButtons() setupButtons()
@ -154,11 +157,11 @@ class ComposeActivity : BaseActivity(),
setupReplyViews(composeOptions?.replyingStatusAuthor, composeOptions?.replyingStatusContent) setupReplyViews(composeOptions?.replyingStatusAuthor, composeOptions?.replyingStatusContent)
val tootText = composeOptions?.tootText val tootText = composeOptions?.tootText
if (!tootText.isNullOrEmpty()) { if (!tootText.isNullOrEmpty()) {
composeEditField.setText(tootText) binding.composeEditField.setText(tootText)
} }
if (!composeOptions?.scheduledAt.isNullOrEmpty()) { if (!composeOptions?.scheduledAt.isNullOrEmpty()) {
composeScheduleView.setDateTime(composeOptions?.scheduledAt) binding.composeScheduleView.setDateTime(composeOptions?.scheduledAt)
} }
setupComposeField(preferences, viewModel.startingText) setupComposeField(preferences, viewModel.startingText)
@ -198,14 +201,14 @@ class ComposeActivity : BaseActivity(),
} }
if (shareBody.isNotBlank()) { if (shareBody.isNotBlank()) {
val start = composeEditField.selectionStart.coerceAtLeast(0) val start = binding.composeEditField.selectionStart.coerceAtLeast(0)
val end = composeEditField.selectionEnd.coerceAtLeast(0) val end = binding.composeEditField.selectionEnd.coerceAtLeast(0)
val left = min(start, end) val left = min(start, end)
val right = max(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 // move edittext cursor to first when shareBody parsed
composeEditField.text.insert(0, "\n") binding.composeEditField.text.insert(0, "\n")
composeEditField.setSelection(0) binding.composeEditField.setSelection(0)
} }
} }
} }
@ -214,58 +217,58 @@ class ComposeActivity : BaseActivity(),
private fun setupReplyViews(replyingStatusAuthor: String?, replyingStatusContent: String?) { private fun setupReplyViews(replyingStatusAuthor: String?, replyingStatusContent: String?) {
if (replyingStatusAuthor != null) { if (replyingStatusAuthor != null) {
composeReplyView.show() binding.composeReplyView.show()
composeReplyView.text = getString(R.string.replying_to, replyingStatusAuthor) binding.composeReplyView.text = getString(R.string.replying_to, replyingStatusAuthor)
val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).apply { sizeDp = 12 } val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).apply { sizeDp = 12 }
ThemeUtils.setDrawableTint(this, arrowDownIcon, android.R.attr.textColorTertiary) ThemeUtils.setDrawableTint(this, arrowDownIcon, android.R.attr.textColorTertiary)
composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null)
composeReplyView.setOnClickListener { binding.composeReplyView.setOnClickListener {
TransitionManager.beginDelayedTransition(composeReplyContentView.parent as ViewGroup) TransitionManager.beginDelayedTransition(binding.composeReplyContentView.parent as ViewGroup)
if (composeReplyContentView.isVisible) { if (binding.composeReplyContentView.isVisible) {
composeReplyContentView.hide() binding.composeReplyContentView.hide()
composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null)
} else { } else {
composeReplyContentView.show() binding.composeReplyContentView.show()
val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).apply { sizeDp = 12 } val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).apply { sizeDp = 12 }
ThemeUtils.setDrawableTint(this, arrowUpIcon, android.R.attr.textColorTertiary) 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?) { private fun setupContentWarningField(startingContentWarning: String?) {
if (startingContentWarning != null) { if (startingContentWarning != null) {
composeContentWarningField.setText(startingContentWarning) binding.composeContentWarningField.setText(startingContentWarning)
} }
composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() } binding.composeContentWarningField.onTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
} }
private fun setupComposeField(preferences: SharedPreferences, startingText: String?) { 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( ComposeAutoCompleteAdapter(
this, this,
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false), preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
) )
) )
composeEditField.setTokenizer(ComposeTokenizer()) binding.composeEditField.setTokenizer(ComposeTokenizer())
composeEditField.setText(startingText) binding.composeEditField.setText(startingText)
composeEditField.setSelection(composeEditField.length()) binding.composeEditField.setSelection(binding.composeEditField.length())
val mentionColour = composeEditField.linkTextColors.defaultColor val mentionColour = binding.composeEditField.linkTextColors.defaultColor
highlightSpans(composeEditField.text, mentionColour) highlightSpans(binding.composeEditField.text, mentionColour)
composeEditField.afterTextChanged { editable -> binding.composeEditField.afterTextChanged { editable ->
highlightSpans(editable, mentionColour) highlightSpans(editable, mentionColour)
updateVisibleCharactersLeft() updateVisibleCharactersLeft()
} }
@ -273,7 +276,7 @@ class ComposeActivity : BaseActivity(),
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093 // work around Android platform bug -> https://issuetracker.google.com/issues/67102093
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.O_MR1) { || 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 -> viewModel.instanceParams.observe { instanceData ->
maximumTootCharacters = instanceData.maxChars maximumTootCharacters = instanceData.maxChars
updateVisibleCharactersLeft() updateVisibleCharactersLeft()
composeScheduleButton.visible(instanceData.supportsScheduled) binding.composeScheduleButton.visible(instanceData.supportsScheduled)
} }
viewModel.emoji.observe { emoji -> setEmojiList(emoji) } viewModel.emoji.observe { emoji -> setEmojiList(emoji) }
combineLiveData(viewModel.markMediaAsSensitive, viewModel.showContentWarning) { markSensitive, showContentWarning -> combineLiveData(viewModel.markMediaAsSensitive, viewModel.showContentWarning) { markSensitive, showContentWarning ->
@ -296,19 +299,19 @@ class ComposeActivity : BaseActivity(),
mediaAdapter.submitList(media) mediaAdapter.submitList(media)
if (media.size != mediaCount) { if (media.size != mediaCount) {
mediaCount = media.size mediaCount = media.size
composeMediaPreviewBar.visible(media.isNotEmpty()) binding.composeMediaPreviewBar.visible(media.isNotEmpty())
updateSensitiveMediaToggle(viewModel.markMediaAsSensitive.value != false, viewModel.showContentWarning.value != false) updateSensitiveMediaToggle(viewModel.markMediaAsSensitive.value != false, viewModel.showContentWarning.value != false)
} }
} }
viewModel.poll.observe { poll -> viewModel.poll.observe { poll ->
pollPreview.visible(poll != null) binding.pollPreview.visible(poll != null)
poll?.let(pollPreview::setPoll) poll?.let(binding.pollPreview::setPoll)
} }
viewModel.scheduledAt.observe { scheduledAt -> viewModel.scheduledAt.observe { scheduledAt ->
if (scheduledAt == null) { if (scheduledAt == null) {
composeScheduleView.resetSchedule() binding.composeScheduleView.resetSchedule()
} else { } else {
composeScheduleView.setDateTime(scheduledAt) binding.composeScheduleView.setDateTime(scheduledAt)
} }
updateScheduleButton() updateScheduleButton()
} }
@ -316,7 +319,7 @@ class ComposeActivity : BaseActivity(),
val active = poll == null val active = poll == null
&& media!!.size != 4 && media!!.size != 4
&& (media.isEmpty() || media.first().type == QueuedMedia.Type.IMAGE) && (media.isEmpty() || media.first().type == QueuedMedia.Type.IMAGE)
enableButton(composeAddMediaButton, active, active) enableButton(binding.composeAddMediaButton, active, active)
enablePollButton(media.isNullOrEmpty()) enablePollButton(media.isNullOrEmpty())
}.subscribe() }.subscribe()
viewModel.uploadError.observe { viewModel.uploadError.observe {
@ -324,52 +327,52 @@ class ComposeActivity : BaseActivity(),
} }
viewModel.setupComplete.observe { viewModel.setupComplete.observe {
// Focus may have changed during view model setup, ensure initial focus is on the edit field // Focus may have changed during view model setup, ensure initial focus is on the edit field
composeEditField.requestFocus() binding.composeEditField.requestFocus()
} }
} }
} }
private fun setupButtons() { private fun setupButtons() {
composeOptionsBottomSheet.listener = this binding.composeOptionsBottomSheet.listener = this
composeOptionsBehavior = BottomSheetBehavior.from(composeOptionsBottomSheet) composeOptionsBehavior = BottomSheetBehavior.from(binding.composeOptionsBottomSheet)
addMediaBehavior = BottomSheetBehavior.from(addMediaBottomSheet) addMediaBehavior = BottomSheetBehavior.from(binding.addMediaBottomSheet)
scheduleBehavior = BottomSheetBehavior.from(composeScheduleView) scheduleBehavior = BottomSheetBehavior.from(binding.composeScheduleView)
emojiBehavior = BottomSheetBehavior.from(emojiView) emojiBehavior = BottomSheetBehavior.from(binding.emojiView)
enableButton(composeEmojiButton, clickable = false, colorActive = false) enableButton(binding.composeEmojiButton, clickable = false, colorActive = false)
// Setup the interface buttons. // Setup the interface buttons.
composeTootButton.setOnClickListener { onSendClicked() } binding.composeTootButton.setOnClickListener { onSendClicked() }
composeAddMediaButton.setOnClickListener { openPickDialog() } binding.composeAddMediaButton.setOnClickListener { openPickDialog() }
composeToggleVisibilityButton.setOnClickListener { showComposeOptions() } binding.composeToggleVisibilityButton.setOnClickListener { showComposeOptions() }
composeContentWarningButton.setOnClickListener { onContentWarningChanged() } binding.composeContentWarningButton.setOnClickListener { onContentWarningChanged() }
composeEmojiButton.setOnClickListener { showEmojis() } binding.composeEmojiButton.setOnClickListener { showEmojis() }
composeHideMediaButton.setOnClickListener { toggleHideMedia() } binding.composeHideMediaButton.setOnClickListener { toggleHideMedia() }
composeScheduleButton.setOnClickListener { onScheduleClick() } binding.composeScheduleButton.setOnClickListener { onScheduleClick() }
composeScheduleView.setResetOnClickListener { resetSchedule() } binding.composeScheduleView.setResetOnClickListener { resetSchedule() }
composeScheduleView.setListener(this) binding.composeScheduleView.setListener(this)
atButton.setOnClickListener { atButtonClicked() } binding.atButton.setOnClickListener { atButtonClicked() }
hashButton.setOnClickListener { hashButtonClicked() } binding.hashButton.setOnClickListener { hashButtonClicked() }
val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary) val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply { colorInt = textColor; sizeDp = 18 } 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 } 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 } 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() } binding.actionPhotoTake.setOnClickListener { initiateCameraApp() }
actionPhotoPick.setOnClickListener { onMediaPick() } binding.actionPhotoPick.setOnClickListener { onMediaPick() }
addPollTextActionTextView.setOnClickListener { openPollDialog() } binding.addPollTextActionTextView.setOnClickListener { openPollDialog() }
} }
private fun setupActionBar() { private fun setupActionBar() {
setSupportActionBar(toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.run { supportActionBar?.run {
title = null title = null
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
@ -388,40 +391,40 @@ class ComposeActivity : BaseActivity(),
val animateAvatars = preferences.getBoolean("animateGifAvatars", false) val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
loadAvatar( loadAvatar(
activeAccount.profilePictureUrl, activeAccount.profilePictureUrl,
composeAvatar, binding.composeAvatar,
avatarSize / 8, avatarSize / 8,
animateAvatars animateAvatars
) )
composeAvatar.contentDescription = getString(R.string.compose_active_account_description, binding.composeAvatar.contentDescription = getString(R.string.compose_active_account_description,
activeAccount.fullName) activeAccount.fullName)
} }
private fun replaceTextAtCaret(text: CharSequence) { private fun replaceTextAtCaret(text: CharSequence) {
// If you select "backward" in an editable, you get SelectionStart > SelectionEnd // If you select "backward" in an editable, you get SelectionStart > SelectionEnd
val start = composeEditField.selectionStart.coerceAtMost(composeEditField.selectionEnd) val start = binding.composeEditField.selectionStart.coerceAtMost(binding.composeEditField.selectionEnd)
val end = composeEditField.selectionStart.coerceAtLeast(composeEditField.selectionEnd) val end = binding.composeEditField.selectionStart.coerceAtLeast(binding.composeEditField.selectionEnd)
val textToInsert = if (start > 0 && !composeEditField.text[start - 1].isWhitespace()) { val textToInsert = if (start > 0 && !binding.composeEditField.text[start - 1].isWhitespace()) {
" $text" " $text"
} else { } else {
text text
} }
composeEditField.text.replace(start, end, textToInsert) binding.composeEditField.text.replace(start, end, textToInsert)
// Set the cursor after the inserted text // Set the cursor after the inserted text
composeEditField.setSelection(start + text.length) binding.composeEditField.setSelection(start + text.length)
} }
fun prependSelectedWordsWith(text: CharSequence) { fun prependSelectedWordsWith(text: CharSequence) {
// If you select "backward" in an editable, you get SelectionStart > SelectionEnd // If you select "backward" in an editable, you get SelectionStart > SelectionEnd
val start = composeEditField.selectionStart.coerceAtMost(composeEditField.selectionEnd) val start = binding.composeEditField.selectionStart.coerceAtMost(binding.composeEditField.selectionEnd)
val end = composeEditField.selectionStart.coerceAtLeast(composeEditField.selectionEnd) val end = binding.composeEditField.selectionStart.coerceAtLeast(binding.composeEditField.selectionEnd)
val editorText = composeEditField.text val editorText = binding.composeEditField.text
if (start == end) { if (start == end) {
// No selection, just insert text at caret // No selection, just insert text at caret
editorText.insert(start, text) editorText.insert(start, text)
// Set the cursor after the inserted text // Set the cursor after the inserted text
composeEditField.setSelection(start + text.length) binding.composeEditField.setSelection(start + text.length)
} else { } else {
var wasWord: Boolean var wasWord: Boolean
var isWord = end < editorText.length && !Character.isWhitespace(editorText[end]) var isWord = end < editorText.length && !Character.isWhitespace(editorText[end])
@ -447,7 +450,7 @@ class ComposeActivity : BaseActivity(),
} }
// Keep the same text (including insertions) selected // 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) { 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 //necessary so snackbar is shown over everything
bar.view.elevation = resources.getDimension(R.dimen.compose_activity_snackbar_elevation) bar.view.elevation = resources.getDimension(R.dimen.compose_activity_snackbar_elevation)
bar.show() bar.show()
@ -478,49 +481,49 @@ class ComposeActivity : BaseActivity(),
private fun updateSensitiveMediaToggle(markMediaSensitive: Boolean, contentWarningShown: Boolean) { private fun updateSensitiveMediaToggle(markMediaSensitive: Boolean, contentWarningShown: Boolean) {
if (viewModel.media.value.isNullOrEmpty()) { if (viewModel.media.value.isNullOrEmpty()) {
composeHideMediaButton.hide() binding.composeHideMediaButton.hide()
} else { } else {
composeHideMediaButton.show() binding.composeHideMediaButton.show()
@ColorInt val color = if (contentWarningShown) { @ColorInt val color = if (contentWarningShown) {
composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp) binding.composeHideMediaButton.setImageResource(R.drawable.ic_hide_media_24dp)
composeHideMediaButton.isClickable = false binding.composeHideMediaButton.isClickable = false
ContextCompat.getColor(this, R.color.transparent_tusky_blue) ContextCompat.getColor(this, R.color.transparent_tusky_blue)
} else { } else {
composeHideMediaButton.isClickable = true binding.composeHideMediaButton.isClickable = true
if (markMediaSensitive) { 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) ContextCompat.getColor(this, R.color.tusky_blue)
} else { } else {
composeHideMediaButton.setImageResource(R.drawable.ic_eye_24dp) binding.composeHideMediaButton.setImageResource(R.drawable.ic_eye_24dp)
ThemeUtils.getColor(this, android.R.attr.textColorTertiary) 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() { 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) ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
} else { } else {
ContextCompat.getColor(this, R.color.tusky_blue) 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) { private fun enableButtons(enable: Boolean) {
composeAddMediaButton.isClickable = enable binding.composeAddMediaButton.isClickable = enable
composeToggleVisibilityButton.isClickable = enable binding.composeToggleVisibilityButton.isClickable = enable
composeEmojiButton.isClickable = enable binding.composeEmojiButton.isClickable = enable
composeHideMediaButton.isClickable = enable binding.composeHideMediaButton.isClickable = enable
composeScheduleButton.isClickable = enable binding.composeScheduleButton.isClickable = enable
composeTootButton.isEnabled = enable binding.composeTootButton.isEnabled = enable
} }
private fun setStatusVisibility(visibility: Status.Visibility) { private fun setStatusVisibility(visibility: Status.Visibility) {
composeOptionsBottomSheet.setStatusVisibility(visibility) binding.composeOptionsBottomSheet.setStatusVisibility(visibility)
composeTootButton.setStatusVisibility(visibility) binding.composeTootButton.setStatusVisibility(visibility)
val iconRes = when (visibility) { val iconRes = when (visibility) {
Status.Visibility.PUBLIC -> R.drawable.ic_public_24dp Status.Visibility.PUBLIC -> R.drawable.ic_public_24dp
@ -529,7 +532,7 @@ class ComposeActivity : BaseActivity(),
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
else -> R.drawable.ic_lock_open_24dp else -> R.drawable.ic_lock_open_24dp
} }
composeToggleVisibilityButton.setImageResource(iconRes) binding.composeToggleVisibilityButton.setImageResource(iconRes)
} }
private fun showComposeOptions() { private fun showComposeOptions() {
@ -545,7 +548,7 @@ class ComposeActivity : BaseActivity(),
private fun onScheduleClick() { private fun onScheduleClick() {
if (viewModel.scheduledAt.value == null) { if (viewModel.scheduledAt.value == null) {
composeScheduleView.openPickDateDialog() binding.composeScheduleView.openPickDateDialog()
} else { } else {
showScheduleView() showScheduleView()
} }
@ -563,7 +566,7 @@ class ComposeActivity : BaseActivity(),
} }
private fun showEmojis() { private fun showEmojis() {
emojiView.adapter?.let { binding.emojiView.adapter?.let {
if (it.itemCount == 0) { if (it.itemCount == 0) {
val errorMessage = getString(R.string.error_no_custom_emojis, accountManager.activeAccount!!.domain) val errorMessage = getString(R.string.error_no_custom_emojis, accountManager.activeAccount!!.domain)
Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show() 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) val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
layoutParams.setMargins(margin, margin, margin, marginBottom) layoutParams.setMargins(margin, margin, margin, marginBottom)
pollPreview.layoutParams = layoutParams binding.pollPreview.layoutParams = layoutParams
pollPreview.setOnClickListener { binding.pollPreview.setOnClickListener {
val popup = PopupMenu(this, pollPreview) val popup = PopupMenu(this, binding.pollPreview)
val editId = 1 val editId = 1
val removeId = 2 val removeId = 2
popup.menu.add(0, editId, 0, R.string.edit_poll) popup.menu.add(0, editId, 0, R.string.edit_poll)
@ -647,7 +650,7 @@ class ComposeActivity : BaseActivity(),
private fun removePoll() { private fun removePoll() {
viewModel.poll.value = null viewModel.poll.value = null
pollPreview.hide() binding.pollPreview.hide()
} }
override fun onVisibilityChanged(visibility: Status.Visibility) { override fun onVisibilityChanged(visibility: Status.Visibility) {
@ -658,39 +661,39 @@ class ComposeActivity : BaseActivity(),
@VisibleForTesting @VisibleForTesting
fun calculateTextLength(): Int { fun calculateTextLength(): Int {
var offset = 0 var offset = 0
val urlSpans = composeEditField.urls val urlSpans = binding.composeEditField.urls
if (urlSpans != null) { if (urlSpans != null) {
for (span in urlSpans) { for (span in urlSpans) {
offset += max(0, span.url.length - MAXIMUM_URL_LENGTH) offset += max(0, span.url.length - MAXIMUM_URL_LENGTH)
} }
} }
var length = composeEditField.length() - offset var length = binding.composeEditField.length() - offset
if (viewModel.showContentWarning.value!!) { if (viewModel.showContentWarning.value!!) {
length += composeContentWarningField.length() length += binding.composeContentWarningField.length()
} }
return length return length
} }
private fun updateVisibleCharactersLeft() { private fun updateVisibleCharactersLeft() {
val remainingLength = maximumTootCharacters - calculateTextLength() 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) { val textColor = if (remainingLength < 0) {
ContextCompat.getColor(this, R.color.tusky_red) ContextCompat.getColor(this, R.color.tusky_red)
} else { } else {
ThemeUtils.getColor(this, android.R.attr.textColorTertiary) ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
} }
composeCharactersLeftView.setTextColor(textColor) binding.composeCharactersLeftView.setTextColor(textColor)
} }
private fun onContentWarningChanged() { private fun onContentWarningChanged() {
val showWarning = composeContentWarningBar.isGone val showWarning = binding.composeContentWarningBar.isGone
viewModel.contentWarningChanged(showWarning) viewModel.contentWarningChanged(showWarning)
updateVisibleCharactersLeft() updateVisibleCharactersLeft()
} }
private fun verifyScheduledTime(): Boolean { 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() { private fun onSendClicked() {
@ -725,14 +728,14 @@ class ComposeActivity : BaseActivity(),
private fun sendStatus() { private fun sendStatus() {
enableButtons(false) enableButtons(false)
val contentText = composeEditField.text.toString() val contentText = binding.composeEditField.text.toString()
var spoilerText = "" var spoilerText = ""
if (viewModel.showContentWarning.value!!) { if (viewModel.showContentWarning.value!!) {
spoilerText = composeContentWarningField.text.toString() spoilerText = binding.composeContentWarningField.text.toString()
} }
val characterCount = calculateTextLength() val characterCount = calculateTextLength()
if ((characterCount <= 0 || contentText.isBlank()) && viewModel.media.value!!.isEmpty()) { 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) enableButtons(true)
} else if (characterCount <= maximumTootCharacters) { } else if (characterCount <= maximumTootCharacters) {
if (viewModel.media.value!!.isNotEmpty()) { if (viewModel.media.value!!.isNotEmpty()) {
@ -747,7 +750,7 @@ class ComposeActivity : BaseActivity(),
}) })
} else { } else {
composeEditField.error = getString(R.string.error_compose_character_limit) binding.composeEditField.error = getString(R.string.error_compose_character_limit)
enableButtons(true) enableButtons(true)
} }
} }
@ -758,7 +761,7 @@ class ComposeActivity : BaseActivity(),
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initiateMediaPicking() initiateMediaPicking()
} else { } 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 { Snackbar.LENGTH_SHORT).apply {
} }
@ -813,12 +816,12 @@ class ComposeActivity : BaseActivity(),
} }
private fun enablePollButton(enable: Boolean) { private fun enablePollButton(enable: Boolean) {
addPollTextActionTextView.isEnabled = enable binding.addPollTextActionTextView.isEnabled = enable
val textColor = ThemeUtils.getColor(this, val textColor = ThemeUtils.getColor(this,
if (enable) android.R.attr.textColorTertiary if (enable) android.R.attr.textColorTertiary
else R.attr.textColorDisabled) else R.attr.textColorDisabled)
addPollTextActionTextView.setTextColor(textColor) binding.addPollTextActionTextView.setTextColor(textColor)
addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN) binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
} }
private fun removeMediaFromQueue(item: QueuedMedia) { private fun removeMediaFromQueue(item: QueuedMedia) {
@ -879,19 +882,18 @@ class ComposeActivity : BaseActivity(),
} }
private fun showContentWarning(show: Boolean) { private fun showContentWarning(show: Boolean) {
TransitionManager.beginDelayedTransition(composeContentWarningBar.parent as ViewGroup) TransitionManager.beginDelayedTransition(binding.composeContentWarningBar.parent as ViewGroup)
@ColorInt val color = if (show) { @ColorInt val color = if (show) {
composeContentWarningBar.show() binding.composeContentWarningBar.show()
composeContentWarningField.setSelection(composeContentWarningField.text.length) binding.composeContentWarningField.setSelection(binding.composeContentWarningField.text.length)
composeContentWarningField.requestFocus() binding.composeContentWarningField.requestFocus()
ContextCompat.getColor(this, R.color.tusky_blue) ContextCompat.getColor(this, R.color.tusky_blue)
} else { } else {
composeContentWarningBar.hide() binding.composeContentWarningBar.hide()
composeEditField.requestFocus() binding.composeEditField.requestFocus()
ThemeUtils.getColor(this, android.R.attr.textColorTertiary) 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 { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -939,8 +941,8 @@ class ComposeActivity : BaseActivity(),
} }
private fun handleCloseButton() { private fun handleCloseButton() {
val contentText = composeEditField.text.toString() val contentText = binding.composeEditField.text.toString()
val contentWarning = composeContentWarningField.text.toString() val contentWarning = binding.composeContentWarningField.text.toString()
if (viewModel.didChange(contentText, contentWarning)) { if (viewModel.didChange(contentText, contentWarning)) {
AlertDialog.Builder(this) AlertDialog.Builder(this)
.setMessage(R.string.compose_save_draft) .setMessage(R.string.compose_save_draft)
@ -974,8 +976,8 @@ class ComposeActivity : BaseActivity(),
private fun setEmojiList(emojiList: List<Emoji>?) { private fun setEmojiList(emojiList: List<Emoji>?) {
if (emojiList != null) { if (emojiList != null) {
emojiView.adapter = EmojiAdapter(emojiList, this@ComposeActivity) binding.emojiView.adapter = EmojiAdapter(emojiList, this@ComposeActivity)
enableButton(composeEmojiButton, true, emojiList.isNotEmpty()) 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.BaseActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.instancemute.fragment.InstanceListFragment import com.keylesspalace.tusky.components.instancemute.fragment.InstanceListFragment
import com.keylesspalace.tusky.databinding.ActivityAccountListBinding
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import javax.inject.Inject import javax.inject.Inject
import kotlinx.android.synthetic.main.toolbar_basic.*
class InstanceListActivity: BaseActivity(), HasAndroidInjector { class InstanceListActivity: BaseActivity(), HasAndroidInjector {
@ -16,9 +16,10 @@ class InstanceListActivity: BaseActivity(), HasAndroidInjector {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val binding = ActivityAccountListBinding.inflate(layoutInflater)
setContentView(R.layout.activity_account_list) setContentView(R.layout.activity_account_list)
setSupportActionBar(toolbar) setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply { supportActionBar?.apply {
setTitle(R.string.title_domain_mutes) setTitle(R.string.title_domain_mutes)
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)

View File

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

View File

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

View File

@ -18,20 +18,19 @@ package com.keylesspalace.tusky.components.scheduled
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.ViewModelProvider import androidx.activity.viewModels
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.databinding.ActivityScheduledTootBinding
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.ScheduledStatus import com.keylesspalace.tusky.entity.ScheduledStatus
import com.keylesspalace.tusky.util.Status import com.keylesspalace.tusky.util.Status
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show 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 import javax.inject.Inject
class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injectable { class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injectable {
@ -39,31 +38,31 @@ class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injec
@Inject @Inject
lateinit var viewModelFactory: ViewModelFactory lateinit var viewModelFactory: ViewModelFactory
lateinit var viewModel: ScheduledTootViewModel private val viewModel: ScheduledTootViewModel by viewModels { viewModelFactory }
private val adapter = ScheduledTootAdapter(this) private val adapter = ScheduledTootAdapter(this)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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 { supportActionBar?.run {
title = getString(R.string.title_scheduled_toot) title = getString(R.string.title_scheduled_toot)
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses) binding.swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses)
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue) binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue)
scheduledTootList.setHasFixedSize(true) binding.scheduledTootList.setHasFixedSize(true)
scheduledTootList.layoutManager = LinearLayoutManager(this) binding.scheduledTootList.layoutManager = LinearLayoutManager(this)
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
scheduledTootList.addItemDecoration(divider) binding.scheduledTootList.addItemDecoration(divider)
scheduledTootList.adapter = adapter binding.scheduledTootList.adapter = adapter
viewModel = ViewModelProvider(this, viewModelFactory)[ScheduledTootViewModel::class.java]
viewModel.data.observe(this) { viewModel.data.observe(this) {
adapter.submitList(it) adapter.submitList(it)
@ -72,31 +71,31 @@ class ScheduledTootActivity : BaseActivity(), ScheduledTootActionListener, Injec
viewModel.networkState.observe(this) { (status) -> viewModel.networkState.observe(this) { (status) ->
when(status) { when(status) {
Status.SUCCESS -> { Status.SUCCESS -> {
progressBar.hide() binding.progressBar.hide()
swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
if(viewModel.data.value?.loadedCount == 0) { if(viewModel.data.value?.loadedCount == 0) {
errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_scheduled_status) binding.errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_scheduled_status)
errorMessageView.show() binding.errorMessageView.show()
} else { } else {
errorMessageView.hide() binding.errorMessageView.hide()
} }
} }
Status.RUNNING -> { Status.RUNNING -> {
errorMessageView.hide() binding.errorMessageView.hide()
if(viewModel.data.value?.loadedCount ?: 0 > 0) { if(viewModel.data.value?.loadedCount ?: 0 > 0) {
swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
} else { } else {
progressBar.show() binding.progressBar.show()
} }
} }
Status.FAILED -> { Status.FAILED -> {
if(viewModel.data.value?.loadedCount ?: 0 >= 0) { if(viewModel.data.value?.loadedCount ?: 0 >= 0) {
progressBar.hide() binding.progressBar.hide()
swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) { binding.errorMessageView.setup(R.drawable.elephant_error, R.string.error_generic) {
refreshStatuses() 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.BottomSheetActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter
import com.keylesspalace.tusky.databinding.ActivitySearchBinding
import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.util.viewBinding
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import kotlinx.android.synthetic.main.activity_search.*
import javax.inject.Inject import javax.inject.Inject
class SearchActivity : BottomSheetActivity(), HasAndroidInjector { class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
@ -41,10 +42,12 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
private val viewModel: SearchViewModel by viewModels { viewModelFactory } private val viewModel: SearchViewModel by viewModels { viewModelFactory }
private val binding by viewBinding(ActivitySearchBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search) setContentView(binding.root)
setSupportActionBar(toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.apply { supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
@ -55,9 +58,9 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector {
} }
private fun setupPages() { private fun setupPages() {
pages.adapter = SearchPagerAdapter(this) binding.pages.adapter = SearchPagerAdapter(this)
TabLayoutMediator(tabs, pages) { TabLayoutMediator(binding.tabs, binding.pages) {
tab, position -> tab, position ->
tab.text = getPageTitle(position) tab.text = getPageTitle(position)
}.attach() }.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" android:layout_height="match_parent"
tools:context="com.keylesspalace.tusky.AboutActivity"> tools:context="com.keylesspalace.tusky.AboutActivity">
<include layout="@layout/toolbar_basic" /> <include
android:id="@+id/includedToolbar"
layout="@layout/toolbar_basic" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -71,7 +71,7 @@
app:layout_constraintStart_toEndOf="@id/accountMuteButton" app:layout_constraintStart_toEndOf="@id/accountMuteButton"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Follow Requested" /> tools:text="Follow Requested" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/accountSubscribeButton" android:id="@+id/accountSubscribeButton"
style="@style/TuskyButton.Outlined" style="@style/TuskyButton.Outlined"
@ -248,20 +248,63 @@
app:layout_constraintTop_toBottomOf="@id/accountFieldList" app:layout_constraintTop_toBottomOf="@id/accountFieldList"
tools:visibility="visible" /> tools:visibility="visible" />
<ViewStub <androidx.constraintlayout.widget.Group
android:id="@+id/accountMovedView" android:id="@+id/accountMovedView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inflatedId="@+id/accountMovedViewLayout" android:visibility="gone"
android:layout="@layout/view_account_moved" app:constraint_referenced_ids="accountMovedText,accountMovedAvatar,accountMovedDisplayName,accountMovedUsername" />
app:layout_constraintTop_toBottomOf="@id/accountRemoveView" />
<androidx.constraintlayout.widget.Barrier <androidx.emoji.widget.EmojiTextView
android:id="@+id/barrierRemote" android:id="@+id/accountMovedText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:barrierDirection="bottom" android:layout_marginTop="12dp"
app:constraint_referenced_ids="accountMovedView,accountMovedViewLayout" /> 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 <LinearLayout
android:id="@+id/accountStatuses" android:id="@+id/accountStatuses"
@ -272,7 +315,7 @@
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@id/accountFollowing" app:layout_constraintEnd_toStartOf="@id/accountFollowing"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrierRemote"> app:layout_constraintTop_toBottomOf="@id/accountMovedAvatar">
<TextView <TextView
android:id="@+id/accountStatusesTextView" android:id="@+id/accountStatusesTextView"
@ -303,7 +346,7 @@
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintEnd_toStartOf="@id/accountFollowers" app:layout_constraintEnd_toStartOf="@id/accountFollowers"
app:layout_constraintStart_toEndOf="@id/accountStatuses" app:layout_constraintStart_toEndOf="@id/accountStatuses"
app:layout_constraintTop_toBottomOf="@id/barrierRemote"> app:layout_constraintTop_toBottomOf="@id/accountMovedAvatar">
<TextView <TextView
android:id="@+id/accountFollowingTextView" android:id="@+id/accountFollowingTextView"
@ -333,7 +376,7 @@
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/accountFollowing" app:layout_constraintStart_toEndOf="@id/accountFollowing"
app:layout_constraintTop_toBottomOf="@id/barrierRemote"> app:layout_constraintTop_toBottomOf="@id/accountMovedAvatar">
<TextView <TextView
android:id="@+id/accountFollowersTextView" android:id="@+id/accountFollowersTextView"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,12 +2,13 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activityScheduledToot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="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 <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

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

View File

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

View File

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