Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 1a4cee7

Browse files
authored
[in_app_purchase] Federated Android implementation (#3841)
* Implement BillingClientWrapper and unit-tests * Android specific implementation * Moved Android specific methods into addition class * Added missing line end * Purchases status to restored after call restorePurchases * Don't force GooglePlayPurchaseParam * Implement registerPlatform method * Added TODO comment to add example * Fixed mistake in API documentation * Added additional assert to test enablePendingPurchase * Update Android implementation with latest platform_interface
1 parent f06a2b4 commit 1a4cee7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+5591
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0
2+
3+
* Initial open-source release.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Copyright 2013 The Flutter Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above
9+
copyright notice, this list of conditions and the following
10+
disclaimer in the documentation and/or other materials provided
11+
with the distribution.
12+
* Neither the name of Google Inc. nor the names of its
13+
contributors may be used to endorse or promote products derived
14+
from this software without specific prior written permission.
15+
16+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# in_app_purchase_android
2+
3+
The Android implementation of [`in_app_purchase`][1].
4+
5+
## Usage
6+
7+
### Import the package
8+
9+
This package has been endorsed, meaning that you only need to add `in_app_purchase`
10+
as a dependency in your `pubspec.yaml`. It will be automatically included in your app
11+
when you depend on `package:in_app_purchase`.
12+
13+
This is what the above means to your `pubspec.yaml`:
14+
15+
```yaml
16+
...
17+
dependencies:
18+
...
19+
in_app_purchase: ^0.6.0
20+
...
21+
```
22+
23+
If you wish to use the Android package only, you can add `in_app_purchase_android` as a
24+
dependency:
25+
26+
```yaml
27+
...
28+
dependencies:
29+
...
30+
in_app_purchase_android: ^1.0.0
31+
...
32+
```
33+
34+
## TODO
35+
- [ ] Add an example application demonstrating the use of the [in_app_purchase_android] package (see also issue [flutter/flutter#81695](https://github.com/flutter/flutter/issues/81695)).
36+
37+
38+
[1]: ../in_app_purchase/in_app_purchase
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include: ../../../analysis_options_legacy.yaml
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
group 'io.flutter.plugins.inapppurchase'
2+
version '1.0-SNAPSHOT'
3+
4+
buildscript {
5+
repositories {
6+
google()
7+
jcenter()
8+
}
9+
10+
dependencies {
11+
classpath 'com.android.tools.build:gradle:3.3.0'
12+
}
13+
}
14+
15+
rootProject.allprojects {
16+
repositories {
17+
google()
18+
jcenter()
19+
}
20+
}
21+
22+
apply plugin: 'com.android.library'
23+
24+
android {
25+
compileSdkVersion 29
26+
27+
defaultConfig {
28+
minSdkVersion 16
29+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
30+
}
31+
lintOptions {
32+
disable 'InvalidPackage'
33+
}
34+
compileOptions {
35+
sourceCompatibility JavaVersion.VERSION_1_8
36+
targetCompatibility JavaVersion.VERSION_1_8
37+
}
38+
}
39+
40+
dependencies {
41+
implementation 'androidx.annotation:annotation:1.0.0'
42+
implementation 'com.android.billingclient:billing:3.0.2'
43+
testImplementation 'junit:junit:4.12'
44+
testImplementation 'org.json:json:20180813'
45+
testImplementation 'org.mockito:mockito-core:3.6.0'
46+
androidTestImplementation 'androidx.test:runner:1.1.1'
47+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
48+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.gradle.jvmargs=-Xmx1536M
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#Mon Oct 29 10:30:44 PDT 2018
2+
distributionBase=GRADLE_USER_HOME
3+
distributionPath=wrapper/dists
4+
zipStoreBase=GRADLE_USER_HOME
5+
zipStorePath=wrapper/dists
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rootProject.name = 'in_app_purchase'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
package="io.flutter.plugins.inapppurchase">
3+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.inapppurchase;
6+
7+
import android.content.Context;
8+
import androidx.annotation.NonNull;
9+
import com.android.billingclient.api.BillingClient;
10+
import io.flutter.plugin.common.MethodChannel;
11+
12+
/** Responsible for creating a {@link BillingClient} object. */
13+
interface BillingClientFactory {
14+
15+
/**
16+
* Creates and returns a {@link BillingClient}.
17+
*
18+
* @param context The context used to create the {@link BillingClient}.
19+
* @param channel The method channel used to create the {@link BillingClient}.
20+
* @param enablePendingPurchases Whether to enable pending purchases. Throws an exception if it is
21+
* false.
22+
* @return The {@link BillingClient} object that is created.
23+
*/
24+
BillingClient createBillingClient(
25+
@NonNull Context context, @NonNull MethodChannel channel, boolean enablePendingPurchases);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.inapppurchase;
6+
7+
import android.content.Context;
8+
import com.android.billingclient.api.BillingClient;
9+
import io.flutter.plugin.common.MethodChannel;
10+
11+
/** The implementation for {@link BillingClientFactory} for the plugin. */
12+
final class BillingClientFactoryImpl implements BillingClientFactory {
13+
14+
@Override
15+
public BillingClient createBillingClient(
16+
Context context, MethodChannel channel, boolean enablePendingPurchases) {
17+
BillingClient.Builder builder = BillingClient.newBuilder(context);
18+
if (enablePendingPurchases) {
19+
builder.enablePendingPurchases();
20+
}
21+
return builder.setListener(new PluginPurchaseListener(channel)).build();
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.inapppurchase;
6+
7+
import android.app.Activity;
8+
import android.app.Application;
9+
import android.content.Context;
10+
import androidx.annotation.VisibleForTesting;
11+
import com.android.billingclient.api.BillingClient;
12+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
13+
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
14+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
15+
import io.flutter.plugin.common.BinaryMessenger;
16+
import io.flutter.plugin.common.MethodChannel;
17+
18+
/** Wraps a {@link BillingClient} instance and responds to Dart calls for it. */
19+
public class InAppPurchasePlugin implements FlutterPlugin, ActivityAware {
20+
21+
@VisibleForTesting
22+
static final class MethodNames {
23+
static final String IS_READY = "BillingClient#isReady()";
24+
static final String START_CONNECTION =
25+
"BillingClient#startConnection(BillingClientStateListener)";
26+
static final String END_CONNECTION = "BillingClient#endConnection()";
27+
static final String ON_DISCONNECT = "BillingClientStateListener#onBillingServiceDisconnected()";
28+
static final String QUERY_SKU_DETAILS =
29+
"BillingClient#querySkuDetailsAsync(SkuDetailsParams, SkuDetailsResponseListener)";
30+
static final String LAUNCH_BILLING_FLOW =
31+
"BillingClient#launchBillingFlow(Activity, BillingFlowParams)";
32+
static final String ON_PURCHASES_UPDATED =
33+
"PurchasesUpdatedListener#onPurchasesUpdated(int, List<Purchase>)";
34+
static final String QUERY_PURCHASES = "BillingClient#queryPurchases(String)";
35+
static final String QUERY_PURCHASE_HISTORY_ASYNC =
36+
"BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)";
37+
static final String CONSUME_PURCHASE_ASYNC =
38+
"BillingClient#consumeAsync(String, ConsumeResponseListener)";
39+
static final String ACKNOWLEDGE_PURCHASE =
40+
"BillingClient#(AcknowledgePurchaseParams params, (AcknowledgePurchaseParams, AcknowledgePurchaseResponseListener)";
41+
42+
private MethodNames() {};
43+
}
44+
45+
private MethodChannel methodChannel;
46+
private MethodCallHandlerImpl methodCallHandler;
47+
48+
/** Plugin registration. */
49+
@SuppressWarnings("deprecation")
50+
public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
51+
InAppPurchasePlugin plugin = new InAppPurchasePlugin();
52+
plugin.setupMethodChannel(registrar.activity(), registrar.messenger(), registrar.context());
53+
((Application) registrar.context().getApplicationContext())
54+
.registerActivityLifecycleCallbacks(plugin.methodCallHandler);
55+
}
56+
57+
@Override
58+
public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) {
59+
setupMethodChannel(
60+
/*activity=*/ null, binding.getBinaryMessenger(), binding.getApplicationContext());
61+
}
62+
63+
@Override
64+
public void onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding binding) {
65+
teardownMethodChannel();
66+
}
67+
68+
@Override
69+
public void onAttachedToActivity(ActivityPluginBinding binding) {
70+
methodCallHandler.setActivity(binding.getActivity());
71+
}
72+
73+
@Override
74+
public void onDetachedFromActivity() {
75+
methodCallHandler.setActivity(null);
76+
methodCallHandler.onDetachedFromActivity();
77+
}
78+
79+
@Override
80+
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
81+
onAttachedToActivity(binding);
82+
}
83+
84+
@Override
85+
public void onDetachedFromActivityForConfigChanges() {
86+
methodCallHandler.setActivity(null);
87+
}
88+
89+
private void setupMethodChannel(Activity activity, BinaryMessenger messenger, Context context) {
90+
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/in_app_purchase");
91+
methodCallHandler =
92+
new MethodCallHandlerImpl(activity, context, methodChannel, new BillingClientFactoryImpl());
93+
methodChannel.setMethodCallHandler(methodCallHandler);
94+
}
95+
96+
private void teardownMethodChannel() {
97+
methodChannel.setMethodCallHandler(null);
98+
methodChannel = null;
99+
methodCallHandler = null;
100+
}
101+
102+
@VisibleForTesting
103+
void setMethodCallHandler(MethodCallHandlerImpl methodCallHandler) {
104+
this.methodCallHandler = methodCallHandler;
105+
}
106+
}

0 commit comments

Comments
 (0)