Android TWA-重定向时防止外部镀铬打开

Android TWA-重定向时防止外部镀铬打开,android,progressive-web-apps,trusted-web-activity,Android,Progressive Web Apps,Trusted Web Activity,我基于PWABuilder开发了一个受信任的Web活动,并已部署在上,但当我已登录时,正确的行为将重定向到仪表板(我使用window.location.assign(“/dashboard”)。但是TWA尝试启动浏览器活动,我如何防止这种行为?并将页面流保持在TWA中?下一个屏幕截图显示了如何尝试打开浏览器: AndroidManifest: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="htt

我基于PWABuilder开发了一个受信任的Web活动,并已部署在上,但当我已登录时,正确的行为将重定向到仪表板(我使用window.location.assign(“/dashboard”)。但是TWA尝试启动浏览器活动,我如何防止这种行为?并将页面流保持在TWA中?下一个屏幕截图显示了如何尝试打开浏览器:

AndroidManifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
    package="miretail.com.pwa">

    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:label="@string/app_name"
        android:allowBackup="true"
        android:supportsRtl="true"
        android:icon="@mipmap/ic_launcher"
        android:theme="@style/SplashTheme"
        tools:ignore="GoogleAppIndexingWarning" >

        <meta-data
            android:name="asset_statements"
            android:resource="@string/asset_statements" />

        <activity android:name="miretail.com.pwa.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

          <activity
            android:name="android.support.customtabs.trusted.LauncherActivity">

            <!-- Edit android:value to change the url opened by the TWA -->
            <meta-data
                android:name="android.support.customtabs.trusted.DEFAULT_URL"
                android:value="@string/app_url" />

            <!-- This intent-filter adds the TWA to the Android Launcher -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <!--
              This intent-filter allows the TWA to handle Intents to open
              airhorner.com.
            -->
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE"/>

                <!-- Edit android:host to handle links to the target URL-->
                <data
                    android:scheme="https"
                    android:host="@string/app_host"/>
            </intent-filter>
        </activity>

    </application>

</manifest>
MainActivity.java:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        loadManifest();
        setDisplay(this);
        setOrientation(this);
        setName(this);
        setContentView(R.layout.activity_main);
        setWebView((WebView) this.findViewById(R.id.webView));
    }

    private void setDisplay(Activity activity) {
        if (this.manifestObject.optString("display").equals("fullscreen")) {
            activity.setTheme(R.style.FullscreenTheme);
        } else {
            activity.setTheme(R.style.AppTheme);
        }
    }

    private void setName(Activity activity) {
        String name = this.manifestObject.optString("name");
        if (!name.isEmpty()) {
            activity.setTitle(name);
        }
    }

    private static final String ANY = "any";
    private static final String NATURAL = "natural";
    private static final String PORTRAIT_PRIMARY = "portrait-primary";
    private static final String PORTRAIT_SECONDARY = "portrait-secondary";
    private static final String LANDSCAPE_PRIMARY = "landscape-primary";
    private static final String LANDSCAPE_SECONDARY = "landscape-secondary";
    private static final String PORTRAIT = "portrait";
    private static final String LANDSCAPE = "landscape";

    private void setOrientation(Activity activity) {
        String orientation = this.manifestObject.optString("orientation");
        switch (orientation) {
            case LANDSCAPE_PRIMARY:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                break;
            case PORTRAIT_PRIMARY:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                break;
            case LANDSCAPE:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
                break;
            case PORTRAIT:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
                break;
            case LANDSCAPE_SECONDARY:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                break;
            case PORTRAIT_SECONDARY:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                break;
            case ANY:
            case NATURAL:
            default:
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                break;
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void setWebView(WebView myWebView) {
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        String start_url = this.manifestObject.optString("start_url");
        String scope = this.manifestObject.optString("scope");
        myWebView.setWebViewClient(new PwaWebViewClient(start_url, scope));
        myWebView.loadUrl(start_url);
    }

    private static final String DEFAULT_MANIFEST_FILE = "manifest.json";
    private JSONObject manifestObject;

    private void loadManifest() {
        if (this.assetExists((DEFAULT_MANIFEST_FILE))) {
            try {
                this.manifestObject = this.loadLocalManifest();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    private boolean assetExists(String asset) {
        final AssetManager assetManager = this.getResources().getAssets();
        try {
            return Arrays.asList(assetManager.list("")).contains(asset);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return false;
    }

    private JSONObject loadLocalManifest() throws JSONException {
        try {
            InputStream inputStream = this.getResources().getAssets().open(DEFAULT_MANIFEST_FILE);
            int size = inputStream.available();
            byte[] bytes = new byte[size];
            int readBytes = inputStream.read(bytes);
            inputStream.close();
            if (readBytes > 0) {
                String jsonString = new String(bytes, "UTF-8");
                return new JSONObject(jsonString);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
和PWAWebClient.java:

package miretail.com.pwa;

import android.content.Intent;
import android.net.Uri;
import android.webkit.*;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Pattern;

class PwaWebViewClient extends WebViewClient {
    private Pattern scope_pattern;

    public PwaWebViewClient(String start_url, String scope) {
        try {
            URL baseUrl = new URL(start_url);
            URL scopeUrl = new URL(baseUrl, scope);
            if (!scopeUrl.toString().endsWith("*")) {
                scopeUrl = new URL(scopeUrl, "*");
            }

            this.scope_pattern = this.regexFromPattern(scopeUrl.toString());
        } catch (MalformedURLException e) {
            this.scope_pattern = null;
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        view.getContext().startActivity(i);
        return true;
    }

    private boolean scoped(String url) {
        return this.scope_pattern == null || this.scope_pattern.matcher(url).matches();
    }

    private Pattern regexFromPattern(String pattern) {
        final String toReplace = "\\.[]{}()^$?+|";
        StringBuilder regex = new StringBuilder();
        for (int i = 0; i < pattern.length(); i++) {
            char c = pattern.charAt(i);
            if (c == '*') {
                regex.append(".");
            } else if (toReplace.indexOf(c) > -1) {
                regex.append('\\');
            }
            regex.append(c);
        }
        return Pattern.compile(regex.toString());
    }
}
package miretail.com.pwa;
导入android.content.Intent;
导入android.net.Uri;
导入android.webkit.*;
导入java.net.MalformedURLException;
导入java.net.URL;
导入java.util.regex.Pattern;
类PwaWebViewClient扩展了WebViewClient{
私有模式范围(u)模式;;
公共PwaWebViewClient(字符串开始\u url,字符串范围){
试一试{
URL baseUrl=新URL(开始URL);
URL scopeUrl=新URL(baseUrl,范围);
如果(!scopeUrl.toString().endsWith(“*”)){
scopeUrl=新URL(scopeUrl,“*”);
}
this.scope_pattern=this.regexFromPattern(scopeUrl.toString());
}捕获(格式错误){
this.scope_pattern=null;
}
}
@抑制警告(“弃用”)
@凌驾
公共布尔值shouldOverrideUrlLoading(WebView视图,字符串url){
Intent i=新的Intent(Intent.ACTION_视图,Uri.parse(url));
view.getContext().startActivity(i);
返回true;
}
私有布尔作用域(字符串url){
返回this.scope_pattern==null | | this.scope_pattern.matcher(url.matches();
}
私有模式regexFromPattern(字符串模式){
最后一个字符串toReplace=“\\.[]{}()^$?+|”;
StringBuilder正则表达式=新的StringBuilder();
对于(int i=0;i-1){
regex.append('\\');
}
regex.append(c);
}
返回Pattern.compile(regex.toString());
}
}

您的
意图过滤器上缺少
android:autoVerify
。添加它可以有效地将您的应用程序声明为处理这些URL的默认方式,并且只有在设置了数字资产链接(您必须构建TWA)的情况下才有效


如果此更改后仍发生问题,请更新此问题

package miretail.com.pwa;

import android.content.Intent;
import android.net.Uri;
import android.webkit.*;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Pattern;

class PwaWebViewClient extends WebViewClient {
    private Pattern scope_pattern;

    public PwaWebViewClient(String start_url, String scope) {
        try {
            URL baseUrl = new URL(start_url);
            URL scopeUrl = new URL(baseUrl, scope);
            if (!scopeUrl.toString().endsWith("*")) {
                scopeUrl = new URL(scopeUrl, "*");
            }

            this.scope_pattern = this.regexFromPattern(scopeUrl.toString());
        } catch (MalformedURLException e) {
            this.scope_pattern = null;
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        view.getContext().startActivity(i);
        return true;
    }

    private boolean scoped(String url) {
        return this.scope_pattern == null || this.scope_pattern.matcher(url).matches();
    }

    private Pattern regexFromPattern(String pattern) {
        final String toReplace = "\\.[]{}()^$?+|";
        StringBuilder regex = new StringBuilder();
        for (int i = 0; i < pattern.length(); i++) {
            char c = pattern.charAt(i);
            if (c == '*') {
                regex.append(".");
            } else if (toReplace.indexOf(c) > -1) {
                regex.append('\\');
            }
            regex.append(c);
        }
        return Pattern.compile(regex.toString());
    }
}