package com.ttstd.controlled; import android.Manifest; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.media.projection.MediaProjectionManager; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.ttstd.controlled.service.ScreenCaptureService; import com.ttstd.controlled.webrtc.WebRtcClient; import org.webrtc.RendererCommon; import org.webrtc.SurfaceViewRenderer; public class MainActivity extends AppCompatActivity { private static final String TAG = "ControlledMain"; private static final int REQUEST_MEDIA_PROJECTION = 1001; private static final int REQUEST_NOTIFICATION_PERMISSION = 1002; private EditText etServerUrl; private EditText etDeviceId; private Button btnStart; private Button btnStop; private TextView tvStatus; private SurfaceViewRenderer localView; private boolean isServiceRunning = false; private ScreenCaptureService screenCaptureService; private boolean isBound = false; private boolean isLocalViewInitialized = false; private final ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ScreenCaptureService.LocalBinder binder = (ScreenCaptureService.LocalBinder) service; screenCaptureService = binder.getService(); isBound = true; setupLocalPreview(); } @Override public void onServiceDisconnected(ComponentName name) { isBound = false; screenCaptureService = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etServerUrl = findViewById(R.id.et_server_url); etDeviceId = findViewById(R.id.et_device_id); btnStart = findViewById(R.id.btn_start); btnStop = findViewById(R.id.btn_stop); tvStatus = findViewById(R.id.tv_status); localView = findViewById(R.id.local_view); // 默认服务器地址 etServerUrl.setText("ws://192.168.100.222:8080/ws/signal"); // 生成设备ID // etDeviceId.setText("controlled_" + UUID.randomUUID().toString().substring(0, 8)); btnStart.setOnClickListener(v -> startScreenSharing()); btnStop.setOnClickListener(v -> stopScreenSharing()); updateUI(false); } private void startScreenSharing() { // 检查通知权限 (Android 13+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_NOTIFICATION_PERMISSION); return; } } // 请求屏幕录制权限 MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); startActivityForResult(projectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_MEDIA_PROJECTION) { if (resultCode == RESULT_OK && data != null) { Intent serviceIntent = new Intent(this, ScreenCaptureService.class); serviceIntent.putExtra(ScreenCaptureService.EXTRA_RESULT_CODE, resultCode); serviceIntent.putExtra(ScreenCaptureService.EXTRA_RESULT_DATA, data); serviceIntent.putExtra(ScreenCaptureService.EXTRA_SERVER_URL, etServerUrl.getText().toString()); serviceIntent.putExtra(ScreenCaptureService.EXTRA_DEVICE_ID, etDeviceId.getText().toString()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE); updateUI(true); Toast.makeText(this, "屏幕共享已启动", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "屏幕录制权限被拒绝", Toast.LENGTH_SHORT).show(); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_NOTIFICATION_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startScreenSharing(); } else { Toast.makeText(this, "需要通知权限才能运行", Toast.LENGTH_SHORT).show(); } } } private void stopScreenSharing() { if (isBound) { unbindService(serviceConnection); isBound = false; } Intent serviceIntent = new Intent(this, ScreenCaptureService.class); stopService(serviceIntent); if (localView != null) { localView.release(); isLocalViewInitialized = false; } updateUI(false); Toast.makeText(this, "屏幕共享已停止", Toast.LENGTH_SHORT).show(); } private void setupLocalPreview() { if (isBound && screenCaptureService != null && !isLocalViewInitialized) { WebRtcClient webRtcClient = screenCaptureService.getWebRtcClient(); if (webRtcClient != null && webRtcClient.getEglContext() != null) { try { localView.init(webRtcClient.getEglContext(), null); localView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); localView.setMirror(false); webRtcClient.setLocalSink(localView); isLocalViewInitialized = true; } catch (Exception e) { Log.e(TAG, "Error initializing local view", e); } } else { // 如果 webRtcClient 还没准备好,稍后重试 localView.postDelayed(this::setupLocalPreview, 500); } } } @Override protected void onDestroy() { super.onDestroy(); if (isBound) { unbindService(serviceConnection); isBound = false; } if (localView != null) { localView.release(); isLocalViewInitialized = false; } } private void updateUI(boolean running) { isServiceRunning = running; btnStart.setEnabled(!running); btnStop.setEnabled(running); etServerUrl.setEnabled(!running); etDeviceId.setEnabled(!running); tvStatus.setText(running ? "状态: 运行中" : "状态: 已停止"); } }