diff options
author | Torsten Grote <t@grobox.de> | 2020-04-30 14:33:08 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-04-30 14:33:08 -0300 |
commit | 056be53a8d51fb6e45167f5d1ec317513b87a906 (patch) | |
tree | cb14e2ca3e10c29cc7ad82fb8499bb375c70dd5a /wallet/src/main/java/net | |
parent | 0d4d6358ac6db7e0a1e936ecfd70ec4bb3262846 (diff) | |
download | taler-android-056be53a8d51fb6e45167f5d1ec317513b87a906.tar.gz taler-android-056be53a8d51fb6e45167f5d1ec317513b87a906.tar.bz2 taler-android-056be53a8d51fb6e45167f5d1ec317513b87a906.zip |
[wallet] show ToS markdown in expandable sections
Diffstat (limited to 'wallet/src/main/java/net')
3 files changed, 180 insertions, 4 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt index 47b6f14..ffaef5a 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt @@ -16,7 +16,6 @@ package net.taler.wallet.withdraw - import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -25,16 +24,20 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController +import io.noties.markwon.Markwon import kotlinx.android.synthetic.main.fragment_review_exchange_tos.* import net.taler.common.fadeIn import net.taler.common.fadeOut -import net.taler.wallet.R import net.taler.wallet.MainViewModel +import net.taler.wallet.R +import java.text.ParseException class ReviewExchangeTosFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val withdrawManager by lazy { model.withdrawManager } + private val markwon by lazy { Markwon.builder(requireContext()).build() } + private val adapter by lazy { TosAdapter(markwon) } override fun onCreateView( inflater: LayoutInflater, @@ -53,8 +56,18 @@ class ReviewExchangeTosFragment : Fragment() { withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer { when (it) { is WithdrawStatus.TermsOfServiceReviewRequired -> { - tosTextView.text = it.tosText - tosTextView.fadeIn() + val sections = try { + // TODO remove next line once exchange delivers proper markdown + val text = it.tosText.replace("****************", "================") + parseTos(markwon, text) + } catch (e: ParseException) { + onTosError(e.message ?: "Unknown Error") + return@Observer + } + adapter.setSections(sections) + tosList.adapter = adapter + tosList.fadeIn() + acceptTosCheckBox.fadeIn() progressBar.fadeOut() } @@ -68,4 +81,12 @@ class ReviewExchangeTosFragment : Fragment() { }) } + private fun onTosError(msg: String) { + tosList.fadeIn() + progressBar.fadeOut() + buttonCard.fadeOut() + errorView.text = getString(R.string.exchange_tos_error, "\n\n$msg") + errorView.fadeIn() + } + } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/TosAdapter.kt b/wallet/src/main/java/net/taler/wallet/withdraw/TosAdapter.kt new file mode 100644 index 0000000..74a798f --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/withdraw/TosAdapter.kt @@ -0,0 +1,90 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler 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, or (at your option) any later version. + * + * GNU Taler 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 + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.withdraw + +import android.transition.TransitionManager.beginDelayedTransition +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import io.noties.markwon.Markwon +import net.taler.wallet.R + +class TosAdapter( + private val markwon: Markwon +) : RecyclerView.Adapter<TosAdapter.TosSectionViewHolder>() { + + private val items = ArrayList<TosSection>() + + init { + setHasStableIds(true) + } + + override fun getItemCount() = items.size + + override fun getItemId(position: Int): Long { + return items[position].node.hashCode().toLong() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TosSectionViewHolder { + val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item_tos, parent, false) + return TosSectionViewHolder(v) + } + + override fun onBindViewHolder(holder: TosSectionViewHolder, position: Int) { + holder.bind(items[position]) + } + + fun setSections(sections: List<TosSection>) { + items.clear() + items.addAll(sections) + notifyDataSetChanged() + } + + inner class TosSectionViewHolder(private val v: View) : RecyclerView.ViewHolder(v) { + private val sectionTitle: TextView = v.findViewById(R.id.sectionTitle) + private val expandButton: ImageView = v.findViewById(R.id.expandButton) + private val sectionText: TextView = v.findViewById(R.id.sectionText) + + fun bind(item: TosSection) { + sectionTitle.text = item.title + showSection(item, item.expanded) + val onClickListener = View.OnClickListener { + if (!item.expanded) beginDelayedTransition(v as ViewGroup) + item.expanded = !item.expanded + showSection(item, item.expanded) + } + sectionTitle.setOnClickListener(onClickListener) + } + + private fun showSection(item: TosSection, show: Boolean) { + if (show) { + expandButton.setImageResource(R.drawable.ic_keyboard_arrow_up) + markwon.setParsedMarkdown(sectionText, markwon.render(item.node)) + sectionText.visibility = VISIBLE + } else { + expandButton.setImageResource(R.drawable.ic_keyboard_arrow_down) + sectionText.visibility = GONE + } + } + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt b/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt new file mode 100644 index 0000000..72a9e34 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt @@ -0,0 +1,65 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler 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, or (at your option) any later version. + * + * GNU Taler 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 + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.withdraw + +import io.noties.markwon.Markwon +import org.commonmark.node.Document +import org.commonmark.node.Heading +import org.commonmark.node.Node +import org.commonmark.node.Text +import java.text.ParseException + +data class TosSection( + val title: String, + val node: Node, + var expanded: Boolean = false +) + +@Throws(ParseException::class) +internal fun parseTos(markwon: Markwon, text: String): List<TosSection> { + var node: Node? = + markwon.parse(text).firstChild ?: throw ParseException("Invalid markdown", 0) + var lastHeading: String? = null + var section = Document() + val sections = ArrayList<TosSection>() + while (node != null) { + val next: Node? = node.next + if (node is Heading && node.level == 1) { + // if lastHeading exists, close previous section + if (lastHeading != null) { + sections.add(TosSection(lastHeading, section)) + section = Document() + } + // check that this is a plain heading + if (node.firstChild !is Text || node.firstChild.next != null) { + throw ParseException( + "Primary heading includes more than just text", sections.size + ) + } + // start new section + lastHeading = (node.firstChild as Text).literal + } else if (lastHeading == null) { + throw ParseException("Found text before first primary heading", 0) + } else { + section.appendChild(node) + } + node = next + } + check(lastHeading != null) + sections.add(TosSection(lastHeading, section)) + return sections +} |