-
Notifications
You must be signed in to change notification settings - Fork 26
Paywalls #649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Paywalls #649
Changes from 67 commits
Commits
Show all changes
78 commits
Select commit
Hold shift + click to select a range
53e264a
Add RevenueCatUI package
facumenzella f9683a5
revert code
facumenzella 0a407d2
Add minimum
facumenzella 151ef83
Moved files to RevenueCat/Android
facumenzella 45ad7d0
Moved files from Runtime to Scripts
facumenzella c4ac9de
Use SimpleJSON
facumenzella e20e4ce
delete extra .meta
facumenzella cdd8ea2
Updated bool IsSupported(); doc
facumenzella be566dd
Added pure code options, removed GameObjecft
facumenzella c550a2e
Split callback
facumenzella d1d075e
added debug logs
facumenzella 1a0e59c
Remove StubPaywallPresenter.cs
facumenzella 8b7784b
Delete RevenueCatUICallbackHandler.cs
facumenzella 69096d4
Add missing pieces
facumenzella c191052
Merge branch 'main' into feat/add-revenuecatui
facumenzella 1ee5dcc
ready
facumenzella 2cbfef6
remove reference to ui package
facumenzella 160ed40
rename to com.revenuecat.unity.ui
facumenzella e03df34
Merge branch 'main' into feat/add-revenuecatui
facumenzella c3bd377
Update Subtester/Assets/Scenes/Main.unity
facumenzella 0ad7ba1
Remove extra Stub.meta
facumenzella a8591ee
Remove README
facumenzella ffca566
Rename .asmdef files
facumenzella 5417e16
Remove customer center code
facumenzella 9ec1edf
Merge branch 'feat/add-revenuecatui' of github.com:RevenueCat/purchas…
facumenzella 6a43d86
remove _isSupportedCache
facumenzella 0a829d0
Update Subtester/Packages/manifest.json
facumenzella 18b9ba1
renamed editor asmdef and deleted extra files
facumenzella 21ac4df
Merge branch 'feat/add-revenuecatui' of github.com:RevenueCat/purchas…
facumenzella 17aed4a
revert Subtester
facumenzella d3cca45
Update Subtester/Assets/Plugins/Android/mainTemplate.gradle
facumenzella f51be9e
Update Subtester/ProjectSettings/AndroidResolverDependencies.xml
facumenzella 1e1ee01
Make properties internal
facumenzella 0143afe
Merge branch 'feat/add-revenuecatui' of github.com:RevenueCat/purchas…
facumenzella 2693a44
minimal paywalls presentation
vegaro 97dec08
clean up
vegaro 980a0e5
Merge branch 'main' into paywalls-android-poc
vegaro b9bb1e0
clean up
vegaro b048332
clean up
vegaro cfbc8d9
move into androidlib
vegaro 1c4c865
add local dependency
vegaro f072bde
updated mainTemplate.gradle
vegaro 2457709
update PurchasesListener
vegaro 44bc909
gitignore
vegaro eb27acf
add log to AndroidPaywallPresenter
vegaro 5da75f8
add displayCloseButton
vegaro 56258da
fix compilation of Subtester when exporting package
vegaro be4ef13
use componentActivity
vegaro 649cff4
presentPaywallIfNeeded
vegaro e55f375
add close button to log
vegaro 26961f3
add PresentPaywallIfNeeded to PurchasesListener
vegaro 07afd3d
update AndroidResolverDependencies
vegaro dde2af5
implements PaywallResultHandler
vegaro 96d73f6
rename to PaywallTrampolineActivity and made transparent
vegaro cf1a839
remove unneeded comment
vegaro 09651b4
TODOs on logs
vegaro 8065057
remove UnityBridge
vegaro 82643ac
Paywalls iOS (#675)
facumenzella 52a8260
Merge branch 'main' into paywalls-android-poc
vegaro 40f1404
small PR comments
vegaro 830e978
Merge branch 'paywalls-android-poc' of github.com:RevenueCat/purchase…
vegaro 588ea5d
Another TODO
vegaro 79dfe20
static functions in trampoline activity
vegaro bb5af22
create PaywallUnityOptions
vegaro 34b2f4a
add PaywallUnityOptions to signature
vegaro bba5ea9
remove gameObjectName from parameters
vegaro 903aad4
fix versions
vegaro bed291d
Extract paywall result strings to constants in PaywallTrampolineActiv…
vegaro 3ec3b42
fix indentation
vegaro cba2097
change default display close button
vegaro aabecf5
AndroidApplication.currentActivity
vegaro bcc83a6
better handling of exceptions on AndroidJavaClass
vegaro b36f845
remove MonoBehaviour
vegaro e032186
remove ?? "default"
vegaro ed13583
Merge branch 'main' into paywalls-android-poc
vegaro c4fa9e7
fix PurchasesListener
vegaro 1601b1b
update version
vegaro 08005f2
Merge branch 'main' into paywalls-android-poc
vegaro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or 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 |
|---|---|---|
|
|
@@ -27,3 +27,4 @@ fastlane/.env | |
| # CircleCI folders | ||
| vendor/ | ||
| .bundle/ | ||
| RevenueCatUI/Plugins/Android/RevenueCatUI.androidlib/build | ||
This file contains hidden or 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 was deleted.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
17 changes: 17 additions & 0 deletions
17
RevenueCatUI/Plugins/Android/RevenueCatUI.androidlib/AndroidManifest.xml
This file contains hidden or 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 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:tools="http://schemas.android.com/tools"> | ||
|
|
||
| <application> | ||
| <activity | ||
| android:name=".PaywallTrampolineActivity" | ||
| android:exported="false" | ||
| android:theme="@android:style/Theme.Translucent.NoTitleBar" | ||
| tools:node="merge" /> | ||
| </application> | ||
|
|
||
| <!-- No special permissions required --> | ||
|
|
||
| </manifest> | ||
|
|
||
|
|
50 changes: 50 additions & 0 deletions
50
RevenueCatUI/Plugins/Android/RevenueCatUI.androidlib/build.gradle
This file contains hidden or 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,50 @@ | ||
| apply plugin: 'com.android.library' | ||
|
|
||
| dependencies { | ||
| implementation fileTree(dir: 'libs', include: ['*.jar']) | ||
| implementation 'com.revenuecat.purchases:purchases-hybrid-common:17.8.0' | ||
| implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.8.0' | ||
| implementation 'androidx.activity:activity:1.8.2' | ||
| // TODO: Automatically update this to the latest version | ||
| } | ||
|
|
||
| android { | ||
| namespace 'com.revenuecat.purchasesunity.ui' | ||
|
|
||
| compileSdk getProperty("unity.compileSdkVersion") as int | ||
| buildToolsVersion = getProperty("unity.buildToolsVersion") | ||
|
|
||
| compileOptions { | ||
| sourceCompatibility JavaVersion.valueOf(getProperty("unity.javaCompatabilityVersion")) | ||
| targetCompatibility JavaVersion.valueOf(getProperty("unity.javaCompatabilityVersion")) | ||
| } | ||
|
|
||
| sourceSets { | ||
| main { | ||
| manifest.srcFile 'AndroidManifest.xml' | ||
| java.srcDirs = ['src/main/java'] | ||
| res.srcDirs = ['src/main/res'] | ||
| assets.srcDirs = ['src/main/assets'] | ||
| jniLibs.srcDirs = ['src/main/jniLibs'] | ||
| } | ||
| } | ||
|
|
||
| def unityLib = project(':unityLibrary').extensions.getByName('android') | ||
|
|
||
| defaultConfig { | ||
| consumerProguardFiles "consumer-proguard.pro" | ||
| minSdkVersion unityLib.defaultConfig.minSdkVersion.mApiLevel | ||
| targetSdkVersion unityLib.defaultConfig.targetSdkVersion.mApiLevel | ||
| } | ||
|
|
||
| lintOptions { | ||
| abortOnError false | ||
| } | ||
|
|
||
| buildTypes { | ||
| release { | ||
| minifyEnabled false | ||
| proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules. pro' | ||
| } | ||
| } | ||
| } |
1 change: 1 addition & 0 deletions
1
RevenueCatUI/Plugins/Android/RevenueCatUI.androidlib/proguard-android-optimize.txt
This file contains hidden or 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 @@ | ||
| -keep class com.unity3d.player.** { *; } |
185 changes: 185 additions & 0 deletions
185
....androidlib/src/main/java/com/revenuecat/purchasesunity/ui/PaywallTrampolineActivity.java
This file contains hidden or 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,185 @@ | ||
| package com.revenuecat.purchasesunity.ui; | ||
|
|
||
| import android.app.Activity; | ||
| import android.content.Intent; | ||
| import android.os.Bundle; | ||
| import android.util.Log; | ||
|
|
||
| import androidx.annotation.Nullable; | ||
| import androidx.activity.ComponentActivity; | ||
|
|
||
| import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivityLauncher; | ||
| import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallResult; | ||
| import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallResultHandler; | ||
| import com.revenuecat.purchases.PresentedOfferingContext; | ||
|
|
||
| public class PaywallTrampolineActivity extends ComponentActivity implements PaywallResultHandler { | ||
tonidero marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| public static final String EXTRA_PAYWALL_OPTIONS = "rc_paywall_options"; | ||
|
|
||
| private static final String TAG = "PurchasesUnity"; | ||
|
|
||
| private PaywallActivityLauncher launcher; | ||
|
|
||
| @Override | ||
| protected void onCreate(@Nullable Bundle savedInstanceState) { | ||
| super.onCreate(savedInstanceState); | ||
|
|
||
| final Intent source = getIntent(); | ||
| PaywallUnityOptions options = source.getParcelableExtra(EXTRA_PAYWALL_OPTIONS); | ||
|
|
||
| if (options == null) { | ||
| Log.e(TAG, "PaywallUnityOptions is null; cannot launch paywall"); | ||
| RevenueCatUI.sendPaywallResult("ERROR"); | ||
| finish(); | ||
| return; | ||
| } | ||
|
|
||
| launcher = new PaywallActivityLauncher(this, this); | ||
|
|
||
| if (options.getRequiredEntitlementIdentifier() != null) { | ||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "Using launchIfNeeded for entitlement '" + options.getRequiredEntitlementIdentifier() + "'"); | ||
| launchPaywallIfNeeded(options); | ||
| } else { | ||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "No entitlement check required, presenting paywall directly"); | ||
| launchPaywall(options); | ||
| } | ||
| } | ||
|
|
||
| private void launchPaywallIfNeeded(PaywallUnityOptions options) { | ||
| String requiredEntitlementIdentifier = options.getRequiredEntitlementIdentifier(); | ||
| String offeringId = options.getOfferingId(); | ||
| boolean shouldDisplayDismissButton = options.getShouldDisplayDismissButton(); | ||
|
|
||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "Launching paywall if needed with PaywallActivityLauncher"); | ||
| Log.d(TAG, "Options - entitlement: " + requiredEntitlementIdentifier + ", offering: " + offeringId + ", dismissButton: " + shouldDisplayDismissButton); | ||
|
|
||
| if (offeringId != null) { | ||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "Using launchIfNeeded with offering ID"); | ||
| launcher.launchIfNeeded( | ||
| requiredEntitlementIdentifier, | ||
| offeringId, | ||
| new PresentedOfferingContext(offeringId), // TODO: pass PresentedOfferingContext data | ||
vegaro marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| null, // fontProvider | ||
| shouldDisplayDismissButton, | ||
| false, // edgeToEdge | ||
| paywallDisplayResult -> { | ||
| if (!paywallDisplayResult) { | ||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "PaywallDisplayCallback: paywall not needed"); | ||
| RevenueCatUI.sendPaywallResult("NOT_PRESENTED"); | ||
| finish(); | ||
| } | ||
| // If paywallDisplayResult is true, the paywall will be shown and result will come through normal callback | ||
| } | ||
| ); | ||
| } else { | ||
| Log.w(TAG, "launchIfNeeded requires an offering ID, falling back to regular launch"); | ||
| launchPaywall(options); | ||
| } | ||
| } | ||
|
|
||
| private void launchPaywall(PaywallUnityOptions options) { | ||
| String offeringId = options.getOfferingId(); | ||
| boolean shouldDisplayDismissButton = options.getShouldDisplayDismissButton(); | ||
|
|
||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "Launching paywall with PaywallActivityLauncher"); | ||
| Log.d(TAG, "Options - offering: " + offeringId + ", dismissButton: " + shouldDisplayDismissButton); | ||
|
|
||
| if (offeringId != null) { | ||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "Launching paywall with offering ID"); | ||
| launcher.launch(offeringId, | ||
| new PresentedOfferingContext(offeringId), // TODO: pass PresentedOfferingContext data | ||
| null, // fontProvider | ||
| shouldDisplayDismissButton); | ||
| } else { | ||
| // TODO: Remove debug log before shipping | ||
| Log.d(TAG, "Launching paywall with standard method"); | ||
| launcher.launch( | ||
| null, // offering (Offering object, not String) | ||
| null, // fontProvider | ||
| shouldDisplayDismissButton | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void onActivityResult(PaywallResult result) { | ||
| try { | ||
| if (result != null) { | ||
| sendPaywallResult(result); | ||
| } | ||
| } finally { | ||
| finish(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private void sendPaywallResult(PaywallResult result) { | ||
| final String resultName; | ||
| if (result instanceof PaywallResult.Purchased) { | ||
| resultName = "PURCHASED"; | ||
| } else if (result instanceof PaywallResult.Restored) { | ||
| resultName = "RESTORED"; | ||
| } else if (result instanceof PaywallResult.Cancelled) { | ||
| resultName = "CANCELLED"; | ||
| } else if (result instanceof PaywallResult.Error) { | ||
| resultName = "ERROR"; | ||
| } else { | ||
| resultName = "CANCELLED"; | ||
| } | ||
|
|
||
| RevenueCatUI.sendPaywallResult(resultName); | ||
| } | ||
|
|
||
| public static void presentPaywall(Activity activity, @Nullable String offeringIdentifier, boolean displayCloseButton) { | ||
| if (activity == null) { | ||
| Log.e(TAG, "Activity is null; cannot launch paywall"); | ||
| RevenueCatUI.sendPaywallResult("ERROR"); | ||
| return; | ||
| } | ||
|
|
||
| PaywallUnityOptions options = new PaywallUnityOptions(offeringIdentifier, displayCloseButton, null); | ||
|
|
||
| Intent intent = new Intent(activity, PaywallTrampolineActivity.class); | ||
| intent.putExtra(EXTRA_PAYWALL_OPTIONS, options); | ||
|
|
||
| try { | ||
| activity.startActivity(intent); | ||
| } catch (Throwable t) { | ||
| Log.e(TAG, "Error launching PaywallTrampolineActivity", t); | ||
| RevenueCatUI.sendPaywallResult("ERROR"); | ||
| } | ||
| } | ||
|
|
||
| public static void presentPaywallIfNeeded(Activity activity, String requiredEntitlementIdentifier, @Nullable String offeringIdentifier, boolean displayCloseButton) { | ||
| if (activity == null) { | ||
| Log.e(TAG, "Activity is null; cannot launch paywall"); | ||
| RevenueCatUI.sendPaywallResult("ERROR"); | ||
| return; | ||
| } | ||
|
|
||
| if (requiredEntitlementIdentifier == null) { | ||
| Log.e(TAG, "Required entitlement identifier is null; cannot launch paywall if needed"); | ||
| RevenueCatUI.sendPaywallResult("ERROR"); | ||
| return; | ||
| } | ||
|
|
||
| PaywallUnityOptions options = new PaywallUnityOptions(offeringIdentifier, displayCloseButton, requiredEntitlementIdentifier); | ||
|
|
||
| Intent intent = new Intent(activity, PaywallTrampolineActivity.class); | ||
| intent.putExtra(EXTRA_PAYWALL_OPTIONS, options); | ||
|
|
||
| try { | ||
| activity.startActivity(intent); | ||
| } catch (Throwable t) { | ||
| Log.e(TAG, "Error launching PaywallTrampolineActivity for presentPaywallIfNeeded", t); | ||
| RevenueCatUI.sendPaywallResult("ERROR"); | ||
vegaro marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come to think of it, why are we not moving this to PHC? At that point we could even remove the intermediate
PaywallFragment, right? In any case, using this trampoline pattern in PHC would also avoid the need forFragmentActivityin purchases-flutter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it but I don't know how we would do the
UnityBridge.sendMessage(gameObject, method, resultName));, since we can't doonActivityResultdirectly fromRevenueCatUI.java.I think the only way would be to have a singleton in phc that we use as a message passing mechanism.
Or am I missing anything?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hah, I wrote this before I saw this comment. Great minds and all that 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm so I'm not in love with holding references to objects like the listener statically... I think for most cases it would be ok, but could potentially lead to some memory leaks, and we should make sure the cleanup process works correctly... So in general I would try to avoid it, but considering that in Unity we kinda have to, I could see that we might want to move the logic to PHC in case it can be useful for other hybrids...
IMO, I do prefer the current production pattern we use in the other hybrids, where we don't hold anything statically as long as it works. I would only use this static holder approach when there is no alternative. Do we have other use cases for other hybrids where we need this? if so, I would be ok moving it to PHC. If not, I think better to keep it to Unity, and leave the other hybrids use the existing approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the main advantage would be to move Flutter to this system, so developers don't need to implement
FlutterFragmentActivityThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I kinda agree that I would prefer the
FlutterFragmentActivityapproach over the staticPaywallResultListenerRegistryas well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea that's fair. I'm totally okay going with this for now!
Down the line we can look at using this pattern in PHC. Maybe we don't move
PaywallTrampolineActivityto PHC, but we create a separate one. That way we can still avoid thePaywallFragment, and don't need to rely on keeping a reference to the listener. That way it would work like this:But again, that's something we can worry about later. Let's go with what we have for now!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From trampoline to trampoline!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gymnasts! 💪