logo
Développement
Rechercher
Guide d'intégration de l'espace de travail côté Android

Guide d'intégration de l'espace de travail côté Android

Introduction

Ce guide présente en détail comment intégrer l'espace de travail GPTBots dans une application Android, y compris la demande d'autorisations, l'interaction entre le natif et le H5 ainsi que d'autres configurations connexes.

GPTBots fournit également un projet de DÉMO pour l'espace de travail, qui peut vous aider à démarrer rapidement. Projet de DÉMO côté Android : android-webview-bridge


Liste des autorisations

Autorisations de base

Afin de garantir le bon fonctionnement des fonctionnalités liées à l'espace de travail, il est nécessaire de configurer les autorisations suivantes dans AndroidManifest.xml :

<!-- 网络权限 --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 录音权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- 存储权限 --> <!-- Android 13以下 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Android 13及以上 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- 其他辅助权限 --> <uses-permission android:name="android.permission.WAKE_LOCK" />
                      
                      <!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- 录音权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

<!-- 存储权限 -->
<!-- Android 13以下 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android 13及以上 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- 其他辅助权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />

                    
Ce bloc de code dans la fenêtre flottante

Gestion des autorisations

Afin de garantir que l'application puisse accéder normalement à des autorisations sensibles telles que le microphone, l'appareil photo et la galerie lors de son exécution, l'application doit demander dynamiquement les autorisations d'exécution nécessaires à son démarrage :

// Android 13及以上请求的权限 private final String[] permissionsForAndroid13 = { Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_VIDEO }; // Android 13以下请求的权限 private final String[] permissionsForBelowAndroid13 = { Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE };
                      
                      // Android 13及以上请求的权限
private final String[] permissionsForAndroid13 = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.READ_MEDIA_IMAGES,
    Manifest.permission.READ_MEDIA_AUDIO,
    Manifest.permission.READ_MEDIA_VIDEO
};

// Android 13以下请求的权限
private final String[] permissionsForBelowAndroid13 = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE
};

                    
Ce bloc de code dans la fenêtre flottante

Configuration de la WebView

Configuration de base

L'APP crée une instance de WebView et effectue les réglages nécessaires :

WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); // 启用 JavaScript webSettings.setDomStorageEnabled(true); // 启用 DOM 存储 API webSettings.setAllowFileAccess(true); // 允许访问文件 webSettings.setAllowContentAccess(true); // 允许访问内容 URL webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // 允许混合内容 webSettings.setMediaPlaybackRequiresUserGesture(false); // 不需要用户手势即可播放媒体
                      
                      WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true); // 启用 JavaScript
webSettings.setDomStorageEnabled(true); // 启用 DOM 存储 API
webSettings.setAllowFileAccess(true); // 允许访问文件
webSettings.setAllowContentAccess(true); // 允许访问内容 URL
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // 允许混合内容
webSettings.setMediaPlaybackRequiresUserGesture(false); // 不需要用户手势即可播放媒体

                    
Ce bloc de code dans la fenêtre flottante

Configuration relative à l'enregistrement audio

webSettings.setAllowFileAccessFromFileURLs(true); // 允许文件 URL 访问文件 webSettings.setAllowUniversalAccessFromFileURLs(true); // 允许通用访问 webSettings.setDatabaseEnabled(true); // 启用数据库 webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 设置缓存模式
                      
                      webSettings.setAllowFileAccessFromFileURLs(true); // 允许文件 URL 访问文件
webSettings.setAllowUniversalAccessFromFileURLs(true); // 允许通用访问
webSettings.setDatabaseEnabled(true); // 启用数据库
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 设置缓存模式

                    
Ce bloc de code dans la fenêtre flottante

Réglage du User-Agent

String userAgent = webSettings.getUserAgentString(); webSettings.setUserAgentString(userAgent + " WebViewApp/1.0");
                      
                      String userAgent = webSettings.getUserAgentString();
webSettings.setUserAgentString(userAgent + " WebViewApp/1.0");

                    
Ce bloc de code dans la fenêtre flottante

Accès à l'espace de travail

Construction de l'URL

L'accès à l'espace de travail nécessite les paramètres Base URL et AiToken :

String baseUrl = "https://gptbots.ai/space/h5/home"; String aiToken = "YOUR_AI_TOKEN"; // 替换为实际的AiToken String fullUrl = baseUrl + "?AiToken=" + aiToken;
                      
                      String baseUrl = "https://gptbots.ai/space/h5/home";
String aiToken = "YOUR_AI_TOKEN"; // 替换为实际的AiToken
String fullUrl = baseUrl + "?AiToken=" + aiToken;

                    
Ce bloc de code dans la fenêtre flottante

L'AiToken par défaut est préconfiguré dans l'application ; si vous souhaitez le modifier, vous pouvez le saisir sur la page de connexion.

Pour la méthode de génération de l'AiToken, veuillez consulter Chiffrement de l'AiToken.

Chargement de la page

webView.loadUrl(fullUrl);
                      
                      webView.loadUrl(fullUrl);

                    
Ce bloc de code dans la fenêtre flottante

Interaction entre la WebView et le natif

Enregistrement de l'interface JavaScript

webView.addJavascriptInterface(new JSBridge(), "agentWebBridge");
                      
                      webView.addJavascriptInterface(new JSBridge(), "agentWebBridge");

                    
Ce bloc de code dans la fenêtre flottante

La page H5 appelle les méthodes natives via l'objet global agentWebBridge.


Gestion des autorisations

Demande d'autorisations de la WebView

Lorsque la page H5 demande des autorisations spéciales (telles que l'enregistrement audio), il est nécessaire de traiter la demande d'autorisation de la WebView :

webView.setWebChromeClient(new WebChromeClient() { @Override public void onPermissionRequest(PermissionRequest request) { String[] requestedResources = request.getResources(); boolean hasAudioPermission = false; for (String resource : requestedResources) { if (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(resource)) { hasAudioPermission = true; break; } } if (hasAudioPermission) { // 检查应用是否有录音权限 if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { request.grant(requestedResources); } else { // 请求系统权限 requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE); // 存储权限请求,在获得权限后处理 pendingPermissionRequest = request; } } else { // 其他权限直接授予 request.grant(requestedResources); } } });
                      
                      webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onPermissionRequest(PermissionRequest request) {
        String[] requestedResources = request.getResources();
        boolean hasAudioPermission = false;

        for (String resource : requestedResources) {
            if (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(resource)) {
                hasAudioPermission = true;
                break;
            }
        }

        if (hasAudioPermission) {
            // 检查应用是否有录音权限
            if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
                request.grant(requestedResources);
            } else {
                // 请求系统权限
                requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE);
                // 存储权限请求,在获得权限后处理
                pendingPermissionRequest = request;
            }
        } else {
            // 其他权限直接授予
            request.grant(requestedResources);
        }
    }
});

                    
Ce bloc de code dans la fenêtre flottante

Gestion de la sélection de fichiers

Traiter l'opération de sélection de fichiers de la page H5 :

webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { // 保存回调 this.filePathCallback = filePathCallback; // 创建文件选择 Intent Intent intent = fileChooserParams.createIntent(); startActivityForResult(intent, REQUEST_FILE_CHOOSER); return true; } }); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_FILE_CHOOSER) { if (filePathCallback != null) { Uri[] results = null; if (resultCode == RESULT_OK && data != null) { // 处理选择结果 // ... } filePathCallback.onReceiveValue(results); filePathCallback = null; } } }
                      
                      webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        // 保存回调
        this.filePathCallback = filePathCallback;

        // 创建文件选择 Intent
        Intent intent = fileChooserParams.createIntent();
        startActivityForResult(intent, REQUEST_FILE_CHOOSER);
        return true;
    }
});

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_FILE_CHOOSER) {
        if (filePathCallback != null) {
            Uri[] results = null;

            if (resultCode == RESULT_OK && data != null) {
                // 处理选择结果
                // ...
            }

            filePathCallback.onReceiveValue(results);
            filePathCallback = null;
        }
    }
}

                    
Ce bloc de code dans la fenêtre flottante

Exemples de fonctionnalités

Fermeture de la WebView

La page H5 peut demander la fermeture de la WebView de la manière suivante :

var message = { eventType: "click", data: { value: "close" } }; agentWebBridge.callNative(JSON.stringify(message));
                      
                      var message = {
    eventType: "click",
    data: {
        value: "close"
    }
};
agentWebBridge.callNative(JSON.stringify(message));

                    
Ce bloc de code dans la fenêtre flottante

Traitement côté natif :

public void onClick(JSONObject data) { String value = data.optString("value"); if (TextUtils.equals(value, "close")) { closeWeb(data); } } public void closeWeb(JSONObject data) { // 通知 H5 即将关闭 JSONObject willCloseData = new JSONObject(); willCloseData.put("value", data.optString("value")); willCloseData.put("reason", "user_request"); willCloseData.put("delay", 1000); willCloseData.put("timestamp", System.currentTimeMillis()); webViewBridge.callH5(WebViewBridge.EVENT_CLICK, willCloseData); // 延迟关闭 Activity webView.postDelayed(() -> { finish(); }, 1000); }
                      
                      public void onClick(JSONObject data) {
    String value = data.optString("value");
    if (TextUtils.equals(value, "close")) {
        closeWeb(data);
    }
}

public void closeWeb(JSONObject data) {
    // 通知 H5 即将关闭
    JSONObject willCloseData = new JSONObject();
    willCloseData.put("value", data.optString("value"));
    willCloseData.put("reason", "user_request");
    willCloseData.put("delay", 1000);
    willCloseData.put("timestamp", System.currentTimeMillis());

    webViewBridge.callH5(WebViewBridge.EVENT_CLICK, willCloseData);
    // 延迟关闭 Activity
    webView.postDelayed(() -> {
        finish();
    }, 1000);
}

                    
Ce bloc de code dans la fenêtre flottante

Documents connexes