In the recent project, it is necessary to realize the function that the H5 page opened in WebView can be opened in other applications. This article will record the implementation scheme of this function.

The browser of the mobile phone can open other applications through two protocols:

  1. The Scheme protocol (on which Android Deep Links is based).
  2. Agreement of Intent.

1. Implement the Scheme protocol

The Scheme protocol format is:

[scheme]://[host]/[path]? [query]Copy the code

If a page in your App needs to respond to the Scheme protocol, add the following code to Mainfest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.minigame.sdktest"> <application> <activity android:name="com.minigame.sdktest.ui.HomeActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action The android: name = "android. Intent. Action. The VIEW" / > / / to respond to implicit intent must provide this category < category The android: name = "android. Intent. The category. The DEFAULT" / > / / from the browser to open the application must provide the category "category The android: name = "android. Intent. Category. BROWSABLE" / > / / representative resolve URL format for the Activity < data android: host = "www.minigame.vip" android:scheme="https" /> <data android:host="minigame.vip" android:scheme="jump" /> </intent-filter> </activity> </application> </manifest>Copy the code

Note: If multiple data are included in an intent-filter, the matching rule covers all combinations of data. To match a unique URL, an intent-filter can contain only one data. In the example above www.minigame.vip, https://minigame.v… Can open MainActivity.

WebView supports Scheme protocol

The browser of mobile phone supports Scheme protocol, but WebView needs to be implemented by itself. The code is as follows:

webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView? , request:WebResourceRequest?) : Boolean { val url = request? .url val scheme = url? .scheme val host = url? .host when (scheme) {" HTTPS "-> {// Only certain hosts are filtered to determine whether to jump. Also don't filter the if (= = "www.minigame.vip" host) {gotoOtherAppBySchemeProtocol (url)}} - > {" jump" gotoOtherAppBySchemeProtocol(url) } else -> {} } return super.shouldOverrideUrlLoading(view, request) } } private fun gotoOtherAppBySchemeProtocol(url: // intents = intents (intent.action_view, intents) {// intents = intents (intent.action_view, intents); url) // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) // if (isActivityExits(intent)) { // startActivity(intent) // } try { val intent = Intent(Intent.ACTION_VIEW, url) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) startActivity(intent) }  catch (e: ActivityNotFoundException) {/ / by ActivityNotFound exceptions to deal directly with them to ensure the program doesn't crash e.p rintStackTrace ()}} / * * * intent corresponding Activity exists * * above Android R (11), according to the official documentation (https://developer.android.com/training/package-visibility) * need through two ways to ensure that returns a value * 1. = QUERY_ALL_PACKAGES = QUERY_ALL_PACKAGES = QUERY_ALL_PACKAGES = QUERY_ALL_PACKAGES = QUERY_ALL_PACKAGES */ private fun isActivityExits(intent: intent): Boolean { val resolveActivity = intent.resolveActivity(packageManager) return resolveActivity ! = null }Copy the code

Default Scheme format (Https, Http)

The implementation effect is as follows:

As you can see, there’s a box that pops up asking the user to choose which App to open, which doesn’t work very well.

As mentioned in the official documentation, on Android M (6.0) and above, if you verify that you are the owner of your app and website, the check box will not pop up and will go directly to your app. To verify application and website ownership, you need to:

  1. Enable automatic validation in Mainfest:
<intent-filter  android:autoVerify="true">
    ...
</intent-filter>
Copy the code
  1. Provide assetlinks.json on your website:
https://domain.name/.well-known/assetlinks.json
Copy the code

The assetlink.json file can be generated using the App Links Assistant in AndroidStudio. For details, refer to the official documentation.

Custom Scheme

The implementation effect is as follows:

And you can see that I just opened my App. The WebView shows that a web page cannot be opened, which can be handled according to business requirements, such as staying on the previous page or loading a new web address.

2. Implement the Intent

The format of the Intent is:

intent://[host]##Intent; package=[String]; action=[String]; category=[String]; component=[String]; scheme=[String]; S.browser_fallback_url=[String]; end;Copy the code

You can only fill in the required parameters, for example:

intent://www.minigame.vip##Intent; package=com.minigame.sdktest; scheme=https; S.browser_fallback_url=https://www.minigame.vip; end;Copy the code

The configuration required for App to respond to an Intent is the same as that required for Scheme.

Webviews support intEnts

Mobile browsers support Intent, but webViews need to be implemented by themselves. The code is as follows:

webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView? , request:WebResourceRequest?) : Boolean { val url = request? .url val scheme = url? .scheme if("intent" == scheme){ gotoOtherAppByIntentProtocol(url) } return super.shouldOverrideUrlLoading(view, request) } } private fun gotoOtherAppByIntentProtocol(url: Uri) { val stringUrl = url.toString() val fallbackUrl: String = if (stringUrl.contains("S.browser_fallback_url")) { stringUrl.substring(stringUrl.indexOf("S.browser_fallback_url"), stringUrl.indexOf("; end")) } else { "" } try { val intent = Intent.parseUri(url.toString(), Intent.URI_INTENT_SCHEME) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) startActivity(intent) } catch (e: URISyntaxException) { e.printStackTrace() } catch (e: ActivityNotFoundException) {/ / by ActivityNotFound exceptions to deal directly with them to ensure the program doesn't crash e.p rintStackTrace ()}}Copy the code

The implementation effect is as follows:

The problems are the same as with custom schemes.

3. Use the package name to open the application

In addition to opening the application through protocol, you can also pass the package name of the App to be opened to the Android terminal through JS interaction, and open the application through the package name. The code is as follows:

/ / Settings are open JavaScript webView. Settings. JavaScriptEnabled = true / / allow the js pop-up window webView.settings.javaScriptCanOpenWindowsAutomatically = true webView.addJavascriptInterface(object : JsInterface { @JavascriptInterface override fun openApp(packageName: String?) { gotoOtherAppByPackageName(packageName) } }, "JsInterface") fun gotoOtherAppByPackageName(packageName: String?) { if (! packageName.isNullOrEmpty()) { if (checkInstallStatus(this, packageName)) { val intent = packageManager.getLaunchIntentForPackage(packageName) if (intent ! Flags = intent.flag_activity_new_task startActivity(intent)}}}} /** * Determine whether the app is installed based on the package name */ fun checkInstallStatus(context: Context, packageName: String): Boolean { return try { val packageInfo = context.packageManager.getPackageInfo(packageName, 0) packageInfo ! = null } catch (e: NameNotFoundException) { e.printStackTrace() false } }Copy the code

The implementation effect is as follows:

conclusion

The above schemes can meet the function that the H5 page opened in WebView can open other applications, which can be selected as required.