16. AI Features

Currently developed AI-related features include: AI Cloud Service, AI Dial

Important

The public version project sent out cannot normally use AI-related functions after compilation. You need to register and activate the relevant functions at iFLYTEK first. Configure 【APPID】【APISecret】【APIKey】 at the corresponding positions.

16.1. AI Cloud Service

Note

To ensure compatibility with AI Dial function, if the App uses the AI Cloud Service function, you must use the optimized AI Cloud Service function (AIManager) instead of the pre-optimization version (AIServeManager). How to distinguish between pre-optimization and post-optimization AI cloud service functions? The optimized AI cloud service function is integrated within AIManager.class, while the pre-optimization version is implemented separately in AIServeManager.class.

Firmware corresponding feature: 【iFLYTEK AI】

App corresponding feature: 【Device】->【AI Cloud Service】.

  • During device initialization, the device tells the App whether AI cloud service functionality is supported.

  • When the device starts recording, it sends an RCSP command to inform the App to start recording. Then it begins transmitting recording data.

  • When the device stops recording, it sends an RCSP command to inform the App to stop recording, whether speech recognition results need to be transmitted, whether semantic recognition results need to be transmitted, and whether TTS playback is needed.

  • After receiving the start recording command, the App converts the received real-time audio data from Opus to PCM format and sends the converted PCM results to the iFLYTEK server. After receiving the stop recording command, the App sends a recording end command to the iFLYTEK server. iFLYTEK first returns speech recognition results, then voice recognition results, followed by semantic recognition results. The App performs TTS conversion based on semantic recognition results.

  • The App sends voice recognition results, semantic recognition results, and TTS playback status to the device.

16.1.1. Sequence Diagram

../../_images/ai_serve_single_timing_diagram.jpg

16.1.2. iFLYTEK Configuration

If not using iFLYTEK’s SDK but switching to another platform’s AISDK, you can skip this step.

The public HealthAide project distributed will not work properly with AI cloud service functionality after compilation. You need to register and activate 【Spark Cognitive Model - Spark Model V1.5】, 【Speech Recognition】, and 【Speech Synthesis】 functions at iFLYTEK.

Configure 【APPID】【APISecret】【APIKey】 at the corresponding locations in IflytekChatWrapper.class IflytekIatWrapper.class IflytekTtsWrapper.class.

//IflytekChatWrapper.class

public IflytekChatWrapper(Context context) {
    ......
    if (!TextUtils.isEmpty(BuildConfig.IFLYTEK_APP_ID)) {//TODO Need to comment out this line
        //TODO Please apply first on the iFLYTEK platform, fill in the correct appId, appKey, apiSecret. Otherwise, it will crash
        final AiHelper.Params.Builder params = AiHelper.Params.builder()
                .appId("ffffffff"/**APPID**/)
                .apiKey("fffffffffffffffffffffffffffffff"/**APIKey**/)
                .apiSecret("ffffffffffffffffffffffffffffffff"/**APISecret**/)
                .workDir(rootFile.getPath());
        ......
    }
}
//IflytekIatWrapper.class

public IflytekIatWrapper(Context context) {
    //Enable log
    Setting.setShowLog(true);
    //Initialize and create speech configuration object
    if (!TextUtils.isEmpty(BuildConfig.IFLYTEK_APP_ID)) {//TODO Need to comment out this line
        //TODO Please apply first on the iFLYTEK platform, fill in the correct appId. Otherwise, it will crash
        SpeechUtility.createUtility(context, SpeechConstant.APPID + "=" + "ffffffff"/**APPID**/);
    }
    //Initialize recognition without UI recognition object
    mSpeechRecognizer = SpeechRecognizer.createRecognizer(context, mIatInitListener);
}
//IflytekTtsWrapper.class

public IflytekTtsWrapper(Context context) {
    //Enable log
    Setting.setShowLog(true);
    //Initialize and create speech configuration object
    if (!TextUtils.isEmpty(BuildConfig.IFLYTEK_APP_ID)) {//TODO Need to comment out this line
        //TODO Please apply first on the iFLYTEK platform, fill in the correct appId. Otherwise, it will crash
        SpeechUtility.createUtility(context, SpeechConstant.APPID + "=" + "ffffffff"/**APPID**/);
    }
    // Initialize synthesis object
    mTts = SpeechSynthesizer.createSynthesizer(context, mTtsInitListener);
}

16.1.3. Usage Example

/**
* @ClassName: AICloudServerDemo
* @Description: AI Cloud Service Function Example
* @Author: ZhangHuanMing
* @CreateDate: 2023/12/28 11:23
*/
public class AICloudServerDemo {
    private AIManager mAIManager;
    private final Observer<SessionInfo> mRecordStatusObserver = sessionInfo -> {
        if (sessionInfo != null) {
            switch (sessionInfo.getStatus()) {
                case SessionInfo.STATE_RECORD_START://Start recording
                case SessionInfo.STATE_RECORDING://Recording
                case SessionInfo.STATE_IDLE://Default state
                case SessionInfo.STATE_RECORD_END://Recording ended
                case SessionInfo.STATE_IAT_END://Voice recognition ended
                case SessionInfo.STATE_NLP_END://Semantic recognition ended
                case SessionInfo.STATE_FAIL://Exception
                    //Get current conversation message
                    List<AICloudMessage>  aiCloudMessageList=  sessionInfo.getSessionMessageList();
                    for (AICloudMessage aiCloudMessage:aiCloudMessageList) {
                        //Get the type of dialogue. 0; user, 1; AI
                        int itemType =  aiCloudMessage.getItemType();
                        //Get the message data entity
                        AICloudMessageEntity entity =  aiCloudMessage.getEntity();
                        entity.getAiCloudState();//Speech recognition status for this message
                        entity.getDevMac();//Device mac
                        entity.getId();//Unique id of the message
                        entity.getRevId();//Id of the reply message corresponding to the message
                        entity.getRole();//Message role, 0; user, 1; AI
                        entity.getText();//Message text
                        entity.getTime();//Message time
                        entity.getUid();//Unique id of the user
                    }
                    break;
            }
        }
    };
    /**
    * Initialize AIManager
    */
    @Test
    public void init(Context context){
        //Step1. Initialization
        AIManager.init(context, WatchManager.getInstance());
        mAIManager = AIManager.getInstance();
        //Step2. Monitor conversation status. Choose observeForever or observe based on usage scenario
        mAIManager.getAICloudServe().currentSessionMessageMLD.observeForever(mRecordStatusObserver);
    }

    /**
    * Get recording start time
    */
    @Test
    public long getStartRecordTime(){
        return mAIManager.getAICloudServe().getStartRecordTime();
    }


    /**
    * Stop speech synthesis
    */
    @Test
    public void stopTTS(){
        mAIManager.getAICloudServe().stopTTS();
    }
}

16.1.4. Integrating Other AI Platforms

Please refer to IflytekAICloudServeHandler.class, IflytekAIIatHandler.class calling methods of BaseAICloudServeHandler, BaseAIIatHandler

//BaseAIIatHandler.class
//Basic speech recognition processing
public abstract class BaseAIIatHandler extends BaseAIHandler {
    /**
    * Need to implement
    */
    //Device disconnect
    abstract void onDeviceDisconnect();
    // Start recording
    abstract void onRecordStart();
    // Receive recording data (decoded to PCM format)
    abstract void onRecordData(byte[] recordData);
    // Recording end
    abstract void onRecordStop();
    // Recording cancel
    abstract void onRecordCancel();


    /**
    * Need to call
    */
    //Speech recognition exception flag
    void setRecognizerFail(boolean recognizerFail);
    //Network exception flag
    void setNetworkWrong(boolean networkWrong);
    //Recognition result
    void onRecognizerResult(String iatString);
    //Recognition failure
    void onRecognizerFail();
}
//BaseAICloudServeHandler.class
//AI cloud service processing flow
public abstract class BaseAICloudServeHandler extends BaseAIChatHandler {
    /**
    * Need to implement
    */
    /**
    * Stop TTS synthesis playback
    */
    abstract void onStopTTS();

    /**
    * Start TTS synthesis playback
    */
    abstract void onStartTTS(String ttsText);

    /**
    * Start conversation
    *
    * @param userText User text
    */
    abstract void onStartChat(String userText);

    /**
    * Handle conversation status
    */
    abstract void onHandlerSessionStatus(int status);

    /**
    * Need to call
    */
    //Add semantic text conversation content
    protected void addNlpTTSMessage(String nlpText);
    //Start semantic conversation
    protected void onStartChat();
    //TTS playback failed
    protected void onPlayTTSFail();
    //TTS playback ended
    protected void onPlayTTSStop();
}

16.1.5. Whether AI Cloud Service is Supported

How to determine if the device supports AI cloud service functionality

WatchConfigure configure  mWatchManager.getWatchConfigure(device);
boolean isSupportAICloud = configure == null || configure.getFunctionOption() != null && configure.getFunctionOption().isSupportAICloud();;

16.2. AI Dial

Firmware corresponding feature: 【AI Dial】

App corresponding feature: 【Device】->【AI Dial】.

  • During device initialization, the device informs the App whether AI dial functionality is supported.

  • When the device enters the AI dial interface, it sends an RCSP command to inform the App that the AI dial function is being used.

  • When the device starts recording, it sends an RCSP command to inform the App to start recording. Then it begins transmitting recording data.

  • When the device stops recording, it sends an RCSP command to inform the App to stop recording.

  • After receiving the start recording command, the App converts the received real-time audio data from Opus to PCM format and sends the converted PCM results to the iFLYTEK server. After receiving the stop recording command, the App sends a recording end command to the iFLYTEK server. iFLYTEK first returns speech recognition results, then voice recognition results.

  • The App synchronizes voice recognition results to the device, which displays the speech recognition results. The user confirms generating the AI dial.

  • The App calls the text-to-image interface to generate images, crops them according to screen size to create thumbnails sent to the device. The user confirms installing the dial.

  • The App generates a custom dial based on the image and installs it on the device.

16.2.1. Sequence Diagram

../../_images/ai_dial_single_timing_diagram.jpg

16.2.2. iFLYTEK Configuration

If not using iFLYTEK’s SDK but switching to another platform’s AISDK, you can skip this step.**

The public HealthAide project distributed will not work properly with AI dial functionality after compilation. You need to register and activate 【Spark Cognitive Model - Image Generation】, 【Speech Recognition】, and 【Speech Synthesis】 functions at iFLYTEK.

Configure 【APPID】【APISecret】【APIKey】 at the corresponding locations in IflytekChatWrapper.class IflytekIatWrapper.class IflytekTextToImageWrapper.class.

//IflytekChatWrapper.class

public IflytekChatWrapper(Context context) {
    ......
    if (!TextUtils.isEmpty(BuildConfig.IFLYTEK_APP_ID)) {//TODO Need to comment out this line
        //TODO Please apply first on the iFLYTEK platform, fill in the correct appId, appKey, apiSecret. Otherwise, it will crash
        final AiHelper.Params.Builder params = AiHelper.Params.builder()
                .appId("ffffffff"/**APPID**/)
                .apiKey("fffffffffffffffffffffffffffffff"/**APIKey**/)
                .apiSecret("ffffffffffffffffffffffffffffffff"/**APISecret**/)
                .workDir(rootFile.getPath());
        ......
    }
}
//IflytekIatWrapper.class

public IflytekIatWrapper(Context context) {
    //Enable log
    Setting.setShowLog(true);
    //Initialize and create speech configuration object
    if (!TextUtils.isEmpty(BuildConfig.IFLYTEK_APP_ID)) {//TODO Need to comment out this line
        //TODO Please apply first on the iFLYTEK platform, fill in the correct appId. Otherwise, it will crash
        SpeechUtility.createUtility(context, SpeechConstant.APPID + "=" + "ffffffff"/**APPID**/);
    }
    //Initialize recognition without UI recognition object
    mSpeechRecognizer = SpeechRecognizer.createRecognizer(context, mIatInitListener);
}
//IflytekTextToImageWrapper.class

public void startChat(String usrInputText, TextToImageCallback callback) throws Exception {
        String appid = "ffffffff";/**APPID**/
        String apiSecret = "fffffffffffffffffffffffffffffff";/**APIKey**/
        String apiKey = "ffffffffffffffffffffffffffffffff";/**APISecret**/
        ......
}

16.2.3. Usage Example

/**
* @ClassName: AIDialDemo
* @Description: AI Dial Function Example
* @Author: ZhangHuanMing
* @CreateDate: 2023/12/28 11:39
*/
public class AIDialDemo {
    private AIManager mAIManager;
    /**
    * Initialize AIManager
    */
    @Test
    public void init(Context context){
        //Step1. Initialization
        AIManager.init(context, WatchManager.getInstance());
        mAIManager = AIManager.getInstance();
    }

    /**
    * Set AI Dial Style
    */
    @Test
    public void setStyle(String style){
        mAIManager.getAIDial().setCurrentAIDialStyle(style);
    }
}

16.2.4. Integrating Other AI Platforms

Please refer to IflytekAIDialHandler.class, IflytekAIIatHandler.class calling methods of BaseAIDialHandler, BaseAIIatHandler

//BaseAIIatHandler.class
//Basic speech recognition processing
public abstract class BaseAIIatHandler extends BaseAIHandler {
    /**
    * Need to implement
    */
    //Device disconnect
    abstract void onDeviceDisconnect();
    // Start recording
    abstract void onRecordStart();
    // Receive recording data (decoded to PCM format)
    abstract void onRecordData(byte[] recordData);
    // Recording end
    abstract void onRecordStop();
    // Recording cancel
    abstract void onRecordCancel();


    /**
    * Need to call
    */
    //Speech recognition exception flag
    void setRecognizerFail(boolean recognizerFail);
    //Network exception flag
    void setNetworkWrong(boolean networkWrong);
    //Recognition result
    void onRecognizerResult(String iatString);
    //Recognition failure
    void onRecognizerFail();
}
//BaseAIDialHandler.class
//AI dial processing flow
public abstract class BaseAIDialHandler extends BaseAIChatHandler {
    /**
    * Start conversation (text-to-image), need to implement
    */
    abstract void onStartAIChat(String userText);
    /**
    * Process AI image (text-to-image), need to call
    * @param srcImagePath Local image path
    */
    protected void handleAIChatImage(String srcImagePath);
}

16.2.5. Whether AI Dial is Supported

How to determine if the device supports AI dial functionality

WatchConfigure configure  mWatchManager.getWatchConfigure(device);
boolean isSupportAIDial = configure == null || configure.getFunctionOption() != null && configure.getFunctionOption().isSupportAIDial();;

16.2.6. Local Testing of AI Dial

During testing, to avoid wasting the server’s text-to-image resources, the AI dial has local testing functionality. When releasing the official version, please disable this.

When opening the App, copy resources from assert/aidial to /Android/data/com.jieli.healthaide/files/aidial. (Select one image from 1 to 10 for AI dial generation)

Disable local testing functionality:

//HealthConstant.class
public class HealthConstant {
    .....
    //Test AI dial functionality
    public final static boolean TEST_AI_DIAL_FUNCTION = false;
    .....
}

16.2.7. AI Dial Thumbnails

Currently, the thumbnail file size for AI dials defaults to 240*240. The device side can push thumbnail size when entering the AI dial interface. To modify the default thumbnail size, please change the thumbHeight and thumbWidth properties in AIDialWrapper.java.

//AIDialWrapper.class
private final OnWatchCallback mWatchCallback = new OnWatchCallback() {
    @Override
    public void onRcspCommand(BluetoothDevice device, CommandBase command) {
        super.onRcspCommand(device, command);
        if (command.getId() == Command.CMD_AI_OPERATE) {
            AIOperateCmd aiOperateCmd = (AIOperateCmd) command;
            AIOperateParam param = aiOperateCmd.getParam();
            if (param.getOp() == AttrAndFunCode.AI_OP_AI_DIAL) {
                switch (param.getFlag()) {
                    case AttrAndFunCode.AI_DIAL_OP_UI://Device notifies App of AI dial interface changes
                        Integer state = param.getAiDialFunUIState();
                        Integer scaleZoomHeight = param.getScaleZoomHeight();
                        Integer scaleZoomWidth = param.getScaleZoomWidth();
                        if (state != null) {
                            onDevNotifyAIDialUIChange(state);
                        }
                        if (scaleZoomHeight != null) {
                            thumbHeight = scaleZoomHeight;
                        } else {
                            thumbHeight = 240;
                        }
                        if (scaleZoomWidth != null) {
                            thumbWidth = scaleZoomWidth;
                        } else {
                            thumbWidth = 240;
                        }
                        break;

                }
                aiOperateCmd.setParam(new AIOperateParam.AIOperateResultParam(0));
                aiOperateCmd.setStatus(StateCode.STATUS_SUCCESS);
                mWatchManager.sendCommandResponse(mWatchManager.getConnectedDevice(), aiOperateCmd, null);
            }
        }
    }
};

16.3. Code Architecture

../../_images/ai_code_architecture.png