Skip to content
Snippets Groups Projects
AccountAdapter.kt 6.52 KiB
Newer Older
 *  Copyright (C) 2004-2023 Savoir-faire Linux Inc.
Adrien Béraud's avatar
Adrien Béraud committed
 *  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
Adrien Béraud's avatar
Adrien Béraud committed
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Adrien Béraud's avatar
Adrien Béraud committed
 *  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
Adrien Béraud's avatar
Adrien Béraud committed
 *  along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package cx.ring.client

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import cx.ring.R
import cx.ring.databinding.ItemToolbarSpinnerBinding
import cx.ring.utils.DeviceUtils
import cx.ring.views.AvatarDrawable
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.CompositeDisposable
import net.jami.model.Account
import net.jami.model.Profile
import net.jami.services.AccountService
import net.jami.services.ConversationFacade

/**
 * Adapter allowing to select account user want to use.
 */
class AccountAdapter(
    context: Context,
    accounts: List<Account>,
    val disposable: CompositeDisposable,
    var mAccountService: AccountService,
    var mConversationFacade: ConversationFacade,
) : ArrayAdapter<Account>(context, R.layout.item_toolbar_spinner, accounts) {
    private val mInflater: LayoutInflater = LayoutInflater.from(context)
    private val ip2ipString = context.getString(R.string.account_type_ip2ip)
    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {

        // Try to recycle the view
        val holder: ViewHolder
        var view = convertView
        if (view != null) {
            holder = view.tag as ViewHolder
            holder.loader.clear()
            holder.binding.logo.setImageDrawable(null)
            holder.binding.title.text = null
            holder.binding.subtitle.text = null
        } else { // Create a new view
            holder = ViewHolder(
                ItemToolbarSpinnerBinding.inflate(mInflater, parent, false),
                disposable
            )
            view = holder.binding.root
            view.setTag(holder)
        }

        // Items can be:
        // - available accounts (type=TYPE_ACCOUNT)
        // - a button to create a new account (type=TYPE_CREATE_ACCOUNT)
        val type = getItemViewType(position)
        if (type == TYPE_ACCOUNT) {
            val account = getItem(position)!!
            // Update the unread counter (sum of unread conversations and pending conversations)
            holder.loader.add(
                mAccountService.getObservableAccountProfile(account.accountId).switchMap {
                    Observable.combineLatest(
                        account.unreadConversations,
                        account.getPendingSubject()
                    ) { unreadConversationCounter, pendingConversationList ->
                        unreadConversationCounter + pendingConversationList.size
                    }
                }
                    .observeOn(DeviceUtils.uiScheduler)
                    .subscribe {
                        if (it > 0) {
                            holder.binding.invitationBadge.visibility = View.VISIBLE
                            holder.binding.invitationBadge.text = it.toString()
                        } else holder.binding.invitationBadge.visibility = View.GONE
                    }
            )

            // Subscribe to account profile changes to update:
            // - avatar
            // - name (will display the jami if name is not setup)
            holder.loader.add(mAccountService.getObservableAccountProfile(account.accountId)
                .observeOn(DeviceUtils.uiScheduler)
                .subscribe({ profile ->
                    val subtitle = getUri(account, ip2ipString)
                    holder.binding.logo.setImageDrawable(
                        AvatarDrawable.build(
                            holder.binding.root.context,
                            profile.first,
                            profile.second,
                            true,
                            profile.first.isRegistered
                        )
                    )
                    holder.binding.title.text = getTitle(profile.first, profile.second)
                    if (holder.binding.title.text == subtitle) {
                        holder.binding.subtitle.visibility = View.GONE
                    } else {
                        holder.binding.subtitle.visibility = View.VISIBLE
                        holder.binding.subtitle.text = subtitle
                    }
                }) { e: Throwable -> Log.e(TAG, "Error loading avatar", e) })
            holder.binding.invitationBadge.visibility = View.GONE
            holder.binding.title.setText(
                if (type == TYPE_CREATE_ACCOUNT) R.string.add_ring_account_title
                else R.string.add_sip_account_title
            )
            holder.binding.logo.setImageResource(R.drawable.baseline_add_24)
            holder.binding.subtitle.visibility = View.GONE
        return view
    }

    override fun getItemViewType(position: Int): Int {
        if (position == super.getCount()) {
            return TYPE_CREATE_ACCOUNT
        return TYPE_ACCOUNT
    override fun getCount(): Int = super.getCount() + 1
    private fun getTitle(account: Account, profile: Profile): String =
        profile.displayName.orEmpty().ifEmpty {
            account.registeredName.ifEmpty {
                account.alias.orEmpty().ifEmpty {
                    if (account.isSip) context.getString(R.string.sip_account)
                    else context.getString(R.string.ring_account)
    private class ViewHolder(val binding: ItemToolbarSpinnerBinding, parentDisposable: CompositeDisposable) {
        val loader = CompositeDisposable().apply { parentDisposable.add(this) }
Adrien Béraud's avatar
Adrien Béraud committed
    private fun getUri(account: Account, defaultNameSip: CharSequence): String =
        if (account.isIP2IP) defaultNameSip.toString() else account.displayUri!!

    companion object {
        private val TAG = AccountAdapter::class.simpleName!!
        const val TYPE_ACCOUNT = 0
        const val TYPE_CREATE_ACCOUNT = 1