吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2451|回复: 7
收起左侧

[Java 转载] 【AI文字识别】HarmonyOS Codelab挑战赛记录

[复制链接]
看看天空 发表于 2021-4-24 11:30

文字识别

项目启动

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/Untitled.png

布局文件

base/graphic/background_ability_main.xml

<?xml version="1.0" encoding="UTF-8" ?> 
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos" 
       ohos:shape="rectangle"> 
    <solid 
        ohos:color="#FFFFFF"/> 
</shape>

base/graphic/background_ability_page.xml

<?xml version="1.0" encoding="UTF-8" ?> 
 <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" 
        ohos:shape="rectangle"> 
     <solid 
         ohos:color="#FFFAF0"/> 
 </shape>

base/graphic/button_element.xml

<?xml version="1.0" encoding="utf-8"?> 
 <shape 
     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
     ohos:shape="rectangle"> 
     <corners 
         ohos:radius="100"/> 
     <solid 
         ohos:color="#FF007DFE"/> 
 </shape>

base/layout/ability_main.xml

<?xml version="1.0" encoding="utf-8"?> 
<DirectionalLayout 
    xmlns:ohos="http://schemas.huawei.com/res/ohos" 
    ohos:height="match_parent" 
    ohos:width="match_parent" 
    ohos:orientation="vertical" 
    ohos:background_element="$graphic:background_ability_page" 
    > 

    <Text 
        ohos:id="$+id:text_helloworld" 
        ohos:height="match_content" 
        ohos:width="match_content" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:layout_alignment="horizontal_center" 
        ohos:text="关键词搜索图片" 
        ohos:text_size="30fp" 
        ohos:top_margin="5vp" 
        /> 

    <Text 
        ohos:id="$+id:picture_list" 
        ohos:height="match_content" 
        ohos:width="match_content" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:layout_alignment="horizontal_center" 
        ohos:text="图片列表" 
        ohos:text_size="20fp" 
        ohos:top_margin="15vp" 
        /> 

    <ListContainer 
        ohos:id="$+id:picture_list_show" 
        ohos:height="200vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal" 
        ohos:left_margin="5vp" 
        ohos:right_margin="5vp" 
        /> 

    <Text 
        ohos:id="$+id:word_seg_title" 
        ohos:height="match_content" 
        ohos:width="match_content" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:left_margin="5vp" 
        ohos:text="请输入关键词:" 
        ohos:text_size="25fp" 
        ohos:top_margin="10vp" 
        /> 

    <TextField 
        ohos:id="$+id:word_seg_text" 
        ohos:height="match_content" 
        ohos:width="match_parent" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:hint="Enter a statement." 
        ohos:left_padding="5vp" 
        ohos:right_padding="5vp" 
        ohos:text_alignment="vertical_center" 
        ohos:text_size="20fp" 
        ohos:top_margin="5vp"/> 

    <Button 
        ohos:id="$+id:button_search" 
        ohos:width="match_content" 
        ohos:height="match_content" 
        ohos:text_size="20fp" 
        ohos:text="开始通用文字识别" 
        ohos:layout_alignment="horizontal_center" 
        ohos:top_margin="10vp" 
        ohos:top_padding="1vp" 
        ohos:bottom_padding="1vp" 
        ohos:right_padding="20vp" 
        ohos:left_padding="20vp" 
        ohos:text_color="white" 
        ohos:background_element="$graphic:button_element" 
        ohos:center_in_parent="true" 
        ohos:align_parent_bottom="true" 
        ohos:bottom_margin="5vp"/> 

    <Text 
        ohos:id="$+id:picture_list_result" 
        ohos:height="match_content" 
        ohos:width="match_content" 
        ohos:background_element="$graphic:background_ability_main" 
        ohos:layout_alignment="horizontal_center" 
        ohos:text="搜索结果" 
        ohos:text_size="20fp" 
        ohos:top_margin="5vp" 
        /> 

    <ListContainer 
        ohos:id="$+id:picture_list_match" 
        ohos:height="200vp" 
        ohos:width="match_parent" 
        ohos:orientation="horizontal" 
        ohos:left_margin="5vp" 
        ohos:right_margin="5vp" 
        /> 
</DirectionalLayout>

base/layout/item_image_layout.xml

<?xml version="1.0" encoding="utf-8"?> 
<DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos" 
                   ohos:height="200vp" 
                   ohos:width="205vp"> 

    <Image 
        ohos:id="$+id:select_picture_list" 
        ohos:height="200vp" 
        ohos:width="200vp" 
        ohos:layout_alignment="horizontal_center" 
        ohos:top_margin="1vp" 
        ohos:scale_mode="stretch" 
        /> 

</DirectionalLayout>

在"resources\base\media"目录下添加8张jpg图片(分别命名为1-8.jpg)

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/1.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/2.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/3.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/4.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/5.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/6.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/7.jpg

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/8.jpg

provider 目录

PictureProvider.java

package com.example.ocrbaseonai.provider;

import com.example.ocrbaseonai.ResourceTable;
import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.LayoutScatter;
import ohos.app.Context;

import java.util.Optional;

public class PictureProvider extends BaseItemProvider {
    private int[] pictureLists;
    private Context context;

    /**
     *  picture provider
     *
     * home.php?mod=space&uid=952169 pictureLists pictureLists
     * @param context context
     */
    public PictureProvider(int[] pictureLists, Context context) {
        this.pictureLists = pictureLists;
        this.context = context;
    }

    @Override
    public int getCount() {
        return pictureLists == null ? 0 : pictureLists.length;
    }

    @Override
    public Object getItem(int position) {
        return Optional.of(this.pictureLists[position]);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public Component getComponent(int var1, Component var2, ComponentContainer var3) {
        ViewHolder viewHolder = null;
        Component component = var2;
        if (component == null) {
            component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_image_layout,
                    null, false);
            viewHolder = new ViewHolder();
            Component componentImage = component.findComponentById(ResourceTable.Id_select_picture_list);
            if (componentImage instanceof Image) {
                viewHolder.image = (Image) componentImage;
            }
            component.setTag(viewHolder);
        } else {
            if (component.getTag() instanceof ViewHolder) {
                viewHolder = (ViewHolder) component.getTag();
            }
        }
        if (viewHolder != null) {
            viewHolder.image.setPixelMap(pictureLists[var1]);
        }
        return component;
    }

    private static class ViewHolder {
        Image image;
    }
}

slice 目录

MainAbilitySlice.java

package com.example.ocrbaseonai.slice;

import com.example.ocrbaseonai.ResourceTable;
import com.example.ocrbaseonai.provider.PictureProvider;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;

import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.TextField;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class MainAbilitySlice extends AbilitySlice {
    private static final int LIST_CONTAINER_ID_SHOW = ResourceTable.Id_picture_list_show;
    private static final int LIST_CONTAINER_ID_MATCH = ResourceTable.Id_picture_list_match;
    private static final int NEG_ONE = -1;
    private static final int ZERO = 0;
    private static final int ONE = 1;
    private static final int TWO = 2;
    private Context slice;
    private EventRunner runner;
    private MyEventHandle myEventHandle;
    private int[] pictureLists = new int[]{ResourceTable.Media_1, ResourceTable.Media_2,
            ResourceTable.Media_3, ResourceTable.Media_4, ResourceTable.Media_5,
            ResourceTable.Media_6, ResourceTable.Media_7, ResourceTable.Media_8};
    private Component selectComponent;
    private int selectPosition;
    private Button button;
    private TextField textField;
    private Map<Integer, String> imageInfos;
    private int[] matchPictures;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);

        slice = MainAbilitySlice.this;

        // 展示图片列表
        setSelectPicture(pictureLists, LIST_CONTAINER_ID_SHOW);

        // 所有图片通用文字识别
        wordRecognition();

        // 设置需要分词的语句
        Component componentText = findComponentById(ResourceTable.Id_word_seg_text);
        if (componentText instanceof TextField) {
            textField = (TextField) componentText;
        }

        // 点击按钮进行文字识别
        Component componentSearch = findComponentById(ResourceTable.Id_button_search);
        if (componentSearch instanceof Button) {
            button = (Button) componentSearch;
            button.setClickedListener(listener -> wordSegment());
        }
    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    // 设置图片选择区域
    private void setSelectPicture(int[] pictures, int id) {
        // 获取图片
        PictureProvider newsTypeAdapter = new PictureProvider(pictures, this);

        Component componentById = findComponentById(id);
        if (componentById instanceof ListContainer) {
            ListContainer listContainer = (ListContainer) componentById;
            listContainer.setItemProvider(newsTypeAdapter);
        }
    }

    // 通用文字识别
    private void wordRecognition() {
        initHandler();
        WordRecognition wordRecognition = new WordRecognition();
        wordRecognition.setParams(slice, pictureLists, myEventHandle);
        wordRecognition.sendResult(null);
    }

    // 分词
    private void wordSegment() {
        // 组装关键词,作为分词对象
        String requestData = "{\"text\":" + textField.getText() + ",\"type\":0}";
        initHandler();
        new WordSegment().wordSegment(slice, requestData, myEventHandle);
    }

    // 匹配图片
    private void matchImage(List<String> list) {
        Set<Integer> matchSets = new HashSet<>();
        for (String str: list) {
            for (Integer key : imageInfos.keySet()) {
                if (imageInfos.get(key).indexOf(str) != NEG_ONE) {
                    matchSets.add(key);
                }
            }
        }
        // 获得匹配的图片
        matchPictures = new int[matchSets.size()];
        int i = 0;
        for (int match: matchSets) {
            matchPictures[i] = match;
            i++;
        }
        // 展示图片
        setSelectPicture(matchPictures, LIST_CONTAINER_ID_MATCH);
    }

    private void initHandler() {
        runner = EventRunner.getMainEventRunner();
        if (runner == null) {
            return;
        }
        myEventHandle = new MyEventHandle(runner);
    }

    public class MyEventHandle extends EventHandler {
        MyEventHandle(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            int eventId = event.eventId;
            if (eventId == ONE) {
                // 通用文字识别
                if (event.object instanceof Map) {
                    imageInfos = (Map) event.object;
                }
            }
            if (eventId == TWO) {
                // 分词
                if (event.object instanceof List) {
                    List<String> lists = (List) event.object;
                    if ((lists.size() > ZERO) && (!"no keywords".equals(lists.get(ZERO)))) {
                        // 根据输入关键词 匹配图片
                        matchImage(lists);
                    }
                }
            }
        }
    }
}

LogUtil.java

package com.example.ocrbaseonai.slice;

import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

public class LogUtil {
    private static final String TAG_LOG = "LogUtil";

    private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, LogUtil.TAG_LOG);

    private static final String LOG_FORMAT = "%{public}s: %{public}s";

    private LogUtil() {
    }

    public static void info(String tag, String msg) {
        HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg);
    }

    public static void error(String tag, String msg) {
        HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg);
    }
}

WordRecognition.java

package com.example.ocrbaseonai.slice;

import ohos.ai.cv.common.ConnectionCallback;
import ohos.ai.cv.common.VisionCallback;
import ohos.ai.cv.common.VisionConfiguration;
import ohos.ai.cv.common.VisionImage;
import ohos.ai.cv.common.VisionManager;
import ohos.ai.cv.text.ITextDetector;
import ohos.ai.cv.text.Text;
import ohos.ai.cv.text.TextConfiguration;
import ohos.ai.cv.text.TextDetectType;
import ohos.app.Context;
import ohos.eventhandler.InnerEvent;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.global.resource.ResourceManager;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class WordRecognition {
    private static final boolean IS_ASYNC = false;
    private static final int IS_ASYNC_CODE = 700;
    private Context slice;
    private ITextDetector textDetector;
    private PixelMap pixelMap;
    private MainAbilitySlice.MyEventHandle handle;
    private int[] pictureLists;
    private int mediaId;
    private Map maps = new HashMap<>();
    private int index;
    private int result;

    public void setParams(Context context, int[] pictureIds, MainAbilitySlice.MyEventHandle myEventHandle) {
        slice = context;
        pictureLists = pictureIds;
        handle = myEventHandle;
    }

    public void wordRecognition(Context context, int resId, MainAbilitySlice.MyEventHandle myEventHandle) {
        mediaId = resId;
        // 实例化ITextDetector接口 
        textDetector = VisionManager.getTextDetector(context);

        // 实例化VisionImage对象image,并传入待检测图片pixelMap 
        pixelMap = getPixelMap(resId);
        VisionImage image = VisionImage.fromPixelMap(pixelMap);

        // 定义VisionCallback<Text>回调,异步模式下用到 
        VisionCallback<Text> visionCallback = getVisionCallback();

        // 定义ConnectionCallback回调,实现连接能力引擎成功与否后的操作 
        ConnectionCallback connectionCallback = getConnectionCallback(image, visionCallback);

        // 建立与能力引擎的连接 
        VisionManager.init(context, connectionCallback);
    }

    private VisionCallback getVisionCallback() {
        return new VisionCallback<Text>() {
            @Override
            public void onResult(Text text) {
                sendResult(text.getValue());
            }

            @Override
            public void onError(int i) {
            }

            @Override
            public void onProcessing(float v) {
            }
        };
    }

    private ConnectionCallback getConnectionCallback(VisionImage image, VisionCallback<Text> visionCallback) {
        return new ConnectionCallback() {
            @Override
            public void onServiceConnect() {
                // 实例化Text对象text 
                Text text = new Text();

                // 通过TextConfiguration配置textDetector()方法的运行参数 
                TextConfiguration.Builder builder = new TextConfiguration.Builder();
                builder.setProcessMode(VisionConfiguration.MODE_IN);
                builder.setDetectType(TextDetectType.TYPE_TEXT_DETECT_FOCUS_SHOOT);
                builder.setLanguage(TextConfiguration.AUTO);
                TextConfiguration config = builder.build();
                textDetector.setVisionConfiguration(config);
                // 调用ITextDetector的detect()方法 
                if (!IS_ASYNC) {
                    int result2 = textDetector.detect(image, text, null); // 同步 
                    sendResult(text.getValue());
                } else {
                    int result2 = textDetector.detect(image, null, visionCallback); // 异步 
                }
            }

            @Override
            public void onServiceDisconnect() {
                // 释放 
                if ((!IS_ASYNC && (result == 0)) || (IS_ASYNC && (result == IS_ASYNC_CODE))) {
                    textDetector.release();
                }
                if (pixelMap != null) {
                    pixelMap.release();
                    pixelMap = null;
                }
                VisionManager.destroy();
            }
        };
    }

    public void sendResult(String value) {
        if (textDetector != null) {
            textDetector.release();
        }
        if (pixelMap != null) {
            pixelMap.release();
            pixelMap = null;
            VisionManager.destroy();
        }
        if (value != null) {
            maps.put(mediaId, value);
        }
        if ((maps != null) && (maps.size() == pictureLists.length)) {
            InnerEvent event = InnerEvent.get(1, 0, maps);
            handle.sendEvent(event);
        } else {
            wordRecognition(slice, pictureLists[index], handle);
            index++;
        }
    }

    // 获取图片 
    private PixelMap getPixelMap(int resId) {
        ResourceManager manager = slice.getResourceManager();

        byte[] datas = new byte[0];
        try {
            Resource resource = manager.getResource(resId);
            datas = readBytes(resource);
            resource.close();
        } catch (IOException | NotExistException e) {
            LogUtil.error("get pixelmap failed, read resource bytes failed, ", e.getLocalizedMessage());
        }

        ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
        srcOpts.formatHint = "image/jpg";
        ImageSource imageSource;
        imageSource = ImageSource.create(datas, srcOpts);
        ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
        decodingOpts.desiredSize = new Size(0, 0);
        decodingOpts.desiredRegion = new Rect(0, 0, 0, 0);
        decodingOpts.desiredPixelFormat = PixelFormat.ARGB_8888;
        pixelMap = imageSource.createPixelmap(decodingOpts);
        return pixelMap;
    }

    private static byte[] readBytes(Resource resource) {
        final int bufferSize = 1024;
        final int ioEnd = -1;

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffers = new byte[bufferSize];
        byte[] results = new byte[0];
        while (true) {
            try {
                int readLen = resource.read(buffers, 0, bufferSize);
                if (readLen == ioEnd) {
                    results = output.toByteArray();
                    break;
                }
                output.write(buffers, 0, readLen);
            } catch (IOException e) {
                LogUtil.error("OrcAbilitySlice.getPixelMap", "read resource failed ");
                break;
            } finally {
                try {
                    output.close();
                } catch (IOException e) {
                    LogUtil.error("OrcAbilitySlice.getPixelMap", "close output failed");
                }
            }
        }
        return results;
    }
}

WordSegment.java

package com.example.ocrbaseonai.slice;

import ohos.ai.nlu.NluClient;
import ohos.ai.nlu.NluRequestType;
import ohos.ai.nlu.OnResultListener;
import ohos.ai.nlu.ResponseResult;
import ohos.app.Context;
import ohos.eventhandler.InnerEvent;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class WordSegment {
    private static final boolean IS_ASYNC = true;
    private static final String WORDS = "words";
    private static final int ZERO = 0;
    private static final int TWO = 2;
    private static final int STEP = 8;
    private Context slice;
    private MainAbilitySlice.MyEventHandle handle;

    public void wordSegment(Context context, String requestData, MainAbilitySlice.MyEventHandle myEventHandle) {
        slice = context;
        handle = myEventHandle;

        // 使用NluClient静态类进行初始化,通过异步方式获取服务的连接。
        NluClient.getInstance().init(context, new OnResultListener<Integer>() {
            @Override
            public void onResult(Integer resultCode) {
                if (!IS_ASYNC) {
                    // 同步
                    ResponseResult responseResult = NluClient.getInstance().getWordSegment(requestData,
                            NluRequestType.REQUEST_TYPE_LOCAL);
                    sendResult(responseResult.getResponseResult());
                    release();
                } else {
                    // 异步
                    wordSegmentAsync(requestData);
                }
            }
        }, true);
    }

    private void wordSegmentAsync(String requestData) {
        ResponseResult responseResult = NluClient.getInstance().getWordSegment(requestData,
                NluRequestType.REQUEST_TYPE_LOCAL, new OnResultListener<ResponseResult>() {
                    @Override
                    public void onResult(ResponseResult asyncResult) {
                        sendResult(asyncResult.getResponseResult());
                        release();
                    }
                });
    }

    private void sendResult(String result) {
        List lists = null; // 分词识别结果
        // 将result中分词结果转换成list
        if (result.contains("\"message\":\"success\"")) {
            String words = result.substring(result.indexOf(WORDS) + STEP,
                    result.lastIndexOf("]")).replaceAll("\"", "");
            if ((words == null) || ("".equals(words))) {
                lists = new ArrayList(1);
                lists.add("no keywords"); // 未识别到分词结果,返回"no keywords"
            } else {
                lists = Arrays.asList(words.split(","));
            }
        }

        InnerEvent event = InnerEvent.get(TWO, ZERO, lists);
        handle.sendEvent(event);
    }

    private void release() {
        NluClient.getInstance().destroy(slice);
    }
}

MainAbility.java 和 MyApplication.java 保持初始状态不变

效果展示

%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB%2034d36eaefcea472e808f86172019d528/Untitled%201.png

输入关键字会闪退,可能是本机内存不够,导致无法弹出输入键盘就闪退了

免费评分

参与人数 1吾爱币 +10 热心值 +1 收起 理由
苏紫方璇 + 10 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

huluwa123 发表于 2021-4-24 11:45
流批流批
andyfky 发表于 2021-4-24 12:03
Anyling 发表于 2021-4-24 13:03
xiaoxs11 发表于 2021-4-24 13:39
膜拜大佬!!
 楼主| 看看天空 发表于 2021-4-24 15:57
Anyling 发表于 2021-4-24 13:03
不明觉厉,有没有大神给科普下

并没有多厉害,鸿蒙官网的例子,拿来跑了一遍
未晴雾香 发表于 2021-7-18 22:33
NS1553G0_M~L[S~GI]R77N1.png 我也用的官网的例子,但是匹配的图片数是0,不知道哪里有问题
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-3 23:21

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表