From fc4b47aee4ba97983622db738e1409b45302427f Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Sun, 7 Mar 2021 19:24:01 +0100 Subject: [PATCH] migrating to ViewBinding part 4: Adapters (#2095) --- .../tusky/adapter/AccountFieldAdapter.kt | 45 +++--- .../tusky/adapter/AccountFieldEditAdapter.kt | 38 ++--- .../tusky/adapter/AccountSelectionAdapter.kt | 28 ++-- .../tusky/adapter/EmojiAdapter.kt | 46 +++--- .../tusky/adapter/FollowRequestViewHolder.kt | 52 ++++--- .../tusky/adapter/FollowRequestsAdapter.java | 8 +- .../tusky/adapter/HashtagViewHolder.kt | 16 -- .../tusky/adapter/ListSelectionAdapter.kt | 21 +-- .../tusky/adapter/MutesAdapter.java | 1 - .../tusky/adapter/NetworkStateViewHolder.kt | 21 ++- .../tusky/adapter/NotificationsAdapter.java | 8 +- .../tusky/adapter/PollAdapter.kt | 68 ++++----- .../adapter/PreviewPollOptionsAdapter.kt | 3 +- .../keylesspalace/tusky/adapter/TabAdapter.kt | 92 ++++++------ .../announcements/AnnouncementAdapter.kt | 141 +++++++++--------- .../compose/dialog/AddPollDialog.kt | 1 - .../compose/dialog}/AddPollOptionsAdapter.kt | 38 ++--- .../conversation/ConversationAdapter.kt | 28 +++- .../tusky/components/drafts/DraftsAdapter.kt | 10 +- .../adapter/DomainMutesAdapter.kt | 40 +++-- .../scheduled/ScheduledTootAdapter.kt | 46 ++---- .../search/adapter/SearchHashtagsAdapter.kt | 19 ++- ...{BindingViewHolder.kt => BindingHolder.kt} | 2 +- .../main/res/layout/item_follow_request.xml | 97 +++++++----- .../item_follow_request_notification.xml | 96 ------------ app/src/main/res/layout/item_hashtag.xml | 1 - app/src/main/res/layout/item_picker_list.xml | 1 - 27 files changed, 424 insertions(+), 543 deletions(-) delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/HashtagViewHolder.kt rename app/src/main/java/com/keylesspalace/tusky/{adapter => components/compose/dialog}/AddPollOptionsAdapter.kt (62%) rename app/src/main/java/com/keylesspalace/tusky/util/{BindingViewHolder.kt => BindingHolder.kt} (82%) delete mode 100644 app/src/main/res/layout/item_follow_request_notification.xml diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt index e395a7e66..fe3b15f82 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldAdapter.kt @@ -19,60 +19,57 @@ import android.text.method.LinkMovementMethod import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.ViewGroup -import android.view.View -import android.widget.TextView import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemAccountFieldBinding import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.Field import com.keylesspalace.tusky.entity.IdentityProof import com.keylesspalace.tusky.interfaces.LinkListener import com.keylesspalace.tusky.util.* -import kotlinx.android.synthetic.main.item_account_field.view.* -class AccountFieldAdapter(private val linkListener: LinkListener, private val animateEmojis: Boolean) : RecyclerView.Adapter() { +class AccountFieldAdapter( + private val linkListener: LinkListener, + private val animateEmojis: Boolean +) : RecyclerView.Adapter>() { var emojis: List = emptyList() var fields: List> = emptyList() override fun getItemCount() = fields.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_account_field, parent, false) - return ViewHolder(view) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemAccountFieldBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: BindingHolder, position: Int) { val proofOrField = fields[position] + val nameTextView = holder.binding.accountFieldName + val valueTextView = holder.binding.accountFieldValue if(proofOrField.isLeft()) { val identityProof = proofOrField.asLeft() - viewHolder.nameTextView.text = identityProof.provider - viewHolder.valueTextView.text = LinkHelper.createClickableText(identityProof.username, identityProof.profileUrl) + nameTextView.text = identityProof.provider + valueTextView.text = LinkHelper.createClickableText(identityProof.username, identityProof.profileUrl) - viewHolder.valueTextView.movementMethod = LinkMovementMethod.getInstance() + valueTextView.movementMethod = LinkMovementMethod.getInstance() - viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) + valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) } else { val field = proofOrField.asRight() - val emojifiedName = field.name.emojify(emojis, viewHolder.nameTextView, animateEmojis) - viewHolder.nameTextView.text = emojifiedName + val emojifiedName = field.name.emojify(emojis, nameTextView, animateEmojis) + nameTextView.text = emojifiedName - val emojifiedValue = field.value.emojify(emojis, viewHolder.valueTextView, animateEmojis) - LinkHelper.setClickableText(viewHolder.valueTextView, emojifiedValue, null, linkListener) + val emojifiedValue = field.value.emojify(emojis, valueTextView, animateEmojis) + LinkHelper.setClickableText(valueTextView, emojifiedValue, null, linkListener) if(field.verifiedAt != null) { - viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) + valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) } else { - viewHolder.valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 ) + valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0 ) } } } - - class ViewHolder(rootView: View) : RecyclerView.ViewHolder(rootView) { - val nameTextView: TextView = rootView.accountFieldName - val valueTextView: TextView = rootView.accountFieldValue - } - } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt index 768c28859..29cbec285 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt @@ -15,18 +15,16 @@ package com.keylesspalace.tusky.adapter -import androidx.recyclerview.widget.RecyclerView import android.text.Editable import android.text.TextWatcher import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup -import android.widget.EditText -import com.keylesspalace.tusky.R +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.databinding.ItemEditFieldBinding import com.keylesspalace.tusky.entity.StringField -import kotlinx.android.synthetic.main.item_edit_field.view.* +import com.keylesspalace.tusky.util.BindingHolder -class AccountFieldEditAdapter : RecyclerView.Adapter() { +class AccountFieldEditAdapter : RecyclerView.Adapter>() { private val fieldData = mutableListOf() @@ -54,20 +52,20 @@ class AccountFieldEditAdapter : RecyclerView.Adapter { + val binding = ItemEditFieldBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { - viewHolder.nameTextView.setText(fieldData[position].first) - viewHolder.valueTextView.setText(fieldData[position].second) + override fun onBindViewHolder(holder: BindingHolder, position: Int) { + holder.binding.accountFieldName.setText(fieldData[position].first) + holder.binding.accountFieldValue.setText(fieldData[position].second) - viewHolder.nameTextView.addTextChangedListener(object: TextWatcher { + holder.binding.accountFieldName.addTextChangedListener(object: TextWatcher { override fun afterTextChanged(newText: Editable) { - fieldData[viewHolder.adapterPosition].first = newText.toString() + fieldData[holder.adapterPosition].first = newText.toString() } override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} @@ -75,9 +73,9 @@ class AccountFieldEditAdapter : RecyclerView.Adapter(context, R.layout.item_autocomplete_account) { - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - var view = convertView - if (convertView == null) { - val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - view = layoutInflater.inflate(R.layout.item_autocomplete_account, parent, false) + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val binding = if (convertView == null) { + ItemAutocompleteAccountBinding.inflate(LayoutInflater.from(context), parent, false) + } else { + ItemAutocompleteAccountBinding.bind(convertView) } - view!! val account = getItem(position) if (account != null) { - val username = view.username - val displayName = view.display_name - val avatar = view.avatar - val pm = PreferenceManager.getDefaultSharedPreferences(avatar.context) + val pm = PreferenceManager.getDefaultSharedPreferences(binding.avatar.context) val animateEmojis = pm.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) - username.text = account.fullName - displayName.text = account.displayName.emojify(account.emojis, displayName, animateEmojis) + binding.username.text = account.fullName + binding.displayName.text = account.displayName.emojify(account.emojis, binding.displayName, animateEmojis) - val avatarRadius = avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp) + val avatarRadius = context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp) val animateAvatar = pm.getBoolean("animateGifAvatars", false) - loadAvatar(account.profilePictureUrl, avatar, avatarRadius, animateAvatar) + loadAvatar(account.profilePictureUrl, binding.avatar, avatarRadius, animateAvatar) } - return view + return binding.root } } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt index 70a6163db..2640caaca 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt @@ -15,48 +15,44 @@ package com.keylesspalace.tusky.adapter -import androidx.recyclerview.widget.RecyclerView import android.view.LayoutInflater import android.view.ViewGroup -import android.widget.ImageView +import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide -import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemEmojiButtonBinding import com.keylesspalace.tusky.entity.Emoji +import com.keylesspalace.tusky.util.BindingHolder import java.util.* -class EmojiAdapter(emojiList: List, private val onEmojiSelectedListener: OnEmojiSelectedListener) : RecyclerView.Adapter() { - private val emojiList : List +class EmojiAdapter( + emojiList: List, + private val onEmojiSelectedListener: OnEmojiSelectedListener +) : RecyclerView.Adapter>() { - init { - this.emojiList = emojiList.filter { emoji -> emoji.visibleInPicker == null || emoji.visibleInPicker } - .sortedBy { it.shortcode.toLowerCase(Locale.ROOT) } + private val emojiList : List = emojiList.filter { emoji -> emoji.visibleInPicker == null || emoji.visibleInPicker } + .sortedBy { it.shortcode.toLowerCase(Locale.ROOT) } + + override fun getItemCount() = emojiList.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemEmojiButtonBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun getItemCount(): Int { - return emojiList.size - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojiHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_emoji_button, parent, false) as ImageView - return EmojiHolder(view) - } - - override fun onBindViewHolder(viewHolder: EmojiHolder, position: Int) { + override fun onBindViewHolder(holder: BindingHolder, position: Int) { val emoji = emojiList[position] + val emojiImageView = holder.binding.root - Glide.with(viewHolder.emojiImageView) + Glide.with(emojiImageView) .load(emoji.url) - .into(viewHolder.emojiImageView) + .into(emojiImageView) - viewHolder.emojiImageView.setOnClickListener { + emojiImageView.setOnClickListener { onEmojiSelectedListener.onEmojiSelected(emoji.shortcode) } - viewHolder.emojiImageView.contentDescription = emoji.shortcode + emojiImageView.contentDescription = emoji.shortcode } - - class EmojiHolder(val emojiImageView: ImageView) : RecyclerView.ViewHolder(emojiImageView) - } interface OnEmojiSelectedListener { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt index 8fa14731c..f19dde822 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt @@ -1,55 +1,67 @@ +/* Copyright 2021 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + package com.keylesspalace.tusky.adapter import android.graphics.Typeface import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.StyleSpan -import android.view.View -import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.interfaces.AccountActionListener import com.keylesspalace.tusky.util.* -import kotlinx.android.synthetic.main.item_follow_request_notification.view.* -internal class FollowRequestViewHolder( - itemView: View, - private val showHeader: Boolean) : RecyclerView.ViewHolder(itemView) { - private var id: String? = null +class FollowRequestViewHolder( + private val binding: ItemFollowRequestBinding, + private val showHeader: Boolean +) : RecyclerView.ViewHolder(binding.root) { fun setupWithAccount(account: Account, animateAvatar: Boolean, animateEmojis: Boolean) { - id = account.id val wrappedName = account.name.unicodeWrap() val emojifiedName: CharSequence = wrappedName.emojify(account.emojis, itemView, animateEmojis) - itemView.displayNameTextView.text = emojifiedName + binding.displayNameTextView.text = emojifiedName if (showHeader) { val wholeMessage: String = itemView.context.getString(R.string.notification_follow_request_format, wrappedName) - itemView.notificationTextView?.text = SpannableStringBuilder(wholeMessage).apply { + binding.notificationTextView.text = SpannableStringBuilder(wholeMessage).apply { setSpan(StyleSpan(Typeface.BOLD), 0, wrappedName.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) }.emojify(account.emojis, itemView, animateEmojis) } - itemView.notificationTextView?.visible(showHeader) + binding.notificationTextView.visible(showHeader) val format = itemView.context.getString(R.string.status_username_format) val formattedUsername = String.format(format, account.username) - itemView.usernameTextView.text = formattedUsername - val avatarRadius = itemView.avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) - loadAvatar(account.avatar, itemView.avatar, avatarRadius, animateAvatar) + binding.usernameTextView.text = formattedUsername + val avatarRadius = binding.avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) + loadAvatar(account.avatar, binding.avatar, avatarRadius, animateAvatar) } - fun setupActionListener(listener: AccountActionListener) { - itemView.acceptButton.setOnClickListener { + fun setupActionListener(listener: AccountActionListener, accountId: String) { + binding.acceptButton.setOnClickListener { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { - listener.onRespondToFollowRequest(true, id, position) + listener.onRespondToFollowRequest(true, accountId, position) } } - itemView.rejectButton.setOnClickListener { + binding.rejectButton.setOnClickListener { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { - listener.onRespondToFollowRequest(false, id, position) + listener.onRespondToFollowRequest(false, accountId, position) } } - itemView.setOnClickListener { listener.onViewAccount(id) } + itemView.setOnClickListener { listener.onViewAccount(accountId) } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java index 9ba598842..ef14618e6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java @@ -23,6 +23,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding; import com.keylesspalace.tusky.interfaces.AccountActionListener; public class FollowRequestsAdapter extends AccountAdapter { @@ -37,9 +38,8 @@ public class FollowRequestsAdapter extends AccountAdapter { switch (viewType) { default: case VIEW_TYPE_ACCOUNT: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_follow_request, parent, false); - return new FollowRequestViewHolder(view, false); + ItemFollowRequestBinding binding = ItemFollowRequestBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new FollowRequestViewHolder(binding, false); } case VIEW_TYPE_FOOTER: { View view = LayoutInflater.from(parent.getContext()) @@ -54,7 +54,7 @@ public class FollowRequestsAdapter extends AccountAdapter { if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) { FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder; holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis); - holder.setupActionListener(accountActionListener); + holder.setupActionListener(accountActionListener, accountList.get(position).getId()); } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/HashtagViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/HashtagViewHolder.kt deleted file mode 100644 index c70076c83..000000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/HashtagViewHolder.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.keylesspalace.tusky.adapter - -import android.view.View -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.interfaces.LinkListener - -class HashtagViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val hashtag: TextView = itemView.findViewById(R.id.hashtag) - - fun setup(tag: String, listener: LinkListener) { - hashtag.text = String.format("#%s", tag) - hashtag.setOnClickListener { listener.onViewTag(tag) } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt index f9b19c697..d64784274 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ListSelectionAdapter.kt @@ -21,21 +21,22 @@ import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemPickerListBinding import com.keylesspalace.tusky.entity.MastoList -import kotlinx.android.synthetic.main.item_picker_list.view.* -class ListSelectionAdapter(context: Context) : ArrayAdapter(context, R.layout.item_autocomplete_hashtag) { +class ListSelectionAdapter(context: Context) : ArrayAdapter(context, R.layout.item_picker_list) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - - val view = convertView - ?: layoutInflater.inflate(R.layout.item_picker_list, parent, false) - - getItem(position)?.let { list -> - view.title.text = list.title + val binding = if (convertView == null) { + ItemPickerListBinding.inflate(LayoutInflater.from(context), parent, false) + } else { + ItemPickerListBinding.bind(convertView) } - return view + getItem(position)?.let { list -> + binding.root.text = list.title + } + + return binding.root } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java index e1a30759a..20140fffd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java @@ -9,7 +9,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.view.ViewCompat; -import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.RecyclerView; import com.keylesspalace.tusky.R; diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NetworkStateViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/NetworkStateViewHolder.kt index 66065c7a4..b45ca95f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NetworkStateViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NetworkStateViewHolder.kt @@ -16,29 +16,28 @@ package com.keylesspalace.tusky.adapter import androidx.recyclerview.widget.RecyclerView -import android.view.View import android.view.ViewGroup +import com.keylesspalace.tusky.databinding.ItemNetworkStateBinding import com.keylesspalace.tusky.util.NetworkState import com.keylesspalace.tusky.util.Status import com.keylesspalace.tusky.util.visible -import kotlinx.android.synthetic.main.item_network_state.view.* -class NetworkStateViewHolder(itemView: View, +class NetworkStateViewHolder(private val binding: ItemNetworkStateBinding, private val retryCallback: () -> Unit) -: RecyclerView.ViewHolder(itemView) { +: RecyclerView.ViewHolder(binding.root) { fun setUpWithNetworkState(state: NetworkState?, fullScreen: Boolean) { - itemView.progressBar.visible(state?.status == Status.RUNNING) - itemView.retryButton.visible(state?.status == Status.FAILED) - itemView.errorMsg.visible(state?.msg != null) - itemView.errorMsg.text = state?.msg - itemView.retryButton.setOnClickListener { + binding.progressBar.visible(state?.status == Status.RUNNING) + binding.retryButton.visible(state?.status == Status.FAILED) + binding.errorMsg.visible(state?.msg != null) + binding.errorMsg.text = state?.msg + binding.retryButton.setOnClickListener { retryCallback() } if(fullScreen) { - itemView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + binding.root.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT } else { - itemView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT + binding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java index 833d18f46..cdc43741f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -39,6 +39,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding; import com.keylesspalace.tusky.entity.Account; import com.keylesspalace.tusky.entity.Emoji; import com.keylesspalace.tusky.entity.Notification; @@ -125,9 +126,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter { return new FollowViewHolder(view, statusDisplayOptions); } case VIEW_TYPE_FOLLOW_REQUEST: { - View view = inflater - .inflate(R.layout.item_follow_request_notification, parent, false); - return new FollowRequestViewHolder(view, true); + ItemFollowRequestBinding binding = ItemFollowRequestBinding.inflate(inflater, parent, false); + return new FollowRequestViewHolder(binding, true); } case VIEW_TYPE_PLACEHOLDER: { View view = inflater @@ -233,7 +233,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter { if (payloadForHolder == null) { FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder; holder.setupWithAccount(concreteNotificaton.getAccount(), statusDisplayOptions.animateAvatars(), statusDisplayOptions.animateEmojis()); - holder.setupActionListener(accountActionListener); + holder.setupActionListener(accountActionListener, concreteNotificaton.getAccount().getId()); } } default: diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt index 0208b9530..fa69e3386 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt @@ -18,19 +18,18 @@ package com.keylesspalace.tusky.adapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.CheckBox -import android.widget.RadioButton -import android.widget.TextView import androidx.emoji.text.EmojiCompat import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemPollBinding import com.keylesspalace.tusky.entity.Emoji -import com.keylesspalace.tusky.util.* +import com.keylesspalace.tusky.util.BindingHolder +import com.keylesspalace.tusky.util.emojify +import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.viewdata.PollOptionViewData import com.keylesspalace.tusky.viewdata.buildDescription import com.keylesspalace.tusky.viewdata.calculatePercent -class PollAdapter: RecyclerView.Adapter() { +class PollAdapter: RecyclerView.Adapter>() { private var pollOptions: List = emptyList() private var voteCount: Int = 0 @@ -64,39 +63,42 @@ class PollAdapter: RecyclerView.Adapter() { } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PollViewHolder { - return PollViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_poll, parent, false)) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemPollBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun getItemCount(): Int { - return pollOptions.size - } + override fun getItemCount() = pollOptions.size - override fun onBindViewHolder(holder: PollViewHolder, position: Int) { + override fun onBindViewHolder(holder: BindingHolder, position: Int) { val option = pollOptions[position] - holder.resultTextView.visible(mode == RESULT) - holder.radioButton.visible(mode == SINGLE) - holder.checkBox.visible(mode == MULTIPLE) + val resultTextView = holder.binding.statusPollOptionResult + val radioButton = holder.binding.statusPollRadioButton + val checkBox = holder.binding.statusPollCheckbox + + resultTextView.visible(mode == RESULT) + radioButton.visible(mode == SINGLE) + checkBox.visible(mode == MULTIPLE) when(mode) { RESULT -> { val percent = calculatePercent(option.votesCount, votersCount, voteCount) - val emojifiedPollOptionText = buildDescription(option.title, percent, holder.resultTextView.context) - .emojify(emojis, holder.resultTextView, animateEmojis) - holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) + val emojifiedPollOptionText = buildDescription(option.title, percent, resultTextView.context) + .emojify(emojis, resultTextView, animateEmojis) + resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) val level = percent * 100 - holder.resultTextView.background.level = level - holder.resultTextView.setOnClickListener(resultClickListener) + resultTextView.background.level = level + resultTextView.setOnClickListener(resultClickListener) } SINGLE -> { - val emojifiedPollOptionText = option.title.emojify(emojis, holder.radioButton, animateEmojis) - holder.radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText) - holder.radioButton.isChecked = option.selected - holder.radioButton.setOnClickListener { + val emojifiedPollOptionText = option.title.emojify(emojis, radioButton, animateEmojis) + radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText) + radioButton.isChecked = option.selected + radioButton.setOnClickListener { pollOptions.forEachIndexed { index, pollOption -> pollOption.selected = index == holder.adapterPosition notifyItemChanged(index) @@ -104,10 +106,10 @@ class PollAdapter: RecyclerView.Adapter() { } } MULTIPLE -> { - val emojifiedPollOptionText = option.title.emojify(emojis, holder.checkBox, animateEmojis) - holder.checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText) - holder.checkBox.isChecked = option.selected - holder.checkBox.setOnCheckedChangeListener { _, isChecked -> + val emojifiedPollOptionText = option.title.emojify(emojis, checkBox, animateEmojis) + checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText) + checkBox.isChecked = option.selected + checkBox.setOnCheckedChangeListener { _, isChecked -> pollOptions[holder.adapterPosition].selected = isChecked } } @@ -121,13 +123,3 @@ class PollAdapter: RecyclerView.Adapter() { const val MULTIPLE = 2 } } - - - -class PollViewHolder(view: View): RecyclerView.ViewHolder(view) { - - val resultTextView: TextView = view.findViewById(R.id.status_poll_option_result) - val radioButton: RadioButton = view.findViewById(R.id.status_poll_radio_button) - val checkBox: CheckBox = view.findViewById(R.id.status_poll_checkbox) - -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt index 328e96267..4206f7cfe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt @@ -63,5 +63,4 @@ class PreviewPollOptionsAdapter: RecyclerView.Adapter() { } - -class PreviewViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) \ No newline at end of file +class PreviewViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt index b4517dc6a..e2236503d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt @@ -18,19 +18,21 @@ package com.keylesspalace.tusky.adapter import android.content.res.ColorStateList import android.view.LayoutInflater import android.view.MotionEvent -import android.view.View import android.view.ViewGroup import androidx.core.view.size import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding import com.google.android.material.chip.Chip import com.keylesspalace.tusky.HASHTAG import com.keylesspalace.tusky.LIST import com.keylesspalace.tusky.R import com.keylesspalace.tusky.TabData +import com.keylesspalace.tusky.databinding.ItemTabPreferenceBinding +import com.keylesspalace.tusky.databinding.ItemTabPreferenceSmallBinding +import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.ThemeUtils import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show -import kotlinx.android.synthetic.main.item_tab_preference.view.* interface ItemInteractionListener { fun onTabAdded(tab: TabData) @@ -44,61 +46,69 @@ interface ItemInteractionListener { class TabAdapter(private var data: List, private val small: Boolean, private val listener: ItemInteractionListener, - private var removeButtonEnabled: Boolean = false) : RecyclerView.Adapter() { + private var removeButtonEnabled: Boolean = false +) : RecyclerView.Adapter>() { fun updateData(newData: List) { this.data = newData notifyDataSetChanged() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val layoutId = if (small) { - R.layout.item_tab_preference_small + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = if (small) { + ItemTabPreferenceSmallBinding.inflate(LayoutInflater.from(parent.context), parent, false) } else { - R.layout.item_tab_preference + ItemTabPreferenceBinding.inflate(LayoutInflater.from(parent.context), parent, false) } - val view = LayoutInflater.from(parent.context).inflate(layoutId, parent, false) - return ViewHolder(view) + return BindingHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { + override fun onBindViewHolder(holder: BindingHolder, position: Int) { val context = holder.itemView.context val tab = data[position] - if (!small && tab.id == LIST) { - holder.itemView.textView.text = tab.arguments.getOrNull(1).orEmpty() - } else { - holder.itemView.textView.setText(tab.text) - } - holder.itemView.textView.setCompoundDrawablesRelativeWithIntrinsicBounds(tab.icon, 0, 0, 0) + if (small) { - holder.itemView.textView.setOnClickListener { + val binding = holder.binding as ItemTabPreferenceSmallBinding + + binding.textView.setText(tab.text) + + binding.textView.setCompoundDrawablesRelativeWithIntrinsicBounds(tab.icon, 0, 0, 0) + + binding.textView.setOnClickListener { listener.onTabAdded(tab) } - } - holder.itemView.imageView?.setOnTouchListener { _, event -> - if (event.action == MotionEvent.ACTION_DOWN) { - listener.onStartDrag(holder) - true + + } else { + val binding = holder.binding as ItemTabPreferenceBinding + + if (tab.id == LIST) { + binding.textView.text = tab.arguments.getOrNull(1).orEmpty() } else { - false + binding.textView.setText(tab.text) } - } - holder.itemView.removeButton?.setOnClickListener { - listener.onTabRemoved(holder.adapterPosition) - } - if (holder.itemView.removeButton != null) { - holder.itemView.removeButton.isEnabled = removeButtonEnabled + + binding.textView.setCompoundDrawablesRelativeWithIntrinsicBounds(tab.icon, 0, 0, 0) + + binding.imageView.setOnTouchListener { _, event -> + if (event.action == MotionEvent.ACTION_DOWN) { + listener.onStartDrag(holder) + true + } else { + false + } + } + binding.removeButton.setOnClickListener { + listener.onTabRemoved(holder.adapterPosition) + } + binding.removeButton.isEnabled = removeButtonEnabled ThemeUtils.setDrawableTint( holder.itemView.context, - holder.itemView.removeButton.drawable, + binding.removeButton.drawable, (if (removeButtonEnabled) android.R.attr.textColorTertiary else R.attr.textColorDisabled) ) - } - - if (!small) { if (tab.id == HASHTAG) { - holder.itemView.chipGroup.show() + binding.chipGroup.show() /* * The chip group will always contain the actionChip (it is defined in the xml layout). @@ -107,9 +117,9 @@ class TabAdapter(private var data: List, */ tab.arguments.forEachIndexed { i, arg -> - val chip = holder.itemView.chipGroup.getChildAt(i).takeUnless { it.id == R.id.actionChip } as Chip? + val chip = binding.chipGroup.getChildAt(i).takeUnless { it.id == R.id.actionChip } as Chip? ?: Chip(context).apply { - holder.itemView.chipGroup.addView(this, holder.itemView.chipGroup.size - 1) + binding.chipGroup.addView(this, binding.chipGroup.size - 1) chipIconTint = ColorStateList.valueOf(ThemeUtils.getColor(context, android.R.attr.textColorPrimary)) } @@ -126,16 +136,16 @@ class TabAdapter(private var data: List, } } - while(holder.itemView.chipGroup.size - 1 > tab.arguments.size) { - holder.itemView.chipGroup.removeViewAt(tab.arguments.size) + while(binding.chipGroup.size - 1 > tab.arguments.size) { + binding.chipGroup.removeViewAt(tab.arguments.size) } - holder.itemView.actionChip.setOnClickListener { + binding.actionChip.setOnClickListener { listener.onActionChipClicked(tab, holder.adapterPosition) } } else { - holder.itemView.chipGroup.hide() + binding.chipGroup.hide() } } } @@ -148,6 +158,4 @@ class TabAdapter(private var data: List, notifyDataSetChanged() } } - - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt index b54b15554..5014b52e2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt @@ -19,19 +19,17 @@ import android.view.ContextThemeWrapper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.core.view.size import androidx.recyclerview.widget.RecyclerView import com.google.android.material.chip.Chip -import com.google.android.material.chip.ChipGroup import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemAnnouncementBinding import com.keylesspalace.tusky.entity.Announcement import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.interfaces.LinkListener +import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.LinkHelper import com.keylesspalace.tusky.util.emojify -import kotlinx.android.synthetic.main.item_announcement.view.* - interface AnnouncementActionListener: LinkListener { fun openReactionPicker(announcementId: String, target: View) @@ -44,16 +42,74 @@ class AnnouncementAdapter( private val listener: AnnouncementActionListener, private val wellbeingEnabled: Boolean = false, private val animateEmojis: Boolean = false -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter>() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnnouncementViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_announcement, parent, false) - return AnnouncementViewHolder(view) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemAnnouncementBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun onBindViewHolder(viewHolder: AnnouncementViewHolder, position: Int) { - viewHolder.bind(items[position]) + override fun onBindViewHolder(holder: BindingHolder, position: Int) { + val item = items[position] + + val text = holder.binding.text + val chips = holder.binding.chipGroup + val addReactionChip = holder.binding.addReactionChip + + LinkHelper.setClickableText(text, item.content, null, listener) + + // If wellbeing mode is enabled, announcement badge counts should not be shown. + if (wellbeingEnabled) { + // Since reactions are not visible in wellbeing mode, + // we shouldn't be able to add any ourselves. + addReactionChip.visibility = View.GONE + return + } + + item.reactions.forEachIndexed { i, reaction -> + (chips.getChildAt(i)?.takeUnless { it.id == R.id.addReactionChip } as Chip? + ?: Chip(ContextThemeWrapper(chips.context, R.style.Widget_MaterialComponents_Chip_Choice)).apply { + isCheckable = true + checkedIcon = null + chips.addView(this, i) + }) + .apply { + val emojiText = if (reaction.url == null) { + reaction.name + } else { + context.getString(R.string.emoji_shortcode_format, reaction.name) + } + this.text = ("$emojiText ${reaction.count}") + .emojify( + listOf(Emoji( + reaction.name, + reaction.url ?: "", + reaction.staticUrl ?: "", + null + )), + this, + animateEmojis + ) + + isChecked = reaction.me + + setOnClickListener { + if (reaction.me) { + listener.removeReaction(item.id, reaction.name) + } else { + listener.addReaction(item.id, reaction.name) + } + } + } + } + + while (chips.size - 1 > item.reactions.size) { + chips.removeViewAt(item.reactions.size) + } + + addReactionChip.setOnClickListener { + listener.openReactionPicker(item.id, it) + } } override fun getItemCount() = items.size @@ -62,67 +118,4 @@ class AnnouncementAdapter( this.items = items notifyDataSetChanged() } - - inner class AnnouncementViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { - private val text: TextView = view.text - private val chips: ChipGroup = view.chipGroup - private val addReactionChip: Chip = view.addReactionChip - - fun bind(item: Announcement) { - LinkHelper.setClickableText(text, item.content, null, listener) - - // If wellbeing mode is enabled, announcement badge counts should not be shown. - if (wellbeingEnabled) { - // Since reactions are not visible in wellbeing mode, - // we shouldn't be able to add any ourselves. - addReactionChip.visibility = View.GONE - return - } - - item.reactions.forEachIndexed { i, reaction -> - (chips.getChildAt(i)?.takeUnless { it.id == R.id.addReactionChip } as Chip? - ?: Chip(ContextThemeWrapper(view.context, R.style.Widget_MaterialComponents_Chip_Choice)).apply { - isCheckable = true - checkedIcon = null - chips.addView(this, i) - }) - .apply { - val emojiText = if (reaction.url == null) { - reaction.name - } else { - view.context.getString(R.string.emoji_shortcode_format, reaction.name) - } - text = ("$emojiText ${reaction.count}") - .emojify( - listOf(Emoji( - reaction.name, - reaction.url ?: "", - reaction.staticUrl ?: "", - null - )), - this, - animateEmojis - ) - - isChecked = reaction.me - - setOnClickListener { - if (reaction.me) { - listener.removeReaction(item.id, reaction.name) - } else { - listener.addReaction(item.id, reaction.name) - } - } - } - } - - while (chips.size - 1 > item.reactions.size) { - chips.removeViewAt(item.reactions.size) - } - - addReactionChip.setOnClickListener { - listener.openReactionPicker(item.id, it) - } - } - } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt index 09da54626..6ace77bc3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt @@ -22,7 +22,6 @@ import android.view.LayoutInflater import android.view.WindowManager import androidx.appcompat.app.AlertDialog import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.adapter.AddPollOptionsAdapter import com.keylesspalace.tusky.databinding.DialogAddPollBinding import com.keylesspalace.tusky.entity.NewPoll diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AddPollOptionsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt similarity index 62% rename from app/src/main/java/com/keylesspalace/tusky/adapter/AddPollOptionsAdapter.kt rename to app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt index 602419925..6a0b6a871 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AddPollOptionsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt @@ -13,17 +13,16 @@ * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ -package com.keylesspalace.tusky.adapter +package com.keylesspalace.tusky.components.compose.dialog import android.text.InputFilter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageButton import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.textfield.TextInputEditText -import com.google.android.material.textfield.TextInputLayout import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemAddPollOptionBinding +import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.onTextChanged import com.keylesspalace.tusky.util.visible @@ -32,7 +31,7 @@ class AddPollOptionsAdapter( private val maxOptionLength: Int, private val onOptionRemoved: (Boolean) -> Unit, private val onOptionChanged: (Boolean) -> Unit -): RecyclerView.Adapter() { +): RecyclerView.Adapter>() { val pollOptions: List get() = options.toList() @@ -42,11 +41,12 @@ class AddPollOptionsAdapter( notifyItemInserted(options.size - 1) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val holder = ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_add_poll_option, parent, false)) - holder.editText.filters = arrayOf(InputFilter.LengthFilter(maxOptionLength)) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemAddPollOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val holder = BindingHolder(binding) + binding.optionEditText.filters = arrayOf(InputFilter.LengthFilter(maxOptionLength)) - holder.editText.onTextChanged { s, _, _, _ -> + binding.optionEditText.onTextChanged { s, _, _, _ -> val pos = holder.adapterPosition if(pos != RecyclerView.NO_POSITION) { options[pos] = s.toString() @@ -59,15 +59,15 @@ class AddPollOptionsAdapter( override fun getItemCount() = options.size - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.editText.setText(options[position]) + override fun onBindViewHolder(holder: BindingHolder, position: Int) { + holder.binding.optionEditText.setText(options[position]) - holder.textInputLayout.hint = holder.textInputLayout.context.getString(R.string.poll_new_choice_hint, position + 1) + holder.binding.optionTextInputLayout.hint = holder.binding.root.context.getString(R.string.poll_new_choice_hint, position + 1) - holder.deleteButton.visible(position > 1, View.INVISIBLE) + holder.binding.deleteButton.visible(position > 1, View.INVISIBLE) - holder.deleteButton.setOnClickListener { - holder.editText.clearFocus() + holder.binding.deleteButton.setOnClickListener { + holder.binding.optionEditText.clearFocus() options.removeAt(holder.adapterPosition) notifyItemRemoved(holder.adapterPosition) onOptionRemoved(validateInput()) @@ -81,12 +81,4 @@ class AddPollOptionsAdapter( return true } - } - - -class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { - val textInputLayout: TextInputLayout = itemView.findViewById(R.id.optionTextInputLayout) - val editText: TextInputEditText = itemView.findViewById(R.id.optionEditText) - val deleteButton: ImageButton = itemView.findViewById(R.id.deleteButton) -} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt index 6d6aee481..376d3cd56 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt @@ -1,3 +1,18 @@ +/* Copyright 2021 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + package com.keylesspalace.tusky.components.conversation import android.view.LayoutInflater @@ -10,6 +25,7 @@ import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.R import com.keylesspalace.tusky.adapter.NetworkStateViewHolder +import com.keylesspalace.tusky.databinding.ItemNetworkStateBinding import com.keylesspalace.tusky.interfaces.StatusActionListener import com.keylesspalace.tusky.util.NetworkState import com.keylesspalace.tusky.util.StatusDisplayOptions @@ -49,11 +65,15 @@ class ConversationAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) return when (viewType) { - R.layout.item_network_state -> NetworkStateViewHolder(view, retryCallback) - R.layout.item_conversation -> ConversationViewHolder(view, statusDisplayOptions, - listener) + R.layout.item_network_state -> { + val binding = ItemNetworkStateBinding.inflate(LayoutInflater.from(parent.context), parent, false) + NetworkStateViewHolder(binding, retryCallback) + } + R.layout.item_conversation -> { + val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) + ConversationViewHolder(view, statusDisplayOptions, listener) + } else -> throw IllegalArgumentException("unknown view type $viewType") } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt index 5dfbceac8..7fd224aca 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt @@ -23,7 +23,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.databinding.ItemDraftBinding import com.keylesspalace.tusky.db.DraftEntity -import com.keylesspalace.tusky.util.BindingViewHolder +import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.visible @@ -35,7 +35,7 @@ interface DraftActionListener { class DraftsAdapter( private val listener: DraftActionListener -) : PagedListAdapter>( +) : PagedListAdapter>( object : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: DraftEntity, newItem: DraftEntity): Boolean { return oldItem.id == newItem.id @@ -47,11 +47,11 @@ class DraftsAdapter( } ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { val binding = ItemDraftBinding.inflate(LayoutInflater.from(parent.context), parent, false) - val viewHolder = BindingViewHolder(binding) + val viewHolder = BindingHolder(binding) binding.draftMediaPreview.layoutManager = LinearLayoutManager(binding.root.context, RecyclerView.HORIZONTAL, false) binding.draftMediaPreview.adapter = DraftMediaAdapter { @@ -63,7 +63,7 @@ class DraftsAdapter( return viewHolder } - override fun onBindViewHolder(holder: BindingViewHolder, position: Int) { + override fun onBindViewHolder(holder: BindingHolder, position: Int) { getItem(position)?.let { draft -> holder.binding.root.setOnClickListener { listener.onOpenDraft(draft) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/instancemute/adapter/DomainMutesAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/instancemute/adapter/DomainMutesAdapter.kt index 62ab7ef31..de699d126 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/instancemute/adapter/DomainMutesAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/instancemute/adapter/DomainMutesAdapter.kt @@ -1,22 +1,31 @@ package com.keylesspalace.tusky.components.instancemute.adapter import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.instancemute.interfaces.InstanceActionListener -import kotlinx.android.synthetic.main.item_muted_domain.view.* +import com.keylesspalace.tusky.databinding.ItemMutedDomainBinding +import com.keylesspalace.tusky.util.BindingHolder + +class DomainMutesAdapter( + private val actionListener: InstanceActionListener +): RecyclerView.Adapter>() { -class DomainMutesAdapter(private val actionListener: InstanceActionListener): RecyclerView.Adapter() { var instances: MutableList = mutableListOf() var bottomLoading: Boolean = false - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_muted_domain, parent, false), actionListener) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemMutedDomainBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.setupWithInstance(instances[position]) + override fun onBindViewHolder(holder: BindingHolder, position: Int) { + val instance = instances[position] + + holder.binding.mutedDomain.text = instance + holder.binding.mutedDomainUnmute.setOnClickListener { + actionListener.mute(false, instance, holder.adapterPosition) + } } override fun getItemCount(): Int { @@ -37,21 +46,10 @@ class DomainMutesAdapter(private val actionListener: InstanceActionListener): Re notifyItemInserted(instances.size) } - fun removeItem(position: Int) - { + fun removeItem(position: Int) { if (position >= 0 && position < instances.size) { instances.removeAt(position) notifyItemRemoved(position) } } - - - class ViewHolder(rootView: View, private val actionListener: InstanceActionListener): RecyclerView.ViewHolder(rootView) { - fun setupWithInstance(instance: String) { - itemView.muted_domain.text = instance - itemView.muted_domain_unmute.setOnClickListener { - actionListener.mute(false, instance, adapterPosition) - } - } - } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt index ea12d1ffa..414130ddb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootAdapter.kt @@ -18,13 +18,11 @@ package com.keylesspalace.tusky.components.scheduled import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.TextView import androidx.paging.PagedListAdapter import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemScheduledTootBinding import com.keylesspalace.tusky.entity.ScheduledStatus +import com.keylesspalace.tusky.util.BindingHolder interface ScheduledTootActionListener { fun edit(item: ScheduledStatus) @@ -33,7 +31,7 @@ interface ScheduledTootActionListener { class ScheduledTootAdapter( val listener: ScheduledTootActionListener -) : PagedListAdapter( +) : PagedListAdapter>( object: DiffUtil.ItemCallback(){ override fun areItemsTheSame(oldItem: ScheduledStatus, newItem: ScheduledStatus): Boolean { return oldItem.id == newItem.id @@ -46,40 +44,24 @@ class ScheduledTootAdapter( } ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TootViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_scheduled_toot, parent, false) - return TootViewHolder(view) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemScheduledTootBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun onBindViewHolder(viewHolder: TootViewHolder, position: Int) { - getItem(position)?.let{ - viewHolder.bind(it) - } - } - - - inner class TootViewHolder(view: View) : RecyclerView.ViewHolder(view) { - - private val text: TextView = view.findViewById(R.id.text) - private val edit: ImageButton = view.findViewById(R.id.edit) - private val delete: ImageButton = view.findViewById(R.id.delete) - - fun bind(item: ScheduledStatus) { - edit.isEnabled = true - delete.isEnabled = true - text.text = item.params.text - edit.setOnClickListener { v: View -> + override fun onBindViewHolder(holder: BindingHolder, position: Int) { + getItem(position)?.let{ item -> + holder.binding.edit.isEnabled = true + holder.binding.delete.isEnabled = true + holder.binding.text.text = item.params.text + holder.binding.edit.setOnClickListener { v: View -> v.isEnabled = false listener.edit(item) } - delete.setOnClickListener { v: View -> + holder.binding.delete.setOnClickListener { v: View -> v.isEnabled = false listener.delete(item) } - } - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt index 71863d430..ebc021602 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt @@ -19,24 +19,23 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.paging.PagedListAdapter import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView -import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.adapter.HashtagViewHolder +import com.keylesspalace.tusky.databinding.ItemHashtagBinding import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.interfaces.LinkListener +import com.keylesspalace.tusky.util.BindingHolder class SearchHashtagsAdapter(private val linkListener: LinkListener) - : PagedListAdapter(HASHTAG_COMPARATOR) { + : PagedListAdapter>(HASHTAG_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_hashtag, parent, false) - return HashtagViewHolder(view) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + val binding = ItemHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BindingHolder(binding) } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + override fun onBindViewHolder(holder: BindingHolder, position: Int) { getItem(position)?.let { (name) -> - (holder as HashtagViewHolder).setup(name, linkListener) + holder.binding.root.text = String.format("#%s", name) + holder.binding.root.setOnClickListener { linkListener.onViewTag(name) } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/BindingViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/util/BindingHolder.kt similarity index 82% rename from app/src/main/java/com/keylesspalace/tusky/util/BindingViewHolder.kt rename to app/src/main/java/com/keylesspalace/tusky/util/BindingHolder.kt index 14aee81b5..a7a4c9720 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/BindingViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/BindingHolder.kt @@ -3,6 +3,6 @@ package com.keylesspalace.tusky.util import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding -class BindingViewHolder( +class BindingHolder( val binding: T ) : RecyclerView.ViewHolder(binding.root) diff --git a/app/src/main/res/layout/item_follow_request.xml b/app/src/main/res/layout/item_follow_request.xml index 36e442c5c..e970e1289 100644 --- a/app/src/main/res/layout/item_follow_request.xml +++ b/app/src/main/res/layout/item_follow_request.xml @@ -1,52 +1,69 @@ - + android:paddingRight="16dp" + android:paddingBottom="10dp"> + + + android:layout_marginTop="10dp" + android:contentDescription="@string/action_view_profile" + tools:src="@drawable/avatar_default" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/notificationTextView" /> - + android:layout_height="wrap_content" + android:layout_marginStart="14dp" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?android:textColorPrimary" + android:textSize="?attr/status_text_large" + android:textStyle="normal|bold" + app:layout_constraintStart_toEndOf="@id/avatar" + app:layout_constraintTop_toTopOf="@id/avatar" + app:layout_constraintBottom_toTopOf="@id/usernameTextView" + tools:text="Display name" /> - - - - - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_follow_request_notification.xml b/app/src/main/res/layout/item_follow_request_notification.xml deleted file mode 100644 index d4db2a3dd..000000000 --- a/app/src/main/res/layout/item_follow_request_notification.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_hashtag.xml b/app/src/main/res/layout/item_hashtag.xml index a158240c9..efdb24f72 100644 --- a/app/src/main/res/layout/item_hashtag.xml +++ b/app/src/main/res/layout/item_hashtag.xml @@ -1,6 +1,5 @@