Skip to content

Commit

Permalink
Merge pull request #131 from superwall/develop
Browse files Browse the repository at this point in the history
1.1.9
  • Loading branch information
ianrumac authored Jul 1, 2024
2 parents 942b9f3 + 1c4546b commit 43ac4be
Show file tree
Hide file tree
Showing 17 changed files with 628 additions and 336 deletions.
74 changes: 74 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Contributing to Superwall

We want to make contributing to this project as easy and transparent as
possible, and actively welcome your pull requests. If you run into problems,
please open an issue on GitHub.

## Pull Requests

1. Fork the repo and create your branch from `develop`.
2. Run the setup script `./scripts/setup.sh`
3. If you've added code that should be tested, add tests.
4. If you've changed APIs, update the documentation.
5. Ensure the test suite passes.
6. Make sure your code lints (see below).
7. Tag @ianrumac or @yusuftor in the pull request.
8. Add an entry to the [CHANGELOG.md](../CHANGELOG.md) for any breaking changes, enhancements, or bug fixes.
9. After the PR is merged, delete the branch.

## Coding Style


### Ktlint

To maintain readability and achieve code consistency, we follow the [Kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html)
and [Android's Kotlin style guide](https://developer.android.com/kotlin/style-guide).
We use [Ktlint](https://github.com/pinterest/ktlint) to check for style errors.

To do this locally, you can either run the setup script or install ktlint using `brew install ktlint` and either:
* Run `ktlint` from the root folder to detect style issues immediately.
* Use `ktlint installGitPreCommitHook` to install a pre-commit hook that will run ktlint before every commit.

### IntelliJ IDEA

To configure IntelliJ IDEA to work with ktlint, you can use one of the following approaches:

* Install the [ktlint plugin](https://plugins.jetbrains.com/plugin/15057-ktlint) and follow the instructions.
* Use the IntelliJ configuration and follow the official [Ktlint Guide](https://pinterest.github.io/ktlint/latest/rules/configuration-intellij-idea/) to avoid conflicts with IntelliJ IDEA's built-in formatting.

### Documentation

Public classes and methods must contain detailed documentation.

## Editing the code

Open the module: `./superwall/` containing the SDK itself.

For the example app's and test app's, open either the:
* `./app/` for the testing application
* `./example/` for the example application


## Git Workflow

We have two branches: `master` and `develop`.

All pull requests are set to merge into `develop`, with the exception of a hotfix on `master`.

Name your branch `author/feature/<feature name>` for consistency.

When we're ready to cut a new release, we update the `version` in [build.gradle](/superwall/build.gradle.kts) and merge `develop` into `master`.

## Testing

If you add new code, please make sure it gets tested! When fixing bugs, try to reproduce the bug in a unit test and then fix the test. This makes sure we never regress that issue again.
Before creating a pull request, run all unit and integration tests. Make sure they all pass and fix any broken tests.
We also have a GitHub Action that runs the tests on push.

## Issues

We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue.

## License

By contributing to `Superwall`, you agree that your contributions will be licensed under the LICENSE file in the root directory of this source tree.
14 changes: 14 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Changes in this pull request

-

### Checklist

- [ ] All unit tests pass.
- [ ] All UI tests pass.
- [ ] Demo project builds and runs.
- [ ] I added/updated tests or detailed why my change isn't tested.
- [ ] I added an entry to the `CHANGELOG.md` for any breaking changes, enhancements, or bug fixes.
- [ ] I have run `ktlint` in the main directory and fixed any issues.
- [ ] I have updated the SDK documentation as well as the online docs.
- [ ] I have reviewed the [contributing guide](https://github.com/superwall/Superwall-Android/tree/master/.github/CONTRIBUTING.md)
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

The changelog for `Superwall`. Also see the [releases](https://github.com/superwall/Superwall-Android/releases) on GitHub.

## 1.1.9

### Deprecations

- Deprecated configuration method `Superwall.configure(applicationContext: Context, ...)` in favor of `Superwall.configure(applicationContext: Application, ...)` to enforce type safety. The rest of the method signature remains the same.

### Fixes

- SW-2878: and it's related leaks. The `PaywallViewController` was not being properly detached when activity was stopped, causing memory leaks.
- SW-2872: Fixes issue where `deviceAttributes` event and fetching would not await for IP geo to complete.
- Fixes issues on tablet devices where the paywall would close after rotation/configuration change.

## 1.1.8

### Enhancements
Expand Down
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ dependencies {
androidTestImplementation(libs.test.rules)
androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.ui.test.junit4)
debugImplementation(libs.leakcanary.android)

// Debug
debugImplementation(libs.ui.tooling)
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ browser_version = "1.5.0"
gradle_plugin_version = "8.4.1"
javascriptengineVersion = "1.0.0-beta01"
kotlinxCoroutinesGuavaVersion = "1.8.1"
leakcanaryAndroidVersion = "2.14"
lifecycleProcessVersion = "2.8.1"
mockk = "1.13.8"
revenue_cat_version = "7.7.1"
Expand Down Expand Up @@ -37,6 +38,7 @@ serialization_version = "1.6.0"
# SQL

javascriptengine = { module = "androidx.javascriptengine:javascriptengine", version.ref = "javascriptengineVersion" }
leakcanary-android = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakcanaryAndroidVersion" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room_runtime_version" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room_runtime_version" }
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room_runtime_version" }
Expand Down
25 changes: 25 additions & 0 deletions scripts/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

cd "$(dirname "$0")/.."


echo 'Checking for Ktlint...'
if which ktlint >/dev/null; then
echo 'KtLint already installed ✅'
else
if which brew >/dev/null; then
echo 'Installing Ktlint...'
brew install ktlint
else
echo "
Error: Ktlint could not be installed! ❌
Check installation instructions from https://pinterest.github.io/ktlint/latest/ for manual
install, or brew install ktlint. Then run this script again."
exit 1
fi
fi

echo 'Installing githooks...'
ktlint installGitPreCommitHook
echo 'Done! ✅'

2 changes: 1 addition & 1 deletion superwall/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ plugins {
id("signing")
}

version = "1.1.8"
version = "1.1.9"

android {
compileSdk = 34
Expand Down
71 changes: 62 additions & 9 deletions superwall/src/main/java/com/superwall/sdk/Superwall.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.superwall.sdk

import android.app.Application
import android.content.Context
import android.net.Uri
import androidx.lifecycle.ViewModelProvider
import androidx.work.WorkManager
import com.superwall.sdk.analytics.internal.track
import com.superwall.sdk.analytics.internal.trackable.InternalSuperwallEvent
Expand All @@ -24,23 +26,35 @@ import com.superwall.sdk.paywall.presentation.internal.dismiss
import com.superwall.sdk.paywall.presentation.internal.state.PaywallResult
import com.superwall.sdk.paywall.vc.PaywallViewController
import com.superwall.sdk.paywall.vc.SuperwallPaywallActivity
import com.superwall.sdk.paywall.vc.SuperwallStoreOwner
import com.superwall.sdk.paywall.vc.ViewModelFactory
import com.superwall.sdk.paywall.vc.ViewStorageViewModel
import com.superwall.sdk.paywall.vc.delegate.PaywallViewControllerEventDelegate
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.*
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.Closed
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.Custom
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.InitiatePurchase
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.InitiateRestore
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.OpenedDeepLink
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.OpenedURL
import com.superwall.sdk.paywall.vc.web_view.messaging.PaywallWebEvent.OpenedUrlInSafari
import com.superwall.sdk.storage.ActiveSubscriptionStatus
import com.superwall.sdk.store.ExternalNativePurchaseController
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.update
import java.util.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class Superwall(
internal var context: Context,
context: Context,
private var apiKey: String,
private var purchaseController: PurchaseController?,
options: SuperwallOptions?,
Expand All @@ -49,10 +63,19 @@ class Superwall(
) : PaywallViewControllerEventDelegate {
private var _options: SuperwallOptions? = options
private val ioScope = CoroutineScope(Dispatchers.IO)
internal var context: Context = context.applicationContext

// Add a private variable for the purchase task
private var purchaseTask: Job? = null

private val viewStorageViewModel =
ViewModelProvider(
SuperwallStoreOwner(),
ViewModelFactory(),
).get(ViewStorageViewModel::class.java)

internal fun viewStore(): ViewStorageViewModel = viewStorageViewModel

internal val presentationItems: PresentationItems = PresentationItems()

/**
Expand Down Expand Up @@ -163,7 +186,8 @@ class Superwall(
*/
val latestPaywallInfo: PaywallInfo?
get() {
val presentedPaywallInfo = dependencyContainer.paywallManager.presentedViewController?.info
val presentedPaywallInfo =
dependencyContainer.paywallManager.presentedViewController?.info
return presentedPaywallInfo ?: presentationItems.paywallInfo
}

Expand Down Expand Up @@ -217,7 +241,7 @@ class Superwall(
* @return The configured [Superwall] instance.
*/
fun configure(
applicationContext: Context,
applicationContext: Application,
apiKey: String,
purchaseController: PurchaseController? = null,
options: SuperwallOptions? = null,
Expand Down Expand Up @@ -259,6 +283,25 @@ class Superwall(
true
}
}

@Deprecated(
"This constructor is too ambiguous and will be removed in upcoming versions. Use Superwall.configure(Application, ...) instead.",
)
fun configure(
applicationContext: Context,
apiKey: String,
purchaseController: PurchaseController? = null,
options: SuperwallOptions? = null,
activityProvider: ActivityProvider? = null,
completion: (() -> Unit)? = null,
) = configure(
applicationContext.applicationContext as Application,
apiKey,
purchaseController,
options,
activityProvider,
completion,
)
}

private lateinit var _dependencyContainer: DependencyContainer
Expand All @@ -284,7 +327,8 @@ class Superwall(
)
}

val cachedSubsStatus = dependencyContainer.storage.get(ActiveSubscriptionStatus) ?: SubscriptionStatus.UNKNOWN
val cachedSubsStatus =
dependencyContainer.storage.get(ActiveSubscriptionStatus) ?: SubscriptionStatus.UNKNOWN
setSubscriptionStatus(cachedSubsStatus)

addListeners()
Expand Down Expand Up @@ -329,7 +373,8 @@ class Superwall(
*/
fun togglePaywallSpinner(isHidden: Boolean) {
ioScope.launch {
val paywallViewController = dependencyContainer.paywallManager.presentedViewController ?: return@launch
val paywallViewController =
dependencyContainer.paywallManager.presentedViewController ?: return@launch
paywallViewController.togglePaywallSpinner(isHidden)
}
}
Expand All @@ -353,7 +398,9 @@ class Superwall(
* Removes all of Superwall's pending local notifications.
*/
fun cancelAllScheduledNotifications() {
WorkManager.getInstance(context).cancelAllWorkByTag(SuperwallPaywallActivity.NOTIFICATION_CHANNEL_ID)
WorkManager
.getInstance(context)
.cancelAllWorkByTag(SuperwallPaywallActivity.NOTIFICATION_CHANNEL_ID)
}

// MARK: - Reset
Expand Down Expand Up @@ -457,6 +504,7 @@ class Superwall(
closeReason = PaywallCloseReason.ManualClose,
)
}

is InitiatePurchase -> {
if (purchaseTask != null) {
// If a purchase is already in progress, do not start another
Expand All @@ -475,18 +523,23 @@ class Superwall(
}
}
}

is InitiateRestore -> {
dependencyContainer.transactionManager.tryToRestore(paywallViewController)
}

is OpenedURL -> {
dependencyContainer.delegateAdapter.paywallWillOpenURL(url = paywallEvent.url)
}

is OpenedUrlInSafari -> {
dependencyContainer.delegateAdapter.paywallWillOpenURL(url = paywallEvent.url)
}

is OpenedDeepLink -> {
dependencyContainer.delegateAdapter.paywallWillOpenDeepLink(url = paywallEvent.url)
}

is Custom -> {
dependencyContainer.delegateAdapter.handleCustomPaywallAction(name = paywallEvent.string)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import java.util.*
import java.util.Date

interface AppManagerDelegate {
suspend fun didUpdateAppSession(appSession: AppSession)
Expand Down Expand Up @@ -110,7 +110,13 @@ class AppSessionManager(
val userAttributes = delegate.makeUserAttributesEvent()

Superwall.instance.track(InternalSuperwallEvent.SessionStart())
Superwall.instance.track(InternalSuperwallEvent.DeviceAttributes(deviceAttributes))
if (didTrackAppLaunch) {
Superwall.instance.track(
InternalSuperwallEvent.DeviceAttributes(
deviceAttributes,
),
)
}
Superwall.instance.track(userAttributes)
}
} else {
Expand Down
Loading

0 comments on commit 43ac4be

Please sign in to comment.