android可见即可说实现方案
- 依赖于科大讯飞的asr识别能力,使用Android无障碍服务获取页面文本作为热词,注册到讯飞api,注册过后语音识别到热词的asr返回,利用WindowManager和无障碍的点击实现可见即可说功能
##  无障碍服务获取需要注册的热词
```
package com..model;import android.accessibilityservice.AccessibilityService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.lifecycle.Observer;
import .HotWordsBean;
import .GsonUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;public class MyAccessibilityService extends AccessibilityService implements IAccessibilityHotWord {private String TAG = MyAccessibilityService.class.getSimpleName();private StringBuilder hotWords = new StringBuilder();private VrSpService vrSpeechService;private HotWordsBean hotWordsBean = new HotWordsBean();private AccessibilityNodeInfo rootInActiveWindow;private Set<AccessibilityNodeInfo> accessibilityNodeInfoSet = new HashSet<>();HotWordsBean.UserDataBean userDataBean = new HotWordsBean.UserDataBean();HotWordsBean.UserDataBean.CMD cmd = new HotWordsBean.UserDataBean.CMD();private String hotWordJsonString = "";private Context context;@Overridepublic void onCreate() {super.onCreate();Log.i(TAG, "------------ super.onCreate --------------------: ");bindVrSpeechService();context = this;HotWordReceiver.hotWordLiveData.observeForever( new Observer<String>() {@Overridepublic void onChanged(String hotWord) {Log.d(TAG, "onChanged: ytf ------------ hotWord change :" + hotWord);if (accessibilityNodeInfoSet.size() > 0) {for (AccessibilityNodeInfo nodeInfo : accessibilityNodeInfoSet) {if (nodeInfo.getText() != null && nodeInfo.getText().toString().equalsIgnoreCase(hotWord)) {Log.d(TAG, "ytf, hotWord Shot:" + hotWord);handlePerformAction(hotWord);}}} else {Log.d(TAG, "ytf hotWord Shot: accessibilityNodeInfoSet size = " + accessibilityNodeInfoSet.size());}}});}@Overridepublic void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {String packageName = accessibilityEvent.getPackageName() == null ? "" : accessibilityEvent.getPackageName().toString();if (!"com.saicmotor.settings".equals(packageName)) {return;}int eventType = accessibilityEvent.getEventType();
// Log.d(TAG, "ytf,onAccessibilityEvent [eventType: " + eventType + "], [ packageName: " + packageName + "]");switch (eventType) {
// case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:case AccessibilityEvent.TYPE_VIEW_CLICKED:accessibilityNodeInfoSet.clear();hotWords.setLength(0);rootInActiveWindow = getRootInActiveWindow();if (null == rootInActiveWindow) {Log.d(TAG, "ytf, onAccessibilityEvent: rootInActiveWindow == null");return;} else {recycle(rootInActiveWindow);}String[] splitHotWords = hotWords.toString().split(",");//vrSpeechService.notifyHotWordLoad(GsonUtil.HOT_WORD_TEST_1);hotWordsBean.setHotWords(splitHotWords);cmd.setActiveStatus("");userDataBean.setCmd(cmd);hotWordsBean.setUserData(userDataBean);try {hotWordJsonString = GsonUtil.getInstance().getGson().toJson(hotWordsBean);if (hotWordJsonString != null) {vrSpeechService.notifyHotWordLoad(hotWordJsonString);Log.d(TAG, "onAccessibilityEvent: ytf, hotWordJsonString = " + hotWordJsonString);}} catch (Exception e) {Log.e(TAG, "onAccessibilityEvent: ytf, e: " + e.toString());}break;default:break;}}private void recycle(AccessibilityNodeInfo info) {if (info.getChildCount() == 0) {if ("android.widget.SeekBar".equals(info.getClassName())) {
// Class<? extends AccessibilityNodeInfo> aClass = info.getClass();
// Log.d(TAG, "recycle: ytf aClass = " + aClass.getSimpleName());
// SeekBar seekBar = (SeekBar) info.getClassName();
// Log.d(TAG, "recycle: ytf, SeekBar 调节前:" + seekBar.getProgress() + ", getViewIdResourceName:" + info.getViewIdResourceName());
// Bundle bundle = new Bundle();
// bundle.putFloat(AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE, 50.0f);
// seekBar.performAccessibilityAction(R.id.accessibilityActionSetProgress, bundle);
// Log.d(TAG, "recycle: ytf, SeekBar 调节后:" + seekBar.getProgress() + ", getViewIdResourceName:" + info.getViewIdResourceName());} else if ("android.widget.ScrollView".contentEquals(info.getClassName())) {
// Log.d(TAG, "recycle: ytf, android.widget.ScrollView ACTION_SCROLL_FORWARD");
// info.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);}
// Log.i(TAG, "recycle ytf, [ClassName: " + info.getClassName() + "], [Text: " + info.getText() + "], [resId: " + info.getViewIdResourceName() + "]");if (null != info.getText()) {String text = info.getText().toString();hotWords.append(text + ",");accessibilityNodeInfoSet.add(info);}} else {for (int i = 0; i < info.getChildCount(); i++) {if (info.getChild(i) != null) {
// Log.d(TAG, "ytf 容器: [" + info.getClassName() + "], [resId:" + info.getViewIdResourceName() + "]");recycle(info.getChild(i));}}}}private void handlePerformAction(String targetHotWord) {Log.d(TAG, "handlePerformAction: ytf, accessibilityNodeInfoSet.size = " + accessibilityNodeInfoSet.size());for (AccessibilityNodeInfo nodeInfo : accessibilityNodeInfoSet) {
// Log.d(TAG, "ytf handlePerformAction: nodeInfo.getText().toString() = " + nodeInfo.getText().toString() + ", targetHotWord = " + targetHotWord);if (nodeInfo.getText().toString().equalsIgnoreCase(targetHotWord)) {Log.d(TAG, "ytf, 命中可见即可说 handlePerformAction: " + nodeInfo.getText().toString());forceClick(nodeInfo);
// nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}private void forceClick(AccessibilityNodeInfo nodeInfo) {Log.d(TAG, "forceClick: ytf,------------");try {Rect rect = new Rect();nodeInfo.getBoundsInScreen(rect);Log.d(TAG, "ytf, forceClick: " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom);int x = (rect.left + rect.right) / 2;int y = (rect.top + rect.bottom) / 2;String cmd = "input tap " + String.valueOf(x) + " " + String.valueOf(y);ProcessBuilder builder = new ProcessBuilder();String[] order = {"input","tap",String.valueOf(x),String.valueOf(y)};try {builder.command(order).start();Log.d(TAG, "ytf, forceClick: [ " + x + ", " + y + "]");} catch (IOException e) {Log.d(TAG, "ytf, forceClick: error: " + e.toString());}} catch (Exception e) {Log.e(TAG, "ytf,error forceClick: " + e.toString());}}@Overridepublic void onInterrupt() {Log.i(TAG, "ytf, onInterrupt");}private void bindVrSpeechService() {Intent intent = new Intent(getApplicationContext(), VrSpeechService.class);boolean result = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);Log.d(TAG, "vrSpeechService: ytf bind result:" + result);}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {try {vrSpeechService = ((VrSpeechService.LocalBinder) iBinder).getService();Log.d(TAG, "onServiceConnected: ytf, vrSpeechService bind ");vrSpeechService.setAccessibilityHotWord((IAccessibilityHotWord) context);} catch (Exception e) {e.printStackTrace();Log.e(TAG, "ytf, onServiceConnected: " + e.toString() );}}@Overridepublic void onServiceDisconnected(ComponentName componentName) {}};@Overridepublic void hotWordShot(String hotWord) {
// Log.d(TAG, "ytf, hotWordShot: " + hotWord);if (accessibilityNodeInfoSet.size() > 0) {for (AccessibilityNodeInfo nodeInfo : accessibilityNodeInfoSet) {if (nodeInfo.getText().toString().equalsIgnoreCase(hotWord)) {Log.d(TAG, "ytf, hotWordShot:" + hotWord);handlePerformAction(hotWord);}}} else {Log.d(TAG, "ytf hotWordShot: accessibilityNodeInfoSet size = " + accessibilityNodeInfoSet.size());}}
}```
-
清单文件:
<meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/accessibility" /></service><receiver android:name="com.saicmotor.voiceservice.model.HotWordReceiver"android:exported="true"android:enabled="true"><intent-filter android:priority="1000"><action android:name="com.saicmotor.voiceservice.hotword"/></intent-filter></receiver>
-
@xml/accessibility
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeAllMask"android:accessibilityFeedbackType="feedbackGeneric"android:canRetrieveWindowContent="true"android:canPerformGestures="true"android:accessibilityFlags="flagReportViewIds"android:notificationTimeout="2000"/>
-
模拟asr热词命中
/**
* @Author yangtianfu
* @Date 2023/9/15 13:05
* @Describe 监听热词回传执行可见可说
* adb shell am broadcast -a com.saicmotor.voiceservice.hotword -n com.saicmotor.voiceservice/.model.HotWordReceiver --es hotWord “sound”
*/
public class HotWordReceiver extends BroadcastReceiver {
private static final String TAG = “HotWordReceiver”;
private final String ACTION_HOT_WORD_RECEIVER = “com.saicmotor.voiceservice.hotword”;
// private IAccessibilityHotWord iAccessibilityHotWord;
public static MutableLiveData hotWordLiveData = new MutableLiveData<>();@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();Log.d(TAG, "ytf, onReceive: intent,action = " + action);if (ACTION_HOT_WORD_RECEIVER.equals(action)) {String hotWord = intent.getStringExtra("hotWord");Log.d(TAG, "ytf, onReceive: com.saicmotor.voiceservice.hotword :" + hotWord);hotWordLiveData.postValue(hotWord);}}
}
## 科大讯飞注册热词热词格式:public static final String HOT_WORD_TEST_1 = "{\n" +" \"HotWords\":[\"High\"],\n" +" \"UserData\":{\n" +" \"cmd\":{\n" +" \"activeStatus\":\"bg\",\n" +" \"data\":{\n" +"\n" +" },\n" +" \"sceneStatus\":\"default\"\n" +" }\n" +" }\n" +"}";int result = libisssr.uploadData(hotWords, 2);
##  VuiService.java语音Vui服务(带有语音形象的app)
透明activity无法跨应用实现点击穿透效果,会导致可见即可说点击无效果,需要在service中使用WindowManager的addview方法,把语音的app作为view添加到WindowManager中,这样就可以实现语音app全透明状态下识别到asr之后可以利用Android无障碍服务去点击指定位置或者指定控件。public class VoiceVuiService extends Service {private WindowManager.LayoutParams mParams;private WindowManager mWindowManager;private ImageView voiceImage;private TextView textView;@Overridepublic void onCreate() {mParams = new WindowManager.LayoutParams();//设置type.系统提示型窗口,一般都在应用程序窗口之上.mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
// mParams.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;//设置效果为背景透明.
// mParams.format = PixelFormat.RGBA_8888;mParams.format = PixelFormat.TRANSLUCENT;//设置flags.不可聚焦及不可使用按钮对悬浮窗进行操控.
// mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
// WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;mParams.alpha = 0.8f;mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);DisplayMetrics dm = new DisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(dm);mParams.width = 136;mParams.height = 136;
// mParams.x = 100;
// mParams.y = 100;voiceImage = new ImageView(this);voiceImage.setBackground(getResources().getDrawable(R.mipmap.assistant100025));textView = new TextView(this);textView.setTextSize(36);textView.setTextColor(Color.RED);textView.setText("语音形象");// vrView = new HSPortraitVrViewForA11V(getApplicationContext());
// vrView.startFlipping();Log.d(TAG, "initView: ytf, dm.widthPixels = " + dm.widthPixels + ",mParams.height = " + mParams.height);mParams.gravity = Gravity.TOP;LogUtils.i(TAG, "onCreate.....");super.onCreate();-----------------------@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {LogUtils.i(TAG, "onStartCommand,LteService onStartCommand");// Android 8以上特殊处理setNotificationChannel();
// mWindowManager.addView(voiceImage, mParams);
// mWindowManager.addView(textView, mParams);// if(CommonUtils.isUseHalfServiceMode()){
// if(offlineAgentService == null){
// bindHalfEngineService();
// }
// }return START_STICKY;}
-------------------------------@Overridepublic void onDestroy() {// if (voiceImage.getParent() != null) {
// mWindowManager.removeView(voiceImage);
// }
// if (textView.getParent() != null) {
// mWindowManager.removeView(textView);
// }super.onDestroy();}
相关文章:
android可见即可说实现方案
依赖于科大讯飞的asr识别能力,使用Android无障碍服务获取页面文本作为热词,注册到讯飞api,注册过后语音识别到热词的asr返回,利用WindowManager和无障碍的点击实现可见即可说功能 ##  无障碍服务获取需要注册的热词package com..mo…...

Pikachu Burte Force(暴力破解)
一、Burte Force(暴力破解)概述 “暴力破解”是一攻击具手段,在web攻击中,一般会使用这种手段对应用系统的认证信息进行获取。 其过程就是使用大量的认证信息在认证接口进行尝试登录,直到得到正确的结果。 为了提高…...

SpringMVC之JSON返回及异常处理
目录 JSON处理 导入依赖 配置Spring-mvc.xml ResponseBody注解使用 测试 目录 JSON处理 导入依赖 配置Spring-mvc.xml ResponseBody注解使用 测试 Jackson 定义 用法 常用注解 统一异常处理 为什么要全局异常处理? 异常处理思路 SpringMVC异常分类 综…...
SkyWalking快速上手(六)——告警
文章目录 前言一、什么是SkyWalking的告警功能二、为什么要使用SkyWalking的告警功能1. 及时发现异常情况2. 提高故障处理效率3. 避免数据丢失和损坏4. 提升系统性能和稳定性 三、如何使用SkyWalking的告警功能1. 告警规则2. 告警通知3. 告警持续时间 四、注意事项1、合理设置告…...
docker run:--privileged=true选项解析(特权模式:赋予容器几乎与主机相同的权限)
文章目录 Docker的--privilegedtrue选项1. Docker 容器的安全性1.1 Linux Namespace 和 Capabilities1.2 限制和权限 2. Docker的--privilegedtrue选项2.1 --privilegedtrue的作用2.2 --privilegedtrue的风险 3. 结论 Docker的–privilegedtrue选项 Docker在创建和运行容器时&…...

计算机专业毕业设计项目推荐06-工作室管理系统(Java+Vue+Mysql)
工作室管理系统(JavaSpringVueMysql) **介绍****系统总体开发情况-功能模块****各部分模块实现****最后想说的****联系方式** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较…...

Python 文件的读写操作
文章目录 1. 文件对象1.1 文件打开方式1.1.1 打开文件1.1.2 关闭文件1.1.3 访问模式 1.2文件读取1.2.1 read()1.2.2 readline()1.2.3 readlines() 1.3 文件迭代1.4 文件输入1.4.1 write()1.4.2 writelines() 1. 文件对象 文件读写操作: 把大象放冰箱里,…...

多线程回顾、集合Collection、Set、List等基本知识
多线程回顾 问: 多线程的两种创建方式? 继承Thread类实现Runnable接口线程池Callable 问:多线程通常会遇到线程安全问题? 什么情况下会遇到线程安全问题? 答:一个数据被多个线程访问(有读有写) 解决这个问题的方式? SE:同步锁 synchronized A : 同步代码块 B : 同步方法…...

分享5款用起来很好用的软件,总有一款适合你
很多软件用起来很好用,但是由于这样那样的原因,一直没什么知名度,但是不代表它们不好用,我的任务就是把这些宝藏分享给大家。 1.图像处理——Photoscissors Photoscissors是一款在线图像背景移除工具,可以让你轻松地从…...

大数据学习1.5-单机Hadoop
1.修改主机信息 vi /etc/hosts 2.修改信息如下(这里第三位一定是自己的IP 每个人都不一样) 192.168.216.140 hadoop01 192.168.216.141 hadoop02 192.168.216.142 hadoop033.修改Hadoop配置信息-1进入配置信息文件 cd /usr/local/hadoop/hadoop-2.7.1/etc/hadoop/ 4.修改Had…...

Cesium对实体元素鼠标点击popup div信息框
一、简介 设置div信息框模板,给实体元素绑定事件,同步空间位置,然后在回调函数弹出信息框。 二、示例源码 <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" …...
有多条业务线,mysql建多库多表比较好还是一个库多个表比较好呢?
这个问题的答案取决于您的具体需求。以下是一些需要考虑的因素: 数据独立性:如果您的业务线之间的数据是独立的,并且不太可能需要进行跨业务线的查询,那么将它们分成多个数据库可能是有意义的。这样可以使每个业务线的数据更加独…...

C++---异常处理
异常处理 异常处理try语句块和throw表达式异常的抛出和捕获异常的抛出和匹配原则 异常安全异常规范标准异常 异常处理 异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围。当程序的某部分检测到一个他无法处理的问题时,需要用到异常处理…...

接口自动化测试(Python+Requests+Unittest)
(1)接口自动化测试的意义、前后端分离思想 接口自动化测试的优缺点: 优点: 测试复用性。 维护成本相对UI自动化低一些。 为什么UI自动化维护成本更高? 因为前端页面变化太快,而且UI自动化比较耗时(比如等待页面元素的…...

驱动开发,IO多路复用(select,poll,epoll三种实现方式的比较)
1.IO多路复用介绍 在使用单进程或单线程情况下,同时处理多个输入输出请求,需要用到IO多路复用;IO多路复用有select/poll/epoll三种实现方式;由于不需要创建新的进程和线程,减少了系统资源的开销,减少了上下…...

大数据-玩转数据-oracel字符串分割转化为多列
一、建表 create table split_string_test(id integer primary key,test_string varchar2(500) );二、插入测试数据 insert into split_string_test values(1, 10,11,12,13,14,22); insert into split_string_test values(2, 22,23,24); insert into split_string_test valu…...
GCP设置Proxy来连接Cloud SQL
在之前的文章用Google CDC来同步Cloud SQL的数据到Bigquery_gzroy的博客-CSDN博客中,我通过在一个VM上设置反向代理的方式,使得Datastream可以通过私用连接连到Cloud SQL数据库进行数据复制。但是这种方式不太方便,主要是VM的状态我们不太方便…...

Python:为何成为当下最热门的编程语言?
文章目录 🍋引言🍋1. 简单易学🍋2. 多领域应用🍋3. 强大的社区支持🍋4. 丰富的库和框架🍋5. 跨平台兼容🍋6. 开源和免费🍋7. 数据科学和人工智能的崛起🍋8. 自动化和脚本…...
【echarts入门】:vue项目中应用echarts
一.安装echarts 在项目集成终端下载echarts npm install echarts --save 二.全局引入 创建/components/echarts/index.js // 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。 import * as echarts from "echarts/core";/** 引入任…...

Seata 源码篇之AT模式启动流程 - 上 - 02
Seata 源码篇之AT模式启动流程 - 02 自动配置两个关键点 初始化初始化TM初始化RM初始化TC 全局事务执行流程TM 发起全局事务GlobalTransactional 注解处理全局事务的开启 TM 和 RM 执行分支事务IntroductionDelegatingIntroductionInterceptorDelegatePerTargetObjectIntroduct…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...