Improve subscription UI

dev
Aurelien Hubert 7 years ago
parent 47de41044d
commit 48a0e1083f
  1. 58
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt
  2. 9
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/ScreenSlidePageFragment.kt
  3. 41
      app/src/main/res/layout/fragment_screen_slide_page.xml
  4. 23
      app/src/main/res/layout/fragment_subscription.xml

@ -31,10 +31,16 @@ import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.billing.AppGuard import net.pokeranalytics.android.util.billing.AppGuard
import net.pokeranalytics.android.util.billing.IAPProducts import net.pokeranalytics.android.util.billing.IAPProducts
import net.pokeranalytics.android.util.billing.PurchaseDelegate import net.pokeranalytics.android.util.billing.PurchaseDelegate
import java.lang.ref.WeakReference
import java.time.Period import java.time.Period
class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListener, PurchaseDelegate, ViewPager.OnPageChangeListener { class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListener, PurchaseDelegate, ViewPager.OnPageChangeListener {
companion object {
val parallax: Float = 64f.px
}
private var pagerAdapter: ScreenSlidePagerAdapter? = null
private var selectedProduct: SkuDetails? = null private var selectedProduct: SkuDetails? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -85,7 +91,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
// Pager // Pager
// The pager adapter, which provides the pages to the view pager widget. // The pager adapter, which provides the pages to the view pager widget.
val pagerAdapter = ScreenSlidePagerAdapter(requireFragmentManager()) this.pagerAdapter = ScreenSlidePagerAdapter(requireFragmentManager())
this.pager.adapter = pagerAdapter this.pager.adapter = pagerAdapter
this.pager.addOnPageChangeListener(this) this.pager.addOnPageChangeListener(this)
@ -108,7 +114,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
this.pageIndicator.addView(view, layoutParam) this.pageIndicator.addView(view, layoutParam)
} }
this.changeColorOfIndicator(0) this.updatePagerIndicators(0)
} }
@ -118,13 +124,21 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
*/ */
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
private var fragments: HashMap<Int, WeakReference<ScreenSlidePageFragment>> = HashMap()
private inner class FeatureDescriptor(var iconResId: Int, var titleResId: Int, var descResId: Int) private inner class FeatureDescriptor(var iconResId: Int, var titleResId: Int, var descResId: Int)
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position) as ScreenSlidePageFragment
fragments[position] = WeakReference(fragment)
return super.instantiateItem(container, position)
}
private val dataSource: List<FeatureDescriptor> = listOf( private val dataSource: List<FeatureDescriptor> = listOf(
FeatureDescriptor(R.drawable.ic_baseline_all_inclusive_24px, R.string.f_unlimited, R.string.f_unlimited_desc), FeatureDescriptor(R.drawable.ic_baseline_all_inclusive, R.string.f_unlimited, R.string.f_unlimited_desc),
FeatureDescriptor(R.drawable.ic_baseline_wifi_off_24px, R.string.f_offline, R.string.f_offline_desc), FeatureDescriptor(R.drawable.ic_baseline_wifi_off, R.string.f_offline, R.string.f_offline_desc),
FeatureDescriptor(R.drawable.ic_baseline_vpn_key_24px, R.string.f_privacy, R.string.f_privacy_desc), FeatureDescriptor(R.drawable.ic_baseline_vpn_key, R.string.f_privacy, R.string.f_privacy_desc),
FeatureDescriptor(R.drawable.ic_baseline_email_24px, R.string.f_support, R.string.f_support_desc) FeatureDescriptor(R.drawable.ic_baseline_email, R.string.f_support, R.string.f_support_desc)
) )
override fun getCount(): Int = this.dataSource.size override fun getCount(): Int = this.dataSource.size
@ -133,21 +147,27 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
return ScreenSlidePageFragment(d.iconResId, d.titleResId, d.descResId) return ScreenSlidePageFragment(d.iconResId, d.titleResId, d.descResId)
} }
/**
* Return the fragment at [position]
*/
fun getFragment(position: Int): ScreenSlidePageFragment? {
if (fragments.contains(position)) {
return fragments[position]?.get()
}
return null
}
} }
// SkuDetailsResponseListener // SkuDetailsResponseListener
override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList<SkuDetails>?) { override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList<SkuDetails>?) {
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) { if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) {
this.hideLoader() this.hideLoader()
selectedProduct = skuDetailsList.first { it.sku == IAPProducts.PRO.identifier } selectedProduct = skuDetailsList.first { it.sku == IAPProducts.PRO.identifier }
updateUI() updateUI()
} }
} }
private fun updateUI() { private fun updateUI() {
this.selectedProduct?.let { this.selectedProduct?.let {
this.purchase.isEnabled = true this.purchase.isEnabled = true
val perYearString = requireContext().getString(R.string.year_subscription) val perYearString = requireContext().getString(R.string.year_subscription)
@ -162,9 +182,7 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
val formattedFreeTrial = val formattedFreeTrial =
"$freeTrialDays " + requireContext().getString(R.string.days) + " " + requireContext().getString(R.string.free_trial) "$freeTrialDays " + requireContext().getString(R.string.days) + " " + requireContext().getString(R.string.free_trial)
this.freetrial.text = formattedFreeTrial this.freetrial.text = formattedFreeTrial
} }
} }
// PurchaseDelegate // PurchaseDelegate
@ -182,22 +200,30 @@ class SubscriptionFragment : PokerAnalyticsFragment(), SkuDetailsResponseListene
override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
pagerAdapter?.getFragment(position)?.let {
it.updateViewsPosition(-positionOffset * parallax)
}
pagerAdapter?.getFragment(position + 1)?.let {
it.updateViewsPosition((1 - positionOffset) * parallax)
}
}
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
this.changeColorOfIndicator(position) updatePagerIndicators(position)
} }
private fun changeColorOfIndicator(position: Int) { private fun updatePagerIndicators(position: Int) {
this.pageIndicator.children.forEachIndexed { index, view -> this.pageIndicator.children.forEachIndexed { index, view ->
val drawable = view.background val drawable = view.background
when (drawable) { when (drawable) {
is GradientDrawable -> { is GradientDrawable -> {
val color = if (position == index) R.color.white else R.color.quantum_grey val color = if (position == index) R.color.white else R.color.quantum_grey
drawable.setColor(requireContext().getColor(color)) drawable.setColor(requireContext().getColor(color))
} else -> {}
} }
else -> {
}
}
} }
} }

@ -22,6 +22,15 @@ class ScreenSlidePageFragment(var iconResId: Int, var titleResId: Int, var descr
this.icon.setImageResource(this.iconResId) this.icon.setImageResource(this.iconResId)
this.title.text = requireContext().getString(this.titleResId) this.title.text = requireContext().getString(this.titleResId)
this.description.text = requireContext().getString(this.descriptionResId) this.description.text = requireContext().getString(this.descriptionResId)
}
/**
* Update views position
*/
fun updateViewsPosition(position: Float) {
view?.findViewById<View>(R.id.title)?.translationX = position
view?.findViewById<View>(R.id.icon)?.translationX = position
view?.findViewById<View>(R.id.description)?.translationX = position * 2f
} }

@ -7,42 +7,45 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:layout_gravity="center">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title" android:id="@+id/title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeatureTitle" style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeatureTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" /> tools:text="Title" />
<ImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_baseline_all_inclusive"
android:tint="@color/green" android:tint="@color/green"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:src="@drawable/ic_baseline_all_inclusive_24px" app:layout_constraintStart_toStartOf="parent"
android:layout_width="48dp" app:layout_constraintTop_toBottomOf="@id/title" />
android:layout_height="48dp" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:id="@+id/description" android:id="@+id/description"
android:layout_width="match_parent" style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeature"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center" android:gravity="center"
style="@style/PokerAnalyticsTheme.TextView.SubscriptionFeature" android:maxLines="10"
app:layout_constraintTop_toBottomOf="@id/icon" android:paddingLeft="24dp"
app:layout_constraintStart_toStartOf="parent" android:paddingRight="24dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent"
android:maxLines="5" app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintWidth_percent="0.8"
tools:text="Description qui va avec le text youpi. Il peut y avoir plusieurs ligne de texte oui tout a fait." /> tools:text="Description qui va avec le text youpi. Il peut y avoir plusieurs ligne de texte oui tout a fait." />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -39,23 +39,28 @@
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="250dp" android:layout_width="0dp"
android:layout_height="200dp" android:layout_height="0dp"
android:layout_marginTop="100dp" android:layout_marginBottom="8dp"
app:layout_constraintTop_toBottomOf="@id/freetrial" app:layout_constraintBottom_toTopOf="@+id/price"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" /> app:layout_constraintHeight_percent="0.6"
app:layout_constraintVertical_bias="0.4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/freetrial" />
<LinearLayout <LinearLayout
android:orientation="horizontal"
android:id="@+id/pageIndicator" android:id="@+id/pageIndicator"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/pager" android:layout_marginBottom="8dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/price"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintVertical_bias="0.6"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintTop_toBottomOf="@id/pager" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/price" android:id="@+id/price"

Loading…
Cancel
Save