-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Debug view: Initial UI + Usage in MagicWeatherCompose (#1075)
### Description Same as was added in iOS in RevenueCat/purchases-ios#2567. Includes the changes in #1180 This PR adds a debug view in the Android SDK and uses it in the `MagicWeatherCompose` sample app. This debug view is meant to be a Jetpack compose function, even though later we might add convenience accessors for people not using jetpack compose. It's extracted to a different module so it can be published separately from the main SDK. It also attempts to keep all the code in the debug source set, to try to include as few code in the release variants. The provided API is currently 2 composable functions: - `DebugRevenueCatDebugScreen`: This is the actual UI that is part of the debug screen. In case devs want to use it in a custom UI. - `DebugRevenueCatBottomSheet`: This will display the debug screen as a bottom sheet. This is what's used in the `MagicWeatherCompose` sample app. ##### TODO - [ ] Allow visualizing offerings in debug menu - [ ] Support non-composable apps by wrapping it into a fragment, with utility launch methods. - [ ] Split library for debug and release - [ ] Add UI tests - [ ] Support publishing the new module as different libraries for debug and release. - [ ] Remove module local substitution once debugview library has been published https://github.com/RevenueCat/purchases-android/assets/808417/b3a953e4-b96f-4b06-8294-d2bcf9b66c7d
- Loading branch information
Showing
28 changed files
with
571 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
api-tester/src/defaults/kotlin/com/revenuecat/apitester/kotlin/PurchasesDebugViewAPI.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.revenuecat.apitester.kotlin | ||
|
||
import androidx.compose.runtime.Composable | ||
import com.revenuecat.purchases.debugview.DebugRevenueCatBottomSheet | ||
import com.revenuecat.purchases.debugview.DebugRevenueCatScreen | ||
|
||
@Suppress("unused") | ||
private class PurchasesDebugViewAPI { | ||
@Composable | ||
fun CheckDebugView(isVisible: Boolean, onDismissCallback: (() -> Unit)? = null) { | ||
DebugRevenueCatScreen() | ||
DebugRevenueCatBottomSheet(isVisible = isVisible, onDismissCallback = onDismissCallback) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
plugins { | ||
alias libs.plugins.android.library | ||
alias libs.plugins.kotlin.android | ||
} | ||
|
||
// TODO Uncomment apply plugin once debugview package is ready to be published. | ||
//if (!project.getProperties()["ANDROID_VARIANT_TO_PUBLISH"].contains("customEntitlementComputation")) { | ||
// apply plugin: "com.vanniktech.maven.publish" | ||
//} | ||
|
||
apply from: "$rootProject.projectDir/library.gradle" | ||
|
||
android { | ||
namespace 'com.revenuecat.purchases.debugview' | ||
|
||
flavorDimensions = ["apis"] | ||
productFlavors { | ||
defaults { | ||
dimension "apis" | ||
} | ||
} | ||
|
||
defaultConfig { | ||
minSdkVersion 21 // Compose requires minSdkVersion 21 | ||
} | ||
|
||
buildFeatures { | ||
compose true | ||
} | ||
composeOptions { | ||
kotlinCompilerExtensionVersion '1.4.0-alpha02' | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation project(path: ':purchases') | ||
|
||
implementation libs.androidx.core | ||
implementation platform(libs.compose.bom) | ||
implementation libs.compose.ui | ||
implementation libs.compose.ui.graphics | ||
implementation libs.compose.ui.tooling.preview | ||
implementation libs.compose.material | ||
implementation libs.compose.material3 | ||
implementation libs.androidx.lifecycle.runtime.ktx | ||
implementation libs.androidx.lifecycle.viewmodel | ||
implementation libs.androidx.lifecycle.viewmodel.compose | ||
debugImplementation libs.compose.ui.tooling | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
8 changes: 8 additions & 0 deletions
8
debugview/src/debug/kotlin/com/revenuecat/purchases/debugview/DebugRevenueCatViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.revenuecat.purchases.debugview | ||
|
||
import com.revenuecat.purchases.debugview.models.SettingScreenState | ||
import kotlinx.coroutines.flow.StateFlow | ||
|
||
internal interface DebugRevenueCatViewModel { | ||
val state: StateFlow<SettingScreenState> | ||
} |
35 changes: 35 additions & 0 deletions
35
...src/debug/kotlin/com/revenuecat/purchases/debugview/InternalDebugRevenueCatBottomSheet.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.revenuecat.purchases.debugview | ||
|
||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.ModalBottomSheet | ||
import androidx.compose.material3.rememberModalBottomSheetState | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.rememberCoroutineScope | ||
import kotlinx.coroutines.launch | ||
|
||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
internal fun InternalDebugRevenueCatBottomSheet( | ||
isVisible: Boolean = false, | ||
onDismissCallback: (() -> Unit)? = null, | ||
) { | ||
if (isVisible) { | ||
val rcDebugMenuSheetState = rememberModalBottomSheetState( | ||
skipPartiallyExpanded = true, | ||
) | ||
|
||
val scope = rememberCoroutineScope() | ||
|
||
ModalBottomSheet( | ||
sheetState = rcDebugMenuSheetState, | ||
onDismissRequest = { | ||
scope.launch { | ||
rcDebugMenuSheetState.hide() | ||
onDismissCallback?.invoke() | ||
} | ||
}, | ||
) { | ||
InternalDebugRevenueCatScreen() | ||
} | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
...view/src/debug/kotlin/com/revenuecat/purchases/debugview/InternalDebugRevenueCatScreen.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package com.revenuecat.purchases.debugview | ||
|
||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.rememberScrollState | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material.MaterialTheme | ||
import androidx.compose.material.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import androidx.lifecycle.viewmodel.compose.viewModel | ||
import com.revenuecat.purchases.debugview.models.InternalDebugRevenueCatScreenViewModel | ||
import com.revenuecat.purchases.debugview.models.SettingGroupState | ||
import com.revenuecat.purchases.debugview.models.SettingScreenState | ||
import com.revenuecat.purchases.debugview.models.SettingState | ||
import com.revenuecat.purchases.debugview.settings.SettingGroup | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
|
||
@Composable | ||
internal fun InternalDebugRevenueCatScreen( | ||
screenViewModel: DebugRevenueCatViewModel = viewModel<InternalDebugRevenueCatScreenViewModel>(), | ||
) { | ||
Column( | ||
modifier = Modifier | ||
.verticalScroll(rememberScrollState()) | ||
.fillMaxWidth() | ||
.padding(bottom = 16.dp), | ||
) { | ||
Text( | ||
text = "RevenueCat Debug Menu", | ||
style = MaterialTheme.typography.h5, | ||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 16.dp), | ||
) | ||
screenViewModel.state.collectAsState().value.toSettingGroupStates().forEach { SettingGroup(it) } | ||
} | ||
} | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
private fun InternalDebugRevenueCatScreenPreview() { | ||
InternalDebugRevenueCatScreen( | ||
screenViewModel = object : DebugRevenueCatViewModel { | ||
override val state = MutableStateFlow<SettingScreenState>( | ||
SettingScreenState.Configured( | ||
SettingGroupState( | ||
"Configuration", | ||
listOf( | ||
SettingState.Text("SDK version", "3.0.0"), | ||
SettingState.Text("Observer mode", "true"), | ||
), | ||
), | ||
SettingGroupState( | ||
"Customer info", | ||
listOf( | ||
SettingState.Text("Current User ID", "current-user-id"), | ||
SettingState.Text("Active entitlements", "pro, premium"), | ||
), | ||
), | ||
SettingGroupState( | ||
"Offerings", | ||
listOf( | ||
SettingState.Text("current", "TODO"), | ||
SettingState.Text("default", "TODO"), | ||
), | ||
), | ||
), | ||
) | ||
}, | ||
) | ||
} |
104 changes: 104 additions & 0 deletions
104
...otlin/com/revenuecat/purchases/debugview/models/InternalDebugRevenueCatScreenViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package com.revenuecat.purchases.debugview.models | ||
|
||
import android.util.Log | ||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import com.revenuecat.purchases.CustomerInfo | ||
import com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI | ||
import com.revenuecat.purchases.Offerings | ||
import com.revenuecat.purchases.Purchases | ||
import com.revenuecat.purchases.PurchasesException | ||
import com.revenuecat.purchases.awaitCustomerInfo | ||
import com.revenuecat.purchases.awaitOfferings | ||
import com.revenuecat.purchases.debugview.DebugRevenueCatViewModel | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.flow.update | ||
import kotlinx.coroutines.launch | ||
|
||
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class) | ||
internal class InternalDebugRevenueCatScreenViewModel : ViewModel(), DebugRevenueCatViewModel { | ||
override val state: StateFlow<SettingScreenState> | ||
get() = _state | ||
|
||
private var _state: MutableStateFlow<SettingScreenState> = MutableStateFlow( | ||
SettingScreenState.NotConfigured(configurationGroup()), | ||
) | ||
|
||
init { | ||
if (Purchases.isConfigured) { | ||
refreshInfo() | ||
} | ||
} | ||
|
||
private fun refreshInfo() { | ||
viewModelScope.launch { | ||
try { | ||
val offerings = Purchases.sharedInstance.awaitOfferings() | ||
val customerInfo = Purchases.sharedInstance.awaitCustomerInfo() | ||
_state.update { | ||
SettingScreenState.Configured( | ||
configurationGroup(), | ||
customerInfoGroup(customerInfo), | ||
offeringsGroup(offerings), | ||
) | ||
} | ||
} catch (e: PurchasesException) { | ||
Log.e("RevenueCatDebugView", "Error getting RevenueCat SDK info for debug view. Exception: $e") | ||
} | ||
} | ||
} | ||
|
||
private fun configurationGroup(): SettingGroupState { | ||
val storeName = if (Purchases.isConfigured) { | ||
Purchases.sharedInstance.store.name | ||
} else { | ||
"Not configured" | ||
} | ||
val observerMode = if (Purchases.isConfigured) { | ||
"${!Purchases.sharedInstance.finishTransactions}" | ||
} else { | ||
"Not configured" | ||
} | ||
return SettingGroupState( | ||
"Configuration", | ||
listOf( | ||
SettingState.Text("SDK version", Purchases.frameworkVersion), | ||
SettingState.Text("Is configured", "${Purchases.isConfigured}"), | ||
SettingState.Text("Store", storeName), | ||
SettingState.Text("Observer mode", observerMode), | ||
), | ||
) | ||
} | ||
|
||
private fun customerInfoGroup(customerInfo: CustomerInfo): SettingGroupState { | ||
return SettingGroupState( | ||
title = "Customer info", | ||
settings = listOf( | ||
SettingState.Text("Current User ID", Purchases.sharedInstance.appUserID), | ||
SettingState.Text("Original User ID", customerInfo.originalAppUserId), | ||
SettingState.Text( | ||
"Active entitlements", | ||
customerInfo.entitlements.active | ||
.map { "${it.key} until ${it.value.expirationDate}" } | ||
.joinToString("\n") | ||
.takeIf { it.isNotEmpty() } ?: "None", | ||
), | ||
SettingState.Text("Verification result", customerInfo.entitlements.verification.name), | ||
SettingState.Text("Request date", customerInfo.requestDate.toString()), | ||
), | ||
) | ||
} | ||
|
||
private fun offeringsGroup(offerings: Offerings): SettingGroupState { | ||
return SettingGroupState( | ||
title = "Offerings", | ||
settings = offerings.all.values.map { offering -> | ||
SettingState.Text( | ||
title = offering.identifier, | ||
content = "TODO", | ||
) | ||
}, | ||
) | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
debugview/src/debug/kotlin/com/revenuecat/purchases/debugview/models/SettingGroupState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package com.revenuecat.purchases.debugview.models | ||
|
||
internal data class SettingGroupState(val title: String, val settings: List<SettingState>) |
17 changes: 17 additions & 0 deletions
17
debugview/src/debug/kotlin/com/revenuecat/purchases/debugview/models/SettingScreenState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.revenuecat.purchases.debugview.models | ||
|
||
internal sealed class SettingScreenState(open val configuration: SettingGroupState) { | ||
data class NotConfigured(override val configuration: SettingGroupState) : SettingScreenState(configuration) | ||
data class Configured( | ||
override val configuration: SettingGroupState, | ||
val customerInfo: SettingGroupState, | ||
val offerings: SettingGroupState, | ||
) : SettingScreenState(configuration) | ||
|
||
fun toSettingGroupStates(): List<SettingGroupState> { | ||
return when (this) { | ||
is NotConfigured -> listOf(configuration) | ||
is Configured -> listOf(configuration, customerInfo, offerings) | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
debugview/src/debug/kotlin/com/revenuecat/purchases/debugview/models/SettingState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.revenuecat.purchases.debugview.models | ||
|
||
internal sealed class SettingState(open val title: String) { | ||
data class Text(override val title: String, val content: String) : SettingState(title) | ||
} |
Oops, something went wrong.