-
Notifications
You must be signed in to change notification settings - Fork 169
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
Upgrade Flutter Android APIs #74
Changes from 5 commits
0b6d056
96665c8
6b9416e
9f5bae9
90aeee3
da8768f
fba6c5b
2981196
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
include: package:pedantic/analysis_options.1.8.0.yaml | ||
analyzer: | ||
exclude: | ||
# Ignore generated files | ||
- '**/*.g.dart' | ||
- 'lib/src/generated/*.dart' | ||
linter: | ||
rules: | ||
- public_member_api_docs |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
package com.revenuecat.purchases_flutter; | ||
|
||
import android.app.Activity; | ||
import android.content.Context; | ||
|
||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
|
||
|
@@ -19,6 +22,10 @@ | |
import java.util.List; | ||
import java.util.Map; | ||
|
||
import io.flutter.embedding.engine.plugins.FlutterPlugin; | ||
import io.flutter.embedding.engine.plugins.activity.ActivityAware; | ||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; | ||
import io.flutter.plugin.common.BinaryMessenger; | ||
import io.flutter.plugin.common.MethodCall; | ||
import io.flutter.plugin.common.MethodChannel; | ||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler; | ||
|
@@ -31,18 +38,27 @@ | |
/** | ||
* PurchasesFlutterPlugin | ||
*/ | ||
public class PurchasesFlutterPlugin implements MethodCallHandler { | ||
public class PurchasesFlutterPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware { | ||
|
||
private static final String PURCHASER_INFO_UPDATED = "Purchases-PurchaserInfoUpdated"; | ||
|
||
private final Registrar registrar; | ||
private final MethodChannel channel; | ||
// Only set registrar for v1 embedder. | ||
private PluginRegistry.Registrar registrar; | ||
// Only set activity for v2 embedder. Always access activity from getActivity() method. | ||
private Context applicationContext; | ||
private MethodChannel channel; | ||
private Activity activity; | ||
|
||
private static final String PLATFORM_NAME = "flutter"; | ||
private static final String PLUGIN_VERSION = "1.2.0-SNAPSHOT"; | ||
|
||
public PurchasesFlutterPlugin(Registrar registrar, MethodChannel channel) { | ||
this.registrar = registrar; | ||
this.channel = channel; | ||
/** | ||
* Plugin registration. | ||
*/ | ||
public static void registerWith(Registrar registrar) { | ||
PurchasesFlutterPlugin instance = new PurchasesFlutterPlugin(); | ||
instance.onAttachedToEngine(registrar.messenger(), registrar.context()); | ||
instance.registrar = registrar; | ||
registrar.addViewDestroyListener(new PluginRegistry.ViewDestroyListener() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure we need this, but I don't think it would hurt to keep it since we already have it. |
||
@Override | ||
public boolean onViewDestroy(FlutterNativeView flutterNativeView) { | ||
|
@@ -56,12 +72,51 @@ public boolean onViewDestroy(FlutterNativeView flutterNativeView) { | |
}); | ||
} | ||
|
||
/** | ||
* Plugin registration. | ||
*/ | ||
public static void registerWith(Registrar registrar) { | ||
final MethodChannel channel = new MethodChannel(registrar.messenger(), "purchases_flutter"); | ||
channel.setMethodCallHandler(new PurchasesFlutterPlugin(registrar, channel)); | ||
@Override | ||
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { | ||
onAttachedToEngine(binding.getBinaryMessenger(), binding.getApplicationContext()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the new API |
||
} | ||
|
||
private void onAttachedToEngine(BinaryMessenger messenger, Context applicationContext) { | ||
this.channel = new MethodChannel(messenger, "purchases_flutter"); | ||
this.applicationContext = applicationContext; | ||
this.channel.setMethodCallHandler(this); | ||
} | ||
|
||
@Override | ||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { | ||
try { | ||
Purchases.getSharedInstance().close(); | ||
} catch (UninitializedPropertyAccessException e) { | ||
// there's no instance so all good | ||
} | ||
channel.setMethodCallHandler(null); | ||
this.channel = null; | ||
this.applicationContext = null; | ||
} | ||
|
||
@Override | ||
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { | ||
this.activity = binding.getActivity(); | ||
} | ||
|
||
@Override | ||
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { | ||
onAttachedToActivity(binding); | ||
} | ||
|
||
@Override | ||
public void onDetachedFromActivity() { | ||
this.activity = null; | ||
} | ||
|
||
@Override | ||
public void onDetachedFromActivityForConfigChanges() { | ||
onDetachedFromActivity(); | ||
} | ||
|
||
public Activity getActivity() { | ||
return registrar != null ? registrar.activity() : activity; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is how they do in https://github.com/flutter/plugins/blob/88e85c6a48063cfab9111689179e2582a99b77ff/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java Since we still need to support the old API for apps that haven't updated yet, we need to check if the |
||
} | ||
|
||
@Override | ||
|
@@ -185,7 +240,7 @@ private void sendEvent(String eventName, @Nullable Map<String, Object> params) { | |
|
||
private void setupPurchases(String apiKey, String appUserID, @Nullable Boolean observerMode, final Result result) { | ||
PlatformInfo platformInfo = new PlatformInfo(PLATFORM_NAME, PLUGIN_VERSION); | ||
CommonKt.configure(this.registrar.context(), apiKey, appUserID, observerMode, platformInfo); | ||
CommonKt.configure(this.applicationContext, apiKey, appUserID, observerMode, platformInfo); | ||
|
||
Purchases.getSharedInstance().setUpdatedPurchaserInfoListener(new UpdatedPurchaserInfoListener() { | ||
@Override | ||
|
@@ -234,7 +289,7 @@ private void purchaseProduct(final String productIdentifier, final String oldSKU | |
@Nullable final Integer prorationMode, final String type, | ||
final Result result) { | ||
CommonKt.purchaseProduct( | ||
this.registrar.activity(), | ||
getActivity(), | ||
productIdentifier, | ||
oldSKU, | ||
prorationMode, | ||
|
@@ -249,7 +304,7 @@ private void purchasePackage(final String packageIdentifier, | |
@Nullable final Integer prorationMode, | ||
final Result result) { | ||
CommonKt.purchasePackage( | ||
this.registrar.activity(), | ||
getActivity(), | ||
packageIdentifier, | ||
offeringIdentifier, | ||
oldSKU, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.revenuecat.purchases_flutter; | ||
|
||
import androidx.test.rule.ActivityTestRule; | ||
|
||
import com.revenuecat.purchases_flutter_example.EmbeddingV1Activity; | ||
|
||
import dev.flutter.plugins.e2e.FlutterRunner; | ||
import org.junit.Rule; | ||
import org.junit.runner.RunWith; | ||
|
||
@RunWith(FlutterRunner.class) | ||
public class EmbeddingV1ActivityTest { | ||
@Rule | ||
public ActivityTestRule<EmbeddingV1Activity> rule = | ||
new ActivityTestRule<>(EmbeddingV1Activity.class); | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test makes sure the old APIs are still supported. I copied this from migration docs https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration#testing-your-plugin |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.revenuecat.purchases_flutter; | ||
|
||
import androidx.test.rule.ActivityTestRule; | ||
import dev.flutter.plugins.e2e.FlutterRunner; | ||
import org.junit.Rule; | ||
import org.junit.runner.RunWith; | ||
import io.flutter.embedding.android.FlutterActivity; | ||
|
||
@RunWith(FlutterRunner.class) | ||
public class MainActivityTest { | ||
@Rule | ||
public ActivityTestRule<FlutterActivity> rule = new ActivityTestRule<>(FlutterActivity.class); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test makes sure the new APIs are supported and the plugin is registered successfully. I copied this from migration docs https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration#testing-your-plugin |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,10 +11,9 @@ | |
android:label="purchases_flutter_example" | ||
android:icon="@mipmap/ic_launcher"> | ||
<activity | ||
android:name=".MainActivity" | ||
android:launchMode="singleTop" | ||
android:name="io.flutter.embedding.android.FlutterActivity" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need a |
||
android:theme="@style/LaunchTheme" | ||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale" | ||
android:hardwareAccelerated="true" | ||
android:windowSoftInputMode="adjustResize"> | ||
<!-- This keeps the window background of the activity showing | ||
|
@@ -29,5 +28,13 @@ | |
<category android:name="android.intent.category.LAUNCHER"/> | ||
</intent-filter> | ||
</activity> | ||
<activity | ||
android:name=".EmbeddingV1Activity" | ||
android:theme="@style/LaunchTheme" | ||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale" | ||
android:hardwareAccelerated="true" | ||
android:windowSoftInputMode="adjustResize"> | ||
</activity> | ||
<meta-data android:name="flutterEmbedding" android:value="2"/> | ||
</application> | ||
</manifest> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.revenuecat.purchases_flutter_example; | ||
|
||
import android.os.Bundle; | ||
import com.revenuecat.purchases_flutter.PurchasesFlutterPlugin; | ||
import io.flutter.app.FlutterActivity; | ||
import io.flutter.view.FlutterMain; | ||
|
||
public class EmbeddingV1Activity extends FlutterActivity { | ||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
FlutterMain.startInitialization(this); | ||
super.onCreate(savedInstanceState); | ||
PurchasesFlutterPlugin.registerWith(registrarFor("com.revenuecat.purchases_flutter")); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. EmbeddingV1Activity.java uses the v1 embedding for the example project in the same folder as MainActivity to keep testing the v1 embedding’s compatibility with your plugin. Note that we have to manually register all the plugins instead of using GeneratedPluginRegistrant, as |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ class _MyAppState extends State<InitialScreen> { | |
Future<void> initPlatformState() async { | ||
await Purchases.setDebugLogsEnabled(true); | ||
await Purchases.setup("api_key"); | ||
Purchases.addAttributionData({}, PurchasesAttributionNetwork.facebook); | ||
await Purchases.addAttributionData({}, PurchasesAttributionNetwork.facebook); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice |
||
PurchaserInfo purchaserInfo = await Purchases.getPurchaserInfo(); | ||
Offerings offerings = await Purchases.getOfferings(); | ||
// If the widget was removed from the tree while the asynchronous platform | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,11 @@ dev_dependencies: | |
purchases_flutter: | ||
path: ../ | ||
|
||
pedantic: ^1.8.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw most plugins use this linter tool so I added it |
||
e2e: ^0.2.1 | ||
flutter_driver: | ||
sdk: flutter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was mentioned in the upgrading docs, it's used to create end to end tests. |
||
|
||
# For information on the generic Dart part of this file, see the | ||
# following page: https://www.dartlang.org/tools/pub/pubspec | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ class Purchases { | |
static final Set<PurchaserInfoUpdateListener> _purchaserInfoUpdateListeners = | ||
Set(); | ||
|
||
static final _channel = new MethodChannel('purchases_flutter') | ||
static final _channel = MethodChannel('purchases_flutter') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
..setMethodCallHandler((MethodCall call) async { | ||
switch (call.method) { | ||
case "Purchases-PurchaserInfoUpdated": | ||
|
@@ -141,7 +141,7 @@ class Purchases { | |
purchaseType = PurchaseType.inapp; | ||
} | ||
return purchaseProduct(productIdentifier, | ||
upgradeInfo: new UpgradeInfo(oldSKU), type: purchaseType); | ||
upgradeInfo: UpgradeInfo(oldSKU), type: purchaseType); | ||
} | ||
|
||
/// Makes a purchase. Returns a [PurchaserInfo] object. Throws a | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,15 +8,19 @@ documentation: https://docs.revenuecat.com/ | |
|
||
environment: | ||
sdk: ">=2.1.0 <3.0.0" | ||
flutter: ">=1.12.0 <2.0.0" | ||
flutter: ">=1.12.13+hotfix.6 <2.0.0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also taken from the upgrading docs, this is the minimum version they offer support for. |
||
|
||
dependencies: | ||
flutter: | ||
sdk: flutter | ||
|
||
dev_dependencies: | ||
pedantic: ^1.8.0 | ||
flutter_test: | ||
sdk: flutter | ||
e2e: ^0.2.1 | ||
flutter_driver: | ||
sdk: flutter | ||
|
||
# For information on the generic Dart part of this file, see the | ||
# following page: https://www.dartlang.org/tools/pub/pubspec | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:e2e/e2e.dart'; | ||
import 'package:purchases_flutter/purchases_flutter.dart'; | ||
|
||
void main() { | ||
E2EWidgetsFlutterBinding.ensureInitialized(); | ||
|
||
testWidgets('Can setup Purchases', (WidgetTester tester) async { | ||
final Future<void> future = Purchases.setup('apiKey', appUserId: 'cesar'); | ||
expect(future, completes); | ||
}); | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also copied from the upgrading docs. I ran it by doing Also saw another example here https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_core/firebase_core/test/firebase_core_e2e.dart This test actually runs in an emulator, so my understanding is we could test a bunch of stuff this way. It only works in Android though. I tried making a purchase and I could see the purchase dialog popping up :) I am not even adding it to circleci for now, but I am going to leave it since it's part of the upgrading docs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we could have either a comment or something documenting how to actually run this test so we don't forget? or even a gradle task that wraps the command |
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.
This is the old API, but we still need to support it