yichael hace 5 meses
padre
commit
bffaaa44d6

+ 19 - 0
src/pages/0-ENABLE-TCPIP.BAT

@@ -0,0 +1,19 @@
+@echo off
+echo ========================================
+echo Enable ADB TCP/IP Mode (Port 5555)
+echo ========================================
+echo.
+
+echo [1/2] List connected devices...
+adb devices
+echo.
+
+echo [2/2] Enable TCP/IP mode...
+adb tcpip 5555
+echo.
+
+echo ========================================
+echo Done
+echo ========================================
+echo.
+pause

+ 3 - 0
src/pages/0-START.BAT

@@ -0,0 +1,3 @@
+@echo off
+cd /d "%~dp0\..\.."
+npm run electron:dev

+ 91 - 0
src/pages/0-TEST-CONNECT.BAT

@@ -0,0 +1,91 @@
+@echo off
+chcp 65001 >nul
+echo ========================================
+echo Test ADB Connection 192.168.2.5:5555
+echo ========================================
+echo.
+
+echo [1/5] Check if ADB is available...
+adb version >nul 2>&1
+if errorlevel 1 (
+    echo Error: ADB not installed or not in PATH
+    echo Please ensure Android SDK Platform Tools is installed
+    pause
+    exit /b 1
+)
+echo ADB is available
+echo.
+
+echo [2/5] Test network connectivity (ping 192.168.2.5)...
+ping -n 2 192.168.2.5 >nul 2>&1
+if errorlevel 1 (
+    echo Cannot ping 192.168.2.5
+    echo   Possible reasons: Phone and computer not on same network, or phone is off
+    echo.
+) else (
+    echo Network connectivity OK
+    echo.
+)
+
+echo [3/5] Check if port 5555 is open...
+powershell -Command "Test-NetConnection -ComputerName 192.168.2.5 -Port 5555 -InformationLevel Quiet" >nul 2>&1
+if errorlevel 1 (
+    echo Port 5555 may not be open or blocked by firewall
+    echo.
+) else (
+    echo Port 5555 is accessible
+    echo.
+)
+
+echo [4/5] Disconnect old connection (if any)...
+adb disconnect 192.168.2.5:5555 >nul 2>&1
+timeout /t 1 /nobreak >nul
+
+echo Attempting to connect 192.168.2.5:5555...
+adb connect 192.168.2.5:5555
+set CONNECT_RESULT=%ERRORLEVEL%
+
+echo.
+if %CONNECT_RESULT% equ 0 (
+    echo Connection command completed
+    echo.
+    timeout /t 2 /nobreak >nul
+) else (
+    echo.
+    echo Connection failed (Error code: %CONNECT_RESULT%)
+    echo.
+    echo ========================================
+    echo Troubleshooting Steps:
+    echo ========================================
+    echo 1. Enable "Developer Options" on phone
+    echo    - Settings ^> About phone ^> Tap "Build number" 7 times
+    echo.
+    echo 2. Enable "USB debugging"
+    echo    - Settings ^> Developer options ^> USB debugging
+    echo.
+    echo 3. Enable "Wireless debugging" or "Network ADB debugging"
+    echo    - Settings ^> Developer options ^> Wireless debugging
+    echo    - Or: Settings ^> Developer options ^> Network ADB debugging
+    echo    - Confirm port is 5555
+    echo.
+    echo 4. If phone doesn't have "Wireless debugging" option, try:
+    echo    a) Connect phone to computer via USB
+    echo    b) Run: adb tcpip 5555
+    echo    c) Unplug USB, then run this script again
+    echo.
+    echo 5. Check if phone and computer are on same Wi-Fi network
+    echo.
+    echo 6. Disable phone firewall or allow ADB through firewall
+    echo.
+)
+
+echo [5/5] Check device list...
+echo.
+adb devices
+echo.
+
+echo ========================================
+echo Test completed
+echo ========================================
+pause
+

+ 276 - 0
src/pages/Devices/Devices.css

@@ -0,0 +1,276 @@
+/* Devices 页面样式:极简风格,响应式设计 */
+
+/* 主容器 */
+.Devices-container {
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  background: #f8f9fa;
+  padding: clamp(0.75rem, 2vw, 1.5rem);
+}
+
+/* 头部区域 */
+.devices-header {
+  width: 100%;
+  box-sizing: border-box;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  gap: clamp(0.5rem, 1.5vw, 1rem);
+  margin-bottom: clamp(0.75rem, 2vw, 1.5rem);
+  padding: clamp(0.5rem, 1.5vw, 1rem);
+  border: 1px solid #e5e7eb;
+  border-radius: clamp(6px, 1vw, 8px);
+  background: #ffffff;
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+  min-height: clamp(40px, 6vw, 48px);
+}
+
+/* 标题 */
+.devices-title {
+  font-size: clamp(1rem, 2.5vw, 1.8rem);
+  font-weight: 600;
+  padding: 0;
+  color: #1a1a1a;
+  letter-spacing: -0.5px;
+  line-height: 1.2;
+  display: flex;
+  align-items: center;
+}
+
+/* 刷新按钮 */
+.refresh-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: clamp(0.25rem, 1vw, 0.5rem);
+  padding: clamp(0.5rem, 1.5vw, 0.625rem) clamp(0.75rem, 2vw, 1.25rem);
+  background: #1a1a1a;
+  color: white;
+  border: none;
+  border-radius: clamp(6px, 1vw, 8px);
+  cursor: pointer;
+  font-size: clamp(0.75rem, 1.5vw, 1rem);
+  font-weight: 500;
+  transition: all 0.2s ease;
+  line-height: 1.2;
+  user-select: none;
+  white-space: nowrap;
+}
+
+.refresh-btn:hover:not(.disabled) {
+  background: #333;
+  transform: translateY(-1px);
+}
+
+.refresh-btn:active:not(.disabled) {
+  transform: translateY(0);
+}
+
+.refresh-btn.disabled {
+  opacity: 0.4;
+  cursor: not-allowed;
+  background: #ccc;
+}
+
+.refresh-btn.disabled:hover {
+  transform: none;
+}
+
+/* 刷新图标 */
+.refresh-icon {
+  font-size: 1em;
+  transition: transform 0.3s ease;
+  display: inline-block;
+}
+
+/* 悬停时图标旋转 */
+.refresh-btn:hover .refresh-icon:not(.spinning) {
+  transform: rotate(180deg);
+}
+
+/* 扫描中图标持续旋转 */
+.refresh-icon.spinning {
+  animation: spin 1s linear infinite;
+}
+
+/* 旋转动画 */
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+/* 滚动区域 */
+.devices-scroll-area {
+  flex: 1;
+  overflow-y: auto;
+  overflow-x: hidden;
+  padding: 0;
+}
+
+/* 自定义滚动条 */
+.devices-scroll-area::-webkit-scrollbar {
+  width: 6px;
+}
+
+.devices-scroll-area::-webkit-scrollbar-track {
+  background: transparent;
+}
+
+.devices-scroll-area::-webkit-scrollbar-thumb {
+  background: #d1d5db;
+  border-radius: 3px;
+}
+
+.devices-scroll-area::-webkit-scrollbar-thumb:hover {
+  background: #9ca3af;
+}
+
+/* 设备项 */
+.device-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: clamp(0.75rem, 1.5vw, 1rem) clamp(0.875rem, 2vw, 1.25rem);
+  margin-bottom: clamp(0.5rem, 1.5vw, 0.75rem);
+  background: white;
+  border-radius: clamp(6px, 1vw, 8px);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  transition: all 0.2s ease;
+  border: 1px solid #e5e7eb;
+}
+
+.device-item:hover {
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  border-color: #d1d5db;
+  transform: translateY(-1px);
+}
+
+/* 设备信息容器 */
+.device-info {
+  flex: 1;
+  min-width: 0;
+}
+
+/* 设备 ID(IP:端口) */
+.device-id {
+  font-size: clamp(0.8rem, 1.8vw, 1.1rem);
+  font-weight: 500;
+  color: #1a1a1a;
+  font-family: 'Courier New', 'Consolas', monospace;
+  word-break: break-all;
+  letter-spacing: 0.2px;
+  line-height: 1.5;
+}
+
+/* 连接按钮 */
+.connect-btn {
+  padding: clamp(0.5rem, 1.5vw, 0.625rem) clamp(0.75rem, 2vw, 1.5rem);
+  background: #1a1a1a;
+  color: white;
+  border: none;
+  border-radius: clamp(6px, 1vw, 8px);
+  cursor: pointer;
+  font-size: clamp(0.75rem, 1.5vw, 1rem);
+  font-weight: 500;
+  transition: all 0.2s ease;
+  white-space: nowrap;
+  margin-left: clamp(0.5rem, 1.5vw, 1rem);
+  user-select: none;
+}
+
+.connect-btn:hover:not(.disabled) {
+  background: #333;
+  transform: translateY(-1px);
+}
+
+.connect-btn:active:not(.disabled) {
+  transform: translateY(0);
+}
+
+.connect-btn.disabled {
+  opacity: 0.4;
+  cursor: not-allowed;
+  background: #ccc;
+}
+
+.connect-btn.disabled:hover {
+  transform: none;
+}
+
+/* 断开按钮 */
+.disconnect-btn {
+  padding: clamp(0.5rem, 1.5vw, 0.625rem) clamp(0.75rem, 2vw, 1.5rem);
+  background: #dc2626;
+  color: white;
+  border: none;
+  border-radius: clamp(6px, 1vw, 8px);
+  cursor: pointer;
+  font-size: clamp(0.75rem, 1.5vw, 1rem);
+  font-weight: 500;
+  transition: all 0.2s ease;
+  white-space: nowrap;
+  user-select: none;
+}
+
+.disconnect-btn:hover:not(.disabled) {
+  background: #b91c1c;
+  transform: translateY(-1px);
+}
+
+.disconnect-btn:active:not(.disabled) {
+  transform: translateY(0);
+}
+
+.disconnect-btn.disabled {
+  opacity: 0.4;
+  cursor: not-allowed;
+  background: #ccc;
+}
+
+.disconnect-btn.disabled:hover {
+  transform: none;
+}
+
+/* 平板适配 */
+@media (min-width: 768px) and (max-width: 1023px) {
+  .devices-header {
+    padding: clamp(0.75rem, 1.5vw, 1rem);
+  }
+}
+
+/* 移动端适配 */
+@media (max-width: 767px) {
+  .Devices-container {
+    padding: clamp(0.5rem, 2vw, 1rem);
+  }
+  
+  .devices-header {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: clamp(0.5rem, 1.5vw, 0.75rem);
+    padding: clamp(0.75rem, 2vw, 1rem);
+  }
+  
+  .refresh-btn {
+    width: 100%;
+    justify-content: center;
+  }
+  
+  .device-item {
+    flex-direction: column;
+    align-items: flex-start;
+    gap: clamp(0.5rem, 1.5vw, 0.75rem);
+  }
+  
+  .connect-btn {
+    width: 100%;
+    margin-left: 0;
+  }
+}

+ 90 - 0
src/pages/Devices/Devices.js

@@ -0,0 +1,90 @@
+import { useState, useEffect, useRef } from 'react';
+
+// Devices 页面的业务逻辑 Hook
+export function DevicesLogic() {
+  // 设备列表(IP:端口格式)
+  const [devices, setDevices] = useState([]);
+  // 加载状态
+  const [loading, setLoading] = useState(false);
+  // 已连接设备集合(用于控制按钮显示)
+  const [connectedDevices, setConnectedDevices] = useState(new Set());
+  // 防止重复扫描标记
+  const hasScanned = useRef(false);
+
+  // 扫描设备列表(只获取已连接设备,不自动连接)
+  const scanDevices = async () => {
+    if (!window.electronAPI || !window.electronAPI.getADBDevices) {
+      console.warn('Electron API 不可用');
+      setDevices([]);
+      return;
+    }
+
+    setLoading(true);
+    try {
+      const deviceList = await window.electronAPI.getADBDevices();
+      // 过滤出 IP:端口格式的设备
+      const ipPortList = (deviceList || [])
+        .map(device => device?.id || device)
+        .filter(Boolean)
+        .filter(id => {
+          const ipPortPattern = /^\d+\.\d+\.\d+\.\d+:\d+$/;
+          return ipPortPattern.test(id);
+        });
+      console.log('devices:', ipPortList);
+      setDevices(ipPortList);
+    } catch (err) {
+      console.error('扫描失败:', err);
+      setDevices([]);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 连接设备
+  const connectDevice = async (ipPort) => {
+    if (!window.electronAPI || !window.electronAPI.connectADBDevice) {
+      console.warn('连接设备功能不可用');
+      return;
+    }
+
+    try {
+      await window.electronAPI.connectADBDevice(ipPort);
+      setConnectedDevices(prev => new Set([...prev, ipPort]));
+    } catch (err) {
+      console.error('连接设备失败:', err);
+    }
+  };
+
+  // 断开设备
+  const disconnectDevice = async (ipPort) => {
+    if (window.electronAPI && window.electronAPI.disconnectADBDevice) {
+      try {
+        await window.electronAPI.disconnectADBDevice(ipPort);
+      } catch (err) {
+        console.error('断开设备失败:', err);
+      }
+    }
+    setConnectedDevices(prev => {
+      const newSet = new Set(prev);
+      newSet.delete(ipPort);
+      return newSet;
+    });
+  };
+
+  // 页面加载时自动扫描
+  useEffect(() => {
+    if (!hasScanned.current) {
+      hasScanned.current = true;
+      scanDevices();
+    }
+  }, []);
+
+  return {
+    devices,
+    loading,
+    scanDevices,
+    connectDevice,
+    disconnectDevice,
+    connectedDevices,
+  };
+}

+ 62 - 0
src/pages/Devices/Devices.jsx

@@ -0,0 +1,62 @@
+import './Devices.css';
+import { DevicesLogic } from './Devices.js';
+
+// Devices 组件:显示设备列表和连接/断开功能
+function Devices() {
+  const { devices, loading, scanDevices, connectDevice, disconnectDevice, connectedDevices } = DevicesLogic();
+
+  // 处理连接设备点击
+  const handleConnect = async (ipPort) => {
+    await connectDevice(ipPort);
+  };
+
+  // 处理断开设备点击
+  const handleDisconnect = async (ipPort) => {
+    await disconnectDevice(ipPort);
+  };
+
+  return (
+    <div className="Devices-container">
+      <div className="devices-header">
+        <div className="devices-title">可连接设备</div>
+        <div 
+          className={`refresh-btn ${loading ? 'disabled' : ''}`}
+          onClick={loading ? undefined : scanDevices}
+        >
+          <div className={`refresh-icon ${loading ? 'spinning' : ''}`}>↻</div>
+          {loading ? '扫描中...' : '刷新'}
+        </div>
+      </div>
+      
+      <div className="devices-scroll-area">
+        {devices.map((ipPort, index) => (
+          <div key={index} className="device-item">
+            <div className="device-info">
+              <div className="device-id">{ipPort}</div>
+            </div>
+            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
+              {!connectedDevices.has(ipPort) && (
+                <div 
+                  className={`connect-btn ${loading ? 'disabled' : ''}`}
+                  onClick={loading ? undefined : () => handleConnect(ipPort)}
+                >
+                  连接
+                </div>
+              )}
+              {connectedDevices.has(ipPort) && (
+                <div 
+                  className={`disconnect-btn ${loading ? 'disabled' : ''}`}
+                  onClick={loading ? undefined : () => handleDisconnect(ipPort)}
+                >
+                  断开
+                </div>
+              )}
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}
+
+export default Devices;

+ 0 - 5
src/pages/home.js

@@ -4,11 +4,6 @@ export function HomeLogic() {
 
   const [showDevices, setShowDevices] = useState(true);
 
-  useEffect(() => {
-    // console.log('页面渲染完成了');
-    // setShowDevices(true);
-  }, []);
-
   return {
     showDevices,
     setShowDevices,