diff --git a/.github/workflows/build+test+deploy.yml b/.github/workflows/build+test+deploy.yml index 70a4ad24..01f42e1f 100644 --- a/.github/workflows/build+test+deploy.yml +++ b/.github/workflows/build+test+deploy.yml @@ -107,7 +107,7 @@ jobs: with: payload: | { - "text": "Please create a new Android Release! https://github.com/superwall-me/Superwall-Android/releases/new?tag=${{steps.version.outputs.prop}}&prerelease=${{steps.prerelease.outputs.status}}" + "text": "Please create a new Android Release! https://github.com/superwall/Superwall-Android/releases/new?tag=${{steps.version.outputs.prop}}&prerelease=${{steps.prerelease.outputs.status}}" } env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml index 30a91f26..80a19bc3 100644 --- a/.github/workflows/on-release.yml +++ b/.github/workflows/on-release.yml @@ -12,5 +12,5 @@ jobs: uses: peter-evans/repository-dispatch@98b1133981c5060126325c279a8840c1711a9fe0 with: token: ${{ secrets.MAIN_REPO_PAT }} - repository: superwall-me/paywall-next + repository: superwall/paywall-next event-type: android-release diff --git a/.gitignore b/.gitignore index 7ba590e1..f6fc6f91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,13 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties -.idea/* +**/*.iml +**/.gradle +**/local.properties +**/.DS_Store +**/build +**/captures +**/.externalNativeBuild +**/.cxx +**/local.properties +**/.idea/ private_key.pepk upload-keystore.jks .env diff --git a/CHANGELOG.md b/CHANGELOG.md index 39037164..599ca504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # CHANGELOG -The changelog for `Superwall`. Also see the [releases](https://github.com/superwall-me/Superwall-Android/releases) on GitHub. +The changelog for `Superwall`. Also see the [releases](https://github.com/superwall/Superwall-Android/releases) on GitHub. + +## 1.0.3-beta.1 + +### Fixes + +- SW-2732: User attributes weren't being sent on app open until identify was called. Now they are +sent every time there's a new session. +- SW-2733: Fixes issue where the spinner would still show on a paywall if a user had previously + purchased on it. +- SW-2744: Fixes issue where using the back button to dismiss a paywall presented via `getPaywall` +would call `didFinish` in the `PaywallViewControllerDelegate` with the incorrect values. +- Fixes issue where an invalid paywall background color would prevent the paywall from opening. If +this happens, it will now default to white. +- SW-2748: Exposes `viewWillAppear`, `viewDidAppear`, `viewWillDisappear` and `viewDidDisappear` +methods of `PaywallViewController` which you must call when using `getPaywall`. ## 1.0.2 diff --git a/README.md b/README.md index 17701ac7..30139117 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ Gradle Compatible - + MIT License Community Active - Version Number + Version Number

@@ -42,7 +42,7 @@ ✏️ | A/B Testing - automatically calculate metrics for different paywalls 📝 | [Online documentation](https://superwall.com/docs/android-beta) up to date 🔀 | [Integrations](https://superwall.com/docs/android-beta) - over a dozen integrations to easily send conversion data where you need it -💯 | Well maintained - [frequent releases](https://github.com/superwall-me/Superwall-Android/releases) +💯 | Well maintained - [frequent releases](https://github.com/superwall/Superwall-Android/releases) 📮 | Great support - email a founder: jake@superwall.com ## Installation @@ -55,7 +55,7 @@ The preferred installation method is with [Gradle](https://superwall.com/docs/in - Open **settings.gradle** - Add `maven { url 'https://mvn.superwall.com/release' }` to your `repositories { ... }` -- Add `implementation "com.superwall.sdk:superwall-android:"` [latest version](https://github.com/superwall-me/Superwall-Android/releases) +- Add `implementation "com.superwall.sdk:superwall-android:"` [latest version](https://github.com/superwall/Superwall-Android/releases) - Make sure you press `Sync Now` - Edit your **AndroidManifest.xml** by adding: ```xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9e2cd5bd..e310e3a2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,8 +10,8 @@ android { defaultConfig { applicationId = "com.superwall.superapp" - minSdkVersion(26) - targetSdkVersion(33) + minSdk = 26 + targetSdk = 34 versionCode = 2 versionName = "1.0.0" @@ -20,7 +20,7 @@ android { } buildTypes { - getByName("release") { + release { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), diff --git a/app/src/main/java/com/superwall/superapp/ComposeActivity.kt b/app/src/main/java/com/superwall/superapp/ComposeActivity.kt index ae9d179c..0dc40d0d 100644 --- a/app/src/main/java/com/superwall/superapp/ComposeActivity.kt +++ b/app/src/main/java/com/superwall/superapp/ComposeActivity.kt @@ -103,7 +103,7 @@ fun ComposeActivityContent(@PreviewParameter(PreviewPaywallDelegateProvider::cla @Composable fun TabContent0(paywallOverrides: PaywallOverrides?, delegate: PaywallViewControllerDelegate) { PaywallComposable( - event = "another_paywall", + event = "no_products", params = mapOf("key" to "value"), paywallOverrides = paywallOverrides, delegate = delegate diff --git a/app/src/main/java/com/superwall/superapp/MainApplication.kt b/app/src/main/java/com/superwall/superapp/MainApplication.kt index 48e94b59..67af64f4 100644 --- a/app/src/main/java/com/superwall/superapp/MainApplication.kt +++ b/app/src/main/java/com/superwall/superapp/MainApplication.kt @@ -35,16 +35,16 @@ class MainApplication : android.app.Application(), SuperwallDelegate { fun configureWithAutomaticInitialization() { Superwall.configure( this, - CONSTANT_API_KEY, + CONSTANT_API_KEY ) Superwall.instance.delegate = this // Make sure we enable the game controller - Superwall.instance.options.isGameControllerEnabled = true + // Superwall.instance.options.isGameControllerEnabled = true } fun configureWithRevenueCatInitialization() { - val purchaseController = RevenueCatPurchaseController(this) + val purchaseController = RevenueCatPurchaseController(this) Superwall.configure( this, @@ -54,7 +54,7 @@ class MainApplication : android.app.Application(), SuperwallDelegate { Superwall.instance.delegate = this // Make sure we enable the game controller - Superwall.instance.options.isGameControllerEnabled = true + // Superwall.instance.options.isGameControllerEnabled = true purchaseController.syncSubscriptionStatus() } diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/app/.gitignore b/example/app/.gitignore similarity index 100% rename from app/.gitignore rename to example/app/.gitignore diff --git a/example/app/build.gradle.kts b/example/app/build.gradle.kts new file mode 100644 index 00000000..9b9dc779 --- /dev/null +++ b/example/app/build.gradle.kts @@ -0,0 +1,72 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.superwall.exampleapp" + compileSdk = 34 + + defaultConfig { + applicationId = "com.superwall.superapp" + minSdk = 26 + targetSdk = 34 + versionCode = 1 + versionName = "1.0.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + getByName("release") { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.4.7" + } + packagingOptions { + resources.excludes += "/META-INF/{AL2.0,LGPL2.1}" + } +} + +dependencies { + implementation(project(":superwall")) + + // Billing + implementation(libs.billing) + implementation(libs.revenue.cat) + + // Compose + implementation(platform(libs.compose.bom)) + implementation(libs.activity.compose) + implementation(libs.ui) + implementation(libs.ui.graphics) + implementation(libs.ui.tooling.preview) + implementation(libs.material3) + + // Core + implementation(libs.core) + implementation(libs.appcompat) + implementation(libs.material) + implementation(libs.constraintlayout) + implementation(libs.core.ktx) + implementation(libs.lifecycle.runtime.ktx) +} \ No newline at end of file diff --git a/example/app/proguard-rules.pro b/example/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/example/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/example/app/src/main/AndroidManifest.xml b/example/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b53db63e --- /dev/null +++ b/example/app/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/example/app/src/main/java/com/superwall/exampleapp/HomeActivity.kt b/example/app/src/main/java/com/superwall/exampleapp/HomeActivity.kt new file mode 100644 index 00000000..254c8575 --- /dev/null +++ b/example/app/src/main/java/com/superwall/exampleapp/HomeActivity.kt @@ -0,0 +1,202 @@ +package com.superwall.exampleapp + +import android.app.AlertDialog +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Divider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.view.WindowCompat +import com.superwall.exampleapp.ui.theme.SuperwallExampleAppTheme +import com.superwall.sdk.Superwall +import com.superwall.sdk.delegate.SubscriptionStatus +import com.superwall.sdk.misc.AlertControllerFactory +import com.superwall.sdk.paywall.presentation.PaywallPresentationHandler +import com.superwall.sdk.paywall.presentation.internal.state.PaywallSkippedReason +import com.superwall.sdk.paywall.presentation.register +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class HomeActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + WindowCompat.setDecorFitsSystemWindows(window, false) + + setContent() { + val subscriptionStatus by Superwall.instance.subscriptionStatus.collectAsState() + SuperwallExampleAppTheme { + HomeScreen( + subscriptionStatus = subscriptionStatus, + onLogOutClicked = { + finish() + } + ) + } + } + } +} + +@Composable +fun HomeScreen( + subscriptionStatus: SubscriptionStatus, + onLogOutClicked: () -> Unit +) { + val context = LocalContext.current + val subscriptionText = when (subscriptionStatus) { + SubscriptionStatus.UNKNOWN -> "Loading subscription status." + SubscriptionStatus.ACTIVE -> "You currently have an active subscription. Therefore, the " + + "paywall will never show. For the purposes of this app, delete and reinstall the " + + "app to clear subscriptions." + + SubscriptionStatus.INACTIVE -> "You do not have an active subscription so the paywall will " + + "show when clicking the button." + } + + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Presenting a Paywall", + style = MaterialTheme.typography.headlineSmall, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(top = 50.dp) + ) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "The Launch Feature button below registers an event \"campaign_trigger\".\n\n" + + "This event has been added to a campaign on the Superwall dashboard.\n\n" + + "When this event is registered, the rules in the campaign are evaluated.\n\n" + + "The rules match and cause a paywall to show.", + textAlign = TextAlign.Center + ) + Spacer(modifier = Modifier.height(16.dp)) + Divider() + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = subscriptionText, + textAlign = TextAlign.Center + ) + } + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + onClick = { + val handler = PaywallPresentationHandler() + handler.onDismiss { paywallInfo -> + println("The paywall dismissed. PaywallInfo: $paywallInfo") + } + handler.onPresent { paywallInfo -> + println("The paywall presented. PaywallInfo: $paywallInfo") + } + handler.onError { error -> + println("The paywall presentation failed with error $error") + } + handler.onSkip { reason -> + when (reason) { + is PaywallSkippedReason.EventNotFound -> { + print("Paywall not shown because this event isn't part of a campaign.") + } + is PaywallSkippedReason.Holdout -> { + print("Paywall not shown because user is in a holdout group in " + + "Experiment: ${reason.experiment.id}") + } + is PaywallSkippedReason.NoRuleMatch -> { + print("Paywall not shown because user doesn't match any rules.") + } + is PaywallSkippedReason.UserIsSubscribed -> { + print("Paywall not shown because user is subscribed.") + } + } + } + + Superwall.instance.register(event = "campaign_trigger", handler = handler) { + // code in here can be remotely configured to execute. Either + // (1) always after presentation or + // (2) only if the user pays + // code is always executed if no paywall is configured to show + val builder = AlertDialog.Builder(context) + .setTitle("Feature Launched") + .setMessage("The feature block was called") + + builder.setPositiveButton("Ok") { _, _ -> } + + val alertDialog = builder.create() + alertDialog.show() + } + }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 50.dp) + .padding(top = 8.dp) + ) { + Text( + text = "Launch Feature", + color = MaterialTheme.colorScheme.onPrimary, + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 18.sp + ) + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Button( + onClick = { + Superwall.instance.reset() + onLogOutClicked() + }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 50.dp) + .padding(vertical = 8.dp) + ) { + Text( + text = "Log Out", + color = MaterialTheme.colorScheme.onPrimary, + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 18.sp + ) + ) + } + } + } + } +} \ No newline at end of file diff --git a/example/app/src/main/java/com/superwall/exampleapp/MainActivity.kt b/example/app/src/main/java/com/superwall/exampleapp/MainActivity.kt new file mode 100644 index 00000000..b465eebe --- /dev/null +++ b/example/app/src/main/java/com/superwall/exampleapp/MainActivity.kt @@ -0,0 +1,168 @@ +package com.superwall.exampleapp + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.view.WindowCompat +import com.superwall.exampleapp.ui.theme.SuperwallExampleAppTheme +import com.superwall.sdk.Superwall +import com.superwall.sdk.identity.identify +import com.superwall.sdk.identity.setUserAttributes + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + Superwall.configure( + applicationContext = this, + apiKey = "pk_3b18882b1683318b710c741f371f40f54e357c6f0baff1f4" + ) + + WindowCompat.setDecorFitsSystemWindows(window, false) + + setContent { + SuperwallExampleAppTheme { + WelcomeScreen() + } + } + } +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun WelcomeScreen() { + val keyboardController = LocalSoftwareKeyboardController.current + var isFocused by remember { mutableStateOf(false) } + var name by remember { mutableStateOf("") } + val context = LocalContext.current + + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(bottom = 20.dp), + contentAlignment = Alignment.Center + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(16.dp)) { + Spacer(modifier = Modifier.weight(1f)) + + Text( + text = "Welcome! Enter your name to get started. Your name will be added to the Paywall user attributes, which can then be accessed and displayed within your paywall.", + color = MaterialTheme.colorScheme.onBackground, + textAlign = TextAlign.Center + ) + + BasicTextField( + value = name, + onValueChange = { name = it }, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 26.dp) + .heightIn(max = 50.dp) + .background(color = Color.White, shape = RoundedCornerShape(25.dp)) + .onFocusChanged { focusState -> + isFocused = focusState.isFocused + }, + keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { + keyboardController?.hide() + startHomeActivity(context, name) + }), + textStyle = TextStyle( + color = Color.Black, + textAlign = TextAlign.Start + ), + singleLine = true, + decorationBox = { innerTextField -> + Row( + modifier = Modifier + .fillMaxHeight() + .padding(horizontal = 16.dp), // Apply horizontal padding to the Row + verticalAlignment = Alignment.CenterVertically // Align text to the center vertically, + ) { + if (name.isEmpty() && !isFocused) { + Text( + "Enter your name", + style = TextStyle(color = Color.Gray) + ) + } + innerTextField() // The inner text field will inherit the Row's padding + } + } + ) + Spacer(modifier = Modifier.weight(1f)) + + Button( + onClick = { startHomeActivity(context, name) }, + modifier = Modifier + .fillMaxWidth() + .heightIn(min = 50.dp) + .padding(vertical = 8.dp) + ) { + Text( + text = "Log In", + color = MaterialTheme.colorScheme.onPrimary, + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 18.sp + ) + ) + } + } + } + } +} + +fun startHomeActivity(context: Context, name: String) { + if (name.isNotBlank()) { + Superwall.instance.setUserAttributes(mapOf( + "firstName" to name + )) + } + + Superwall.instance.identify(userId = "abc") + + val intent = Intent(context, HomeActivity::class.java) + context.startActivity(intent) +} \ No newline at end of file diff --git a/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Color.kt b/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Color.kt new file mode 100644 index 00000000..b9847da8 --- /dev/null +++ b/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.superwall.exampleapp.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Theme.kt b/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Theme.kt new file mode 100644 index 00000000..fe30d3d0 --- /dev/null +++ b/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Theme.kt @@ -0,0 +1,71 @@ +package com.superwall.exampleapp.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Color(0xFF9AFBF0), + background = Color.Black, + surface = Color.Black +) + +private val LightColorScheme = lightColorScheme( + primary = Color(0xFF9AFBF0), + background = Color.Black, + surface = Color.Black + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun SuperwallExampleAppTheme( + darkTheme: Boolean = true, + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = false, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.background.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = false + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Type.kt b/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Type.kt new file mode 100644 index 00000000..c9245aef --- /dev/null +++ b/example/app/src/main/java/com/superwall/exampleapp/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.superwall.exampleapp.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/example/app/src/main/res/drawable/ic_launcher_background.xml b/example/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/example/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/app/src/main/res/drawable/ic_launcher_foreground.xml b/example/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/example/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/example/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/example/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 00000000..6f3b755b --- /dev/null +++ b/example/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/example/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/example/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 00000000..6f3b755b --- /dev/null +++ b/example/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/example/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/example/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..c209e78e Binary files /dev/null and b/example/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/example/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/example/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..b2dfe3d1 Binary files /dev/null and b/example/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/example/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/example/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..4f0f1d64 Binary files /dev/null and b/example/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/example/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/example/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..62b611da Binary files /dev/null and b/example/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/example/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/example/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..948a3070 Binary files /dev/null and b/example/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/example/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/example/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..1b9a6956 Binary files /dev/null and b/example/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/example/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/example/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..28d4b77f Binary files /dev/null and b/example/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/example/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/example/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9287f508 Binary files /dev/null and b/example/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/example/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/example/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..aa7d6427 Binary files /dev/null and b/example/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/example/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/example/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..9126ae37 Binary files /dev/null and b/example/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/example/app/src/main/res/values/colors.xml b/example/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/example/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/example/app/src/main/res/values/strings.xml b/example/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..e78b8998 --- /dev/null +++ b/example/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Superwall Example App + \ No newline at end of file diff --git a/example/app/src/main/res/values/themes.xml b/example/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..efcc5158 --- /dev/null +++ b/example/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +