Skip to content

Commit

Permalink
Update publishing (#279)
Browse files Browse the repository at this point in the history
* Migrate from deprecated SKU to Product

To upgrade:
Rename Sku to Product
Rename SkuStatus to ProductStatus
Buy product requires an offer ProductDetails.SubscriptionOfferDetails received from ProductStatus.productDetails.subscriptionOfferDetails

* Update publishing process

* Fix typo

* Remove unused import
  • Loading branch information
warting authored Jan 12, 2024
1 parent bd6240f commit bd89901
Show file tree
Hide file tree
Showing 31 changed files with 374 additions and 406 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ jobs:
key: gradle-${{ hashFiles('checksum.txt') }}

- name: Publish application
run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
# run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
run: ./gradlew publishAllPublicationsToMavenCentralRepository --rerun-tasks --no-configuration-cache
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}

- name: Upload reports
uses: actions/upload-artifact@v3
Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,13 @@ jobs:
- name: Publish application
run: |
echo VERSION_NAME=1.0.0-SNAPSHOT > ./local.properties
./gradlew publishToSonatype
./gradlew publishAllPublicationsToMavenCentralRepository
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.SIGNING_KEY_ID }}

- name: Upload reports
uses: actions/upload-artifact@v3
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,27 @@ dependencies {

## How to use

All you need to do is to call collect the state of an Sku and then call `buy()` when it is ready:
All you need to do is to call collect the state of an Product and then call `buy()` when it is ready:

```
val earlyBirdProduct = Sku.Subscription("early_bird")
val earlyBirdProduct = Product.Subscription("early_bird")
val earlyBirdProductStatus by earlyBirdProduct.statusFlow.collectAsState(
initial = SkuStatus.Loading(earlyBirdProduct)
initial = ProductStatus.Loading(earlyBirdProduct)
)
when (val earlyBirdProduct = earlyBirdProductStatus) {
is SkuStatus.Available -> {
is ProductStatus.Available -> {
Text("Available to buy!")
Button(onClick = {
// Launch buy flow
earlyBirdProduct.buy()
earlyBirdProduct.buy(offer)
}) {
Text(text = "buy")
}
}
is SkuStatus.Loading -> Text("Loading....")
is SkuStatus.Unavailable -> Text("Unavailable")
is SkuStatus.Owned -> Text("Owned")
is ProductStatus.Loading -> Text("Loading....")
is ProductStatus.Unavailable -> Text("Unavailable")
is ProductStatus.Owned -> Text("Owned")
}
```
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ dependencies {
val composeBom = platform("androidx.compose:compose-bom:2023.06.00")
implementation(composeBom)
androidTestImplementation(composeBom)
implementation(project(":flow"))

implementation(project(":lib"))
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")

Expand Down
28 changes: 17 additions & 11 deletions app/src/main/java/se/warting/sampleapp/advance/AdvanceActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.billingclient.api.ProductDetails
import com.google.android.material.floatingactionbutton.FloatingActionButton
import se.warting.billy.flow.SkuStatus
import kotlinx.coroutines.runBlocking
import se.warting.billy.flow.ProductStatus
import se.warting.sampleapp.R

class AdvanceActivity : AppCompatActivity() {
Expand All @@ -20,28 +22,32 @@ class AdvanceActivity : AppCompatActivity() {
val locationText = findViewById<TextView>(R.id.locationText)
val actionButton = findViewById<FloatingActionButton>(R.id.locationFab)

var buyableProduct: SkuStatus.Available? = null
var buyableProduct: ProductStatus.Available? = null

actionButton.setOnClickListener {
buyableProduct?.buy()
val offer: ProductDetails.SubscriptionOfferDetails? = runBlocking {
buyableProduct?.productDetails?.subscriptionOfferDetails?.firstOrNull()
}

buyableProduct?.buy(offer!!)
}

// Update view based on ViewModel output
viewModel.getViewData().observe(this) { viewData ->

buyableProduct = when (val s = viewData.earlyBirdStatus) {
is SkuStatus.Available -> s
is SkuStatus.Loading -> null
is SkuStatus.Owned -> null
is SkuStatus.Unavailable -> null
is ProductStatus.Available -> s
is ProductStatus.Loading -> null
is ProductStatus.Owned -> null
is ProductStatus.Unavailable -> null
}
locationText.text =
getString(
R.string.products, when (viewData.earlyBirdStatus) {
is SkuStatus.Available -> "Available"
is SkuStatus.Loading -> "Loading"
is SkuStatus.Owned -> "Owned"
is SkuStatus.Unavailable -> "Unavailable"
is ProductStatus.Available -> "Available"
is ProductStatus.Loading -> "Loading"
is ProductStatus.Owned -> "Owned"
is ProductStatus.Unavailable -> "Unavailable"
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import se.warting.billy.flow.Sku
import se.warting.billy.flow.SkuStatus
import se.warting.billy.flow.Product
import se.warting.billy.flow.ProductStatus
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

Expand All @@ -14,7 +14,7 @@ class AdvanceViewModel : ViewModel() {
private val viewData = MutableLiveData<AdvanceViewData>()

init {
Sku.Subscription("early_bird").statusFlow.onEach {
Product.Subscription("early_bird").statusFlow.onEach {
viewData.value =
viewData.value?.copy(
earlyBirdStatus = it,
Expand All @@ -28,5 +28,5 @@ class AdvanceViewModel : ViewModel() {
}

data class AdvanceViewData(
val earlyBirdStatus: SkuStatus = SkuStatus.Loading(Sku.Subscription("early_bird")),
val earlyBirdStatus: ProductStatus = ProductStatus.Loading(Product.Subscription("early_bird")),
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ package se.warting.sampleapp.compose
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import se.warting.billy.flow.Sku
import se.warting.billy.flow.SkuStatus
import se.warting.billy.flow.Product
import se.warting.billy.flow.ProductStatus

@Composable
fun ComposeBillingScreen() {

val earlyBirdProduct = Sku.Subscription("early_bird")
val earlyBirdProduct: Product.Subscription = remember { Product.Subscription("early_bird") }

val earlyBirdProductStatus by earlyBirdProduct.statusFlow.collectAsState(
initial = SkuStatus.Loading(earlyBirdProduct)
initial = ProductStatus.Loading(earlyBirdProduct)
)

Column(
Expand All @@ -32,18 +35,25 @@ fun ComposeBillingScreen() {

Text(earlyBirdProduct.name)

when (@Suppress("UnnecessaryVariable") val status = earlyBirdProductStatus) {
is SkuStatus.Available -> {
when (val status = earlyBirdProductStatus) {
is ProductStatus.Available -> {

Text("Available!")
Button(onClick = {
status.buy()
}) {
Text(text = "buy")

LazyColumn {
items(status.productDetails.subscriptionOfferDetails ?: listOf()) { offer ->
Button(onClick = {
status.buy(offer)
}) {
Text(text = "Buy ${offer.pricingPhases.pricingPhaseList.firstOrNull()!!.formattedPrice}")
}
}
}
}
is SkuStatus.Loading -> Text("Loading....")
is SkuStatus.Unavailable -> Text("Unavailable")
is SkuStatus.Owned -> Text("Owned")

is ProductStatus.Loading -> Text("Loading....")
is ProductStatus.Unavailable -> Text("Unavailable")
is ProductStatus.Owned -> Text("Owned")
}
}
}
8 changes: 1 addition & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ val gitOrLocalVersion: String =
.getProperty("VERSION_NAME", androidGitVersion.name().replace("v", ""))

version = gitOrLocalVersion
group = "se.warting.billy"

allprojects {
apply(plugin = "io.gitlab.arturbosch.detekt")
Expand Down Expand Up @@ -91,10 +92,3 @@ tasks.named("dependencyUpdates", DependencyUpdatesTask::class.java).configure {
isNonStable(candidate.version) && !isNonStable(currentVersion)
}
}


task<Delete>("clean") {
delete(rootProject.buildDir)
}

apply(from = "${rootDir}/gradle/publish-root.gradle")
109 changes: 109 additions & 0 deletions flow/api/flow.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
public final class se/warting/billy/flow/BillingInitializer : androidx/startup/Initializer {
public fun <init> ()V
public synthetic fun create (Landroid/content/Context;)Ljava/lang/Object;
public fun create (Landroid/content/Context;)Lse/warting/billy/flow/BillingProvider;
public fun dependencies ()Ljava/util/List;
}

public final class se/warting/billy/flow/BillingProvider : se/warting/billy/flow/PurchaseObserver {
public static final field Companion Lse/warting/billy/flow/BillingProvider$Companion;
public fun <init> (Lcom/android/billingclient/api/BillingClient;Lse/warting/billy/flow/PurchaseLauncher;Lse/warting/billy/flow/PurchaseObserver;)V
public final fun buy (Lcom/android/billingclient/api/BillingFlowParams;)Lcom/android/billingclient/api/BillingResult;
public fun connected (Z)V
public fun getActiveSubscriptions ()Lkotlinx/coroutines/flow/Flow;
public final fun getBillingClient ()Lcom/android/billingclient/api/BillingClient;
public static final fun getInstance ()Lse/warting/billy/flow/BillingProvider;
public final fun getObserver ()Lse/warting/billy/flow/PurchaseObserver;
public fun getStatusFlow (Lse/warting/billy/flow/Product;)Lkotlinx/coroutines/flow/Flow;
public fun refreshStatus ()V
}

public final class se/warting/billy/flow/BillingProvider$Companion {
public final fun getInstance ()Lse/warting/billy/flow/BillingProvider;
public final fun init (Landroid/content/Context;)Lse/warting/billy/flow/BillingProvider;
public final fun isInitialized ()Z
}

public abstract class se/warting/billy/flow/Product {
public final fun getDetailsFlow ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getName ()Ljava/lang/String;
public abstract fun getProductType ()Ljava/lang/String;
public final fun getStatusFlow ()Lkotlinx/coroutines/flow/Flow;
}

public final class se/warting/billy/flow/Product$InAppProduct : se/warting/billy/flow/Product {
public fun <init> (Ljava/lang/String;)V
public fun getName ()Ljava/lang/String;
public fun getProductType ()Ljava/lang/String;
}

public final class se/warting/billy/flow/Product$Subscription : se/warting/billy/flow/Product {
public fun <init> (Ljava/lang/String;)V
public fun getName ()Ljava/lang/String;
public fun getProductType ()Ljava/lang/String;
}

public abstract class se/warting/billy/flow/ProductStatus {
public abstract fun getType ()Lse/warting/billy/flow/Product;
}

public final class se/warting/billy/flow/ProductStatus$Available : se/warting/billy/flow/ProductStatus {
public fun <init> (Lse/warting/billy/flow/Product;Lcom/android/billingclient/api/ProductDetails;)V
public final fun buy (Lcom/android/billingclient/api/ProductDetails$SubscriptionOfferDetails;)V
public final fun component1 ()Lse/warting/billy/flow/Product;
public final fun component2 ()Lcom/android/billingclient/api/ProductDetails;
public final fun copy (Lse/warting/billy/flow/Product;Lcom/android/billingclient/api/ProductDetails;)Lse/warting/billy/flow/ProductStatus$Available;
public static synthetic fun copy$default (Lse/warting/billy/flow/ProductStatus$Available;Lse/warting/billy/flow/Product;Lcom/android/billingclient/api/ProductDetails;ILjava/lang/Object;)Lse/warting/billy/flow/ProductStatus$Available;
public fun equals (Ljava/lang/Object;)Z
public final fun getProductDetails ()Lcom/android/billingclient/api/ProductDetails;
public fun getType ()Lse/warting/billy/flow/Product;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class se/warting/billy/flow/ProductStatus$Loading : se/warting/billy/flow/ProductStatus {
public fun <init> (Lse/warting/billy/flow/Product;)V
public final fun component1 ()Lse/warting/billy/flow/Product;
public final fun copy (Lse/warting/billy/flow/Product;)Lse/warting/billy/flow/ProductStatus$Loading;
public static synthetic fun copy$default (Lse/warting/billy/flow/ProductStatus$Loading;Lse/warting/billy/flow/Product;ILjava/lang/Object;)Lse/warting/billy/flow/ProductStatus$Loading;
public fun equals (Ljava/lang/Object;)Z
public fun getType ()Lse/warting/billy/flow/Product;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class se/warting/billy/flow/ProductStatus$Owned : se/warting/billy/flow/ProductStatus {
public fun <init> (Lse/warting/billy/flow/Product;Ljava/util/List;)V
public final fun component1 ()Lse/warting/billy/flow/Product;
public final fun component2 ()Ljava/util/List;
public final fun copy (Lse/warting/billy/flow/Product;Ljava/util/List;)Lse/warting/billy/flow/ProductStatus$Owned;
public static synthetic fun copy$default (Lse/warting/billy/flow/ProductStatus$Owned;Lse/warting/billy/flow/Product;Ljava/util/List;ILjava/lang/Object;)Lse/warting/billy/flow/ProductStatus$Owned;
public fun equals (Ljava/lang/Object;)Z
public final fun getPurchase ()Ljava/util/List;
public fun getType ()Lse/warting/billy/flow/Product;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class se/warting/billy/flow/ProductStatus$Unavailable : se/warting/billy/flow/ProductStatus {
public fun <init> (Lse/warting/billy/flow/Product;)V
public final fun component1 ()Lse/warting/billy/flow/Product;
public final fun copy (Lse/warting/billy/flow/Product;)Lse/warting/billy/flow/ProductStatus$Unavailable;
public static synthetic fun copy$default (Lse/warting/billy/flow/ProductStatus$Unavailable;Lse/warting/billy/flow/Product;ILjava/lang/Object;)Lse/warting/billy/flow/ProductStatus$Unavailable;
public fun equals (Ljava/lang/Object;)Z
public fun getType ()Lse/warting/billy/flow/Product;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class se/warting/billy/flow/PurchaseLauncher {
public abstract fun buy (Lcom/android/billingclient/api/BillingFlowParams;)Lcom/android/billingclient/api/BillingResult;
}

public abstract interface class se/warting/billy/flow/PurchaseObserver {
public abstract fun connected (Z)V
public abstract fun getActiveSubscriptions ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getStatusFlow (Lse/warting/billy/flow/Product;)Lkotlinx/coroutines/flow/Flow;
public abstract fun refreshStatus ()V
}

Loading

0 comments on commit bd89901

Please sign in to comment.