Implement freedom build flavors

This is a necessary step in separating the proprietary code (like Google Play Billing) from the FLOSS code so that I can eventually get around to publishing the app on fdroid

Signed-off-by: William Brawner <me@wbrawner.com>
This commit is contained in:
William Brawner 2020-06-10 11:46:05 -07:00
parent c3821fbfc9
commit 9a0685a165
4 changed files with 139 additions and 66 deletions

View file

@ -68,6 +68,14 @@ android {
buildConfigField "boolean", "ENABLE_CUSTOM_CSS", "false" buildConfigField "boolean", "ENABLE_CUSTOM_CSS", "false"
} }
} }
flavorDimensions "freedom"
productFlavors {
play {}
free {
applicationIdSuffix ".free"
versionNameSuffix "-free"
}
}
dexOptions { dexOptions {
jumboMode true jumboMode true
} }
@ -100,8 +108,8 @@ dependencies {
implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.legacy:legacy-support-v13:1.0.0' implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'com.commonsware.cwac:anddown:0.3.0' implementation 'com.commonsware.cwac:anddown:0.3.0'
implementation 'com.android.billingclient:billing:3.0.0' playImplementation 'com.android.billingclient:billing:3.0.0'
implementation 'com.google.firebase:firebase-core:17.4.3' playImplementation 'com.google.firebase:firebase-core:17.4.3'
implementation "androidx.core:core-ktx:1.3.0" implementation "androidx.core:core-ktx:1.3.0"
implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.browser:browser:1.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -116,7 +124,13 @@ dependencies {
implementation 'eu.crydee:syllable-counter:4.0.2' implementation 'eu.crydee:syllable-counter:4.0.2'
} }
apply plugin: 'com.google.gms.google-services' android.productFlavors.each { flavor ->
if (getGradle().getStartParameter().getTaskRequests().toString().toLowerCase().contains(flavor.name)
&& flavor.name == 'play') {
apply plugin: 'com.google.gms.google-services'
}
}
repositories { repositories {
mavenCentral() mavenCentral()
jcenter() jcenter()

View file

@ -0,0 +1,9 @@
package com.wbrawner.simplemarkdown.utility
import android.app.Activity
import androidx.lifecycle.MutableLiveData
import com.google.android.material.button.MaterialButton
class SupportLinkProvider(@Suppress("unused") private val activity: Activity) {
val supportLinks = MutableLiveData<List<MaterialButton>>()
}

View file

@ -6,18 +6,15 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import com.android.billingclient.api.* import androidx.lifecycle.Observer
import com.google.android.material.button.MaterialButton
import com.wbrawner.simplemarkdown.R import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.utility.SupportLinkProvider
import kotlinx.android.synthetic.main.activity_support.* import kotlinx.android.synthetic.main.activity_support.*
class SupportActivity : AppCompatActivity(), BillingClientStateListener, PurchasesUpdatedListener { class SupportActivity : AppCompatActivity() {
private lateinit var billingClient: BillingClient
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_support) setContentView(R.layout.activity_support)
@ -31,11 +28,6 @@ class SupportActivity : AppCompatActivity(), BillingClientStateListener, Purchas
} }
setTitle(R.string.support_title) setTitle(R.string.support_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
billingClient = BillingClient.newBuilder(applicationContext)
.setListener(this)
.enablePendingPurchases()
.build()
billingClient.startConnection(this)
githubButton.setOnClickListener { githubButton.setOnClickListener {
CustomTabsIntent.Builder() CustomTabsIntent.Builder()
.addDefaultShareMenuItem() .addDefaultShareMenuItem()
@ -57,6 +49,11 @@ class SupportActivity : AppCompatActivity(), BillingClientStateListener, Purchas
startActivity(playStoreIntent) startActivity(playStoreIntent)
} }
} }
SupportLinkProvider(this).supportLinks.observe(this, Observer { links ->
links.forEach {
supportButtons.addView(it)
}
})
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -66,56 +63,4 @@ class SupportActivity : AppCompatActivity(), BillingClientStateListener, Purchas
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onBillingSetupFinished(result: BillingResult) {
if (result.responseCode != BillingClient.BillingResponseCode.OK) {
return
}
val skuDetails = SkuDetailsParams.newBuilder()
.setSkusList(listOf("support_the_developer", "tip_coffee", "tip_beer"))
.setType(BillingClient.SkuType.INAPP)
.build()
billingClient.querySkuDetailsAsync(skuDetails) { skuDetailsResponse, skuDetailsList ->
// Process the result.
if (skuDetailsResponse.responseCode != BillingClient.BillingResponseCode.OK || skuDetailsList.isNullOrEmpty()) {
return@querySkuDetailsAsync
}
skuDetailsList.sortedBy { it.priceAmountMicros }.forEach { skuDetails ->
val supportButton = MaterialButton(this@SupportActivity)
supportButton.text = getString(
R.string.support_button_purchase,
skuDetails.title,
skuDetails.price
)
supportButton.setOnClickListener {
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build()
billingClient.launchBillingFlow(this, flowParams)
}
supportButtons.addView(supportButton)
}
}
}
override fun onBillingServiceDisconnected() {
billingClient.startConnection(this)
}
override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList<Purchase>?) {
purchases?.forEach { purchase ->
val consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.consumeAsync(consumeParams) { _, _ ->
Toast.makeText(
this@SupportActivity,
getString(R.string.support_thank_you),
Toast.LENGTH_SHORT
).show()
}
}
}
} }

View file

@ -0,0 +1,105 @@
package com.wbrawner.simplemarkdown.utility
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import com.android.billingclient.api.*
import com.google.android.material.button.MaterialButton
import com.wbrawner.simplemarkdown.R
class SupportLinkProvider(private val activity: Activity) : BillingClientStateListener,
PurchasesUpdatedListener {
val supportLinks = MutableLiveData<List<MaterialButton>>()
private val billingClient: BillingClient = BillingClient.newBuilder(activity.applicationContext)
.setListener(this)
.enablePendingPurchases()
.build()
init {
billingClient.startConnection(this)
activity.application.registerActivityLifecycleCallbacks(
object : Application.ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityDestroyed(activity: Activity) {
billingClient.endConnection()
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityResumed(activity: Activity) {
}
}
)
}
override fun onBillingSetupFinished(result: BillingResult) {
if (result.responseCode != BillingClient.BillingResponseCode.OK) {
return
}
val skuDetails = SkuDetailsParams.newBuilder()
.setSkusList(listOf("support_the_developer", "tip_coffee", "tip_beer"))
.setType(BillingClient.SkuType.INAPP)
.build()
billingClient.querySkuDetailsAsync(skuDetails) { skuDetailsResponse, skuDetailsList ->
// Process the result.
if (skuDetailsResponse.responseCode != BillingClient.BillingResponseCode.OK || skuDetailsList.isNullOrEmpty()) {
return@querySkuDetailsAsync
}
skuDetailsList.sortedBy { it.priceAmountMicros }
.map { skuDetails ->
val supportButton = MaterialButton(activity)
supportButton.text = activity.getString(
R.string.support_button_purchase,
skuDetails.title,
skuDetails.price
)
supportButton.setOnClickListener {
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build()
billingClient.launchBillingFlow(activity, flowParams)
}
supportButton
}
.let {
supportLinks.postValue(it)
}
}
}
override fun onBillingServiceDisconnected() {
billingClient.startConnection(this)
}
override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList<Purchase>?) {
purchases?.forEach { purchase ->
val consumeParams = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.consumeAsync(consumeParams) { _, _ ->
Toast.makeText(
activity,
activity.getString(R.string.support_thank_you),
Toast.LENGTH_SHORT
).show()
}
}
}
}