├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml └── misc.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── bunny │ │ └── shortvideoassist │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── bunny │ │ │ └── shortvideoassist │ │ │ ├── MainActivity.java │ │ │ └── shortVideoAssist.java │ └── res │ │ ├── drawable-v24 │ │ └── shape.xml │ │ ├── layout │ │ └── main_page.xml │ │ ├── mipmap-xhdpi │ │ └── short_video_icon.png │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ └── short_video_assist_config.xml │ └── test │ └── java │ └── com │ └── bunny │ └── shortvideoassist │ └── ExampleUnitTest.java ├── build.gradle ├── core ├── .gitignore ├── build.gradle ├── libs │ └── bdasr_V3_20210628_cfe8c44.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── README.txt │ ├── WakeUp.bin │ └── baidu_speech_grammar.bsg │ ├── java │ └── com │ │ └── baidu │ │ └── aip │ │ └── asrwakeup3 │ │ └── core │ │ ├── inputstream │ │ ├── FileAudioInputStream.java │ │ ├── InFileStream.java │ │ ├── InPipedStream.java │ │ └── MyMicrophoneInputStream.java │ │ ├── mini │ │ ├── ActivityMiniRecog.java │ │ ├── ActivityMiniUnit.java │ │ ├── ActivityMiniWakeUp.java │ │ └── AutoCheck.java │ │ ├── recog │ │ ├── IStatus.java │ │ ├── MyRecognizer.java │ │ ├── RecogResult.java │ │ └── listener │ │ │ ├── ChainRecogListener.java │ │ │ ├── IRecogListener.java │ │ │ ├── MessageStatusRecogListener.java │ │ │ ├── RecogEventAdapter.java │ │ │ └── StatusRecogListener.java │ │ ├── util │ │ ├── FileUtil.java │ │ ├── MyLogger.java │ │ └── bluetooth │ │ │ ├── AndroidAudioManager.java │ │ │ ├── BluetoothReceiver.java │ │ │ └── HeadsetReceiver.java │ │ └── wakeup │ │ ├── MyWakeup.java │ │ ├── WakeUpResult.java │ │ ├── WakeupEventAdapter.java │ │ └── listener │ │ ├── IWakeupListener.java │ │ ├── RecogWakeupListener.java │ │ └── SimpleWakeupListener.java │ ├── jniLibs │ ├── arm64-v8a │ │ ├── libBaiduSpeechSDK.so │ │ ├── libbdEASRAndroid.so │ │ ├── libbdSpilWakeup.so │ │ ├── libbd_easr_s1_merge_normal_20151216.dat.so │ │ └── libvad.dnn.so │ ├── armeabi-v7a │ │ ├── libBaiduSpeechSDK.so │ │ ├── libbdEASRAndroid.so │ │ ├── libbdSpilWakeup.so │ │ ├── libbd_easr_s1_merge_normal_20151216.dat.so │ │ └── libvad.dnn.so │ ├── armeabi │ │ ├── libBaiduSpeechSDK.so │ │ ├── libbdEASRAndroid.so │ │ ├── libbdSpilWakeup.so │ │ ├── libbd_easr_s1_merge_normal_20151216.dat.so │ │ └── libvad.dnn.so │ ├── x86 │ │ ├── libBaiduSpeechSDK.so │ │ ├── libbdEASRAndroid.so │ │ ├── libbdSpilWakeup.so │ │ ├── libbd_easr_s1_merge_normal_20151216.dat.so │ │ └── libvad.dnn.so │ └── x86_64 │ │ ├── libBaiduSpeechSDK.so │ │ ├── libbdEASRAndroid.so │ │ ├── libbdSpilWakeup.so │ │ ├── libbd_easr_s1_merge_normal_20151216.dat.so │ │ └── libvad.dnn.so │ └── res │ ├── layout │ └── common_mini.xml │ └── values │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | 短视频辅助器 -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShortVideoAssistant 2 | 短视频辅助器,语音控制手机刷抖音,运用到百度语音识别的语音唤醒和安卓的无障碍服务,懒人神器 3 | 4 | 解决现实生活中吃零食看抖音弄脏手的问题 5 | 6 | 抖音视频演示: 7 | 8 | https://v.douyin.com/rbSn7sv/ 9 | 10 | 11 | **仅供学习交流,请勿它用** **仅供学习交流,请勿它用** **仅供学习交流,请勿它用** 12 | 13 | 开发环境: 14 | Android Studio 15 | 16 | minSdk 26 17 | targetSdk 32 18 | 19 | 20 | 运用到**百度语音识别的语音唤醒**和**安卓的无障碍服务** 21 | 22 | -------------------- 23 | 24 | **如果你是下载运行打包的话,请看这里(前提是你懂得使用Android Studio,会基本的开发操作)** 25 | 26 | -------------------------- 27 | 28 | 29 | ![image](https://user-images.githubusercontent.com/57706599/194303365-231ef422-5d52-4fbc-b093-d3ce47acb0ee.png) 30 | 31 | 因为需要授权ID和密钥授权,且SDK包已在本项目源码里面,所以直接说明如何获取百度语音的AppID, API Key, Secret Key, 32 | 33 | 34 | 35 | 访问此网站, 36 | 37 | https://login.bce.baidu.com/?account= 38 | 39 | 用百度账号登录,然后选择**语音能力引擎** 40 | 41 | 42 | ![image](https://user-images.githubusercontent.com/57706599/194304172-50ad3c7b-134a-43bf-8499-d1b318452cf3.png) 43 | 44 | ----------------------- 45 | 46 | 点击免费尝鲜的**去领取** 47 | 48 | ![image](https://user-images.githubusercontent.com/57706599/194304725-5e5b9dd9-6cc4-4f86-950a-33d1cc7bc734.png) 49 | 50 | 如图勾选中想要的功能,点**0元领取** 51 | 52 | (语音唤醒唤醒貌似不会限制使用次数,不过有使用时间限制吧,要是过了很久不能用了,可以回来重新配置) 53 | 54 | ![image](https://user-images.githubusercontent.com/57706599/194305449-d636bd54-3ad7-4bcc-8ba8-0bb892aab10e.png) 55 | 56 | 返回到原来的页面,点击**创建应用** 57 | 58 | ![image](https://user-images.githubusercontent.com/57706599/194306539-7b47aade-f532-4e5f-b00d-a87142fccbe6.png) 59 | 60 | 61 | 填写创建应用的信息,应用名称:**短视频辅助器**,语音包名:**com.bunny.shortvideoassist**,应用归属:**个人**,应用描述:随便写写, 62 | 63 | (名字什么的你不喜欢的话,你懂安卓开发的话,可以改名字还有包名,若不知道,还是用我的包名得了,包名记得对应项目的包名喔) 64 | 65 | 然后**立即创建** 66 | 67 | ![image](https://user-images.githubusercontent.com/57706599/194306018-1191bd86-6063-4aa0-803c-b8caed900a25.png) 68 | 69 | 在应用列表,查看AppID, API Key, Secret Key,复制好后面用到 70 | 71 | ![image](https://user-images.githubusercontent.com/57706599/194306907-723c28c1-6af9-449f-962b-56a14d7ca9a5.png) 72 | 73 | 在获取到百度语音的AppID, API Key, Secret Key后,下载本项目的源码,解压后,用Android Studio打开项目,填入信息到下图中core模块->AndroidManifest.xml->三个meta-data的value值 74 | 75 | ![image](https://user-images.githubusercontent.com/57706599/194301124-62e279ce-9126-44ec-824d-da502e0f337f.png) 76 | 77 | 后面运行看是否有错误,若无就可以打包了 78 | 79 | 如果想改唤醒词,请看这里(不过百度语音唤醒可自定义性低) 80 | 81 | https://ai.baidu.com/tech/speech/wake#tech-demo 82 | 83 | 84 | ![image](https://user-images.githubusercontent.com/57706599/194304500-b325ede0-c772-403a-a81c-414bf4649f9c.png) 85 | 86 | 完成修改后的唤醒词文件下载后导入到这里,替换WakeUp.bin文件即可。 87 | 88 | ------------------------------------- 89 | 90 | 91 | 92 | 1.运用到百度语音识别的语音唤醒 93 | 94 | 详细请看百度AI官网:https://ai.baidu.com/tech/speech/wake 95 | 96 | 技术文档:https://ai.baidu.com/ai-doc/SPEECH/Vk38lyr75 97 | 98 | 具体如何接入该功能,请查看百度官方的接入手册 99 | 100 | 101 | 2.安卓的无障碍服务 102 | 103 | 实现刷手机的常用手势,点击,双击,向下滑,向上滑 104 | 105 | 106 | **效果动图展示** 107 | 108 | ![Video_20220914_012331_818](https://user-images.githubusercontent.com/57706599/190066354-7bcb078e-4eba-4666-b25c-0112a715b9b6.gif) 109 | 110 | 111 | ![IMG_20220914_133015](https://user-images.githubusercontent.com/57706599/190067394-1d4a4a67-a9c2-4d20-86d7-94f3ee8c4502.jpg) 112 | 113 | 114 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 32 7 | 8 | defaultConfig { 9 | applicationId "com.bunny.shortvideoassist" 10 | minSdk 26 11 | targetSdk 32 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | } 29 | 30 | dependencies { 31 | 32 | implementation 'androidx.appcompat:appcompat:1.3.0' 33 | implementation 'com.google.android.material:material:1.4.0' 34 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 35 | implementation project(path: ':core') 36 | testImplementation 'junit:junit:4.13.2' 37 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 39 | implementation 'com.squareup.retrofit2:converter-gson:2.4.0' 40 | 41 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/bunny/shortvideoassist/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.bunny.shortvideoassist; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.bunny.shortvideoassist", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/bunny/shortvideoassist/MainActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: 短视频辅助器 3 | * Comments: 主界面类 4 | * JDK version used: 5 | * Author: Bunny Github: https://github.com/bunny-chz/ 6 | * Create Date:2022-03-17 7 | * Version: 1.0 8 | */ 9 | 10 | package com.bunny.shortvideoassist; 11 | 12 | import android.Manifest; 13 | import android.accessibilityservice.AccessibilityServiceInfo; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.content.pm.PackageManager; 17 | import android.os.Bundle; 18 | import android.provider.Settings; 19 | import android.util.DisplayMetrics; 20 | import android.util.Log; 21 | import android.view.WindowManager; 22 | import android.view.accessibility.AccessibilityManager; 23 | import android.widget.Button; 24 | import android.widget.TextView; 25 | import android.widget.Toast; 26 | import androidx.annotation.NonNull; 27 | import androidx.appcompat.app.AppCompatActivity; 28 | import androidx.core.app.ActivityCompat; 29 | import androidx.core.content.ContextCompat; 30 | import com.baidu.aip.asrwakeup3.core.R; 31 | import com.baidu.aip.asrwakeup3.core.inputstream.InFileStream; 32 | import com.baidu.speech.EventListener; 33 | import com.baidu.speech.EventManager; 34 | import com.baidu.speech.EventManagerFactory; 35 | import com.baidu.speech.asr.SpeechConstant; 36 | import org.json.JSONException; 37 | import org.json.JSONObject; 38 | import java.util.ArrayList; 39 | import java.util.List; 40 | import java.util.Map; 41 | import java.util.TreeMap; 42 | 43 | public class MainActivity extends AppCompatActivity implements EventListener { 44 | protected TextView txtLog; 45 | protected TextView txtResult; 46 | protected Button btn; 47 | protected Button btn_accessService; 48 | protected Button stopBtn; 49 | private int width = 0; 50 | private int height = 0; 51 | Intent intent2; 52 | 53 | private EventManager wakeup; 54 | 55 | private final boolean logTime = true; 56 | 57 | private static final String DESC_TEXT = "此为测试日志,上一个文字显示框,输出你说的文字(只能说以下词语)\n\n测试完无错误,请重启软件,开启无障碍服务正常使用本APP\n由于百度语音唤醒对指令的严格规定,刷抖音的7个手势对应的唤醒指令如下(手势→指令)\n" + 58 | "播放→\"播放\"\n" + 59 | "暂停→\"暂停\"\n" + 60 | "滑动到下个视频→\"下一首\"\n" + 61 | "滑动到上个视频→\"上一首\"\n" + 62 | "点赞→\"点赞点赞\"\n" + 63 | "查看评论区→\"查看评论\"\n" + 64 | "关闭评论区→\"关闭评论\""; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | setContentView(com.bunny.shortvideoassist.R.layout.main_page); 70 | initView(); 71 | initPermission(); 72 | getDis(); 73 | isServiceEnabled(); 74 | if(!isServiceEnabled()){ 75 | Toast.makeText(this, "无障碍服务已关闭,请点击 ”开启无障碍“ 按钮,前往设置打开", Toast.LENGTH_SHORT).show(); 76 | } 77 | intent2 = new Intent(MainActivity.this, shortVideoAssist.class); 78 | intent2.putExtra("width", width); 79 | intent2.putExtra("height", height); 80 | startService(intent2); 81 | 82 | wakeup = EventManagerFactory.create(this, "wp"); 83 | // 基于SDK唤醒词集成1.3 注册输出事件 84 | wakeup.registerListener(this); // EventListener 中 onEvent方法 85 | btn.setOnClickListener(v -> start()); 86 | stopBtn.setOnClickListener(v -> stop()); 87 | btn_accessService.setOnClickListener(v -> { 88 | Intent accessibleIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); 89 | startActivity(accessibleIntent); 90 | }); 91 | } 92 | 93 | private void start() { 94 | txtLog.setText(""); 95 | // 基于SDK唤醒词集成第2.1 设置唤醒的输入参数 96 | Map params = new TreeMap<>(); 97 | params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false); 98 | params.put(SpeechConstant.WP_WORDS_FILE, "assets:///WakeUp.bin"); 99 | // "assets:///WakeUp.bin" 表示WakeUp.bin文件定义在assets目录下 100 | InFileStream.setContext(this); 101 | String json; // 这里可以替换成你需要测试的json 102 | json = new JSONObject(params).toString(); 103 | wakeup.send(SpeechConstant.WAKEUP_START, json, null, 0, 0); 104 | printLog("输入参数:" + json); 105 | } 106 | 107 | private void stop() { 108 | wakeup.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0); // 109 | } 110 | 111 | @Override 112 | protected void onDestroy() { 113 | super.onDestroy(); 114 | stopService(intent2); 115 | wakeup.send(SpeechConstant.WAKEUP_STOP, "{}", null, 0, 0); 116 | } 117 | 118 | @Override 119 | public void onEvent(String name, String params, byte[] data, int offset, int length) { 120 | String logTxt = "name: " + name; 121 | if (params != null && !params.isEmpty()) { 122 | logTxt += " ;params :" + params; 123 | JSONObject json; 124 | try { 125 | json = new JSONObject(params); 126 | String result = json.getString("word"); 127 | txtResult.setText(result); 128 | } catch (JSONException e) { 129 | e.printStackTrace(); 130 | } 131 | 132 | } else if (data != null) { 133 | logTxt += " ;data length=" + data.length; 134 | } 135 | printLog(logTxt); 136 | } 137 | 138 | private void printLog(String text) { 139 | if (logTime) { 140 | text += " ;time=" + System.currentTimeMillis(); 141 | } 142 | text += "\n"; 143 | Log.i(getClass().getName(), text); 144 | txtLog.append(text + "\n"); 145 | } 146 | 147 | 148 | private void initView() { 149 | txtResult = findViewById(R.id.txtResult); 150 | txtLog = findViewById(R.id.txtLog); 151 | btn = findViewById(R.id.btn); 152 | stopBtn = findViewById(R.id.btn_stop); 153 | btn_accessService = findViewById(com.bunny.shortvideoassist.R.id.btn_accessService); 154 | txtLog.setText(DESC_TEXT); 155 | } 156 | 157 | /** 158 | * android 6.0 以上需要动态申请权限 159 | */ 160 | private void initPermission() { 161 | String[] permissions = {Manifest.permission.RECORD_AUDIO, 162 | Manifest.permission.ACCESS_NETWORK_STATE, 163 | Manifest.permission.INTERNET, 164 | Manifest.permission.WRITE_EXTERNAL_STORAGE 165 | }; 166 | 167 | ArrayList toApplyList = new ArrayList<>(); 168 | 169 | for (String perm : permissions) { 170 | if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) { 171 | toApplyList.add(perm); 172 | // 进入到这里代表没有权限. 173 | 174 | } 175 | } 176 | String[] tmpList = new String[toApplyList.size()]; 177 | if (!toApplyList.isEmpty()) { 178 | ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123); 179 | } 180 | 181 | } 182 | 183 | @Override 184 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 185 | // 此处为android 6.0以上动态授权的回调,用户自行实现。 186 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 187 | } 188 | 189 | private boolean isServiceEnabled() { 190 | AccessibilityManager accessibilityManager = (AccessibilityManager)getSystemService(Context.ACCESSIBILITY_SERVICE); 191 | 192 | List accessibilityServices = 193 | accessibilityManager.getEnabledAccessibilityServiceList( 194 | AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 195 | for (AccessibilityServiceInfo info : accessibilityServices) { 196 | if (info.getId().contains("com.bunny.shortvideoassist")) { 197 | return true; 198 | } 199 | } 200 | return false; 201 | } 202 | 203 | public void getDis(){ 204 | WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); 205 | DisplayMetrics dm = new DisplayMetrics(); 206 | wm.getDefaultDisplay().getMetrics(dm); 207 | 208 | width = (dm.widthPixels); 209 | height = (dm.heightPixels); 210 | } 211 | 212 | /** 213 | 再按一次退出主界面操作 214 | **/ 215 | long exitTime = 0; 216 | @Override 217 | public void onBackPressed() { 218 | if ((System.currentTimeMillis() - exitTime) > 2000) { 219 | // ToastUtil.makeToastInBottom("再按一次退出应用", MainMyselfActivity); 220 | Toast.makeText(this, "再按一次退出应用", Toast.LENGTH_SHORT).show(); 221 | exitTime = System.currentTimeMillis(); 222 | return; 223 | } 224 | finish(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /app/src/main/java/com/bunny/shortvideoassist/shortVideoAssist.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: 短视频辅助器 3 | * Comments: 主要功能类,无障碍服务,语音唤醒 4 | * JDK version used: 5 | * Author: Bunny Github: https://github.com/bunny-chz/ 6 | * Create Date:2022-03-17 7 | * Version: 1.0 8 | */ 9 | 10 | package com.bunny.shortvideoassist; 11 | 12 | import android.accessibilityservice.AccessibilityService; 13 | import android.accessibilityservice.GestureDescription; 14 | import android.annotation.SuppressLint; 15 | import android.content.Intent; 16 | import android.os.Build; 17 | import android.os.Handler; 18 | import android.os.Message; 19 | import android.util.Log; 20 | import android.view.accessibility.AccessibilityEvent; 21 | import android.view.accessibility.AccessibilityNodeInfo; 22 | import android.widget.Toast; 23 | import android.graphics.Path; 24 | 25 | import androidx.annotation.NonNull; 26 | import androidx.annotation.RequiresApi; 27 | 28 | import com.baidu.aip.asrwakeup3.core.inputstream.InFileStream; 29 | import com.baidu.speech.EventListener; 30 | import com.baidu.speech.EventManager; 31 | import com.baidu.speech.EventManagerFactory; 32 | import com.baidu.speech.asr.SpeechConstant; 33 | 34 | import org.json.JSONException; 35 | import org.json.JSONObject; 36 | 37 | import java.util.List; 38 | import java.util.Map; 39 | import java.util.TreeMap; 40 | 41 | public class shortVideoAssist extends AccessibilityService implements EventListener { 42 | private static final String TAG = "Test"; 43 | private boolean isFirst = true; 44 | private int X=0, Y=0; 45 | 46 | private EventManager wakeup; 47 | private String mResult; 48 | 49 | @Override 50 | public int onStartCommand(Intent intent, int flags, int startId) { 51 | X = intent.getIntExtra("width", 0); 52 | Y = intent.getIntExtra("height", 0); 53 | wakeup = EventManagerFactory.create(this, "wp"); 54 | wakeup.registerListener(this); 55 | start(); 56 | setResult("defaultValue"); 57 | String val = "X: " + X + " Y: " + Y; 58 | Log.d(TAG, val); 59 | return super.onStartCommand(intent, flags, startId); 60 | } 61 | 62 | @Override 63 | public void onAccessibilityEvent(AccessibilityEvent event) { 64 | 65 | if (event == null || event.getPackageName() == null || event.getPackageName().equals("")){ 66 | return; 67 | } 68 | 69 | if (event.getPackageName().equals("com.ss.android.ugc.aweme")){ 70 | final AccessibilityNodeInfo nodeInfo = event.getSource(); 71 | if (nodeInfo != null) { 72 | Log.d(TAG, "nodeinfo:" + nodeInfo.getClassName()); 73 | } 74 | 75 | if (!isFirst){ 76 | return; 77 | } 78 | 79 | if (isFirst){ 80 | isFirst = false; 81 | } 82 | StartThreadGesture(); 83 | } 84 | } 85 | 86 | //开启一个子线程 87 | private void StartThreadGesture() { 88 | new Thread(){ 89 | @Override 90 | public void run() { 91 | do { 92 | try { 93 | Thread.sleep(35); 94 | Message message=new Message(); 95 | message.what=1; 96 | handler.sendMessage(message); 97 | } catch (InterruptedException e) { 98 | e.printStackTrace(); 99 | } 100 | } while (true); 101 | } 102 | }.start(); 103 | } 104 | 105 | 106 | @SuppressLint("HandlerLeak") 107 | private final Handler handler=new Handler(){ 108 | 109 | @Override 110 | public void handleMessage(@NonNull Message msg) { 111 | if (msg.what == 1) { 112 | //ClickIgnoreRemind(); 113 | String result = getResult(); 114 | switch (result) { 115 | case "下一首": 116 | ScrollUp(); 117 | setResult("defaultValue"); 118 | Log.d("滑动", "下个视频"); 119 | break; 120 | case "上一首": 121 | ScrollDown(); 122 | setResult("defaultValue"); 123 | Log.d("滑动", "上个视频"); 124 | break; 125 | case "播放": 126 | SingleClick(); 127 | setResult("defaultValue"); 128 | Log.d("单击", "播放"); 129 | break; 130 | case "暂停": 131 | SingleClick(); 132 | setResult("defaultValue"); 133 | Log.d("单击", "暂停"); 134 | break; 135 | case "点赞点赞": 136 | ClickLike(); 137 | setResult("defaultValue"); 138 | Log.d("双击", "点赞点赞"); 139 | break; 140 | case "查看评论": 141 | ClickOnComment(); 142 | setResult("defaultValue"); 143 | Log.d("单击", "看评论区"); 144 | break; 145 | case "关闭评论": 146 | ClickOffComment(); 147 | setResult("defaultValue"); 148 | Log.d("单击", "关评论区"); 149 | break; 150 | } 151 | } 152 | } 153 | }; 154 | 155 | private void ScrollUp(){ 156 | final android.graphics.Path path = new Path(); 157 | path.moveTo((int)(X/2), (int)(Y*2/3)); 158 | path.lineTo((int)(X/2), 0); 159 | 160 | GestureDescription.Builder builder = new GestureDescription.Builder(); 161 | 162 | GestureDescription gestureDescription = builder.addStroke( 163 | new GestureDescription.StrokeDescription(path, 50, 100) 164 | ).build(); 165 | 166 | dispatchGesture(gestureDescription, new GestureResultCallback() { 167 | @Override 168 | public void onCompleted(GestureDescription gestureDescription) { 169 | super.onCompleted(gestureDescription); 170 | Log.d(TAG, "ScrollUp finish."); 171 | path.close(); 172 | } 173 | 174 | @Override 175 | public void onCancelled(GestureDescription gestureDescription) { 176 | super.onCancelled(gestureDescription); 177 | Log.d(TAG, "ScrollUp cancel."); 178 | } 179 | }, null); 180 | 181 | } 182 | 183 | private void ScrollDown(){ 184 | final android.graphics.Path path = new Path(); 185 | path.moveTo((int)(X/2), (int)(Y/3)); 186 | path.lineTo((int)(X/2), (int)(Y)); 187 | 188 | GestureDescription.Builder builder = new GestureDescription.Builder(); 189 | 190 | GestureDescription gestureDescription = builder.addStroke( 191 | new GestureDescription.StrokeDescription(path, 50, 100) 192 | ).build(); 193 | 194 | dispatchGesture(gestureDescription, new GestureResultCallback() { 195 | @Override 196 | public void onCompleted(GestureDescription gestureDescription) { 197 | super.onCompleted(gestureDescription); 198 | Log.d(TAG, "ScrollDown finish."); 199 | path.close(); 200 | } 201 | 202 | @Override 203 | public void onCancelled(GestureDescription gestureDescription) { 204 | super.onCancelled(gestureDescription); 205 | Log.d(TAG, "ScrollDown cancel."); 206 | } 207 | }, null); 208 | 209 | } 210 | 211 | @RequiresApi(api = Build.VERSION_CODES.N) 212 | private void SingleClick(){ 213 | final android.graphics.Path path = new Path(); 214 | path.moveTo((int)(X/2), (int)(Y/2)); 215 | 216 | GestureDescription.Builder builder = new GestureDescription.Builder(); 217 | 218 | GestureDescription gestureDescription = builder.addStroke( 219 | new GestureDescription.StrokeDescription(path, 0, 50) 220 | ).build(); 221 | 222 | dispatchGesture(gestureDescription, new GestureResultCallback() { 223 | @Override 224 | public void onCompleted(GestureDescription gestureDescription) { 225 | super.onCompleted(gestureDescription); 226 | Log.d(TAG, "click finish."); 227 | path.close(); 228 | } 229 | 230 | @Override 231 | public void onCancelled(GestureDescription gestureDescription) { 232 | super.onCancelled(gestureDescription); 233 | Log.d(TAG, "scroll cancel."); 234 | } 235 | }, null); 236 | 237 | } 238 | 239 | @RequiresApi(api = Build.VERSION_CODES.N) 240 | private void ClickOnComment(){ 241 | final android.graphics.Path path = new Path(); 242 | path.moveTo((int)(X*9/10), (int)(Y*65/100)); 243 | 244 | GestureDescription.Builder builder = new GestureDescription.Builder(); 245 | 246 | GestureDescription gestureDescription = builder.addStroke( 247 | new GestureDescription.StrokeDescription(path, 0, 50) 248 | ).build(); 249 | 250 | dispatchGesture(gestureDescription, new GestureResultCallback() { 251 | @Override 252 | public void onCompleted(GestureDescription gestureDescription) { 253 | super.onCompleted(gestureDescription); 254 | Log.d(TAG, "click finish."); 255 | path.close(); 256 | } 257 | 258 | @Override 259 | public void onCancelled(GestureDescription gestureDescription) { 260 | super.onCancelled(gestureDescription); 261 | Log.d(TAG, "scroll cancel."); 262 | } 263 | }, null); 264 | 265 | } 266 | 267 | @RequiresApi(api = Build.VERSION_CODES.N) 268 | private void ClickOffComment(){ 269 | final android.graphics.Path path = new Path(); 270 | path.moveTo((int)(X/2), (int)(Y/6)); 271 | 272 | GestureDescription.Builder builder = new GestureDescription.Builder(); 273 | 274 | GestureDescription gestureDescription = builder.addStroke( 275 | new GestureDescription.StrokeDescription(path, 0, 50) 276 | ).build(); 277 | 278 | dispatchGesture(gestureDescription, new GestureResultCallback() { 279 | @Override 280 | public void onCompleted(GestureDescription gestureDescription) { 281 | super.onCompleted(gestureDescription); 282 | Log.d(TAG, "click finish."); 283 | path.close(); 284 | } 285 | 286 | @Override 287 | public void onCancelled(GestureDescription gestureDescription) { 288 | super.onCancelled(gestureDescription); 289 | Log.d(TAG, "scroll cancel."); 290 | } 291 | }, null); 292 | 293 | } 294 | 295 | @RequiresApi(api = Build.VERSION_CODES.N) 296 | private void ClickLike(){ 297 | final Path path = new Path(); 298 | path.moveTo((int)(X/2), (int)(Y/2)); 299 | 300 | GestureDescription.Builder builder = new GestureDescription.Builder(); 301 | 302 | GestureDescription gestureDescription = builder.addStroke( 303 | new GestureDescription.StrokeDescription(path, 100, 1) 304 | ).build(); 305 | 306 | dispatchGesture(gestureDescription, new GestureResultCallback() { 307 | @Override 308 | public void onCompleted(GestureDescription gestureDescription) { 309 | super.onCompleted(gestureDescription); 310 | Path path2 = new Path(); 311 | path2.moveTo((int)(X/2), (int)(Y/2)); 312 | 313 | GestureDescription.Builder builder2 = new GestureDescription.Builder(); 314 | 315 | GestureDescription gestureDescription2 = builder2.addStroke( 316 | new GestureDescription.StrokeDescription(path2, 100, 1) 317 | ).build(); 318 | 319 | shortVideoAssist.this.dispatchGesture(gestureDescription2, null, null); 320 | 321 | Log.d(TAG, "ClickLike finish."); 322 | path.close(); 323 | path2.close(); 324 | } 325 | 326 | @Override 327 | public void onCancelled(GestureDescription gestureDescription) { 328 | super.onCancelled(gestureDescription); 329 | Log.d(TAG, "ClickLike cancel."); 330 | } 331 | }, null); 332 | 333 | } 334 | private void ClickIgnoreRemind() { 335 | AccessibilityNodeInfo rootInfo1 = shortVideoAssist.this.getRootInActiveWindow(); 336 | List listInfo1 = rootInfo1.findAccessibilityNodeInfosByText("忽略提醒"); 337 | List listInfo2 = rootInfo1.findAccessibilityNodeInfosByText("我知道了"); 338 | List listInfo3 = rootInfo1.findAccessibilityNodeInfosByText("关闭"); 339 | if (listInfo1.size() ==1 || listInfo3.size() ==1 || listInfo2.size() ==1){ 340 | for (int i=0; i params = new TreeMap<>(); 356 | params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false); 357 | params.put(SpeechConstant.WP_WORDS_FILE, "assets:///WakeUp.bin"); 358 | InFileStream.setContext(this); 359 | String json; 360 | json = new JSONObject(params).toString(); 361 | wakeup.send(SpeechConstant.WAKEUP_START, json, null, 0, 0); 362 | } 363 | 364 | private void stop() { 365 | wakeup.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0); // 366 | } 367 | 368 | @Override 369 | public void onEvent(String name, String params, byte[] data, int offset, int length) { 370 | 371 | if (params != null && !params.isEmpty()) { 372 | try { 373 | JSONObject json = new JSONObject(params); 374 | String word = json.getString("word"); 375 | Log.d("WORD",word); 376 | setResult(word); 377 | } catch (JSONException e) { 378 | e.printStackTrace(); 379 | } 380 | } 381 | } 382 | 383 | public String getResult(){ 384 | return mResult; 385 | } 386 | 387 | public void setResult(String result){ 388 | this.mResult = result; 389 | } 390 | 391 | @Override 392 | public void onInterrupt() { 393 | stop(); 394 | wakeup.send(SpeechConstant.WAKEUP_STOP, "{}", null, 0, 0); 395 | Log.d(TAG, "AccessibilityEvent end."); 396 | } 397 | 398 | @Override 399 | protected void onServiceConnected() { 400 | super.onServiceConnected(); 401 | Toast.makeText(getApplicationContext(), "无障碍服务开启成功,若出现错误,请重新开启", Toast.LENGTH_LONG).show(); 402 | Log.d(TAG,"onServiceConnected: success."); 403 | } 404 | 405 | } 406 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_page.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 21 | 22 | 29 | 30 | 34 | 35 | 39 | 40 | 47 | 48 | 49 | 55 | 56 | 60 |