Skip to content

Commit b72b56c

Browse files
authored
Merge pull request #34 from line/fix/plugin-integration
Migrate to Android v2 plugin spec
2 parents 7feb212 + 868c021 commit b72b56c

File tree

13 files changed

+162
-85
lines changed

13 files changed

+162
-85
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ For more examples, see the [example app](https://github.com/line/flutter_line_sd
2929
## Prerequisites
3030

3131
- iOS 10.0 or later as the deployment target
32-
- Android `minSdkVersion` set to 17 or higher (Android 4.2 or later)
32+
- Android `minSdkVersion` set to 21 or higher (Android 5.0 or later)
3333
- [LINE Login channel linked to your app](https://developers.line.biz/en/docs/line-login/getting-started/)
3434

3535
To access your LINE Login channel from a mobile platform, you need some extra configuration. In the [LINE Developers console][console], go to your LINE Login channel settings, and enter the below information on the **App settings** tab.

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ android {
3333
main.java.srcDirs += 'src/main/kotlin'
3434
}
3535
defaultConfig {
36-
minSdkVersion 18
36+
minSdkVersion 21
3737
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
3838
consumerProguardFiles 'consumer-proguard-rules.pro'
3939
}
Lines changed: 127 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,151 @@
11
package com.linecorp.flutter_line_sdk
22

3+
import android.app.Activity
34
import android.content.Intent
45
import io.flutter.plugin.common.MethodCall
56
import io.flutter.plugin.common.MethodChannel
67
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
78
import io.flutter.plugin.common.MethodChannel.Result
89
import io.flutter.plugin.common.PluginRegistry
10+
import io.flutter.embedding.engine.plugins.FlutterPlugin
11+
import io.flutter.embedding.engine.plugins.activity.ActivityAware
12+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
13+
import io.flutter.plugin.common.BinaryMessenger
914

10-
class FlutterLineSdkPlugin(
11-
private val channel: MethodChannel,
12-
private val lineSdkWrapper: LineSdkWrapper
13-
) : MethodCallHandler, PluginRegistry.ActivityResultListener {
14-
override fun onMethodCall(call: MethodCall, result: Result) = when (call.method) {
15-
"toBeta" -> run {
16-
val channelId: String = call.argument("channelId") ?: ""
17-
val openDiscoveryIdDocumentUrl: String = call.argument("openDiscoveryIdDocumentUrl") ?: ""
18-
val apiServerBaseUrl: String = call.argument("apiServerBaseUrl") ?: ""
19-
val webLoginPageUrl: String = call.argument("webLoginPageUrl") ?: ""
20-
lineSdkWrapper.setupBetaConfig(
21-
channelId,
22-
openDiscoveryIdDocumentUrl,
23-
apiServerBaseUrl,
24-
webLoginPageUrl
25-
)
26-
result.success(null)
27-
}
28-
"setup" -> {
29-
val channelId: String = call.argument<String?>("channelId").orEmpty()
30-
lineSdkWrapper.setupSdk(channelId)
31-
result.success(null)
32-
}
33-
"login" -> {
34-
val scopes = call.argument("scopes") ?: emptyList<String>()
35-
val isWebLogin = call.argument("onlyWebLogin") ?: false
36-
val botPrompt = call.argument("botPrompt") ?: "normal"
37-
val loginRequestCode = call.argument<Int?>("loginRequestCode") ?: DEFAULT_ACTIVITY_RESULT_REQUEST_CODE
38-
lineSdkWrapper.login(
39-
loginRequestCode,
40-
scopes = scopes,
41-
onlyWebLogin = isWebLogin,
42-
botPromptString = botPrompt,
43-
result = result
44-
)
15+
class FlutterLineSdkPlugin : MethodCallHandler, PluginRegistry.ActivityResultListener, FlutterPlugin, ActivityAware {
16+
17+
private var methodChannel: MethodChannel? = null
18+
private val lineSdkWrapper = LineSdkWrapper()
19+
20+
private var activity: Activity? = null
21+
private var activityBinding: ActivityPluginBinding? = null
22+
23+
override fun onMethodCall(call: MethodCall, result: Result) {
24+
when (call.method) {
25+
"toBeta" -> run {
26+
val channelId: String = call.argument("channelId") ?: ""
27+
val openDiscoveryIdDocumentUrl: String = call.argument("openDiscoveryIdDocumentUrl") ?: ""
28+
val apiServerBaseUrl: String = call.argument("apiServerBaseUrl") ?: ""
29+
val webLoginPageUrl: String = call.argument("webLoginPageUrl") ?: ""
30+
lineSdkWrapper.setupBetaConfig(
31+
channelId,
32+
openDiscoveryIdDocumentUrl,
33+
apiServerBaseUrl,
34+
webLoginPageUrl
35+
)
36+
result.success(null)
37+
}
38+
"setup" -> {
39+
val channelId: String = call.argument<String?>("channelId").orEmpty()
40+
val activity = activity
41+
if (activity == null) {
42+
result.error(
43+
"no_activity_found",
44+
"There is no valid Activity found to present LINE SDK Login screen.",
45+
null
46+
)
47+
return
48+
}
49+
lineSdkWrapper.setupSdk(activity, channelId)
50+
result.success(null)
51+
}
52+
"login" -> {
53+
val activity = this.activity
54+
if (activity == null) {
55+
result.error(
56+
"no_activity_found",
57+
"There is no valid Activity found to present LINE SDK Login screen.",
58+
null
59+
)
60+
return
61+
}
62+
63+
val scopes = call.argument("scopes") ?: emptyList<String>()
64+
val isWebLogin = call.argument("onlyWebLogin") ?: false
65+
val botPrompt = call.argument("botPrompt") ?: "normal"
66+
val loginRequestCode = call.argument<Int?>("loginRequestCode") ?: DEFAULT_ACTIVITY_RESULT_REQUEST_CODE
67+
lineSdkWrapper.login(
68+
loginRequestCode,
69+
activity,
70+
scopes = scopes,
71+
onlyWebLogin = isWebLogin,
72+
botPromptString = botPrompt,
73+
result = result
74+
)
75+
}
76+
"getProfile" -> lineSdkWrapper.getProfile(result)
77+
"currentAccessToken" -> lineSdkWrapper.getCurrentAccessToken(result)
78+
"refreshToken" -> lineSdkWrapper.refreshToken(result)
79+
"verifyAccessToken" -> lineSdkWrapper.verifyAccessToken(result)
80+
"getBotFriendshipStatus" -> lineSdkWrapper.getBotFriendshipStatus(result)
81+
"logout" -> lineSdkWrapper.logout(result)
82+
else -> result.notImplemented()
4583
}
46-
"getProfile" -> lineSdkWrapper.getProfile(result)
47-
"currentAccessToken" -> lineSdkWrapper.getCurrentAccessToken(result)
48-
"refreshToken" -> lineSdkWrapper.refreshToken(result)
49-
"verifyAccessToken" -> lineSdkWrapper.verifyAccessToken(result)
50-
"getBotFriendshipStatus" -> lineSdkWrapper.getBotFriendshipStatus(result)
51-
"logout" -> lineSdkWrapper.logout(result)
52-
else -> result.notImplemented()
84+
}
85+
86+
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
87+
onAttachedToEngine(binding.binaryMessenger)
88+
}
89+
90+
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
91+
methodChannel = null;
92+
}
93+
94+
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
95+
bindActivityBinding(binding)
96+
}
97+
98+
override fun onDetachedFromActivity() {
99+
unbindActivityBinding()
100+
}
101+
102+
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
103+
bindActivityBinding(binding)
104+
}
105+
106+
override fun onDetachedFromActivityForConfigChanges() {
107+
unbindActivityBinding()
53108
}
54109

55110
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?): Boolean =
56111
lineSdkWrapper.handleActivityResult(requestCode, resultCode, intent)
57112

113+
private fun bindActivityBinding(binding: ActivityPluginBinding) {
114+
this.activity = binding.activity
115+
this.activityBinding = binding
116+
addActivityResultListener(binding)
117+
}
118+
119+
private fun unbindActivityBinding() {
120+
activityBinding?.removeActivityResultListener(this)
121+
this.activity = null;
122+
this.activityBinding = null
123+
}
124+
125+
private fun onAttachedToEngine(messenger: BinaryMessenger) {
126+
methodChannel = MethodChannel(messenger, CHANNEL_NAME)
127+
methodChannel!!.setMethodCallHandler(this)
128+
}
129+
130+
private fun addActivityResultListener(activityBinding: ActivityPluginBinding) {
131+
activityBinding.addActivityResultListener(this)
132+
}
133+
134+
private fun addActivityResultListener(registrar: PluginRegistry.Registrar) {
135+
registrar.addActivityResultListener(this)
136+
}
137+
58138
companion object {
59139
private const val CHANNEL_NAME = "com.linecorp/flutter_line_sdk"
60140
private const val DEFAULT_ACTIVITY_RESULT_REQUEST_CODE = 8192
61141

62142
@JvmStatic
63143
fun registerWith(registrar: PluginRegistry.Registrar) {
64-
val methodChannel = MethodChannel(registrar.messenger(), CHANNEL_NAME)
65-
val lineSdkPlugin =
66-
FlutterLineSdkPlugin(methodChannel, LineSdkWrapper(registrar.activity()))
67-
methodChannel.setMethodCallHandler(lineSdkPlugin)
68-
registrar.addActivityResultListener(lineSdkPlugin)
144+
FlutterLineSdkPlugin().apply {
145+
onAttachedToEngine(registrar.messenger())
146+
activity = registrar.activity()
147+
addActivityResultListener(registrar)
148+
}
69149
}
70150
}
71151
}

android/src/main/kotlin/com/linecorp/flutter_line_sdk/LineSdkWrapper.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.linecorp.flutter_line_sdk
22

33
import android.app.Activity
44
import android.content.ContentValues.TAG
5+
import android.content.Context
56
import android.content.Intent
67
import android.util.Log
78
import com.google.gson.Gson
@@ -28,30 +29,29 @@ import kotlinx.coroutines.launch
2829
import kotlinx.coroutines.withContext
2930

3031

31-
class LineSdkWrapper(
32-
private val activity: Activity
33-
) {
32+
class LineSdkWrapper {
3433
private lateinit var lineApiClient: LineApiClient
3534
private lateinit var channelId: String
3635
private val gson = Gson()
3736
private var loginRequestCode: Int = 0
3837
private var betaConfig: BetaConfig? = null
3938
private val uiCoroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
4039

41-
fun setupSdk(channelId: String) {
40+
fun setupSdk(activity: Activity, channelId: String) {
4241
runIfDebugBuild { Log.d(TAG, "setupSdk") }
4342

4443
if (!this::channelId.isInitialized) {
4544
this.channelId = channelId
4645
}
4746

48-
lineApiClient = createLineApiClient(channelId)
47+
lineApiClient = createLineApiClient(activity, channelId)
4948
}
5049

5150
var loginResult: Result? = null
5251

5352
fun login(
5453
loginRequestCode: Int,
54+
activity: Activity,
5555
scopes: List<String> = listOf("profile"),
5656
onlyWebLogin: Boolean = false,
5757
botPromptString: String = "normal",
@@ -259,12 +259,12 @@ class LineSdkWrapper(
259259
)
260260
}
261261

262-
private fun createLineApiClient(channelId: String): LineApiClient =
262+
private fun createLineApiClient(activity: Activity, channelId: String): LineApiClient =
263263
if (betaConfig == null) {
264-
LineApiClientBuilder(activity.applicationContext, channelId).build()
264+
LineApiClientBuilder(activity, channelId).build()
265265
} else {
266266
LineApiClientFactory.createLineApiClient(
267-
activity.applicationContext,
267+
activity,
268268
channelId,
269269
betaConfig!!.apiServerBaseUrl
270270
)

example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ android {
3838

3939
defaultConfig {
4040
applicationId "com.linecorp.linesdk.sample"
41-
minSdkVersion 18
41+
minSdkVersion 21
4242
targetSdkVersion 28
4343
versionCode flutterVersionCode.toInteger()
4444
versionName flutterVersionName

example/android/app/src/main/AndroidManifest.xml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
<uses-permission android:name="android.permission.INTERNET" />
55
<application
6-
android:name=".MyApplication"
76
android:label="flutter_line_sdk_example"
87
android:icon="@mipmap/ic_launcher">
8+
<meta-data
9+
android:name="flutterEmbedding"
10+
android:value="2" />
911
<activity
1012
android:name=".MainActivity"
1113
android:launchMode="singleTop"
@@ -14,8 +16,12 @@
1416
android:hardwareAccelerated="true"
1517
android:windowSoftInputMode="adjustResize">
1618
<meta-data
17-
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
18-
android:value="true" />
19+
android:name="io.flutter.embedding.android.SplashScreenDrawable"
20+
android:resource="@drawable/launch_background" />
21+
<meta-data
22+
android:name="io.flutter.embedding.android.NormalTheme"
23+
android:resource="@style/NormalTheme"
24+
/>
1925
<intent-filter>
2026
<action android:name="android.intent.action.MAIN"/>
2127
<category android:name="android.intent.category.LAUNCHER"/>
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
package com.linecorp.linesdk.sample
22

3-
import android.os.Bundle
4-
5-
import io.flutter.app.FlutterActivity
6-
import io.flutter.plugins.GeneratedPluginRegistrant
3+
import io.flutter.embedding.android.FlutterActivity
74

85
class MainActivity: FlutterActivity() {
9-
override fun onCreate(savedInstanceState: Bundle?) {
10-
super.onCreate(savedInstanceState)
11-
GeneratedPluginRegistrant.registerWith(this)
12-
}
136
}

example/android/app/src/main/kotlin/com/linecorp/linesdk/sample/MyApplication.kt

Lines changed: 0 additions & 13 deletions
This file was deleted.

example/android/app/src/main/res/values/styles.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@
55
Flutter draws its first frame -->
66
<item name="android:windowBackground">@drawable/launch_background</item>
77
</style>
8+
<!-- You can name this style whatever you'd like -->
9+
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
10+
<item name="android:windowBackground">@drawable/launch_background</item>
11+
</style>
812
</resources>

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ EXTERNAL SOURCES:
2323

2424
SPEC CHECKSUMS:
2525
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
26-
flutter_line_sdk: 5548efe8090cfb4520f60e6636ebffa3e1f8b1e4
26+
flutter_line_sdk: 5ca4b2a634c15b909d7e4b53f57bb81a21295b63
2727
LineSDKSwift: 25bc89f5f08578a39ec2dcb36ee58e2f920e893c
2828

2929
PODFILE CHECKSUM: 8365118b814fe42c5cc8cc139acc71bf3e2395df

0 commit comments

Comments
 (0)