[*] 信令+设置布局
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -3,8 +3,11 @@
|
||||
*.settings
|
||||
*.classpath
|
||||
*.factorypath
|
||||
|
||||
.vscode
|
||||
|
||||
package-lock.json
|
||||
|
||||
logs
|
||||
target
|
||||
node_modules
|
||||
node_modules
|
||||
|
||||
@@ -55,6 +55,7 @@ Android还在学习之中...
|
||||
## TODO
|
||||
|
||||
* P2P
|
||||
* RTP
|
||||
* 标识 -> ID
|
||||
* 所有字段获取 -> get
|
||||
* 优化JS错误回调 -> platform::error
|
||||
|
||||
72
docs/AOSP.md
Normal file
72
docs/AOSP.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# AOSP
|
||||
|
||||
本文档内容旨在独立定制编译`AOSP`系统,非必需使用。
|
||||
|
||||
## 参考文档
|
||||
|
||||
* https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/
|
||||
* https://source.android.google.cn/source/initializing?hl=zh-cn
|
||||
* https://source.android.google.cn/docs/setup/about/build-numbers?hl=zh-cn
|
||||
* https://developers.google.cn/android/drivers#sargosp2a.220505.008
|
||||
|
||||
## 机器配置
|
||||
|
||||
* 内存`32G`
|
||||
* 十六核`CPU`
|
||||
* 硬盘`300G`
|
||||
* 系统`Ubuntu 18.xx`
|
||||
* 公司网络`100Mbps/s`
|
||||
* 整个下载过程大概需要四到五个小时
|
||||
* 整个编译过程大概需要半到一个小时
|
||||
|
||||
## 源码
|
||||
|
||||
```
|
||||
# 工具
|
||||
mkdir /data/android
|
||||
cd /data/android
|
||||
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o repo
|
||||
chmod a+x repo
|
||||
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
|
||||
|
||||
git config --global user.email "taoyao@acgist.com"
|
||||
git config --global user.name "acgist"
|
||||
|
||||
# 源码
|
||||
./repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-12.1.0_r27
|
||||
./repo sync
|
||||
```
|
||||
|
||||
ROOT权限
|
||||
|
||||
## 裁剪
|
||||
|
||||
```
|
||||
# 应用
|
||||
|
||||
# 驱动
|
||||
|
||||
# root
|
||||
userdebug
|
||||
|
||||
# framework
|
||||
```
|
||||
|
||||
## 编译
|
||||
|
||||
```
|
||||
lunch aosp_arm64-user
|
||||
make -j 8
|
||||
```
|
||||
|
||||
## 刷机
|
||||
|
||||
```
|
||||
adb reboot bootloader
|
||||
fastboot flashall
|
||||
```
|
||||
|
||||
|
||||
https://blog.csdn.net/u012932409/article/details/106792906
|
||||
https://blog.csdn.net/qq_33240707/article/details/123704679
|
||||
https://blog.csdn.net/weixin_42929891/article/details/122667831
|
||||
@@ -278,90 +278,6 @@ SELINUX=disabled
|
||||
---
|
||||
```
|
||||
|
||||
## libwebrtc(可选)
|
||||
|
||||
* https://webrtc.github.io/webrtc-org/native-code/android/
|
||||
* https://webrtc.github.io/webrtc-org/native-code/development/
|
||||
* https://webrtc.github.io/webrtc-org/native-code/development/prerequisite-sw/
|
||||
* https://www.chromium.org/developers/how-tos/install-depot-tools/
|
||||
|
||||
建议直接购买国外的按需使用的主机,用完直接释放,配置建议:
|
||||
|
||||
* 内存`8G`
|
||||
* 四核`CPU`
|
||||
* 硬盘`100G`
|
||||
* 系统`Ubuntu 20.xx`
|
||||
* 宽带按需`100Mbps/s`(不要固定宽带)
|
||||
* 整个编译过程大概需要两到三个小时(不会下载回来很慢)
|
||||
|
||||
```
|
||||
# 编译工具
|
||||
mkdir -p /data
|
||||
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
|
||||
# 源码
|
||||
mkdir -p /data/webrtc
|
||||
cd /data/webrtc
|
||||
fetch --nohooks webrtc_android
|
||||
/data/depot_tools/gclient sync
|
||||
|
||||
# 分支
|
||||
cd src
|
||||
git checkout -b m94 branch-heads/4606
|
||||
/data/depot_tools/gclient sync
|
||||
|
||||
# 编译依赖
|
||||
./build/install-build-deps.sh
|
||||
./build/install-build-deps-android.sh
|
||||
source ./build/android/envsetup.sh
|
||||
|
||||
---
|
||||
'target_os': 'android',
|
||||
'is_clang': True,
|
||||
'is_debug': False,
|
||||
'use_rtti': True,
|
||||
'rtc_use_h264': True,
|
||||
'use_custom_libcxx': False,
|
||||
'rtc_include_tests': False,
|
||||
'is_component_build': False,
|
||||
'treat_warnings_as_errors': False,
|
||||
'use_goma': use_goma,
|
||||
'target_cpu': _GetTargetCpu(arch)
|
||||
---
|
||||
|
||||
# 编译.so
|
||||
./tools_webrtc/android/build_aar.py --build-dir ./out/release-build/
|
||||
# 指定CPU架构:--arch x86 x86_64 arm64-v8a armeabi-v7a
|
||||
|
||||
# 编译.a
|
||||
/data/depot_tools/autoninja -C ./out/release-build/x86 webrtc &&
|
||||
/data/depot_tools/autoninja -C ./out/release-build/x86_64 webrtc &&
|
||||
/data/depot_tools/autoninja -C ./out/release-build/arm64-v8a webrtc &&
|
||||
/data/depot_tools/autoninja -C ./out/release-build/armeabi-v7a webrtc
|
||||
|
||||
# 依赖打包
|
||||
zip -r webrtc.zip out libwebrtc.aar
|
||||
```
|
||||
|
||||
[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc)
|
||||
|
||||
## libmediasoupclient(可选)
|
||||
|
||||
https://mediasoup.org/documentation/v3/libmediasoupclient/installation/
|
||||
|
||||
```
|
||||
# 编译
|
||||
cmake . -B build \
|
||||
-DCMAKE_BUILD_TYPE=Debug | Release \
|
||||
-DMEDIASOUPCLIENT_LOG_DEV=OFF \
|
||||
-DMEDIASOUPCLIENT_LOG_TRACE=OFF \
|
||||
-DMEDIASOUPCLIENT_BUILD_TESTS=OFF \
|
||||
-DLIBWEBRTC_INCLUDE_PATH:PATH=PATH_TO_LIBWEBRTC_SOURCES \
|
||||
-DLIBWEBRTC_BINARY_PATH:PATH=PATH_TO_LIBWEBRTC_BINARY
|
||||
make -C build
|
||||
make install -C build
|
||||
```
|
||||
|
||||
## 下载源码
|
||||
|
||||
```
|
||||
|
||||
90
docs/WebRTC.md
Normal file
90
docs/WebRTC.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# WebRTC
|
||||
|
||||
本文档内容旨在独立编译`WebRTC`项目,非必需使用。
|
||||
|
||||
## libwebrtc
|
||||
|
||||
* https://webrtc.github.io/webrtc-org/native-code/android/
|
||||
* https://webrtc.github.io/webrtc-org/native-code/development/
|
||||
* https://webrtc.github.io/webrtc-org/native-code/development/prerequisite-sw/
|
||||
* https://www.chromium.org/developers/how-tos/install-depot-tools/
|
||||
|
||||
国内镜像需要配置比较麻烦,建议直接按需购买能够访问外网的主机,用完直接释放,配置建议:
|
||||
|
||||
* 内存`8G`
|
||||
* 四核`CPU`
|
||||
* 硬盘`100G`
|
||||
* 系统`Ubuntu 20.xx`
|
||||
* 宽带按需`100Mbps/s`(不要固定宽带)
|
||||
* 整个下载过程大概需要半到一个小时
|
||||
* 整个编译过程大概需要一到两个小时
|
||||
* 最痛苦的就是下载回来速度很慢
|
||||
|
||||
```
|
||||
# 编译工具
|
||||
mkdir -p /data
|
||||
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
|
||||
# 源码
|
||||
mkdir -p /data/webrtc
|
||||
cd /data/webrtc
|
||||
fetch --nohooks webrtc_android
|
||||
/data/depot_tools/gclient sync
|
||||
|
||||
# 分支
|
||||
cd src
|
||||
git checkout -b m94 branch-heads/4606
|
||||
/data/depot_tools/gclient sync
|
||||
|
||||
# 编译依赖
|
||||
./build/install-build-deps.sh
|
||||
./build/install-build-deps-android.sh
|
||||
source ./build/android/envsetup.sh
|
||||
|
||||
# 编译配置:./tools_webrtc/android/build_aar.py
|
||||
---
|
||||
'target_os': 'android',
|
||||
'is_clang': True,
|
||||
'is_debug': False,
|
||||
'use_rtti': True,
|
||||
'rtc_use_h264': True,
|
||||
'use_custom_libcxx': False,
|
||||
'rtc_include_tests': False,
|
||||
'is_component_build': False,
|
||||
'treat_warnings_as_errors': False,
|
||||
'use_goma': use_goma,
|
||||
'target_cpu': _GetTargetCpu(arch)
|
||||
---
|
||||
|
||||
# 编译项目
|
||||
./tools_webrtc/android/build_aar.py --build-dir ./out/release-build/
|
||||
# 指定CPU架构:--arch x86 x86_64 arm64-v8a armeabi-v7a
|
||||
|
||||
# 生成静态库
|
||||
/data/depot_tools/autoninja -C ./out/release-build/x86 webrtc
|
||||
/data/depot_tools/autoninja -C ./out/release-build/x86_64 webrtc
|
||||
/data/depot_tools/autoninja -C ./out/release-build/arm64-v8a webrtc
|
||||
/data/depot_tools/autoninja -C ./out/release-build/armeabi-v7a webrtc
|
||||
|
||||
# 打包
|
||||
zip -r webrtc.zip out libwebrtc.aar
|
||||
```
|
||||
|
||||
[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc)
|
||||
|
||||
## libmediasoupclient
|
||||
|
||||
https://mediasoup.org/documentation/v3/libmediasoupclient/installation/
|
||||
|
||||
```
|
||||
# 编译
|
||||
cmake . -B build \
|
||||
-DCMAKE_BUILD_TYPE=Debug | Release \
|
||||
-DMEDIASOUPCLIENT_LOG_DEV=OFF \
|
||||
-DMEDIASOUPCLIENT_LOG_TRACE=OFF \
|
||||
-DMEDIASOUPCLIENT_BUILD_TESTS=OFF \
|
||||
-DLIBWEBRTC_INCLUDE_PATH:PATH=PATH_TO_LIBWEBRTC_SOURCES \
|
||||
-DLIBWEBRTC_BINARY_PATH:PATH=PATH_TO_LIBWEBRTC_BINARY
|
||||
make -C build
|
||||
make install -C build
|
||||
```
|
||||
@@ -10,8 +10,7 @@
|
||||
|
||||
## 项目配置
|
||||
|
||||
可以自己编译`WebRTC`依赖或者下载已有依赖,
|
||||
项目导入以后拷贝`libmediasoupclient`源码还有`WebRTC`头文件和二进制文件到`deps`目录。
|
||||
可以自己编译`WebRTC`依赖或者下载已有依赖,项目导入以后拷贝`libmediasoupclient`源码还有`WebRTC`头文件和二进制文件到`deps`目录。
|
||||
|
||||
[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc)
|
||||
|
||||
@@ -22,7 +21,7 @@
|
||||
* https://developer.android.google.cn/docs?hl=zh-cn
|
||||
* https://developer.android.google.cn/guide?hl=zh-cn
|
||||
|
||||
## 依赖编译
|
||||
## 依赖编译(可选)
|
||||
|
||||
* https://webrtc.github.io/webrtc-org/native-code/android/
|
||||
* https://webrtc.github.io/webrtc-org/native-code/development/
|
||||
@@ -30,6 +29,11 @@
|
||||
* https://www.chromium.org/developers/how-tos/install-depot-tools/
|
||||
* https://mediasoup.org/documentation/v3/libmediasoupclient/installation/
|
||||
|
||||
## 安卓编译(可选)
|
||||
|
||||
* https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/
|
||||
* https://source.android.google.cn/source/initializing?hl=zh-cn
|
||||
|
||||
## 参考项目
|
||||
|
||||
* https://github.com/haiyangwu/webrtc-android-build
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -11,24 +12,33 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Taoyao">
|
||||
<service
|
||||
android:name=".MediaService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_settings" />
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:label="@string/title_activity_main">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_settings" />
|
||||
|
||||
<service
|
||||
android:name=".MediaService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.acgist.taoyao.client;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* 开机启动
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
|
||||
final Intent mainActivity = new Intent(context, MainActivity.class);
|
||||
mainActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(mainActivity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +1,38 @@
|
||||
package com.acgist.taoyao.client;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.acgist.taoyao.client.databinding.ActivityMainBinding;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 预览界面
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
static {
|
||||
System.loadLibrary("taoyao");
|
||||
}
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
TextView tv = binding.sampleText;
|
||||
tv.setText(stringFromJNI());
|
||||
// 媒体服务
|
||||
final Intent mediaService = new Intent(this, MediaService.class);
|
||||
this.startService(mediaService);
|
||||
// 布局
|
||||
this.binding = ActivityMainBinding.inflate(this.getLayoutInflater());
|
||||
this.setContentView(this.binding.getRoot());
|
||||
// 设置按钮
|
||||
this.binding.settings.setOnClickListener(view -> {
|
||||
final Intent settings = new Intent(this, SettingsActivity.class);
|
||||
this.startService(settings);
|
||||
});
|
||||
}
|
||||
|
||||
public native String stringFromJNI();
|
||||
|
||||
}
|
||||
@@ -4,7 +4,19 @@ import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.acgist.taoyao.client.signal.Taoyao;
|
||||
|
||||
/**
|
||||
* 媒体服务
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class MediaService extends Service {
|
||||
|
||||
static {
|
||||
System.loadLibrary("taoyao");
|
||||
}
|
||||
|
||||
public MediaService() {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,38 @@
|
||||
package com.acgist.taoyao.client;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.acgist.taoyao.client.R;
|
||||
import com.acgist.taoyao.client.databinding.ActivitySettingsBinding;
|
||||
import com.acgist.taoyao.client.signal.Taoyao;
|
||||
|
||||
/**
|
||||
* 设置界面
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private ActivitySettingsBinding binding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_settings);
|
||||
// 布局
|
||||
this.binding = ActivitySettingsBinding.inflate(this.getLayoutInflater());
|
||||
this.setContentView(this.binding.getRoot());
|
||||
// 设置按钮
|
||||
this.binding.connect.setOnClickListener(view -> {
|
||||
// final Taoyao taoyao = new Taoyao(
|
||||
//
|
||||
// );
|
||||
// Log.d(SettingsActivity.class.getSimpleName(), "连接信令:" + taoyao);
|
||||
final Intent main = new Intent(this, MainActivity.class);
|
||||
this.startService(main);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.acgist.taoyao.client.signal;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
@@ -7,8 +10,12 @@ import android.net.wifi.WifiManager;
|
||||
import android.os.BatteryManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import com.acgist.taoyao.boot.model.Header;
|
||||
import com.acgist.taoyao.boot.model.Message;
|
||||
import com.acgist.taoyao.boot.model.MessageCode;
|
||||
import com.acgist.taoyao.boot.model.MessageCodeException;
|
||||
import com.acgist.taoyao.boot.utils.CloseableUtils;
|
||||
import com.acgist.taoyao.boot.utils.JSONUtils;
|
||||
import com.acgist.taoyao.client.media.Recorder;
|
||||
@@ -26,8 +33,11 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
@@ -51,14 +61,21 @@ public final class Taoyao {
|
||||
*/
|
||||
private final String host;
|
||||
/**
|
||||
* 终端ID
|
||||
* 信令版本
|
||||
*/
|
||||
private final String clientId;
|
||||
private final String clientType = "camera";
|
||||
private final String version;
|
||||
/**
|
||||
* 终端名称
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 终端ID
|
||||
*/
|
||||
private final String clientId;
|
||||
/**
|
||||
* 终端类型
|
||||
*/
|
||||
private final String clientType;
|
||||
/**
|
||||
* 桃夭帐号
|
||||
*/
|
||||
@@ -67,55 +84,122 @@ public final class Taoyao {
|
||||
* 桃夭密码
|
||||
*/
|
||||
private final String password;
|
||||
private String version;
|
||||
/**
|
||||
* Socket
|
||||
* 是否关闭
|
||||
*/
|
||||
private Socket socket;
|
||||
private InputStream input;
|
||||
private OutputStream output;
|
||||
private boolean close;
|
||||
/**
|
||||
* 是否连接
|
||||
*/
|
||||
private boolean connect;
|
||||
/**
|
||||
* 超时时间
|
||||
*/
|
||||
private final int timeout;
|
||||
/**
|
||||
* Socket
|
||||
*/
|
||||
private Socket socket;
|
||||
/**
|
||||
* 信令输入
|
||||
*/
|
||||
private InputStream input;
|
||||
/**
|
||||
* 信令输出
|
||||
*/
|
||||
private OutputStream output;
|
||||
/**
|
||||
* 加密工具
|
||||
*/
|
||||
private final Cipher encrypt;
|
||||
/**
|
||||
* 解密工具
|
||||
*/
|
||||
private final Cipher decrypt;
|
||||
/**
|
||||
* 服务上下文
|
||||
*/
|
||||
private final Context context;
|
||||
/**
|
||||
* Wifi管理器
|
||||
*/
|
||||
private final WifiManager wifiManager;
|
||||
/**
|
||||
* 电池管理器
|
||||
*/
|
||||
private final BatteryManager batteryManager;
|
||||
/**
|
||||
* 位置管理器
|
||||
*/
|
||||
private final LocationManager locationManager;
|
||||
// 线程池
|
||||
private final ExecutorService executor = Executors.newFixedThreadPool(8);
|
||||
// 定时任务线程池
|
||||
private final ExecutorService scheduled = Executors.newScheduledThreadPool(2);
|
||||
/**
|
||||
* 请求消息:同步消息
|
||||
*/
|
||||
private final Map<Long, Message> requestMessage;
|
||||
/**
|
||||
* 线程池
|
||||
*/
|
||||
private final ExecutorService executor;
|
||||
/**
|
||||
* 定时任务线程池
|
||||
*/
|
||||
private final ScheduledExecutorService scheduled;
|
||||
/**
|
||||
* 全局单例
|
||||
*/
|
||||
private static Taoyao instance;
|
||||
|
||||
public Taoyao(
|
||||
int port, String host, String algo, String secret, String clientId, String name, String username, String password,
|
||||
WifiManager wifiManager, BatteryManager batteryManager, LocationManager locationManager
|
||||
int port, String host, String version,
|
||||
String name, String clientId, String clientType, String username, String password,
|
||||
int timeout, String algo, String secret,
|
||||
Context context, WifiManager wifiManager, BatteryManager batteryManager, LocationManager locationManager
|
||||
) {
|
||||
this.port = port;
|
||||
this.host = host;
|
||||
this.close = false;
|
||||
this.connect = false;
|
||||
if (algo == null || algo.isEmpty() || algo.equals("PLAINTEXT")) {
|
||||
// 明文
|
||||
this.encrypt = null;
|
||||
this.decrypt = null;
|
||||
} else {
|
||||
this.encrypt = this.buildCipher(Cipher.ENCRYPT_MODE, algo, secret);
|
||||
this.decrypt = this.buildCipher(Cipher.DECRYPT_MODE, algo, secret);
|
||||
}
|
||||
this.clientId = clientId;
|
||||
this.port = port;
|
||||
this.host = host;
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
this.clientId = clientId;
|
||||
this.clientType = clientType;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.timeout = timeout;
|
||||
final boolean plaintext = algo == null || algo.isEmpty() || algo.equals("PLAINTEXT");
|
||||
this.encrypt = plaintext ? null : this.buildCipher(Cipher.ENCRYPT_MODE, algo, secret);
|
||||
this.decrypt = plaintext ? null : this.buildCipher(Cipher.DECRYPT_MODE, algo, secret);
|
||||
this.context = context;
|
||||
this.wifiManager = wifiManager;
|
||||
this.batteryManager = batteryManager;
|
||||
this.locationManager = locationManager;
|
||||
executor.submit(this::read);
|
||||
scheduled.submit(this::heartbeat);
|
||||
this.requestMessage = new ConcurrentHashMap<>();
|
||||
// 读取线程 + 两条处理线程
|
||||
this.executor = Executors.newFixedThreadPool(3);
|
||||
// 心跳线程
|
||||
this.scheduled = Executors.newScheduledThreadPool(1);
|
||||
executor.submit(this::loopMessage);
|
||||
scheduled.scheduleWithFixedDelay(this::heartbeat, 30, 30, TimeUnit.SECONDS);
|
||||
if(Taoyao.instance != null) {
|
||||
Taoyao.instance.close();
|
||||
}
|
||||
Taoyao.instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 信令
|
||||
*/
|
||||
public static final Taoyao getInstance() {
|
||||
return Taoyao.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode 加密/解密
|
||||
* @param name 算法名称
|
||||
* @param secret 密钥
|
||||
*
|
||||
* @return 加解密工具
|
||||
*/
|
||||
private Cipher buildCipher(int mode, String name, String secret) {
|
||||
try {
|
||||
final String algo = name.equals("DES") ? "DES/ECB/PKCS5Padding" : "AES/ECB/PKCS5Padding";
|
||||
@@ -123,7 +207,7 @@ public final class Taoyao {
|
||||
cipher.init(mode, new SecretKeySpec(Base64.getMimeDecoder().decode(secret), name));
|
||||
return cipher;
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
|
||||
// TODO:日志
|
||||
Log.e(Taoyao.class.getSimpleName(), "创建加解密工具异常", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -131,44 +215,51 @@ public final class Taoyao {
|
||||
/**
|
||||
* 连接信令
|
||||
*/
|
||||
public void connect() {
|
||||
this.close();
|
||||
// HiLog.debug(this.label, "连接信令:%s:%d", this.host, this.port);
|
||||
public synchronized void connect() {
|
||||
if(this.close) {
|
||||
return;
|
||||
}
|
||||
// 释放连接
|
||||
this.disconnect();
|
||||
// 开始连接
|
||||
Log.d(Taoyao.class.getSimpleName(), "连接信令:" + this.host + ":" + this.port);
|
||||
this.socket = new Socket();
|
||||
try {
|
||||
// socket.setSoTimeout(5000);
|
||||
this.socket.connect(new InetSocketAddress(this.host, this.port), 5000);
|
||||
// 设置读取超时时间:不要设置一直阻塞
|
||||
// socket.setSoTimeout(this.timeout);
|
||||
this.socket.connect(new InetSocketAddress(this.host, this.port), this.timeout);
|
||||
if (this.socket.isConnected()) {
|
||||
this.input = this.socket.getInputStream();
|
||||
this.output = this.socket.getOutputStream();
|
||||
this.register();
|
||||
this.connect = true;
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// HiLog.error(this.label, "连接信令异常:%s:%d", this.host, this.port);
|
||||
Log.e(Taoyao.class.getSimpleName(), "连接信令异常:" + this.host + ":" + this.port, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void heartbeat() {
|
||||
|
||||
}
|
||||
|
||||
private void read() {
|
||||
/**
|
||||
* 循环读取信令消息
|
||||
*/
|
||||
private void loopMessage() {
|
||||
int length = 0;
|
||||
short messageLength = 0;
|
||||
final byte[] bytes = new byte[1024];
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
|
||||
while (!this.close) {
|
||||
try {
|
||||
while (this.input == null) {
|
||||
// 重连
|
||||
while (!this.close && !this.connect) {
|
||||
this.connect();
|
||||
synchronized (this) {
|
||||
this.wait(5000);
|
||||
this.wait(this.timeout);
|
||||
}
|
||||
}
|
||||
// 读取
|
||||
while ((length = this.input.read(bytes)) >= 0) {
|
||||
buffer.put(bytes, 0, length);
|
||||
while (buffer.position() > 0) {
|
||||
@@ -195,6 +286,7 @@ public final class Taoyao {
|
||||
buffer.get(message);
|
||||
buffer.compact();
|
||||
final String content = new String(this.decrypt.doFinal(message));
|
||||
Log.d(Taoyao.class.getSimpleName(), "处理信令:" + content);
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
Taoyao.this.on(content);
|
||||
@@ -207,16 +299,15 @@ public final class Taoyao {
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Taoyao.class.getSimpleName(), "接收信令异常", e);
|
||||
this.connect();
|
||||
// TODO:日志
|
||||
// log.error("读取异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message 消息
|
||||
* @param message 原始消息
|
||||
*
|
||||
* @return 加密消息
|
||||
*/
|
||||
private byte[] encrypt(Message message) {
|
||||
@@ -225,41 +316,67 @@ public final class Taoyao {
|
||||
try {
|
||||
// 加密
|
||||
final byte[] encryptBytes = this.encrypt.doFinal(bytes);
|
||||
// 发送
|
||||
// 编码
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(Short.BYTES + encryptBytes.length);
|
||||
buffer.putShort((short) encryptBytes.length);
|
||||
buffer.put(encryptBytes);
|
||||
buffer.flip();
|
||||
final byte[] sendBytes = new byte[buffer.capacity()];
|
||||
buffer.get(sendBytes);
|
||||
return sendBytes;
|
||||
// 编码
|
||||
final byte[] encodingBytes = new byte[buffer.capacity()];
|
||||
buffer.get(encodingBytes);
|
||||
return encodingBytes;
|
||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
||||
e.printStackTrace();
|
||||
// log.error("加密异常:{}", message);
|
||||
Log.e(Taoyao.class.getSimpleName(), "加密异常:" + message, e);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message 信令消息
|
||||
*/
|
||||
public void push(Message message) {
|
||||
if (this.output == null) {
|
||||
Log.w(Taoyao.class.getSimpleName(), "通道没有打开:" + message);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.output.write(this.encrypt(message));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Taoyao.class.getSimpleName(), "请求信令异常:" + message, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 信令请求消息
|
||||
*
|
||||
* @return 信令响应消息
|
||||
*/
|
||||
public Message request(Message request) {
|
||||
return null;
|
||||
final Header header = request.getHeader();
|
||||
final Long id = header.getId();
|
||||
this.requestMessage.put(id, request);
|
||||
synchronized (request) {
|
||||
this.push(request);
|
||||
try {
|
||||
request.wait(this.timeout);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(Taoyao.class.getSimpleName(), "请求信令等待异常:" + request, e);
|
||||
}
|
||||
}
|
||||
final Message response = this.requestMessage.remove(id);
|
||||
if (response == null || request.equals(response)) {
|
||||
Log.w(Taoyao.class.getSimpleName(), "请求信令没有响应:" + request);
|
||||
throw MessageCodeException.of(MessageCode.CODE_2001, "请求信令没有响应");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放连接
|
||||
*/
|
||||
private void close() {
|
||||
private void disconnect() {
|
||||
Log.d(Taoyao.class.getSimpleName(), "释放信令:" + this.host + ":" + this.port);
|
||||
this.connect = false;
|
||||
CloseableUtils.close(this.input);
|
||||
CloseableUtils.close(this.output);
|
||||
@@ -272,8 +389,8 @@ public final class Taoyao {
|
||||
/**
|
||||
* 关闭信令
|
||||
*/
|
||||
private void shutdown() {
|
||||
this.close();
|
||||
private void close() {
|
||||
this.disconnect();
|
||||
this.close = true;
|
||||
executor.shutdownNow();
|
||||
scheduled.shutdownNow();
|
||||
@@ -285,7 +402,7 @@ public final class Taoyao {
|
||||
*
|
||||
* @return 消息
|
||||
*/
|
||||
public Message buildMessage(String signal, Object ... args) {
|
||||
public Message buildMessage(String signal, Object... args) {
|
||||
final Map<Object, Object> map = new HashMap<>();
|
||||
if (ArrayUtils.isNotEmpty(args)) {
|
||||
for (int index = 0; index < args.length; index += 2) {
|
||||
@@ -325,10 +442,21 @@ public final class Taoyao {
|
||||
if (header == null) {
|
||||
return;
|
||||
}
|
||||
final Map<String, Object> body = message.body();
|
||||
switch (header.getSignal()) {
|
||||
case "client::register" -> this.register(message, body);
|
||||
default -> Log.i(Taoyao.class.getSimpleName(), "没有适配信令:" + content);
|
||||
final Long id = header.getId();
|
||||
final Message request = this.requestMessage.get(id);
|
||||
if (request != null) {
|
||||
// 同步处理:重新设置响应消息
|
||||
this.requestMessage.put(id, message);
|
||||
// 唤醒等待线程
|
||||
synchronized (request) {
|
||||
request.notifyAll();
|
||||
}
|
||||
} else {
|
||||
final Map<String, Object> body = message.body();
|
||||
switch (header.getSignal()) {
|
||||
case "client::register" -> this.register(message, body);
|
||||
default -> Log.i(Taoyao.class.getSimpleName(), "没有适配信令:" + content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,18 +487,45 @@ public final class Taoyao {
|
||||
*/
|
||||
private void register(Message message, Map<String, Object> body) {
|
||||
final Integer index = (Integer) body.get("index");
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
IdUtils.setClientIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 心跳
|
||||
*/
|
||||
private void heartbeat() {
|
||||
while(!this.close) {
|
||||
final Location location = this.location();
|
||||
this.push(this.buildMessage(
|
||||
"client::heartbeat",
|
||||
"latitude", location == null ? -1 : location.getLatitude(),
|
||||
"longitude", location == null ? -1 : location.getLongitude(),
|
||||
"signal", this.wifiManager == null ? -1 : this.wifiManager.getMaxSignalLevel(),
|
||||
"batter", this.batteryManager == null ? -1 : this.batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY),
|
||||
"charging", this.batteryManager == null ? -1 : this.batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS),
|
||||
"recording", Recorder.getInstance().isActive()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 位置
|
||||
*/
|
||||
private Location location() {
|
||||
if(this.locationManager == null) {
|
||||
if (this.locationManager == null) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
ActivityCompat.checkSelfPermission(this.context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
|
||||
ActivityCompat.checkSelfPermission(this.context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
final Criteria criteria = new Criteria();
|
||||
// 耗电
|
||||
// 功耗
|
||||
criteria.setCostAllowed(false);
|
||||
// 不要海拔
|
||||
criteria.setAltitudeRequired(false);
|
||||
@@ -378,9 +533,13 @@ public final class Taoyao {
|
||||
criteria.setBearingRequired(false);
|
||||
// 精度
|
||||
criteria.setAccuracy(Criteria.ACCURACY_FINE);
|
||||
// 低功耗
|
||||
// 功耗
|
||||
criteria.setPowerRequirement(Criteria.POWER_LOW);
|
||||
final String provider = locationManager.getBestProvider(criteria, true);
|
||||
// 最佳提供者
|
||||
final String provider = this.locationManager.getBestProvider(criteria, true);
|
||||
if (provider == null) {
|
||||
return null;
|
||||
}
|
||||
return this.locationManager.getLastKnownLocation(provider);
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
@@ -2,18 +2,22 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.acgist.taoyao.client.MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sample_text"
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/settings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
android:contentDescription="@string/button_setting"
|
||||
android:src="@drawable/settings"
|
||||
app:borderWidth="16dp"
|
||||
app:fabSize="normal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,5 +1,121 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</LinearLayout>
|
||||
<EditText
|
||||
android:id="@+id/port"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="@string/signal_port"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="number"
|
||||
android:text="9999"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/host"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/signal_host"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="192.168.0.100"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/port"
|
||||
app:layout_constraintVertical_bias="0.02"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/name"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/signal_name"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="mobile"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/host"
|
||||
app:layout_constraintVertical_bias="0.02"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/clientId"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/signal_client_id"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="mobile"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/name"
|
||||
app:layout_constraintVertical_bias="0.02"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/signal_username"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:text="taoyao"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/clientId"
|
||||
app:layout_constraintVertical_bias="0.02"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:hint="@string/signal_password"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textPassword"
|
||||
android:text="taoyao"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/username"
|
||||
app:layout_constraintVertical_bias="0.02"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/connect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/button_connect"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/password"
|
||||
app:layout_constraintVertical_bias="0.02" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.Taoyao" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="reply_entries">
|
||||
<item>Reply</item>
|
||||
<item>Reply to all</item>
|
||||
</string-array>
|
||||
<string-array name="reply_values">
|
||||
<item>reply</item>
|
||||
<item>reply_all</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -1,10 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- 是否开启预览:关闭预览不会播放视频 -->
|
||||
<bool name="preview">true</bool>
|
||||
<string name="version">1.0.0</string>
|
||||
<integer name="timeout">5000</integer>
|
||||
<string name="clientType">MOBILE</string>
|
||||
<string name="algo">DES</string>
|
||||
<string name="secret">DES</string>
|
||||
</resources>
|
||||
@@ -1,16 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">桃夭</string>
|
||||
<string name="title_activity_settings">SettingsActivity</string>
|
||||
|
||||
<string name="messages_header">Messages</string>
|
||||
<string name="sync_header">Sync</string>
|
||||
|
||||
<string name="signature_title">Your signature</string>
|
||||
<string name="reply_title">Default reply action</string>
|
||||
|
||||
<string name="sync_title">Sync email periodically</string>
|
||||
<string name="attachment_title">Download incoming attachments</string>
|
||||
<string name="attachment_summary_on">Automatically download attachments for incoming emails</string>
|
||||
<string name="attachment_summary_off">Only download attachments when manually requested</string>
|
||||
<string name="title_activity_main">桃夭终端预览</string>
|
||||
<string name="title_activity_settings">桃夭终端设置</string>
|
||||
<string name="button_setting">设置</string>
|
||||
<string name="button_connect">连接</string>
|
||||
<string name="signal_port">信令端口</string>
|
||||
<string name="signal_host">信令地址</string>
|
||||
<string name="signal_name">终端名称</string>
|
||||
<string name="signal_client_id">终端标识</string>
|
||||
<string name="signal_username">信令帐号</string>
|
||||
<string name="signal_password">信令密码</string>
|
||||
</resources>
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="Theme.Taoyao" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,3 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<full-backup-content></full-backup-content>
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<data-extraction-rules>
|
||||
<cloud-backup></cloud-backup>
|
||||
</data-extraction-rules>
|
||||
@@ -25,8 +25,10 @@ set(
|
||||
|
||||
set(
|
||||
SOURCE_FILES
|
||||
${SOURCE_DIR}/main.hpp
|
||||
${SOURCE_DIR}/main.cpp
|
||||
${SOURCE_DIR}/rtp.hpp
|
||||
${SOURCE_DIR}/rtp.cpp
|
||||
${SOURCE_DIR}/webrtc.hpp
|
||||
${SOURCE_DIR}/webrtc.cpp
|
||||
)
|
||||
|
||||
set(LIBWEBRTC_BINARY_PATH ${LIBWEBRTC_BINARY_PATH}/${ANDROID_ABI} CACHE STRING "libwebrtc binary path" FORCE)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
|
||||
#include "Device.hpp"
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_com_acgist_taoyao_client_MainActivity_stringFromJNI(
|
||||
JNIEnv *env,
|
||||
jobject /* this */
|
||||
) {
|
||||
mediasoupclient::Device device;
|
||||
if (device.IsLoaded()) {
|
||||
std::string hello = "Hello from C++ true";
|
||||
return env->NewStringUTF(hello.c_str());
|
||||
} else {
|
||||
std::string hello = "Hello from C++ false";
|
||||
return env->NewStringUTF(hello.c_str());
|
||||
}
|
||||
}
|
||||
1
taoyao-client-android/taoyao/media/src/main/cpp/rtp.hpp
Normal file
1
taoyao-client-android/taoyao/media/src/main/cpp/rtp.hpp
Normal file
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
@@ -0,0 +1 @@
|
||||
#include <jni.h>
|
||||
@@ -15,6 +15,7 @@ public enum ClientType {
|
||||
WEB("Web"),
|
||||
MEDIA("媒体服务"),
|
||||
CAMERA("摄像头"),
|
||||
MOBILE("移动端"),
|
||||
OTHER("其他终端");
|
||||
|
||||
/**
|
||||
@@ -26,32 +27,11 @@ public enum ClientType {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是Web
|
||||
*/
|
||||
public boolean web() {
|
||||
return this == WEB;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是媒体服务
|
||||
*/
|
||||
public boolean media() {
|
||||
return this == MEDIA;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是摄像头
|
||||
*/
|
||||
public boolean camera() {
|
||||
return this == CAMERA;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 是否是媒体终端
|
||||
*/
|
||||
public boolean mediaClient() {
|
||||
return this == WEB || this == CAMERA;
|
||||
return this == WEB || this == CAMERA || this == MOBILE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,7 +57,7 @@ public class RoomCreateProtocol extends ProtocolClientAdapter implements Applica
|
||||
|
||||
@Override
|
||||
public void execute(String clientId, ClientType clientType, Client client, Message message, Map<String, Object> body) {
|
||||
if(clientType.web()) {
|
||||
if(clientType.mediaClient()) {
|
||||
// WEB同步创建房间
|
||||
final Room room = this.roomManager.create(
|
||||
MapUtils.get(body, Constant.NAME),
|
||||
|
||||
Reference in New Issue
Block a user