From 837ee2e40d44b83baa007bd70a0b45e6828905c0 Mon Sep 17 00:00:00 2001 From: Ivan Kupalov Date: Sun, 20 Jun 2021 10:18:40 +0200 Subject: [PATCH] Convert some adapters to Kotlin (#2187) * Rename .java adapters to .kt * Convert Account adapters to Kotlin * Apply feedback for adapter refactoring --- .../tusky/adapter/AccountAdapter.java | 116 ------------- .../tusky/adapter/AccountAdapter.kt | 125 +++++++++++++ .../tusky/adapter/BlocksAdapter.java | 106 ----------- .../tusky/adapter/BlocksAdapter.kt | 80 +++++++++ .../tusky/adapter/FollowAdapter.java | 61 ------- .../tusky/adapter/FollowAdapter.kt | 39 +++++ .../tusky/adapter/FollowRequestsAdapter.java | 60 ------- .../tusky/adapter/FollowRequestsAdapter.kt | 41 +++++ .../tusky/adapter/MutesAdapter.java | 131 -------------- .../tusky/adapter/MutesAdapter.kt | 132 ++++++++++++++ .../tusky/adapter/NotificationsAdapter.java | 2 +- .../tusky/adapter/PlaceholderViewHolder.java | 49 ------ .../tusky/adapter/PlaceholderViewHolder.kt | 41 +++++ .../tusky/adapter/ThreadAdapter.java | 164 ------------------ .../tusky/adapter/ThreadAdapter.kt | 129 ++++++++++++++ .../tusky/fragment/AccountListFragment.kt | 2 +- 16 files changed, 589 insertions(+), 689 deletions(-) delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.kt diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java deleted file mode 100644 index 24430dce..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright 2017 Andrew Dawson - * - * 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 androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.keylesspalace.tusky.entity.Account; -import com.keylesspalace.tusky.interfaces.AccountActionListener; -import com.keylesspalace.tusky.util.ListUtils; - -import java.util.ArrayList; -import java.util.List; - -public abstract class AccountAdapter extends RecyclerView.Adapter { - static final int VIEW_TYPE_ACCOUNT = 0; - static final int VIEW_TYPE_FOOTER = 1; - - List accountList; - AccountActionListener accountActionListener; - private boolean bottomLoading; - protected final boolean animateEmojis; - protected final boolean animateAvatar; - - AccountAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) { - this.accountList = new ArrayList<>(); - this.accountActionListener = accountActionListener; - this.animateAvatar = animateAvatar; - this.animateEmojis = animateEmojis; - bottomLoading = false; - } - - @Override - public int getItemCount() { - return accountList.size() + (bottomLoading ? 1 : 0); - } - - @Override - public int getItemViewType(int position) { - if (position == accountList.size() && bottomLoading) { - return VIEW_TYPE_FOOTER; - } else { - return VIEW_TYPE_ACCOUNT; - } - } - - public void update(@NonNull List newAccounts) { - accountList = ListUtils.removeDuplicates(newAccounts); - notifyDataSetChanged(); - } - - public void addItems(@NonNull List newAccounts) { - int end = accountList.size(); - Account last = accountList.get(end - 1); - if (last != null && !findAccount(newAccounts, last.getId())) { - accountList.addAll(newAccounts); - notifyItemRangeInserted(end, newAccounts.size()); - } - } - - public void setBottomLoading(boolean loading) { - boolean wasLoading = bottomLoading; - if(wasLoading == loading) { - return; - } - bottomLoading = loading; - if(loading) { - notifyItemInserted(accountList.size()); - } else { - notifyItemRemoved(accountList.size()); - } - } - - private static boolean findAccount(@NonNull List accounts, String id) { - for (Account account : accounts) { - if (account.getId().equals(id)) { - return true; - } - } - return false; - } - - @Nullable - public Account removeItem(int position) { - if (position < 0 || position >= accountList.size()) { - return null; - } - Account account = accountList.remove(position); - notifyItemRemoved(position); - return account; - } - - public void addItem(@NonNull Account account, int position) { - if (position < 0 || position > accountList.size()) { - return; - } - accountList.add(position, account); - notifyItemInserted(position); - } - - -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.kt new file mode 100644 index 00000000..f68d022f --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountAdapter.kt @@ -0,0 +1,125 @@ +/* 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.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.entity.Account +import com.keylesspalace.tusky.interfaces.AccountActionListener +import com.keylesspalace.tusky.util.removeDuplicates + +/** Generic adapter with bottom loading indicator. */ +abstract class AccountAdapter internal constructor( + var accountActionListener: AccountActionListener, + protected val animateAvatar: Boolean, + protected val animateEmojis: Boolean +) : RecyclerView.Adapter() { + var accountList = mutableListOf() + private var bottomLoading: Boolean = false + + override fun getItemCount(): Int { + return accountList.size + if (bottomLoading) 1 else 0 + } + + abstract fun createAccountViewHolder(parent: ViewGroup): AVH + + abstract fun onBindAccountViewHolder(viewHolder: AVH, position: Int) + + final override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) { + @Suppress("UNCHECKED_CAST") + this.onBindAccountViewHolder(holder as AVH, position) + } + } + + final override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + return when (viewType) { + VIEW_TYPE_ACCOUNT -> this.createAccountViewHolder(parent) + VIEW_TYPE_FOOTER -> this.createFooterViewHolder(parent) + else -> error("Unknown item type: $viewType") + } + } + + private fun createFooterViewHolder( + parent: ViewGroup, + ): RecyclerView.ViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_footer, parent, false) + return LoadingFooterViewHolder(view) + } + + override fun getItemViewType(position: Int): Int { + return if (position == accountList.size && bottomLoading) { + VIEW_TYPE_FOOTER + } else { + VIEW_TYPE_ACCOUNT + } + } + + fun update(newAccounts: List) { + accountList = removeDuplicates(newAccounts) + notifyDataSetChanged() + } + + fun addItems(newAccounts: List) { + val end = accountList.size + val last = accountList[end - 1] + if (newAccounts.none { it.id == last.id }) { + accountList.addAll(newAccounts) + notifyItemRangeInserted(end, newAccounts.size) + } + } + + fun setBottomLoading(loading: Boolean) { + val wasLoading = bottomLoading + if (wasLoading == loading) { + return + } + bottomLoading = loading + if (loading) { + notifyItemInserted(accountList.size) + } else { + notifyItemRemoved(accountList.size) + } + } + + fun removeItem(position: Int): Account? { + if (position < 0 || position >= accountList.size) { + return null + } + val account = accountList.removeAt(position) + notifyItemRemoved(position) + return account + } + + fun addItem(account: Account, position: Int) { + if (position < 0 || position > accountList.size) { + return + } + accountList.add(position, account) + notifyItemInserted(position) + } + + companion object { + const val VIEW_TYPE_ACCOUNT = 0 + const val VIEW_TYPE_FOOTER = 1 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java deleted file mode 100644 index 57cc9035..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.java +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2017 Andrew Dawson - * - * 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.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.preference.PreferenceManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.entity.Account; -import com.keylesspalace.tusky.interfaces.AccountActionListener; -import com.keylesspalace.tusky.util.CustomEmojiHelper; -import com.keylesspalace.tusky.util.ImageLoadingHelper; - -public class BlocksAdapter extends AccountAdapter { - - public BlocksAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) { - super(accountActionListener, animateAvatar, animateEmojis); - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - default: - case VIEW_TYPE_ACCOUNT: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_blocked_user, parent, false); - return new BlockedUserViewHolder(view); - } - case VIEW_TYPE_FOOTER: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_footer, parent, false); - return new LoadingFooterViewHolder(view); - } - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { - if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) { - BlockedUserViewHolder holder = (BlockedUserViewHolder) viewHolder; - holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis); - holder.setupActionListener(accountActionListener); - } - } - - static class BlockedUserViewHolder extends RecyclerView.ViewHolder { - private ImageView avatar; - private TextView username; - private TextView displayName; - private ImageButton unblock; - private String id; - - BlockedUserViewHolder(View itemView) { - super(itemView); - avatar = itemView.findViewById(R.id.blocked_user_avatar); - username = itemView.findViewById(R.id.blocked_user_username); - displayName = itemView.findViewById(R.id.blocked_user_display_name); - unblock = itemView.findViewById(R.id.blocked_user_unblock); - - } - - void setupWithAccount(Account account, boolean animateAvatar, boolean animateEmojis) { - id = account.getId(); - CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName, animateEmojis); - displayName.setText(emojifiedName); - String format = username.getContext().getString(R.string.status_username_format); - String formattedUsername = String.format(format, account.getUsername()); - username.setText(formattedUsername); - int avatarRadius = avatar.getContext().getResources() - .getDimensionPixelSize(R.dimen.avatar_radius_48dp); - ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar); - } - - void setupActionListener(final AccountActionListener listener) { - unblock.setOnClickListener(v -> { - int position = getBindingAdapterPosition(); - if (position != RecyclerView.NO_POSITION) { - listener.onBlock(false, id, position); - } - }); - itemView.setOnClickListener(v -> listener.onViewAccount(id)); - } - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.kt new file mode 100644 index 00000000..fc7cec24 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/BlocksAdapter.kt @@ -0,0 +1,80 @@ +/* Copyright 2017 Andrew Dawson + * + * 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.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.entity.Account +import com.keylesspalace.tusky.interfaces.AccountActionListener +import com.keylesspalace.tusky.util.emojify +import com.keylesspalace.tusky.util.loadAvatar + +/** Displays a list of blocked accounts. */ +class BlocksAdapter( + accountActionListener: AccountActionListener, + animateAvatar: Boolean, + animateEmojis: Boolean +) : AccountAdapter( + accountActionListener, + animateAvatar, + animateEmojis +) { + override fun createAccountViewHolder(parent: ViewGroup): BlockedUserViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_blocked_user, parent, false) + return BlockedUserViewHolder(view) + } + + override fun onBindAccountViewHolder(viewHolder: BlockedUserViewHolder, position: Int) { + viewHolder.setupWithAccount(accountList[position], animateAvatar, animateEmojis) + viewHolder.setupActionListener(accountActionListener) + } + + class BlockedUserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val avatar: ImageView = itemView.findViewById(R.id.blocked_user_avatar) + private val username: TextView = itemView.findViewById(R.id.blocked_user_username) + private val displayName: TextView = itemView.findViewById(R.id.blocked_user_display_name) + private val unblock: ImageButton = itemView.findViewById(R.id.blocked_user_unblock) + private var id: String? = null + + fun setupWithAccount(account: Account, animateAvatar: Boolean, animateEmojis: Boolean) { + id = account.id + val emojifiedName = account.name.emojify(account.emojis, displayName, animateEmojis) + displayName.text = emojifiedName + val format = username.context.getString(R.string.status_username_format) + val formattedUsername = String.format(format, account.username) + username.text = formattedUsername + val avatarRadius = avatar.context.resources + .getDimensionPixelSize(R.dimen.avatar_radius_48dp) + loadAvatar(account.avatar, avatar, avatarRadius, animateAvatar) + } + + fun setupActionListener(listener: AccountActionListener) { + unblock.setOnClickListener { + val position = bindingAdapterPosition + if (position != RecyclerView.NO_POSITION) { + listener.onBlock(false, id, position) + } + } + itemView.setOnClickListener { listener.onViewAccount(id) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java deleted file mode 100644 index 98cb9e4d..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2017 Andrew Dawson - * - * 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 androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.interfaces.AccountActionListener; - -/** Both for follows and following lists. */ -public class FollowAdapter extends AccountAdapter { - - public FollowAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) { - super(accountActionListener, animateAvatar, animateEmojis); - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - default: - case VIEW_TYPE_ACCOUNT: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_account, parent, false); - return new AccountViewHolder(view); - } - case VIEW_TYPE_FOOTER: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_footer, parent, false); - return new LoadingFooterViewHolder(view); - } - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { - if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) { - AccountViewHolder holder = (AccountViewHolder) viewHolder; - holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis); - holder.setupActionListener(accountActionListener); - } - } - -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.kt new file mode 100644 index 00000000..74797b3a --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowAdapter.kt @@ -0,0 +1,39 @@ +/* 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.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.interfaces.AccountActionListener + +/** Displays either a follows or following list. */ +class FollowAdapter( + accountActionListener: AccountActionListener, + animateAvatar: Boolean, + animateEmojis: Boolean +) : AccountAdapter(accountActionListener, animateAvatar, animateEmojis) { + override fun createAccountViewHolder(parent: ViewGroup): AccountViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_account, parent, false) + return AccountViewHolder(view) + } + + override fun onBindAccountViewHolder(viewHolder: AccountViewHolder, position: Int) { + viewHolder.setupWithAccount(accountList[position], animateAvatar, animateEmojis) + viewHolder.setupActionListener(accountActionListener) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java deleted file mode 100644 index ef14618e..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright 2017 Andrew Dawson - * - * 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.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -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 { - - public FollowRequestsAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) { - super(accountActionListener, animateAvatar, animateEmojis); - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - default: - case VIEW_TYPE_ACCOUNT: { - 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()) - .inflate(R.layout.item_footer, parent, false); - return new LoadingFooterViewHolder(view); - } - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { - if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) { - FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder; - holder.setupWithAccount(accountList.get(position), animateAvatar, animateEmojis); - holder.setupActionListener(accountActionListener, accountList.get(position).getId()); - } - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.kt new file mode 100644 index 00000000..11089cd4 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.kt @@ -0,0 +1,41 @@ +/* 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.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.databinding.ItemFollowRequestBinding +import com.keylesspalace.tusky.interfaces.AccountActionListener + +/** Displays a list of follow requests with accept/reject buttons. */ +class FollowRequestsAdapter( + accountActionListener: AccountActionListener, + animateAvatar: Boolean, + animateEmojis: Boolean +) : AccountAdapter(accountActionListener, animateAvatar, animateEmojis) { + override fun createAccountViewHolder(parent: ViewGroup): FollowRequestViewHolder { + val binding = ItemFollowRequestBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + return FollowRequestViewHolder(binding, false) + } + + override fun onBindAccountViewHolder(viewHolder: FollowRequestViewHolder, position: Int) { + viewHolder.setupWithAccount(accountList[position], animateAvatar, animateEmojis) + viewHolder.setupActionListener(accountActionListener, accountList[position].id) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java deleted file mode 100644 index f63af6ca..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.keylesspalace.tusky.adapter; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.core.view.ViewCompat; -import androidx.recyclerview.widget.RecyclerView; - -import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.entity.Account; -import com.keylesspalace.tusky.interfaces.AccountActionListener; -import com.keylesspalace.tusky.util.CustomEmojiHelper; -import com.keylesspalace.tusky.util.ImageLoadingHelper; - -import java.util.HashMap; - -public class MutesAdapter extends AccountAdapter { - private HashMap mutingNotificationsMap; - - public MutesAdapter(AccountActionListener accountActionListener, boolean animateAvatar, boolean animateEmojis) { - super(accountActionListener, animateAvatar, animateEmojis); - mutingNotificationsMap = new HashMap(); - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - default: - case VIEW_TYPE_ACCOUNT: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_muted_user, parent, false); - return new MutesAdapter.MutedUserViewHolder(view); - } - case VIEW_TYPE_FOOTER: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_footer, parent, false); - return new LoadingFooterViewHolder(view); - } - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { - if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) { - MutedUserViewHolder holder = (MutedUserViewHolder) viewHolder; - Account account = accountList.get(position); - holder.setupWithAccount(account, mutingNotificationsMap.get(account.getId()), animateAvatar, animateEmojis); - holder.setupActionListener(accountActionListener); - } - } - - public void updateMutingNotifications(String id, boolean mutingNotifications, int position) { - mutingNotificationsMap.put(id, mutingNotifications); - notifyItemChanged(position); - } - - public void updateMutingNotificationsMap(HashMap newMutingNotificationsMap) { - mutingNotificationsMap.putAll(newMutingNotificationsMap); - notifyDataSetChanged(); - } - - static class MutedUserViewHolder extends RecyclerView.ViewHolder { - private ImageView avatar; - private TextView username; - private TextView displayName; - private ImageButton unmute; - private ImageButton muteNotifications; - private String id; - private boolean notifications; - - MutedUserViewHolder(View itemView) { - super(itemView); - avatar = itemView.findViewById(R.id.muted_user_avatar); - username = itemView.findViewById(R.id.muted_user_username); - displayName = itemView.findViewById(R.id.muted_user_display_name); - unmute = itemView.findViewById(R.id.muted_user_unmute); - muteNotifications = itemView.findViewById(R.id.muted_user_mute_notifications); - } - - void setupWithAccount(Account account, Boolean mutingNotifications, boolean animateAvatar, boolean animateEmojis) { - id = account.getId(); - CharSequence emojifiedName = CustomEmojiHelper.emojify(account.getName(), account.getEmojis(), displayName, animateEmojis); - displayName.setText(emojifiedName); - String format = username.getContext().getString(R.string.status_username_format); - String formattedUsername = String.format(format, account.getUsername()); - username.setText(formattedUsername); - int avatarRadius = avatar.getContext().getResources() - .getDimensionPixelSize(R.dimen.avatar_radius_48dp); - ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar); - - String unmuteString = unmute.getContext().getString(R.string.action_unmute_desc, formattedUsername); - unmute.setContentDescription(unmuteString); - ViewCompat.setTooltipText(unmute, unmuteString); - - if (mutingNotifications == null) { - muteNotifications.setEnabled(false); - notifications = true; - } else { - muteNotifications.setEnabled(true); - notifications = mutingNotifications; - } - - if (notifications) { - muteNotifications.setImageResource(R.drawable.ic_notifications_24dp); - String unmuteNotificationsString = muteNotifications.getContext() - .getString(R.string.action_unmute_notifications_desc, formattedUsername); - muteNotifications.setContentDescription(unmuteNotificationsString); - ViewCompat.setTooltipText(muteNotifications, unmuteNotificationsString); - } else { - muteNotifications.setImageResource(R.drawable.ic_notifications_off_24dp); - String muteNotificationsString = muteNotifications.getContext() - .getString(R.string.action_mute_notifications_desc, formattedUsername); - muteNotifications.setContentDescription(muteNotificationsString); - ViewCompat.setTooltipText(muteNotifications, muteNotificationsString); - } - } - - void setupActionListener(final AccountActionListener listener) { - unmute.setOnClickListener(v -> listener.onMute(false, id, getBindingAdapterPosition(), false)); - muteNotifications.setOnClickListener( - v -> listener.onMute(true, id, getBindingAdapterPosition(), !notifications)); - itemView.setOnClickListener(v -> listener.onViewAccount(id)); - } - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.kt new file mode 100644 index 00000000..d20f783c --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/MutesAdapter.kt @@ -0,0 +1,132 @@ +package com.keylesspalace.tusky.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.entity.Account +import com.keylesspalace.tusky.interfaces.AccountActionListener +import com.keylesspalace.tusky.util.emojify +import com.keylesspalace.tusky.util.loadAvatar +import java.util.* + +/** + * Displays a list of muted accounts with mute/unmute account and mute/unmute notifications + * buttons. + * */ +class MutesAdapter( + accountActionListener: AccountActionListener, + animateAvatar: Boolean, + animateEmojis: Boolean +) : AccountAdapter( + accountActionListener, + animateAvatar, + animateEmojis +) { + private val mutingNotificationsMap = HashMap() + + override fun createAccountViewHolder(parent: ViewGroup): MutedUserViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_muted_user, parent, false) + return MutedUserViewHolder(view) + } + + override fun onBindAccountViewHolder(viewHolder: MutedUserViewHolder, position: Int) { + val account = accountList[position] + viewHolder.setupWithAccount( + account, + mutingNotificationsMap[account.id], + animateAvatar, + animateEmojis + ) + viewHolder.setupActionListener(accountActionListener) + } + + fun updateMutingNotifications(id: String, mutingNotifications: Boolean, position: Int) { + mutingNotificationsMap[id] = mutingNotifications + notifyItemChanged(position) + } + + fun updateMutingNotificationsMap(newMutingNotificationsMap: HashMap?) { + mutingNotificationsMap.putAll(newMutingNotificationsMap!!) + notifyDataSetChanged() + } + + class MutedUserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val avatar: ImageView = itemView.findViewById(R.id.muted_user_avatar) + private val username: TextView = itemView.findViewById(R.id.muted_user_username) + private val displayName: TextView = itemView.findViewById(R.id.muted_user_display_name) + private val unmute: ImageButton = itemView.findViewById(R.id.muted_user_unmute) + private val muteNotifications: ImageButton = + itemView.findViewById(R.id.muted_user_mute_notifications) + + private var id: String? = null + private var notifications = false + + fun setupWithAccount( + account: Account, + mutingNotifications: Boolean?, + animateAvatar: Boolean, + animateEmojis: Boolean + ) { + id = account.id + val emojifiedName = account.name.emojify(account.emojis, displayName, animateEmojis) + displayName.text = emojifiedName + val format = username.context.getString(R.string.status_username_format) + val formattedUsername = String.format(format, account.username) + username.text = formattedUsername + val avatarRadius = avatar.context.resources + .getDimensionPixelSize(R.dimen.avatar_radius_48dp) + loadAvatar(account.avatar, avatar, avatarRadius, animateAvatar) + val unmuteString = + unmute.context.getString(R.string.action_unmute_desc, formattedUsername) + unmute.contentDescription = unmuteString + ViewCompat.setTooltipText(unmute, unmuteString) + if (mutingNotifications == null) { + muteNotifications.isEnabled = false + notifications = true + } else { + muteNotifications.isEnabled = true + notifications = mutingNotifications + } + if (notifications) { + muteNotifications.setImageResource(R.drawable.ic_notifications_24dp) + val unmuteNotificationsString = muteNotifications.context + .getString(R.string.action_unmute_notifications_desc, formattedUsername) + muteNotifications.contentDescription = unmuteNotificationsString + ViewCompat.setTooltipText(muteNotifications, unmuteNotificationsString) + } else { + muteNotifications.setImageResource(R.drawable.ic_notifications_off_24dp) + val muteNotificationsString = muteNotifications.context + .getString(R.string.action_mute_notifications_desc, formattedUsername) + muteNotifications.contentDescription = muteNotificationsString + ViewCompat.setTooltipText(muteNotifications, muteNotificationsString) + } + } + + fun setupActionListener(listener: AccountActionListener) { + unmute.setOnClickListener { + listener.onMute( + false, + id, + bindingAdapterPosition, + false + ) + } + muteNotifications.setOnClickListener { + listener.onMute( + true, + id, + bindingAdapterPosition, + !notifications + ) + } + itemView.setOnClickListener { listener.onViewAccount(id) } + } + } +} \ No newline at end of file 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 b42f4278..e0d8e898 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java @@ -1,4 +1,4 @@ -/* Copyright 2017 Andrew Dawson +/* Copyright 2021 Tusky Contributors * * This file is a part of Tusky. * diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.java deleted file mode 100644 index 9f85d981..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.java +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright 2017 Andrew Dawson - * - * 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 androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.widget.Button; -import android.widget.ProgressBar; - -import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.interfaces.StatusActionListener; - -public final class PlaceholderViewHolder extends RecyclerView.ViewHolder { - - private Button loadMoreButton; - private ProgressBar progressBar; - - public PlaceholderViewHolder(View itemView) { - super(itemView); - loadMoreButton = itemView.findViewById(R.id.button_load_more); - progressBar = itemView.findViewById(R.id.progressBar); - } - - public void setup(final StatusActionListener listener, boolean progress) { - loadMoreButton.setVisibility(progress ? View.GONE : View.VISIBLE); - progressBar.setVisibility(progress ? View.VISIBLE : View.GONE); - - loadMoreButton.setEnabled(true); - loadMoreButton.setOnClickListener(v -> { - loadMoreButton.setEnabled(false); - listener.onLoadMore(getBindingAdapterPosition()); - }); - - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.kt new file mode 100644 index 00000000..df05b6ae --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PlaceholderViewHolder.kt @@ -0,0 +1,41 @@ +/* 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.view.View +import android.widget.Button +import android.widget.ProgressBar +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.interfaces.StatusActionListener + +/** + * Placeholder for different timelines. + * Either displays "load more" button or a progress indicator. + **/ +class PlaceholderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val loadMoreButton: Button = itemView.findViewById(R.id.button_load_more) + private val progressBar: ProgressBar = itemView.findViewById(R.id.progressBar) + + fun setup(listener: StatusActionListener, progress: Boolean) { + loadMoreButton.visibility = if (progress) View.GONE else View.VISIBLE + progressBar.visibility = if (progress) View.VISIBLE else View.GONE + loadMoreButton.isEnabled = true + loadMoreButton.setOnClickListener { v: View? -> + loadMoreButton.isEnabled = false + listener.onLoadMore(bindingAdapterPosition) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java deleted file mode 100644 index 0143cb43..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.java +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright 2017 Andrew Dawson - * - * 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.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.keylesspalace.tusky.R; -import com.keylesspalace.tusky.interfaces.StatusActionListener; -import com.keylesspalace.tusky.util.StatusDisplayOptions; -import com.keylesspalace.tusky.viewdata.StatusViewData; - -import java.util.ArrayList; -import java.util.List; - -public class ThreadAdapter extends RecyclerView.Adapter { - private static final int VIEW_TYPE_STATUS = 0; - private static final int VIEW_TYPE_STATUS_DETAILED = 1; - - private List statuses; - private StatusDisplayOptions statusDisplayOptions; - private StatusActionListener statusActionListener; - private int detailedStatusPosition; - - public ThreadAdapter(StatusDisplayOptions statusDisplayOptions, StatusActionListener listener) { - this.statusDisplayOptions = statusDisplayOptions; - this.statusActionListener = listener; - this.statuses = new ArrayList<>(); - detailedStatusPosition = RecyclerView.NO_POSITION; - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - switch (viewType) { - default: - case VIEW_TYPE_STATUS: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_status, parent, false); - return new StatusViewHolder(view); - } - case VIEW_TYPE_STATUS_DETAILED: { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_status_detailed, parent, false); - return new StatusDetailedViewHolder(view); - } - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { - StatusViewData.Concrete status = statuses.get(position); - if (position == detailedStatusPosition) { - StatusDetailedViewHolder holder = (StatusDetailedViewHolder) viewHolder; - holder.setupWithStatus(status, statusActionListener, statusDisplayOptions); - } else { - StatusViewHolder holder = (StatusViewHolder) viewHolder; - holder.setupWithStatus(status, statusActionListener, statusDisplayOptions); - } - } - - @Override - public int getItemViewType(int position) { - if (position == detailedStatusPosition) { - return VIEW_TYPE_STATUS_DETAILED; - } else { - return VIEW_TYPE_STATUS; - } - } - - @Override - public int getItemCount() { - return statuses.size(); - } - - public void setStatuses(List statuses) { - this.statuses.clear(); - this.statuses.addAll(statuses); - notifyDataSetChanged(); - } - - public void addItem(int position, StatusViewData.Concrete statusViewData) { - statuses.add(position, statusViewData); - notifyItemInserted(position); - } - - public void clearItems() { - int oldSize = statuses.size(); - statuses.clear(); - detailedStatusPosition = RecyclerView.NO_POSITION; - notifyItemRangeRemoved(0, oldSize); - } - - public void addAll(int position, List statuses) { - this.statuses.addAll(position, statuses); - notifyItemRangeInserted(position, statuses.size()); - } - - public void addAll(List statuses) { - int end = statuses.size(); - this.statuses.addAll(statuses); - notifyItemRangeInserted(end, statuses.size()); - } - - public void removeItem(int position) { - statuses.remove(position); - notifyItemRemoved(position); - } - - public void clear() { - statuses.clear(); - detailedStatusPosition = RecyclerView.NO_POSITION; - notifyDataSetChanged(); - } - - public void setItem(int position, StatusViewData.Concrete status, boolean notifyAdapter) { - statuses.set(position, status); - if (notifyAdapter) { - notifyItemChanged(position); - } - } - - @Nullable - public StatusViewData.Concrete getItem(int position) { - if (position >= 0 && position < statuses.size()) { - return statuses.get(position); - } else { - return null; - } - } - - public void setDetailedStatusPosition(int position) { - if (position != detailedStatusPosition - && detailedStatusPosition != RecyclerView.NO_POSITION) { - int prior = detailedStatusPosition; - detailedStatusPosition = position; - notifyItemChanged(prior); - } else { - detailedStatusPosition = position; - } - } - - public int getDetailedStatusPosition() { - return detailedStatusPosition; - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.kt new file mode 100644 index 00000000..1d05dff1 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ThreadAdapter.kt @@ -0,0 +1,129 @@ +/* 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.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.interfaces.StatusActionListener +import com.keylesspalace.tusky.util.StatusDisplayOptions +import com.keylesspalace.tusky.viewdata.StatusViewData + +class ThreadAdapter( + private val statusDisplayOptions: StatusDisplayOptions, + private val statusActionListener: StatusActionListener +) : RecyclerView.Adapter() { + private val statuses = mutableListOf() + var detailedStatusPosition: Int = RecyclerView.NO_POSITION + private set + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusBaseViewHolder { + return when (viewType) { + VIEW_TYPE_STATUS -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_status, parent, false) + StatusViewHolder(view) + } + VIEW_TYPE_STATUS_DETAILED -> { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_status_detailed, parent, false) + StatusDetailedViewHolder(view) + } + else -> error("Unknown item type: $viewType") + } + } + + override fun onBindViewHolder(viewHolder: StatusBaseViewHolder, position: Int) { + val status = statuses[position] + viewHolder.setupWithStatus(status, statusActionListener, statusDisplayOptions) + } + + override fun getItemViewType(position: Int): Int { + return if (position == detailedStatusPosition) { + VIEW_TYPE_STATUS_DETAILED + } else { + VIEW_TYPE_STATUS + } + } + + override fun getItemCount(): Int = statuses.size + + fun setStatuses(statuses: List?) { + this.statuses.clear() + this.statuses.addAll(statuses!!) + notifyDataSetChanged() + } + + fun addItem(position: Int, statusViewData: StatusViewData.Concrete) { + statuses.add(position, statusViewData) + notifyItemInserted(position) + } + + fun clearItems() { + val oldSize = statuses.size + statuses.clear() + detailedStatusPosition = RecyclerView.NO_POSITION + notifyItemRangeRemoved(0, oldSize) + } + + fun addAll(position: Int, statuses: List) { + this.statuses.addAll(position, statuses) + notifyItemRangeInserted(position, statuses.size) + } + + fun addAll(statuses: List) { + val end = statuses.size + this.statuses.addAll(statuses) + notifyItemRangeInserted(end, statuses.size) + } + + fun removeItem(position: Int) { + statuses.removeAt(position) + notifyItemRemoved(position) + } + + fun clear() { + statuses.clear() + detailedStatusPosition = RecyclerView.NO_POSITION + notifyDataSetChanged() + } + + fun setItem(position: Int, status: StatusViewData.Concrete, notifyAdapter: Boolean) { + statuses[position] = status + if (notifyAdapter) { + notifyItemChanged(position) + } + } + + fun getItem(position: Int): StatusViewData.Concrete? = statuses.getOrNull(position) + + fun setDetailedStatusPosition(position: Int) { + if (position != detailedStatusPosition + && detailedStatusPosition != RecyclerView.NO_POSITION + ) { + val prior = detailedStatusPosition + detailedStatusPosition = position + notifyItemChanged(prior) + } else { + detailedStatusPosition = position + } + } + + companion object { + private const val VIEW_TYPE_STATUS = 0 + private const val VIEW_TYPE_STATUS_DETAILED = 1 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt index dc49fbc2..cd37f842 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt @@ -67,7 +67,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct private var id: String? = null private lateinit var scrollListener: EndlessOnScrollListener - private lateinit var adapter: AccountAdapter + private lateinit var adapter: AccountAdapter<*> private var fetching = false private var bottomId: String? = null