TWA-重定向时阻止打开外部Chrome

时间:2019-11-06 21:13:36

标签: android progressive-web-apps trusted-web-activity

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

Screenshot

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>

manifest.json:

{
  "dir": "ltr",
  "lang": "en-us",
  "name": "Mi Retail",
  "scope": "https://app.miretail.com.co/*/*",
  "display": "standalone",
  "start_url": "https://app.miretail.com.co/login",
  "short_name": "Mi Retail",
  "theme_color": "transparent",
  "description": "GROW YOUR RETAIL WITH MI RETAIL. Effortlessly sell, manage, report and grow your retail business in every way with Mi Retail unmatched POS, multi-outlet retail and inventory management system.",
  "orientation": "any",
  "background_color": "transparent",
  "related_applications": [],
  "prefer_related_applications": false,
  "icons": []
}

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());
    }
}

1 个答案:

答案 0 :(得分:0)

您在android:autoVerify上缺少intent-filter。有效地添加它会声明您的应用为处理这些URL的默认方式,并且仅在设置了数字资产链接(您必须构建TWA)的情况下有效。

    <intent-filter android:autoVerify="true">
        <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>

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