Skip to content

Commit

Permalink
Restore missing dialog fragment.
Browse files Browse the repository at this point in the history
  • Loading branch information
io7m committed Nov 15, 2024
1 parent f1d67fe commit 12a55d2
Show file tree
Hide file tree
Showing 2 changed files with 290 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package org.nypl.simplified.ui.accounts

import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.librarysimplified.services.api.Services
import org.librarysimplified.ui.accounts.R
import org.nypl.simplified.accounts.api.AccountID
import org.nypl.simplified.accounts.database.api.AccountType
import org.nypl.simplified.listeners.api.FragmentListenerType
import org.nypl.simplified.listeners.api.fragmentListeners
import org.nypl.simplified.profiles.controller.api.ProfilesControllerType
import org.nypl.simplified.ui.accounts.AccountPickerAdapter.OnAccountClickListener
import org.nypl.simplified.ui.images.ImageAccountIcons
import org.nypl.simplified.ui.images.ImageLoaderType
import org.slf4j.LoggerFactory

/**
* Present a dialog that shows a list of all active accounts for the current profile.
*/

class AccountPickerDialogFragment : BottomSheetDialogFragment(), OnAccountClickListener {

private val logger = LoggerFactory.getLogger(AccountPickerDialogFragment::class.java)
private val listener: FragmentListenerType<AccountPickerEvent> by fragmentListeners()

private lateinit var recyclerView: RecyclerView
private lateinit var imageLoader: ImageLoaderType
private lateinit var profilesController: ProfilesControllerType
private lateinit var accounts: List<AccountType>

companion object {
private const val ARG_CURRENT_ID = "org.nypl.simplified.ui.accounts.CURRENT_ID"
private const val ARG_ADD_ACCOUNT = "org.nypl.simplified.ui.accounts.ADD_ACCOUNT"

fun create(
currentId: AccountID,
showAddAccount: Boolean
): AccountPickerDialogFragment {
return AccountPickerDialogFragment().apply {
arguments = Bundle().apply {
putSerializable(ARG_CURRENT_ID, currentId)
putBoolean(ARG_ADD_ACCOUNT, showAddAccount)
}
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val services = Services.serviceDirectory()
imageLoader = services.requireService(ImageLoaderType::class.java)
profilesController = services.requireService(ProfilesControllerType::class.java)

val accountsMap =
profilesController.profileCurrent().accounts()

accounts = accountsMap.values.toList()
.sortedWith(AccountComparator())
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.account_picker, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val currentId = requireArguments().getSerializable(ARG_CURRENT_ID) as AccountID
val showAddAccount = requireArguments().getBoolean(ARG_ADD_ACCOUNT, false)

recyclerView = view.findViewById(R.id.recyclerView)
recyclerView.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(requireContext())
adapter = AccountPickerAdapter(
accounts,
currentId,
imageLoader,
showAddAccount,
this@AccountPickerDialogFragment
)
}
}

override fun onAccountClick(account: AccountType) {
this.logger.debug("selected account id={}, name={}", account.id, account.provider.displayName)

// Note: In the future consider refactoring this dialog to return a result via
// setFragmentResultListener to decouple it from the profiles logic.
val profile = profilesController.profileCurrent()
val newPreferences = profile
.preferences()
.copy(
mostRecentAccount = account.id
)
profilesController.profileUpdate { it.copy(preferences = newPreferences) }
dismiss()
}

override fun onAddAccountClick() {
this.listener.post(AccountPickerEvent.AddAccount)
dismiss()
}

override fun onCancelClick() {
dismiss()
}
}

class AccountPickerViewHolder(
view: View,
private val imageLoader: ImageLoaderType,
private val listener: OnAccountClickListener
) : RecyclerView.ViewHolder(view) {
private val titleView: TextView = view.findViewById(R.id.accountTitle)
private val activeView: View = view.findViewById(R.id.activeAccount)
private val iconView: ImageView = view.findViewById(R.id.accountIcon)

var account: AccountType? = null

init {
view.setOnClickListener {
account?.let { listener.onAccountClick(it) }
}
}

fun bind(account: AccountType, isCurrent: Boolean) {
this.account = account

titleView.text = account.provider.displayName

if (isCurrent) {
titleView.typeface = Typeface.DEFAULT_BOLD
activeView.visibility = View.VISIBLE
} else {
titleView.typeface = Typeface.DEFAULT
activeView.visibility = View.GONE
}

ImageAccountIcons.loadAccountLogoIntoView(
loader = this.imageLoader.loader,
account = account.provider.toDescription(),
defaultIcon = R.drawable.account_default,
iconView = iconView
)
}
}

class AddAccountViewHolder(
view: View,
private val listener: OnAccountClickListener
) : RecyclerView.ViewHolder(view) {

init {
val titleView: TextView = view.findViewById(R.id.accountTitle)
titleView.setText(R.string.accountAdd)

val iconView: ImageView = view.findViewById(R.id.accountIcon)
iconView.colorFilter =
PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN)
iconView.setImageResource(R.drawable.ic_add)

view.setOnClickListener {
listener.onAddAccountClick()
}
}
}

class CancelViewHolder(
view: View,
private val listener: OnAccountClickListener
) : RecyclerView.ViewHolder(view) {

init {
val titleView: TextView = view.findViewById(R.id.accountTitle)
titleView.setText(R.string.accountCancel)

val iconView: ImageView = view.findViewById(R.id.accountIcon)
iconView.visibility = View.GONE

view.setOnClickListener {
listener.onCancelClick()
}
}
}

class AccountPickerAdapter(
private val accounts: List<AccountType>,
private val currentId: AccountID,
private val imageLoader: ImageLoaderType,
private val showAddAccount: Boolean,
private val listener: OnAccountClickListener
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

companion object {
private const val LIST_ITEM = 1
private const val LIST_ADD_ACCOUNT = 2
private const val LIST_CANCEL = 3
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.account_picker_item, parent, false)
return when (viewType) {
LIST_CANCEL -> CancelViewHolder(view, listener)
LIST_ADD_ACCOUNT -> AddAccountViewHolder(view, listener)
else -> AccountPickerViewHolder(view, imageLoader, listener)
}
}

override fun getItemCount() = accounts.size + if (showAddAccount) {
2 // Show the 'add account' and 'cancel' options
} else {
1 // Show 'cancel' option
}

override fun getItemViewType(position: Int) = when (position) {
accounts.size + 1 -> LIST_CANCEL
accounts.size ->
if (showAddAccount) {
LIST_ADD_ACCOUNT
} else {
LIST_CANCEL
}
else -> LIST_ITEM
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
LIST_ITEM -> {
val item = accounts[position]
(holder as AccountPickerViewHolder).bind(item, item.id == currentId)
}
}
}

interface OnAccountClickListener {
fun onAccountClick(account: AccountType)
fun onAddAccountClick()
fun onCancelClick()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import org.nypl.simplified.feeds.api.FeedGroup
import org.nypl.simplified.listeners.api.FragmentListenerType
import org.nypl.simplified.listeners.api.fragmentListeners
import org.nypl.simplified.profiles.controller.api.ProfilesControllerType
import org.nypl.simplified.ui.accounts.AccountPickerDialogFragment
import org.nypl.simplified.ui.images.ImageAccountIcons
import org.nypl.simplified.ui.images.ImageLoaderType
import org.nypl.simplified.ui.screen.ScreenSizeInformationType
Expand Down Expand Up @@ -575,6 +576,20 @@ class CatalogFeedFragment : Fragment(R.layout.feed), AgeGateDialog.BirthYearSele
return
}

/*
* If we're at the root of a feed and the app is configured such that the user should
* be allowed to change accounts, then display the current account's logo in the toolbar.
*/

if (this.configurationService.showChangeAccountsUi) {
actionBar.setHomeActionContentDescription(R.string.catalogAccounts)
actionBar.setLogo(this.configurationService.brandingAppIcon)
this.toolbar.setLogoOnClickListener {
this.openAccountPickerDialog()
}
return
}

/*
* Otherwise, show nothing.
*/
Expand All @@ -590,6 +605,26 @@ class CatalogFeedFragment : Fragment(R.layout.feed), AgeGateDialog.BirthYearSele
}
}

private fun openAccountPickerDialog() {
try {
return when (val ownership = this.parameters.ownership) {
is OwnedByAccount -> {
val dialog =
AccountPickerDialogFragment.create(
currentId = ownership.accountId,
showAddAccount = this.configurationService.allowAccountsAccess
)
dialog.show(parentFragmentManager, dialog.tag)
}
CollectedFromAccounts -> {
throw IllegalStateException("Can't switch account from collected feed!")
}
}
} catch (e: Exception) {
this.logger.debug("Failed to open account picker dialog: ", e)
}
}

private fun configureFacets(
facetsByGroup: Map<String, List<FeedFacet>>,
sortFacets: Boolean
Expand Down

0 comments on commit 12a55d2

Please sign in to comment.