build: 更新项目至studio panda

This commit is contained in:
2026-06-02 10:59:18 +08:00
parent 9e602a6cd1
commit 3e0e9f3259
10 changed files with 730 additions and 39 deletions

View File

@@ -29,6 +29,10 @@ android {
enabled true enabled true
} }
viewBinding {
enabled true
}
javaCompileOptions { javaCompileOptions {
annotationProcessorOptions { annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()] arguments = [AROUTER_MODULE_NAME: project.getName()]
@@ -101,7 +105,7 @@ dependencies {
implementation 'com.jeremyliao:live-event-bus-x:1.7.3' implementation 'com.jeremyliao:live-event-bus-x:1.7.3'
implementation 'org.webrtc:google-webrtc:1.0.32006' implementation 'org.webrtc:google-webrtc:1.0.32006'
implementation 'org.java-websocket:Java-WebSocket:1.5.3' // implementation 'org.java-websocket:Java-WebSocket:1.5.3'
//MMKV //MMKV
implementation 'com.tencent:mmkv-static:1.2.14' implementation 'com.tencent:mmkv-static:1.2.14'

View File

@@ -14,7 +14,9 @@ import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
import android.view.TextureView; import android.view.TextureView;
import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -23,7 +25,11 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.ttstd.remoteservice.R; import com.ttstd.remoteservice.R;
import com.ttstd.remoteservice.network.WebSocketCallback;
import com.ttstd.remoteservice.network.WebSocketManager;
import com.ttstd.remoteservice.service.ScreenCaptureService2; import com.ttstd.remoteservice.service.ScreenCaptureService2;
import org.webrtc.CapturerObserver; import org.webrtc.CapturerObserver;
@@ -46,7 +52,9 @@ import org.webrtc.VideoTrack;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class WebRTCScreenCaptureActivity extends AppCompatActivity { import okio.ByteString;
public class WebRTCScreenCaptureActivity extends AppCompatActivity implements WebSocketCallback {
private static final String TAG = "WebRTCScreenCaptureActivity"; private static final String TAG = "WebRTCScreenCaptureActivity";
// 请求码 // 请求码
private static final int REQUEST_CODE_SCREEN_CAPTURE = 1001; private static final int REQUEST_CODE_SCREEN_CAPTURE = 1001;
@@ -71,7 +79,8 @@ public class WebRTCScreenCaptureActivity extends AppCompatActivity {
private int screenDpi; private int screenDpi;
// UI控件 // UI控件
private Button btnStartPush; private TextView tvStatus;
private Button btConnect, btnStartPush;
private SurfaceViewRenderer localRender; // 本地预览(可选) private SurfaceViewRenderer localRender; // 本地预览(可选)
private TextureView textureView; private TextureView textureView;
@@ -82,16 +91,27 @@ public class WebRTCScreenCaptureActivity extends AppCompatActivity {
private String remoteSdp = ""; // 替换为远端实际SDP private String remoteSdp = ""; // 替换为远端实际SDP
private List<IceCandidate> remoteIceCandidates = new ArrayList<>(); private List<IceCandidate> remoteIceCandidates = new ArrayList<>();
private WebSocketManager webSocketManager;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webrtc_screen_capture); setContentView(R.layout.activity_webrtc_screen_capture);
// 初始化控件 // 初始化控件
tvStatus = findViewById(R.id.tv_status);
btConnect = findViewById(R.id.bt_connect);
btnStartPush = findViewById(R.id.btn_start_push); btnStartPush = findViewById(R.id.btn_start_push);
localRender = findViewById(R.id.local_render); localRender = findViewById(R.id.local_render);
textureView = findViewById(R.id.textureView); textureView = findViewById(R.id.textureView);
btConnect.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
initWebSocket();
}
});
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override @Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
@@ -173,6 +193,15 @@ public class WebRTCScreenCaptureActivity extends AppCompatActivity {
startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE); startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE);
} }
private void initWebSocket() {
Log.e(TAG, "initWebSocket: ");
// 初始化 WebSocket 管理器
webSocketManager = WebSocketManager.getInstance();
// String wsUrl = "ws://175.178.213.60:2310/signaling/981964879";
String wsUrl = "ws://192.168.100.111:2310/signaling/981964879";
webSocketManager.init(wsUrl, this);
}
/** /**
* 初始化WebRTC核心组件 * 初始化WebRTC核心组件
*/ */
@@ -368,8 +397,139 @@ public class WebRTCScreenCaptureActivity extends AppCompatActivity {
return iceServers; return iceServers;
} }
/**
* 发送SDP给远端信令服务器
*/
private void sendSdpToRemote(SessionDescription sdp) { private void sendSdpToRemote(SessionDescription sdp) {
// TODO: 实现信令发送逻辑例如通过WebSocket发送SDP字符串 JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("msg_type", 1);
JsonObject sdpJson = new JsonObject();
sdpJson.addProperty("type", sdp.type.canonicalForm()); // "offer" 或 "answer"
sdpJson.addProperty("sdp", sdp.description);
// TODO: 2026/3/11 之后改为唯一标识码
sdpJson.addProperty("fromUser", "981964879"); // 标识发送方
sdpJson.addProperty("timestamp", System.currentTimeMillis());
jsonObject.add("content", sdpJson);
String json = jsonObject.toString();
Log.d(TAG, "发送SDP到信令服务器: ");
Log.e(TAG, "sendSdpToRemote: SDP内容: " + json);
webSocketManager.sendMessage(json);
}
/**
* 发送ICE Candidate给远端信令服务器
*/
private void sendIceCandidateToRemote(IceCandidate iceCandidate) {
Log.d(TAG, "Send ICE Candidate to Remote: " + iceCandidate.sdpMid + " " + iceCandidate.sdpMLineIndex + " " + iceCandidate.sdp);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("msg_type", 2);
JsonObject iceJson = new JsonObject();
iceJson.addProperty("sdpMid", iceCandidate.sdpMid);
iceJson.addProperty("sdpMLineIndex", iceCandidate.sdpMLineIndex);
iceJson.addProperty("sdp", iceCandidate.sdp);
// TODO: 2026/3/11 之后改为唯一标识码
iceJson.addProperty("fromUser", "981964879");
iceJson.addProperty("timestamp", System.currentTimeMillis());
jsonObject.add("content", iceJson);
String json = jsonObject.toString();
Log.d(TAG, "发送ICE Candidate到信令服务器: ");
Log.e(TAG, "sendIceCandidateToRemote: ICE内容: " + json);
webSocketManager.sendMessage(json);
}
/**
* 处理信令服务器响应接收远端的Answer SDP
*/
private void handleSignalingResponse(String response) {
JsonObject jsonResponse = JsonParser.parseString(response).getAsJsonObject();
;
// 检查是否包含远端的Answer SDP
if (jsonResponse.has("type") && "answer".equals(jsonResponse.get("type").getAsString())) {
String remoteSdp = jsonResponse.get("sdp").getAsString();
SessionDescription remoteSessionDescription =
new SessionDescription(SessionDescription.Type.ANSWER, remoteSdp);
// 设置远端SDP
peerConnection.setRemoteDescription(new SimpleSdpObserver(), remoteSessionDescription);
Log.d(TAG, "已设置远端Answer SDP");
}
// 检查是否包含远端的ICE Candidate
if (jsonResponse.has("candidate")) {
String sdpMid = jsonResponse.get("sdpMid").getAsString();
int sdpMLineIndex = jsonResponse.get("sdpMLineIndex").getAsInt();
String candidate = jsonResponse.get("candidate").getAsString();
IceCandidate remoteIceCandidate = new IceCandidate(sdpMid, sdpMLineIndex, candidate);
peerConnection.addIceCandidate(remoteIceCandidate);
Log.d(TAG, "已添加远端ICE Candidate");
}
}
@Override
public void onConnected() {
Log.e(TAG, "onConnected: ");
tvStatus.setText("websocket已连接");
btConnect.setEnabled(false);
}
@Override
public void onDisconnected(String reason) {
Log.e(TAG, "onDisconnected: " + reason);
tvStatus.setText("websocket已断开:" + reason);
btConnect.setEnabled(true);
}
@Override
public void onMessage(String message) {
Log.e(TAG, "onMessage: " + message);
}
@Override
public void onMessage(ByteString bytes) {
Log.e(TAG, "onMessage: ");
}
@Override
public void onError(String error) {
Log.e(TAG, "onError: " + error);
tvStatus.setText("websocket错误" + error);
btConnect.setEnabled(true);
}
/**
* 简化的SdpObserver实现
*/
private static class SimpleSdpObserver implements SdpObserver {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
}
@Override
public void onSetSuccess() {
Log.d(TAG, "SDP设置成功");
}
@Override
public void onCreateFailure(String error) {
Log.e(TAG, "创建SDP失败: " + error);
}
@Override
public void onSetFailure(String error) {
Log.e(TAG, "设置SDP失败: " + error);
}
} }
/** /**
@@ -476,15 +636,6 @@ public class WebRTCScreenCaptureActivity extends AppCompatActivity {
} }
} }
/**
* 发送ICE候选到远端示例实际需通过WebSocket/HTTP信令服务器
*/
private void sendIceCandidateToRemote(IceCandidate iceCandidate) {
// 这里仅做日志打印,实际需替换为信令发送逻辑
Log.d(TAG, "Send ICE Candidate to Remote: " + iceCandidate.sdpMid + " " + iceCandidate.sdpMLineIndex + " " + iceCandidate.sdp);
// 示例将iceCandidate转为JSON发送到远端服务器
}
/** /**
* 处理权限请求结果 * 处理权限请求结果
*/ */
@@ -606,6 +757,10 @@ public class WebRTCScreenCaptureActivity extends AppCompatActivity {
// rootEglBase.release(); // rootEglBase.release();
// } // }
if (webSocketManager != null) {
webSocketManager.disconnect();
}
// 重置按钮 // 重置按钮
btnStartPush.setText("开始推流"); btnStartPush.setText("开始推流");
btnStartPush.setOnClickListener(v -> checkPermissionsAndStart()); btnStartPush.setOnClickListener(v -> checkPermissionsAndStart());

View File

@@ -24,7 +24,6 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
private static final int REQUEST_CODE_SCREEN_CAPTURE = 7897; private static final int REQUEST_CODE_SCREEN_CAPTURE = 7897;
private MediaProjectionManager mMediaProjectionManager; private MediaProjectionManager mMediaProjectionManager;
@Override @Override
public boolean setNightMode() { public boolean setNightMode() {
return true; return true;

View File

@@ -0,0 +1,18 @@
package com.ttstd.remoteservice.config;
/**
* WebSocket 配置类
* 用于配置WebSocket连接的各种参数
*/
public class WebSocketConfig {
public String wsUrl; // WebSocket服务器地址
public static final long heartbeatInterval = 30000; // 心跳间隔默认10秒
public static final long reconnectInterval = 5000; // 重连间隔默认5秒
public static final int maxReconnectAttempts = 5; // 最大重连次数
public static final boolean autoReconnect = true; // 是否自动重连
public static final boolean needHeartbeat = true; // 是否需要心跳检测
public static final long connectTimeout = 10; // 连接超时时间(秒)
public static final long readTimeout = 10; // 读取超时时间(秒)
public static final long writeTimeout = 10; // 写入超时时间(秒)
}

View File

@@ -0,0 +1,42 @@
package com.ttstd.remoteservice.network;
import okio.ByteString;
/**
* WebSocket 回调接口
* 所有回调都在主线程执行,方便更新 UI
*/
public interface WebSocketCallback {
/**
* 连接成功
*/
void onConnected();
/**
* 连接断开
*
* @param reason 断开原因
*/
void onDisconnected(String reason);
/**
* 收到文本消息
*
* @param message 文本内容
*/
void onMessage(String message);
/**
* 收到二进制消息
*
* @param bytes 二进制数据
*/
void onMessage(ByteString bytes);
/**
* 连接发生错误
*
* @param error 错误信息
*/
void onError(String error);
}

View File

@@ -0,0 +1,413 @@
package com.ttstd.remoteservice.network;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.gson.JsonObject;
import com.ttstd.remoteservice.config.WebSocketConfig;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
/**
* WebSocket 管理类(单例模式)
* 功能:自动重连、心跳检测、连接状态管理
*/
public class WebSocketManager {
private static final String TAG = "WebSocketManager";
private static volatile WebSocketManager instance;
private OkHttpClient client;
private WebSocket webSocket;
private WebSocketCallback callback;
private String wsUrl;
// 连接状态
private boolean isConnected = false;
private boolean shouldReconnect = true;
private Handler mainHandler = new Handler(Looper.getMainLooper());
private Runnable reconnectRunnable;
private Runnable heartbeatRunnable;
// 重连相关
private int reconnectAttempts = 0;
// 消息队列(线程安全)
private ConcurrentLinkedQueue<String> messageQueue = new ConcurrentLinkedQueue<>();
// 连接状态锁
private final Object connectionLock = new Object();
private WebSocketManager() {
// 私有构造函数
}
public static WebSocketManager getInstance() {
if (instance == null) {
synchronized (WebSocketManager.class) {
if (instance == null) {
instance = new WebSocketManager();
}
}
}
return instance;
}
/**
* 初始化 WebSocket 连接
*/
public void init(String url, WebSocketCallback callback) {
this.wsUrl = url;
this.callback = callback;
client = new OkHttpClient.Builder()
.connectTimeout(WebSocketConfig.connectTimeout, TimeUnit.SECONDS)
.readTimeout(WebSocketConfig.readTimeout, TimeUnit.SECONDS)
.writeTimeout(WebSocketConfig.writeTimeout, TimeUnit.SECONDS)
.pingInterval(10, TimeUnit.SECONDS) // 设置心跳
.retryOnConnectionFailure(true)
.build();
connect();
}
/**
* 建立 WebSocket 连接
*/
private void connect() {
if (wsUrl == null) {
Log.e(TAG, "WebSocket URL 不能为 null");
return;
}
if (TextUtils.isEmpty(wsUrl)) {
Log.e(TAG, "WebSocketConfig or URL is null");
return;
}
try {
Request request = new Request.Builder()
.url(wsUrl)
.build();
webSocket = client.newWebSocket(request, new InnerWebSocketListener());
Log.d(TAG, "Connecting to WebSocket: " + wsUrl);
} catch (Exception e) {
Log.e(TAG, "Connect failed: " + e.getMessage());
notifyError("连接失败: " + e.getMessage());
}
}
/**
* 发送文本消息
*/
public boolean sendMessage(String message) {
if (webSocket != null && isConnected) {
boolean sent = webSocket.send(message);
if (sent) {
Log.d(TAG, "Message sent: " + message);
} else {
// 发送失败,加入队列
messageQueue.offer(message);
Log.w(TAG, "消息发送失败,已加入队列: " + message);
}
return sent;
} else {
// 未连接,加入队列等待重发
messageQueue.offer(message);
Log.e(TAG, "WebSocket not connected, cannot send message");
notifyError("WebSocket未连接无法发送消息");
return false;
}
}
/**
* 发送二进制消息
*/
public boolean sendMessage(ByteString bytes) {
if (webSocket != null && isConnected) {
return webSocket.send(bytes);
} else {
Log.e(TAG, "WebSocket not connected, cannot send binary message");
return false;
}
}
/**
* 关闭 WebSocket 连接
*/
public void disconnect() {
Log.d(TAG, "Disconnecting WebSocket");
shouldReconnect = false;
// 停止重连和心跳
stopReconnect();
stopHeartbeat();
if (webSocket != null) {
webSocket.close(1000, "正常关闭");
webSocket = null;
}
isConnected = false;
Log.d(TAG, "WebSocket 连接已关闭");
reconnectAttempts = 0;
}
/**
* 获取连接状态
*/
public boolean isConnected() {
return isConnected;
}
/**
* 开始心跳检测
*/
private void startHeartbeat() {
stopHeartbeat();
heartbeatRunnable = new Runnable() {
@Override
public void run() {
if (isConnected && webSocket != null) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("msg_type", 0);
jsonObject.addProperty("content", "ping");
// 发送心跳消息(可以是空消息或特定协议)
boolean sent = webSocket.send(jsonObject.toString());
Log.d(TAG, "发送心跳包");
if (sent) {
// 继续下一次心跳
mainHandler.postDelayed(this, WebSocketConfig.heartbeatInterval);
} else {
// 发送失败,尝试重连
handleDisconnect("Heartbeat failed");
}
}
}
};
mainHandler.postDelayed(heartbeatRunnable, WebSocketConfig.heartbeatInterval);
Log.d(TAG, "Heartbeat started");
}
/**
* 处理断开连接
*/
private void handleDisconnect(String reason) {
Log.d(TAG, "Handle disconnect: " + reason);
isConnected = false;
stopHeartbeat();
notifyDisconnected(reason);
// 自动重连逻辑
if (WebSocketConfig.autoReconnect && reconnectAttempts < WebSocketConfig.maxReconnectAttempts) {
scheduleReconnect();
}
}
/**
* 安排重连
*/
private void scheduleReconnect() {
if (!shouldReconnect) return;
reconnectAttempts++;
Log.d(TAG, "Scheduling reconnect attempt " + reconnectAttempts + " after " + WebSocketConfig.reconnectInterval + "ms");
reconnectRunnable = new Runnable() {
@Override
public void run() {
Log.d(TAG, "尝试重连 WebSocket");
connect();
}
};
mainHandler.postDelayed(reconnectRunnable, WebSocketConfig.heartbeatInterval);
}
/**
* 停止重连
*/
private void stopReconnect() {
if (heartbeatRunnable != null) {
mainHandler.removeCallbacks(heartbeatRunnable);
heartbeatRunnable = null;
}
reconnectAttempts = 0;
}
/**
* 停止心跳检测
*/
private void stopHeartbeat() {
if (heartbeatRunnable != null) {
mainHandler.removeCallbacks(heartbeatRunnable);
heartbeatRunnable = null;
}
reconnectAttempts = 0;
}
/**
* 内部WebSocket监听器
*/
private class InnerWebSocketListener extends WebSocketListener {
@Override
public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) {
Log.d(TAG, "WebSocket connected successfully");
isConnected = true;
reconnectAttempts = 0; // 重置重连计数器
// 连接成功,发送队列中的积压消息
processMessageQueue();
// 在主线程通知连接成功
mainHandler.post(() -> {
if (callback != null) {
callback.onConnected();
}
});
// 开始心跳检测
startHeartbeat();
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
Log.d(TAG, "Received text message: " + text);
// 在主线程通知消息接收
mainHandler.post(() -> {
if (callback != null) {
callback.onMessage(text);
}
});
}
@Override
public void onMessage(@NonNull WebSocket webSocket, @NonNull ByteString bytes) {
Log.d(TAG, "Received binary message, size: " + bytes.size());
// 在主线程通知消息接收
mainHandler.post(() -> {
if (callback != null) {
callback.onMessage(bytes);
}
});
}
@Override
public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
Log.d(TAG, "WebSocket closing: " + code + " - " + reason);
webSocket.close(1000, null);
}
@Override
public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
Log.d(TAG, "WebSocket closed: " + code + " - " + reason);
handleDisconnect("Connection closed: " + reason);
}
@Override
public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) {
Log.e(TAG, "WebSocket failure: " + t.getMessage());
handleDisconnect("Connection failure: " + t.getMessage());
}
}
/**
* 处理消息队列(发送所有积压消息)
*/
private void processMessageQueue() {
new Thread(() -> {
synchronized (connectionLock) {
while (!messageQueue.isEmpty()) {
String message = messageQueue.poll();
if (message != null && isConnected && webSocket != null) {
boolean sent = webSocket.send(message);
if (sent) {
Log.d(TAG, "队列消息发送成功: " + message);
} else {
// 发送失败,重新放回队列头部
messageQueue.offer(message);
Log.e(TAG, "队列消息发送失败,已重新排队: " + message);
break; // 暂停发送,避免连续失败
}
// 添加短暂延迟,避免洪水攻击服务器
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}).start();
}
/**
* 获取队列大小(用于监控)
*/
public int getQueueSize() {
return messageQueue.size();
}
/**
* 清空消息队列
*/
public void clearQueue() {
messageQueue.clear();
Log.d(TAG, "消息队列已清空");
}
/**
* 通知错误(主线程)
*/
private void notifyError(String error) {
mainHandler.post(() -> {
if (callback != null) {
callback.onError(error);
}
});
}
/**
* 通知断开连接(主线程)
*/
private void notifyDisconnected(String reason) {
mainHandler.post(() -> {
if (callback != null) {
callback.onDisconnected(reason);
}
});
}
/**
* 清理资源
*/
public void release() {
disconnect();
if (client != null) {
client.dispatcher().executorService().shutdown();
}
instance = null;
Log.d(TAG, "WebSocketManager released");
}
}

View File

@@ -1,12 +1,41 @@
package com.ttstd.remoteservice.utils; package com.ttstd.remoteservice.utils;
import android.annotation.SuppressLint;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.Method;
import java.util.List; import java.util.List;
public class SystemUtils { public class SystemUtils {
/**
* 获取设备序列号
*
* @return
*/
@SuppressLint("MissingPermission")
public static String getSerial() {
String serial = "unknow";
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+
serial = Build.getSerial();
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
serial = Build.SERIAL;
} else {//8.0-
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
}
} catch (Exception e) {
e.printStackTrace();
Log.e("e", "读取设备序列号异常:" + e.toString());
}
return serial;
}
public static boolean isMainProcessName(Context cxt, int pid) { public static boolean isMainProcessName(Context cxt, int pid) {
String packageName = cxt.getPackageName(); String packageName = cxt.getPackageName();
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);

View File

@@ -1,9 +1,39 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <layout 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"
tools:context=".activity.WebRTCScreenCaptureActivity">
<data>
</data>
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<TextView
android:id="@+id/tv_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="等待连接..."
android:textColor="#000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/bt_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="连接websocket"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button <Button
android:id="@+id/btn_start_push" android:id="@+id/btn_start_push"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -24,3 +54,4 @@
android:layout_weight="1" /> android:layout_weight="1" />
</LinearLayout> </LinearLayout>
</layout>

View File

@@ -13,7 +13,7 @@ buildscript {
maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/google' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.4' classpath 'com.android.tools.build:gradle:8.13.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip