hoony's web study

728x90
반응형

Android Intent for InAppWebView


Web을 기반으로 하는 App을 만들다 보니 intent://,  snssdk1180:// 등과 같이 웹프로토콜이 아닌 App Scheme로 링크를 요청하는 경우에는 WebView 자체에서 처리를 하지 못합니다. 
이런 경우에 처리하는 방법입니다. 

InAppWebView 기본 옵션 변경

우선 기본 옵션을 변경해야합니다. 

initialOptions: InAppWebViewGroupOptions(
           crossPlatform: InAppWebViewOptions(
               useShouldOverrideUrlLoading: true,
               ...
           ),
           ...

위와 같이 옵션을 변경하시면 InAppWebView에서 제공하는 EventHandler 중

shouldOverrideUrlLoading 함수를 이용 가능합니다.

이제 아래에서, Android에 method를 invoke 해서 intent를 처리하는 로직을

작성해보도록 하겠습니다.

우선 Kotlin 으로 해서 Method Channel 을 구성해야합니다. 

package com.example.inappwebviewintent

import android.content.ActivityNotFoundException
import android.content.Intent
import android.util.Log
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.net.URISyntaxException
 


class MainActivity: FlutterActivity() {
    private val CHANNEL = "Channel Name"

    //MethodChannel 구현
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if(call.method == "getAppUrl") {                // Intent:// 스키마를 통한 URL파싱
                try {
                    val url: String? = call.argument("url")

                    if(url == null) {
                        result.error("9999", "URL PARAMETER IS NULL", null)
                    } else {
                        Log.i("[getAppUrl] url", url)
                        val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
                        result.success(intent.dataString)
                    }
                } catch (e: URISyntaxException) {
                    result.notImplemented()
                } catch (e: ActivityNotFoundException) {
                    result.notImplemented()
                }
            } else if(call.method == "getMarketUrl") {          // 들어온 URL을 통해 package 명 및 market 다운로드 주소 반환
                try {
                    val url: String? = call.argument("url")
                    if(url == null) {
                        result.error("9999", "URL PARAMETER IS NULL", null)
                    } else {
                        Log.i("[getMarketUrl] url", url)
                        val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
                        val scheme = intent.scheme
                        val packageName = intent.getPackage()
                        if (packageName != null) {
                            result.success("market://details?id=$packageName")
                        }
                        result.notImplemented()
                    }
                } catch (e: URISyntaxException) {
                    result.notImplemented()
                } catch (e: ActivityNotFoundException) {
                    result.notImplemented()
                }
            } else {
                result.notImplemented()
            }
        }
    }

}


이후 Flutter에서 해당 Method Channel을 이용하면 됩니다.
우리는 url_launcher 를 사용했습니다. 
https://pub.dev/packages/url_launcher
 

 

url_launcher | Flutter Package

Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes.

pub.dev

아래와 같이 채널명을 선언을 합니다. 
Kotlin에서 선언한 것과 동일하게 선언해야합니다. 

static const methodChannel = MethodChannel('Channel Name');

 

아래 함수는  Url에서 Scheme가 어떤 것인지를 확인하는 함수입니다. 

bool isAppLink(Uri url) {
     final appScheme = url.scheme;

     return appScheme != 'http' &&
         appScheme != 'https' &&
         appScheme != 'about:blank' &&
         appScheme != 'data';
   }
반응형

아래는 InAppWebView  Method 내에서 사용하는 방법입니다. 

shouldOverrideUrlLoading: (controller, navigationAction) async {

         print('====================shouldOverrideUrlLoading====================');
         var curUrl = navigationAction.request.url;

         if (curUrl == null) return NavigationActionPolicy.CANCEL;

         // URL String의 Shceme가 http, https 가 아닌 지 검거
         if (isAppLink(curUrl)) {
           await controller.stopLoading();

           var scheme = curUrl.scheme;

           if (scheme == "intent") {        // 일반적인 Intent의 경우
             if (Platform.isAndroid) {
               try {
                 final parsedIntent = await methodChannel.invokeMethod('getAppUrl', {'url': curUrl.toString()});
                 print(parsedIntent);

                 if (await canLaunchUrl(Uri.parse(parsedIntent))) {
                   launchUrl(parsedIntent);
                 } else {
                   final marketUrl = await methodChannel.invokeMethod('getMarketUrl', {'url': curUrl.toString()});
                   launchUrl(marketUrl);
                 }
               } on PlatformException catch (e) {
                 // 오류 처리
                 print('${e.message}');
               }
             }
           } else if (scheme.contains('snssdk')) {    // TikTok 인경우 이미 파싱이 되어진 상태로 넘어온다. 따라서, 실행가능여부 검사 후 실행 및 패키지 다운로드로 진행
             if (Platform.isAndroid) {
               try {
                 if (await canLaunchUrl(curUrl)) {
                   launchUrl(curUrl.toString());
                 } else {
                   launchUrl('market://details?id=com.ss.android.ugc.trill');
                 }
               } on PlatformException catch (e) {
                 // 오류 처리
                 print('${e.message}');
               }
             }
           }

           return NavigationActionPolicy.CANCEL;
         } else {
           return NavigationActionPolicy.ALLOW;
         }


         print(
             '====================shouldOverrideUrlLoading====================');
       }


InAppWebView를 사용해서 타사의 웹을 띄우는것이 쉬울줄 알았지만 이런 부분도 해결해야하는지는 이번 기회에 알게되었답니다. 

즐거운 Fluttering 되세요. 

글 내용이 마음에 드시면 좋아요! 구독 부탁드립니다. 

728x90

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading