logo
Desarrollo
Buscar
Guía de instrucciones para la integración del espacio de trabajo en Android

Guía de instrucciones para la integración del espacio de trabajo en Android

Introducción

Esta guía explica en detalle cómo integrar el espacio de trabajo de GPTBots en aplicaciones Android, incluyendo la solicitud de permisos, la interacción entre el código nativo y H5, así como otras configuraciones relacionadas.

GPTBots también ofrece un proyecto DEMO del espacio de trabajo que puede ayudarle a comenzar rápidamente. Proyecto DEMO para Android: android-webview-bridge


Lista de permisos

Permisos básicos

Para garantizar que las funciones relacionadas con el espacio de trabajo estén disponibles con normalidad, debe configurar los siguientes permisos en 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" />

                    
Este bloque de código en una ventana flotante

Gestión de permisos

Para asegurar que la aplicación pueda acceder correctamente al micrófono, la cámara, la galería y otros permisos sensibles en tiempo de ejecución, la aplicación debe solicitar dinámicamente los permisos de tiempo de ejecución necesarios al iniciarse:

// 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
};

                    
Este bloque de código en una ventana flotante

Configuración de WebView

Configuración básica

La APP crea una instancia de WebView y realiza los ajustes necesarios:

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); // 不需要用户手势即可播放媒体

                    
Este bloque de código en una ventana flotante

Configuración relacionada con la grabación de 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); // 设置缓存模式

                    
Este bloque de código en una ventana flotante

Configuración de User-Agent

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

                    
Este bloque de código en una ventana flotante

Acceso al espacio de trabajo

Construcción de la URL

El acceso al espacio de trabajo requiere los parámetros Base URL y 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;

                    
Este bloque de código en una ventana flotante

El AiToken predeterminado ya viene preconfigurado en la aplicación; si necesita cambiarlo, puede introducirlo en la página de inicio de sesión.

Para conocer el método de generación del AiToken, consulte Cifrado de AiToken.

Carga de la página

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

                    
Este bloque de código en una ventana flotante

Interacción entre WebView y el código nativo

Registro de la interfaz JavaScript

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

                    
Este bloque de código en una ventana flotante

La página H5 invoca los métodos nativos a través del objeto global agentWebBridge.


Gestión de permisos

Solicitud de permisos de WebView

Cuando la página H5 solicita permisos especiales (como la grabación de audio), es necesario gestionar la solicitud de permisos de 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);
        }
    }
});

                    
Este bloque de código en una ventana flotante

Gestión de la selección de archivos

Gestión de la operación de selección de archivos de la página 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;
        }
    }
}

                    
Este bloque de código en una ventana flotante

Ejemplos de funcionalidad

Cierre del WebView

La página H5 puede solicitar el cierre del WebView de la siguiente manera:

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

                    
Este bloque de código en una ventana flotante

Gestión en el lado nativo:

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

                    
Este bloque de código en una ventana flotante

Documentos relacionados