mirror of https://github.com/tasks/tasks
New purchase dialog
parent
afd5469d72
commit
e10d78c712
@ -1,204 +0,0 @@
|
||||
package org.tasks.billing
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.OnClick
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.tasks.LocalBroadcastManager
|
||||
import org.tasks.R
|
||||
import org.tasks.databinding.ActivityPurchaseBinding
|
||||
import org.tasks.dialogs.DialogBuilder
|
||||
import org.tasks.dialogs.IconLayoutManager
|
||||
import org.tasks.injection.ThemedInjectingAppCompatActivity
|
||||
import org.tasks.locale.Locale
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val EXTRA_MONTHLY = "extra_monthly"
|
||||
private const val EXTRA_PRICE = "extra_price"
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PurchaseActivity : ThemedInjectingAppCompatActivity(), OnPurchasesUpdated, Toolbar.OnMenuItemClickListener {
|
||||
|
||||
@Inject lateinit var inventory: Inventory
|
||||
@Inject lateinit var dialogBuilder: DialogBuilder
|
||||
@Inject lateinit var billingClient: BillingClient
|
||||
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
||||
@Inject lateinit var locale: Locale
|
||||
|
||||
private lateinit var binding: ActivityPurchaseBinding
|
||||
private lateinit var adapter: PurchaseAdapter
|
||||
|
||||
private var currentSubscription: Purchase? = null
|
||||
|
||||
private val purchaseReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityPurchaseBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
ButterKnife.bind(this)
|
||||
|
||||
adapter = PurchaseAdapter(this, tasksTheme, locale, ::onPriceChanged)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
binding.buttons.check(
|
||||
if (savedInstanceState.getBoolean(EXTRA_MONTHLY)) R.id.button_monthly else R.id.button_annually)
|
||||
adapter.selected = savedInstanceState.getInt(EXTRA_PRICE)
|
||||
|
||||
}
|
||||
|
||||
binding.buttons.addOnButtonCheckedListener { group: MaterialButtonToggleGroup?, id: Int, checked: Boolean -> this.onButtonChecked(group!!, id, checked) }
|
||||
|
||||
val toolbar = binding.toolbar.toolbar
|
||||
toolbar.setNavigationIcon(R.drawable.ic_outline_arrow_back_24px)
|
||||
toolbar.setNavigationContentDescription(R.string.back)
|
||||
toolbar.setNavigationOnClickListener { finish() }
|
||||
toolbar.setTitle(R.string.upgrade_to_pro)
|
||||
toolbar.inflateMenu(R.menu.menu_purchase_activity)
|
||||
toolbar.setOnMenuItemClickListener(this)
|
||||
|
||||
themeColor.apply(toolbar)
|
||||
|
||||
setWaitScreen(true)
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@OnClick(R.id.subscribe)
|
||||
fun subscribe() {
|
||||
if (currentSubscriptionSelected() && currentSubscription?.isCanceled == true) {
|
||||
billingClient.initiatePurchaseFlow(
|
||||
this, currentSubscription!!.sku, BillingClientImpl.TYPE_SUBS, null)
|
||||
} else {
|
||||
billingClient.initiatePurchaseFlow(this, String.format("%s_%02d", if (isMonthly()) "monthly" else "annual", adapter.selected),
|
||||
BillingClientImpl.TYPE_SUBS,
|
||||
currentSubscription?.sku)
|
||||
}
|
||||
billingClient.addPurchaseCallback(this)
|
||||
}
|
||||
|
||||
private fun onButtonChecked(group: MaterialButtonToggleGroup, id: Int, checked: Boolean) {
|
||||
if (id == R.id.button_monthly) {
|
||||
if (!checked && group.checkedButtonId != R.id.button_annually) {
|
||||
group.check(R.id.button_monthly)
|
||||
}
|
||||
} else {
|
||||
if (!checked && group.checkedButtonId != R.id.button_monthly) {
|
||||
group.check(R.id.button_annually)
|
||||
}
|
||||
}
|
||||
updateSubscribeButton()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putBoolean(EXTRA_MONTHLY, isMonthly())
|
||||
outState.putInt(EXTRA_PRICE, adapter.selected)
|
||||
}
|
||||
|
||||
private fun isMonthly() = binding.buttons.checkedButtonId == R.id.button_monthly
|
||||
|
||||
private fun setWaitScreen(isWaitScreen: Boolean) {
|
||||
Timber.d("setWaitScreen(%s)", isWaitScreen)
|
||||
binding.recyclerView.visibility = if (isWaitScreen) View.GONE else View.VISIBLE
|
||||
binding.buttons.visibility = if (isWaitScreen) View.GONE else View.VISIBLE
|
||||
binding.subscribe.visibility = if (isWaitScreen) View.GONE else View.VISIBLE
|
||||
binding.screenWait.visibility = if (isWaitScreen) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
localBroadcastManager.registerPurchaseReceiver(purchaseReceiver)
|
||||
billingClient.queryPurchases()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
localBroadcastManager.unregisterReceiver(purchaseReceiver)
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
currentSubscription = inventory.subscription
|
||||
if (adapter.selected == 0) {
|
||||
adapter.selected = currentSubscription?.subscriptionPrice ?: 3
|
||||
if (currentSubscription != null) {
|
||||
binding.buttons.check(if (currentSubscription?.isMonthly == true) R.id.button_monthly else R.id.button_annually)
|
||||
}
|
||||
}
|
||||
binding.unsubscribe.visibility = if (currentSubscription == null || currentSubscription?.isCanceled == true) View.GONE else View.VISIBLE
|
||||
updateSubscribeButton()
|
||||
setWaitScreen(false)
|
||||
adapter.submitList((1..15).toList())
|
||||
binding.recyclerView.layoutManager = IconLayoutManager(this)
|
||||
binding.recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private fun updateSubscribeButton() {
|
||||
binding.subscribe.isEnabled = true
|
||||
if (currentSubscription == null) {
|
||||
binding.subscribe.setText(R.string.button_subscribe)
|
||||
} else if (currentSubscriptionSelected()) {
|
||||
if (currentSubscription!!.isCanceled) {
|
||||
binding.subscribe.setText(R.string.button_restore_subscription)
|
||||
} else {
|
||||
binding.subscribe.setText(R.string.button_current_subscription)
|
||||
binding.subscribe.isEnabled = false
|
||||
}
|
||||
} else {
|
||||
binding.subscribe.setText(if (isUpgrade()) R.string.button_upgrade else R.string.button_downgrade)
|
||||
}
|
||||
}
|
||||
|
||||
private fun currentSubscriptionSelected() =
|
||||
currentSubscription != null
|
||||
&& isMonthly() == currentSubscription!!.isMonthly
|
||||
&& adapter.selected == currentSubscription!!.subscriptionPrice
|
||||
|
||||
private fun isUpgrade() = if (isMonthly() == currentSubscription!!.isMonthly) {
|
||||
currentSubscription!!.subscriptionPrice!! < adapter.selected
|
||||
} else {
|
||||
isMonthly()
|
||||
}
|
||||
|
||||
private fun onPriceChanged(price: Int) {
|
||||
adapter.selected = price
|
||||
updateSubscribeButton()
|
||||
}
|
||||
|
||||
@OnClick(R.id.unsubscribe)
|
||||
fun manageSubscription() {
|
||||
startActivity(
|
||||
Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse(getString(R.string.manage_subscription_url, currentSubscription!!.sku))))
|
||||
}
|
||||
|
||||
override fun onPurchasesUpdated(success: Boolean) {
|
||||
if (success) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
return if (item?.itemId == R.id.menu_more_info) {
|
||||
startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.subscription_help_url))))
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
package org.tasks.billing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import org.tasks.Callback;
|
||||
import org.tasks.R;
|
||||
import org.tasks.locale.Locale;
|
||||
import org.tasks.themes.Theme;
|
||||
|
||||
public class PurchaseAdapter extends ListAdapter<Integer, PurchaseHolder> {
|
||||
|
||||
private final Context context;
|
||||
private final Theme theme;
|
||||
private final Locale locale;
|
||||
private final Callback<Integer> onPriceChanged;
|
||||
private int selected;
|
||||
|
||||
PurchaseAdapter(Context context, Theme theme, Locale locale, Callback<Integer> onPriceChanged) {
|
||||
super(new DiffCallback());
|
||||
this.context = context;
|
||||
this.theme = theme;
|
||||
this.locale = locale;
|
||||
this.onPriceChanged = onPriceChanged;
|
||||
}
|
||||
|
||||
public int getSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(int price) {
|
||||
int previous = selected;
|
||||
this.selected = price;
|
||||
notifyItemChanged(previous - 1, null);
|
||||
notifyItemChanged(price - 1, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PurchaseHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view =
|
||||
theme.getLayoutInflater(context).inflate(R.layout.dialog_purchase_cell, parent, false);
|
||||
return new PurchaseHolder(view, onPriceChanged, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PurchaseHolder holder, int position) {
|
||||
int price = position + 1;
|
||||
holder.bind(price, price == selected);
|
||||
}
|
||||
|
||||
private static class DiffCallback extends ItemCallback<Integer> {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull Integer oldItem, @NonNull Integer newItem) {
|
||||
return oldItem.equals(newItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull Integer oldItem, @NonNull Integer newItem) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,250 @@
|
||||
package org.tasks.billing
|
||||
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.app.Dialog
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.OnClick
|
||||
import com.google.android.material.slider.Slider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
|
||||
import org.tasks.LocalBroadcastManager
|
||||
import org.tasks.R
|
||||
import org.tasks.databinding.ActivityPurchaseBinding
|
||||
import org.tasks.dialogs.DialogBuilder
|
||||
import org.tasks.locale.Locale
|
||||
import org.tasks.themes.Theme
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PurchaseDialog : DialogFragment(), OnPurchasesUpdated {
|
||||
|
||||
private val purchaseReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
setup()
|
||||
}
|
||||
}
|
||||
|
||||
@Inject lateinit var tasksTheme: Theme
|
||||
@Inject lateinit var inventory: Inventory
|
||||
@Inject lateinit var dialogBuilder: DialogBuilder
|
||||
@Inject lateinit var billingClient: BillingClient
|
||||
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
||||
@Inject lateinit var locale: Locale
|
||||
|
||||
private lateinit var binding: ActivityPurchaseBinding
|
||||
private lateinit var markwon: Markwon
|
||||
|
||||
private var currentSubscription: Purchase? = null
|
||||
private var priceChanged = false
|
||||
private var nameYourPrice = false
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = ActivityPurchaseBinding.inflate(layoutInflater)
|
||||
ButterKnife.bind(this, binding.root)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
binding.slider.value = savedInstanceState.getFloat(EXTRA_PRICE)
|
||||
priceChanged = savedInstanceState.getBoolean(EXTRA_PRICE_CHANGED)
|
||||
nameYourPrice = savedInstanceState.getBoolean(EXTRA_NAME_YOUR_PRICE)
|
||||
}
|
||||
|
||||
binding.slider.addOnChangeListener(this::onPriceChanged)
|
||||
binding.slider.setLabelFormatter {
|
||||
"$${it - .01}"
|
||||
}
|
||||
binding.text.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
markwon = Markwon.builder(requireContext())
|
||||
.usePlugin(StrikethroughPlugin.create())
|
||||
.build()
|
||||
|
||||
setWaitScreen(true)
|
||||
|
||||
return dialogBuilder.newDialog()
|
||||
.setView(binding.root)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun updateText() {
|
||||
var benefits = "### ${getString(R.string.upgrade_header)}"
|
||||
if (nameYourPrice) {
|
||||
benefits += """
|
||||
---
|
||||
#### ~~${getString(R.string.upgrade_sync_with_tasks)}~~
|
||||
"""
|
||||
} else {
|
||||
benefits += """
|
||||
---
|
||||
#### [${getString(R.string.upgrade_sync_with_tasks)} (BETA)](https://tasks.org/sync)
|
||||
* **${getString(R.string.upgrade_no_platform_lock_in)}** — ${getString(R.string.upgrade_open_internet_standards)}
|
||||
* **${getString(R.string.upgrade_customer)}** — ${getString(R.string.upgrade_privacy)}
|
||||
* ${getString(R.string.upgrade_coming_soon)}
|
||||
"""
|
||||
}
|
||||
benefits += """
|
||||
---
|
||||
#### ${getString(R.string.upgrade_synchronization)}
|
||||
* [${getString(R.string.davx5)}](https://tasks.org/docs/davx5.html)
|
||||
* [${getString(R.string.caldav)}](https://tasks.org/docs/caldav_intro.html)
|
||||
* [${getString(R.string.upgrade_etesync)}](https://tasks.org/docs/etesync_intro.html)
|
||||
* ${getString(R.string.upgrade_google_tasks)}
|
||||
---
|
||||
#### ${getString(R.string.upgrade_additional_features)}
|
||||
* ${getString(R.string.upgrade_themes)}
|
||||
* ${getString(R.string.upgrade_google_places)}
|
||||
* [${getString(R.string.upgrade_tasker)}](https://tasks.org/docs/tasker.html)
|
||||
---
|
||||
* ${getString(R.string.upgrade_free_trial)}
|
||||
* **${getString(R.string.upgrade_downgrade)}** — ${getString(R.string.upgrade_balance)}
|
||||
* **${getString(R.string.upgrade_cancel)}** — ${getString(R.string.upgrade_benefits_retained)}
|
||||
"""
|
||||
|
||||
binding.text.text = markwon.toMarkdown(benefits)
|
||||
}
|
||||
|
||||
@OnClick(R.id.pay_annually)
|
||||
fun subscribeAnnually() {
|
||||
initiatePurchase(false, 30)
|
||||
}
|
||||
|
||||
@OnClick(R.id.pay_monthly)
|
||||
fun subscribeMonthly() {
|
||||
initiatePurchase(true, 3)
|
||||
}
|
||||
|
||||
private fun initiatePurchase(isMonthly: Boolean, price: Int) {
|
||||
val newSku = String.format("%s_%02d", if (isMonthly) "monthly" else "annual", price)
|
||||
billingClient.initiatePurchaseFlow(
|
||||
requireActivity(),
|
||||
newSku,
|
||||
BillingClientImpl.TYPE_SUBS,
|
||||
currentSubscription?.sku?.takeIf { it != newSku })
|
||||
billingClient.addPurchaseCallback(this)
|
||||
}
|
||||
|
||||
@OnClick(R.id.pay_other)
|
||||
fun nameYourPrice() {
|
||||
nameYourPrice = !nameYourPrice
|
||||
setWaitScreen(false)
|
||||
binding.scroll.scrollTo(0, 0)
|
||||
updateSubscribeButton()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putFloat(EXTRA_PRICE, binding.slider.value)
|
||||
outState.putBoolean(EXTRA_PRICE_CHANGED, priceChanged)
|
||||
outState.putBoolean(EXTRA_NAME_YOUR_PRICE, nameYourPrice)
|
||||
}
|
||||
|
||||
private fun setWaitScreen(isWaitScreen: Boolean) {
|
||||
Timber.d("setWaitScreen(%s)", isWaitScreen)
|
||||
binding.slider.isVisible = !isWaitScreen && nameYourPrice
|
||||
binding.payOther.isVisible = !isWaitScreen
|
||||
binding.payOther.setText(if (nameYourPrice) R.string.back else R.string.more_options)
|
||||
binding.tasksOrgButtonPanel.isVisible = !isWaitScreen
|
||||
binding.screenWait.isVisible = isWaitScreen
|
||||
updateText()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
localBroadcastManager.registerPurchaseReceiver(purchaseReceiver)
|
||||
billingClient.queryPurchases()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
localBroadcastManager.unregisterReceiver(purchaseReceiver)
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
currentSubscription = inventory.subscription
|
||||
if (!priceChanged) {
|
||||
binding.slider.value =
|
||||
currentSubscription
|
||||
?.subscriptionPrice
|
||||
?.coerceAtMost(25)
|
||||
?.toFloat() ?: 10f
|
||||
}
|
||||
updateSubscribeButton()
|
||||
setWaitScreen(false)
|
||||
}
|
||||
|
||||
private fun updateSubscribeButton() {
|
||||
val sliderValue = binding.slider.value.toInt()
|
||||
val annualPrice = if (nameYourPrice) sliderValue else 30
|
||||
val monthlyPrice = if (nameYourPrice) sliderValue else 3
|
||||
val constrained = resources.getBoolean(R.bool.width_constrained)
|
||||
binding.payAnnually.let {
|
||||
it.isEnabled = true
|
||||
it.text = getString(
|
||||
if (constrained) R.string.price_per_year_abbreviated else R.string.price_per_year,
|
||||
annualPrice - .01
|
||||
)
|
||||
it.setOnClickListener {
|
||||
initiatePurchase(false, if (nameYourPrice) sliderValue else 30)
|
||||
}
|
||||
}
|
||||
binding.payMonthly.let {
|
||||
it.isEnabled = true
|
||||
it.text = getString(
|
||||
if (constrained) R.string.price_per_month_abbreviated else R.string.price_per_month,
|
||||
monthlyPrice - .01
|
||||
)
|
||||
it.setOnClickListener {
|
||||
initiatePurchase(true, if (nameYourPrice) sliderValue else 3)
|
||||
}
|
||||
it.isVisible = !nameYourPrice || sliderValue < 3
|
||||
}
|
||||
currentSubscription?.let {
|
||||
binding.payMonthly.isEnabled =
|
||||
it.isCanceled || !it.isMonthly || monthlyPrice != it.subscriptionPrice
|
||||
binding.payAnnually.isEnabled =
|
||||
it.isCanceled || it.isMonthly || annualPrice != it.subscriptionPrice
|
||||
}
|
||||
}
|
||||
|
||||
private fun onPriceChanged(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
priceChanged = true
|
||||
}
|
||||
updateSubscribeButton()
|
||||
}
|
||||
|
||||
override fun onPurchasesUpdated(success: Boolean) {
|
||||
if (success) {
|
||||
dismiss()
|
||||
targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, null)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_PRICE = "extra_price"
|
||||
private const val EXTRA_PRICE_CHANGED = "extra_price_changed"
|
||||
private const val EXTRA_NAME_YOUR_PRICE = "extra_name_your_price"
|
||||
@JvmStatic
|
||||
val FRAG_TAG_PURCHASE_DIALOG = "frag_tag_purchase_dialog"
|
||||
|
||||
@JvmStatic
|
||||
fun newPurchaseDialog(): PurchaseDialog {
|
||||
return PurchaseDialog()
|
||||
}
|
||||
|
||||
fun newPurchaseDialog(target: Fragment, rc: Int): PurchaseDialog {
|
||||
val dialog = PurchaseDialog()
|
||||
dialog.setTargetFragment(target, rc)
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package org.tasks.billing;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import org.tasks.Callback;
|
||||
import org.tasks.R;
|
||||
import org.tasks.locale.Locale;
|
||||
|
||||
public class PurchaseHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final Callback<Integer> onClick;
|
||||
private final Locale locale;
|
||||
|
||||
@BindView(R.id.price)
|
||||
MaterialButton button;
|
||||
|
||||
private int price;
|
||||
|
||||
PurchaseHolder(@NonNull View view, Callback<Integer> onClick, Locale locale) {
|
||||
super(view);
|
||||
this.locale = locale;
|
||||
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
this.onClick = onClick;
|
||||
}
|
||||
|
||||
@OnClick(R.id.price)
|
||||
void onClick() {
|
||||
onClick.call(price);
|
||||
}
|
||||
|
||||
public void bind(int price, boolean selected) {
|
||||
this.price = price;
|
||||
button.setText(String.format("$%s", locale.formatNumber(price)));
|
||||
button.setChecked(selected);
|
||||
}
|
||||
}
|
||||
@ -1,127 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar"
|
||||
layout="@layout/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/scroll"
|
||||
android:layout_above="@id/bottom_panel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:scrollbars="none"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/upgrade_blurb_1"
|
||||
android:padding="@dimen/keyline_first"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/upgrade_blurb_2"
|
||||
android:padding="@dimen/keyline_first"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/upgrade_blurb_3"
|
||||
android:padding="@dimen/keyline_first"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/upgrade_blurb_4"
|
||||
android:padding="@dimen/keyline_first"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/screen_wait"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:visibility="gone"
|
||||
app:checkedButton="@id/button_monthly"
|
||||
app:singleSelection="true"
|
||||
android:paddingStart="@dimen/keyline_content_inset"
|
||||
android:paddingEnd="@dimen/keyline_content_inset">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_monthly"
|
||||
style="@style/OutlineButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/monthly" />
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:textSize="16sp"
|
||||
android:padding="@dimen/keyline_second"/>
|
||||
</ScrollView>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_annually"
|
||||
style="@style/OutlineButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/annually" />
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_below="@id/scroll"
|
||||
style="@style/task_edit_row_divider"
|
||||
/>
|
||||
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_panel"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/keyline_first"
|
||||
android:paddingBottom="@dimen/keyline_first"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/screen_wait"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="25"
|
||||
android:value="10"
|
||||
android:stepSize="1"
|
||||
app:trackColorActive="?attr/colorSecondary"
|
||||
app:thumbColor="?attr/colorSecondary"
|
||||
app:tickColorActive="?attr/colorOnSecondary"
|
||||
app:tickColorInactive="?attr/colorSecondary"
|
||||
app:tickColor="@android:color/transparent"
|
||||
app:trackColorInactive="@color/text_tertiary"
|
||||
android:visibility="gone"
|
||||
android:layout_marginStart="@dimen/keyline_first"
|
||||
android:layout_marginEnd="@dimen/keyline_first"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/tasks_org_button_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/pay_annually"
|
||||
style="@style/OutlineButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:paddingStart="@dimen/keyline_content_inset"
|
||||
android:paddingEnd="@dimen/keyline_content_inset" />
|
||||
tools:text="$2.99/year"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pay_monthly"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/subscribe"
|
||||
android:id="@+id/pay_monthly"
|
||||
style="@style/OutlineButton"
|
||||
android:padding="@dimen/keyline_first"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="@dimen/keyline_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:strokeColor="@color/button_accent_stroke"
|
||||
android:text="@string/button_subscribe" />
|
||||
tools:text="$2.99/month"
|
||||
android:layout_marginStart="@dimen/keyline_first"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pay_annually"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/unsubscribe"
|
||||
style="@style/OutlineButton"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/pay_other"
|
||||
style="@style/TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/keyline_first"
|
||||
android:padding="@dimen/keyline_first"
|
||||
app:strokeColor="?attr/colorSecondary"
|
||||
android:text="@string/button_unsubscribe"
|
||||
android:visibility="gone" />
|
||||
android:text="@string/more_options"
|
||||
android:textColor="@color/text_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pay_annually"
|
||||
tools:text="@string/back"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tasks="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/menu_more_info"
|
||||
android:title="@string/button_more_info"
|
||||
android:icon="@drawable/ic_outline_help_outline_24px"
|
||||
android:contentDescription="@string/button_more_info"
|
||||
tasks:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="width_constrained">false</bool>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue