mirror of
https://github.com/TheFunny/ArisuAutoSweeper
synced 2026-10-19 16:22:05 +00:00
feat: enhance device connection handling and droidcast resolution management
This commit is contained in:
+115
-24
@@ -3,6 +3,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ from adbutils.errors import AdbError
|
|||||||
|
|
||||||
import module.config.server as server_
|
import module.config.server as server_
|
||||||
import platform
|
import platform
|
||||||
from module.base.decorator import Config, cached_property, del_cached_property
|
from module.base.decorator import Config, cached_property, del_cached_property, run_once
|
||||||
from module.base.utils import SelectedGrids, ensure_time
|
from module.base.utils import SelectedGrids, ensure_time
|
||||||
from module.device.connection_attr import ConnectionAttr
|
from module.device.connection_attr import ConnectionAttr
|
||||||
from module.device.method.utils import (
|
from module.device.method.utils import (
|
||||||
@@ -84,10 +85,17 @@ class AdbDeviceWithStatus(AdbDevice):
|
|||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def port(self) -> int:
|
||||||
|
try:
|
||||||
|
return int(self.serial.split(':')[1])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
return 0
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def may_mumu12_family(self):
|
def may_mumu12_family(self):
|
||||||
# 127.0.0.1:16XXX
|
# 127.0.0.1:16XXX
|
||||||
return len(self.serial) == 15 and self.serial.startswith('127.0.0.1:16')
|
return 16384 <= self.port <= 17408
|
||||||
|
|
||||||
|
|
||||||
class Connection(ConnectionAttr):
|
class Connection(ConnectionAttr):
|
||||||
@@ -274,6 +282,7 @@ class Connection(ConnectionAttr):
|
|||||||
@cached_property
|
@cached_property
|
||||||
def nemud_app_keep_alive(self) -> str:
|
def nemud_app_keep_alive(self) -> str:
|
||||||
res = self.adb_getprop('nemud.app_keep_alive')
|
res = self.adb_getprop('nemud.app_keep_alive')
|
||||||
|
logger.attr('nemud.app_keep_alive', res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
@@ -282,7 +291,6 @@ class Connection(ConnectionAttr):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
res = self.nemud_app_keep_alive
|
res = self.nemud_app_keep_alive
|
||||||
logger.attr('nemud.app_keep_alive', res)
|
|
||||||
if res == '':
|
if res == '':
|
||||||
# Empty property, probably MuMu6 or MuMu12 version < 3.5.6
|
# Empty property, probably MuMu6 or MuMu12 version < 3.5.6
|
||||||
return True
|
return True
|
||||||
@@ -297,6 +305,15 @@ class Connection(ConnectionAttr):
|
|||||||
logger.warning(f'Invalid nemud.app_keep_alive value: {res}')
|
logger.warning(f'Invalid nemud.app_keep_alive value: {res}')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def is_mumu_over_version_356(self) -> bool:
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
bool: If MuMu12 version >= 3.5.6,
|
||||||
|
which has nemud.app_keep_alive and always be a vertical device
|
||||||
|
"""
|
||||||
|
return self.nemud_app_keep_alive != ''
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def _nc_server_host_port(self):
|
def _nc_server_host_port(self):
|
||||||
"""
|
"""
|
||||||
@@ -547,14 +564,14 @@ class Connection(ConnectionAttr):
|
|||||||
# Disconnect offline device before connecting
|
# Disconnect offline device before connecting
|
||||||
for device in self.list_device():
|
for device in self.list_device():
|
||||||
if device.status == 'offline':
|
if device.status == 'offline':
|
||||||
logger.warning(f'Device {serial} is offline, disconnect it before connecting')
|
logger.warning(f'Device {device.serial} is offline, disconnect it before connecting')
|
||||||
self.adb_disconnect(serial)
|
self.adb_disconnect(device.serial)
|
||||||
elif device.status == 'unauthorized':
|
elif device.status == 'unauthorized':
|
||||||
logger.error(f'Device {serial} is unauthorized, please accept ADB debugging on your device')
|
logger.error(f'Device {device.serial} is unauthorized, please accept ADB debugging on your device')
|
||||||
elif device.status == 'device':
|
elif device.status == 'device':
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
logger.warning(f'Device {serial} is is having a unknown status: {device.status}')
|
logger.warning(f'Device {device.serial} is is having a unknown status: {device.status}')
|
||||||
|
|
||||||
# Skip for emulator-5554
|
# Skip for emulator-5554
|
||||||
if 'emulator-' in serial:
|
if 'emulator-' in serial:
|
||||||
@@ -762,23 +779,45 @@ class Connection(ConnectionAttr):
|
|||||||
If serial=='auto' and only 1 device detected, use it
|
If serial=='auto' and only 1 device detected, use it
|
||||||
"""
|
"""
|
||||||
logger.hr('Detect device')
|
logger.hr('Detect device')
|
||||||
logger.info('Here are the available devices, '
|
available = SelectedGrids([])
|
||||||
'copy to Alas.Emulator.Serial to use it or set Alas.Emulator.Serial="auto"')
|
devices = SelectedGrids([])
|
||||||
devices = self.list_device()
|
|
||||||
|
|
||||||
# Show available devices
|
@run_once
|
||||||
available = devices.select(status='device')
|
def brute_force_connect():
|
||||||
for device in available:
|
logger.info('Brute force connect')
|
||||||
logger.info(device.serial)
|
from deploy.Windows.emulator import EmulatorManager
|
||||||
if not len(available):
|
manager = EmulatorManager()
|
||||||
logger.info('No available devices')
|
manager.brute_force_connect()
|
||||||
|
|
||||||
# Show unavailable devices if having any
|
for _ in range(2):
|
||||||
unavailable = devices.delete(available)
|
logger.info('Here are the available devices, '
|
||||||
if len(unavailable):
|
'copy to Alas.Emulator.Serial to use it or set Alas.Emulator.Serial="auto"')
|
||||||
logger.info('Here are the devices detected but unavailable')
|
devices = self.list_device()
|
||||||
for device in unavailable:
|
|
||||||
logger.info(f'{device.serial} ({device.status})')
|
# Show available devices
|
||||||
|
available = devices.select(status='device')
|
||||||
|
for device in available:
|
||||||
|
logger.info(device.serial)
|
||||||
|
if not len(available):
|
||||||
|
logger.info('No available devices')
|
||||||
|
|
||||||
|
# Show unavailable devices if having any
|
||||||
|
unavailable = devices.delete(available)
|
||||||
|
if len(unavailable):
|
||||||
|
logger.info('Here are the devices detected but unavailable')
|
||||||
|
for device in unavailable:
|
||||||
|
logger.info(f'{device.serial} ({device.status})')
|
||||||
|
|
||||||
|
# brute_force_connect
|
||||||
|
if self.config.Emulator_Serial == 'auto' and available.count == 0:
|
||||||
|
logger.warning(f'No available device found')
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
brute_force_connect()
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
# Auto device detection
|
# Auto device detection
|
||||||
if self.config.Emulator_Serial == 'auto':
|
if self.config.Emulator_Serial == 'auto':
|
||||||
@@ -788,7 +827,7 @@ class Connection(ConnectionAttr):
|
|||||||
raise RequestHumanTakeover
|
raise RequestHumanTakeover
|
||||||
elif available.count == 1:
|
elif available.count == 1:
|
||||||
logger.info(f'Auto device detection found only one device, using it')
|
logger.info(f'Auto device detection found only one device, using it')
|
||||||
self.serial = available[0].serial
|
self.config.Emulator_Serial = self.serial = available[0].serial
|
||||||
del_cached_property(self, 'adb')
|
del_cached_property(self, 'adb')
|
||||||
elif available.count == 2 \
|
elif available.count == 2 \
|
||||||
and available.select(serial='127.0.0.1:7555') \
|
and available.select(serial='127.0.0.1:7555') \
|
||||||
@@ -797,7 +836,7 @@ class Connection(ConnectionAttr):
|
|||||||
# For MuMu12 serials like 127.0.0.1:7555 and 127.0.0.1:16384
|
# For MuMu12 serials like 127.0.0.1:7555 and 127.0.0.1:16384
|
||||||
# ignore 7555 use 16384
|
# ignore 7555 use 16384
|
||||||
remain = available.select(may_mumu12_family=True).first_or_none()
|
remain = available.select(may_mumu12_family=True).first_or_none()
|
||||||
self.serial = remain.serial
|
self.config.Emulator_Serial = self.serial = remain.serial
|
||||||
del_cached_property(self, 'adb')
|
del_cached_property(self, 'adb')
|
||||||
else:
|
else:
|
||||||
logger.critical('Multiple devices found, auto device detection cannot decide which to choose, '
|
logger.critical('Multiple devices found, auto device detection cannot decide which to choose, '
|
||||||
@@ -806,6 +845,7 @@ class Connection(ConnectionAttr):
|
|||||||
|
|
||||||
# Handle LDPlayer
|
# Handle LDPlayer
|
||||||
# LDPlayer serial jumps between `127.0.0.1:5555+{X}` and `emulator-5554+{X}`
|
# LDPlayer serial jumps between `127.0.0.1:5555+{X}` and `emulator-5554+{X}`
|
||||||
|
# No config write since it's dynamic
|
||||||
port_serial, emu_serial = get_serial_pair(self.serial)
|
port_serial, emu_serial = get_serial_pair(self.serial)
|
||||||
if port_serial and emu_serial:
|
if port_serial and emu_serial:
|
||||||
# Might be LDPlayer, check connected devices
|
# Might be LDPlayer, check connected devices
|
||||||
@@ -832,6 +872,57 @@ class Connection(ConnectionAttr):
|
|||||||
f'Using serial: {emu_serial}')
|
f'Using serial: {emu_serial}')
|
||||||
self.serial = emu_serial
|
self.serial = emu_serial
|
||||||
|
|
||||||
|
# Redirect MuMu12 from 127.0.0.1:7555 to 127.0.0.1:16xxx
|
||||||
|
if self.serial == '127.0.0.1:7555':
|
||||||
|
for _ in range(2):
|
||||||
|
mumu12 = available.select(may_mumu12_family=True)
|
||||||
|
if mumu12.count == 1:
|
||||||
|
emu_serial = mumu12.first_or_none().serial
|
||||||
|
logger.warning(f'Redirect MuMu12 {self.serial} to {emu_serial}')
|
||||||
|
self.config.Emulator_Serial = self.serial = emu_serial
|
||||||
|
break
|
||||||
|
elif mumu12.count >= 2:
|
||||||
|
logger.warning(f'Multiple MuMu12 serial found, cannot redirect')
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Only 127.0.0.1:7555
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
# is_mumu_over_version_356 and nemud_app_keep_alive was cached
|
||||||
|
# Acceptable since it's the same device
|
||||||
|
logger.warning(f'Device {self.serial} is MuMu12 but corresponding port not found')
|
||||||
|
brute_force_connect()
|
||||||
|
devices = self.list_device()
|
||||||
|
# Show available devices
|
||||||
|
available = devices.select(status='device')
|
||||||
|
for device in available:
|
||||||
|
logger.info(device.serial)
|
||||||
|
if not len(available):
|
||||||
|
logger.info('No available devices')
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# MuMu6
|
||||||
|
break
|
||||||
|
|
||||||
|
# MuMu12 uses 127.0.0.1:16385 if port 16384 is occupied, auto redirect
|
||||||
|
# No config write since it's dynamic
|
||||||
|
if self.is_mumu12_family:
|
||||||
|
matched = False
|
||||||
|
for device in available.select(may_mumu12_family=True):
|
||||||
|
if device.port == self.port:
|
||||||
|
# Exact match
|
||||||
|
matched = True
|
||||||
|
break
|
||||||
|
if not matched:
|
||||||
|
for device in available.select(may_mumu12_family=True):
|
||||||
|
if -2 <= device.port - self.port <= 2:
|
||||||
|
# Port switched
|
||||||
|
logger.info(f'MuMu12 port switches from {self.serial} to {device.serial}')
|
||||||
|
del_cached_property(self, 'port')
|
||||||
|
del_cached_property(self, 'is_mumu12_family')
|
||||||
|
del_cached_property(self, 'is_mumu_family')
|
||||||
|
self.serial = device.serial
|
||||||
|
break
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def list_package(self, show_log=True):
|
def list_package(self, show_log=True):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -122,6 +122,18 @@ class ConnectionAttr:
|
|||||||
def is_wsa(self):
|
def is_wsa(self):
|
||||||
return bool(re.match(r'^wsa', self.serial))
|
return bool(re.match(r'^wsa', self.serial))
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def port(self) -> int:
|
||||||
|
try:
|
||||||
|
return int(self.serial.split(':')[1])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def is_mumu12_family(self):
|
||||||
|
# 127.0.0.1:16XXX
|
||||||
|
return 16384 <= self.port <= 17408
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_mumu_family(self):
|
def is_mumu_family(self):
|
||||||
# 127.0.0.1:7555
|
# 127.0.0.1:7555
|
||||||
@@ -129,9 +141,8 @@ class ConnectionAttr:
|
|||||||
return self.serial == '127.0.0.1:7555' or self.is_mumu12_family
|
return self.serial == '127.0.0.1:7555' or self.is_mumu12_family
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_mumu12_family(self):
|
def is_nox_family(self):
|
||||||
# 127.0.0.1:16384 + 32*n
|
return 62001 <= self.port <= 63025
|
||||||
return len(self.serial) == 15 and self.serial.startswith('127.0.0.1:16')
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_emulator(self):
|
def is_emulator(self):
|
||||||
@@ -177,7 +188,8 @@ class ConnectionAttr:
|
|||||||
rf"SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config") as key:
|
rf"SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config") as key:
|
||||||
port = QueryValueEx(key, "BstAdbPort")[0]
|
port = QueryValueEx(key, "BstAdbPort")[0]
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.error(rf'Unable to find registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config')
|
logger.error(
|
||||||
|
rf'Unable to find registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests\{folder_name}\Config')
|
||||||
logger.error('Please confirm that your are using BlueStack 4 hyper-v and not regular BlueStacks 4')
|
logger.error('Please confirm that your are using BlueStack 4 hyper-v and not regular BlueStacks 4')
|
||||||
logger.error(r'Please check if there is any other emulator instances under '
|
logger.error(r'Please check if there is any other emulator instances under '
|
||||||
r'registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests')
|
r'registry HKEY_LOCAL_MACHINE\SOFTWARE\BlueStacks_bgp64_hyperv\Guests')
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import collections
|
|||||||
from module.base.timer import Timer
|
from module.base.timer import Timer
|
||||||
from module.device.app_control import AppControl
|
from module.device.app_control import AppControl
|
||||||
from module.device.control import Control
|
from module.device.control import Control
|
||||||
from module.device.platform import Platform
|
|
||||||
from module.device.screenshot import Screenshot
|
from module.device.screenshot import Screenshot
|
||||||
from module.exception import (
|
from module.exception import (
|
||||||
EmulatorNotRunningError,
|
EmulatorNotRunningError,
|
||||||
@@ -55,7 +54,7 @@ def show_function_call():
|
|||||||
logger.info('Function calls:' + ''.join(func_list))
|
logger.info('Function calls:' + ''.join(func_list))
|
||||||
|
|
||||||
|
|
||||||
class Device(Screenshot, Control, AppControl, Platform):
|
class Device(Screenshot, Control, AppControl):
|
||||||
_screen_size_checked = False
|
_screen_size_checked = False
|
||||||
detect_record = set()
|
detect_record = set()
|
||||||
click_record = collections.deque(maxlen=15)
|
click_record = collections.deque(maxlen=15)
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class Adb(Connection):
|
|||||||
if image is None:
|
if image is None:
|
||||||
raise ImageTruncated('Empty image after cv2.imdecode')
|
raise ImageTruncated('Empty image after cv2.imdecode')
|
||||||
|
|
||||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image)
|
cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image)
|
||||||
if image is None:
|
if image is None:
|
||||||
raise ImageTruncated('Empty image after cv2.cvtColor')
|
raise ImageTruncated('Empty image after cv2.cvtColor')
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ class DroidCast(Uiautomator2):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_droidcast_port: int = 0
|
_droidcast_port: int = 0
|
||||||
|
droidcast_width: int = 0
|
||||||
|
droidcast_height: int = 0
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def droidcast_session(self):
|
def droidcast_session(self):
|
||||||
@@ -114,14 +116,35 @@ class DroidCast(Uiautomator2):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def droidcast_url(self, url='/preview'):
|
def droidcast_url(self, url='/preview'):
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
w, h = self.droidcast_width, self.droidcast_height
|
||||||
|
if self.orientation == 0:
|
||||||
|
return f'http://127.0.0.1:{self._droidcast_port}{url}?width={w}&height={h}'
|
||||||
|
elif self.orientation == 1:
|
||||||
|
return f'http://127.0.0.1:{self._droidcast_port}{url}?width={h}&height={w}'
|
||||||
|
else:
|
||||||
|
# logger.warning('DroidCast receives invalid device orientation')
|
||||||
|
pass
|
||||||
|
|
||||||
return f'http://127.0.0.1:{self._droidcast_port}{url}'
|
return f'http://127.0.0.1:{self._droidcast_port}{url}'
|
||||||
|
|
||||||
def droidcast_raw_url(self, url='/screenshot'):
|
def droidcast_raw_url(self, url='/screenshot'):
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
w, h = self.droidcast_width, self.droidcast_height
|
||||||
|
if self.orientation == 0:
|
||||||
|
return f'http://127.0.0.1:{self._droidcast_port}{url}?width={w}&height={h}'
|
||||||
|
elif self.orientation == 1:
|
||||||
|
return f'http://127.0.0.1:{self._droidcast_port}{url}?width={h}&height={w}'
|
||||||
|
else:
|
||||||
|
# logger.warning('DroidCast receives invalid device orientation')
|
||||||
|
pass
|
||||||
|
|
||||||
return f'http://127.0.0.1:{self._droidcast_port}{url}'
|
return f'http://127.0.0.1:{self._droidcast_port}{url}'
|
||||||
|
|
||||||
def droidcast_init(self):
|
def droidcast_init(self):
|
||||||
logger.hr('DroidCast init')
|
logger.hr('DroidCast init')
|
||||||
self.droidcast_stop()
|
self.droidcast_stop()
|
||||||
|
self._droidcast_update_resolution()
|
||||||
|
|
||||||
logger.info('Pushing DroidCast apk')
|
logger.info('Pushing DroidCast apk')
|
||||||
self.adb_push(self.config.DROIDCAST_FILEPATH_LOCAL, self.config.DROIDCAST_FILEPATH_REMOTE)
|
self.adb_push(self.config.DROIDCAST_FILEPATH_LOCAL, self.config.DROIDCAST_FILEPATH_REMOTE)
|
||||||
@@ -151,10 +174,25 @@ class DroidCast(Uiautomator2):
|
|||||||
else:
|
else:
|
||||||
logger.error(f'Unknown DROIDCAST_VERSION: {self.config.DROIDCAST_VERSION}')
|
logger.error(f'Unknown DROIDCAST_VERSION: {self.config.DROIDCAST_VERSION}')
|
||||||
|
|
||||||
|
def _droidcast_update_resolution(self):
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
logger.info('Update droidcast resolution')
|
||||||
|
w, h = self.resolution_uiautomator2(cal_rotation=False)
|
||||||
|
self.get_orientation()
|
||||||
|
# 720, 1280
|
||||||
|
# mumu12 > 3.5.6 is always a vertical device
|
||||||
|
self.droidcast_width, self.droidcast_height = w, h
|
||||||
|
logger.info(f'Droicast resolution: {(w, h)}')
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def screenshot_droidcast(self):
|
def screenshot_droidcast(self):
|
||||||
self.config.DROIDCAST_VERSION = 'DroidCast'
|
self.config.DROIDCAST_VERSION = 'DroidCast'
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
if not self.droidcast_width or not self.droidcast_height:
|
||||||
|
self._droidcast_update_resolution()
|
||||||
|
|
||||||
resp = self.droidcast_session.get(self.droidcast_url(), timeout=3)
|
resp = self.droidcast_session.get(self.droidcast_url(), timeout=3)
|
||||||
|
|
||||||
if resp.status_code == 404:
|
if resp.status_code == 404:
|
||||||
raise DroidCastVersionIncompatible('DroidCast server does not have /preview')
|
raise DroidCastVersionIncompatible('DroidCast server does not have /preview')
|
||||||
image = resp.content
|
image = resp.content
|
||||||
@@ -174,16 +212,27 @@ class DroidCast(Uiautomator2):
|
|||||||
if image is None:
|
if image is None:
|
||||||
raise ImageTruncated('Empty image after cv2.cvtColor')
|
raise ImageTruncated('Empty image after cv2.cvtColor')
|
||||||
|
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
if self.orientation == 1:
|
||||||
|
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def screenshot_droidcast_raw(self):
|
def screenshot_droidcast_raw(self):
|
||||||
self.config.DROIDCAST_VERSION = 'DroidCast_raw'
|
self.config.DROIDCAST_VERSION = 'DroidCast_raw'
|
||||||
|
shape = (720, 1280)
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
if not self.droidcast_width or not self.droidcast_height:
|
||||||
|
self._droidcast_update_resolution()
|
||||||
|
if self.droidcast_height and self.droidcast_width:
|
||||||
|
shape = (self.droidcast_height, self.droidcast_width)
|
||||||
|
|
||||||
image = self.droidcast_session.get(self.droidcast_raw_url(), timeout=3).content
|
image = self.droidcast_session.get(self.droidcast_raw_url(), timeout=3).content
|
||||||
# DroidCast_raw returns a RGB565 bitmap
|
# DroidCast_raw returns a RGB565 bitmap
|
||||||
|
|
||||||
try:
|
try:
|
||||||
arr = np.frombuffer(image, dtype=np.uint16).reshape((720, 1280))
|
arr = np.frombuffer(image, dtype=np.uint16).reshape(shape)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
if len(image) < 500:
|
if len(image) < 500:
|
||||||
logger.warning(f'Unexpected screenshot: {image}')
|
logger.warning(f'Unexpected screenshot: {image}')
|
||||||
@@ -231,6 +280,11 @@ class DroidCast(Uiautomator2):
|
|||||||
cv2.add(b, m, dst=b)
|
cv2.add(b, m, dst=b)
|
||||||
|
|
||||||
image = cv2.merge([r, g, b])
|
image = cv2.merge([r, g, b])
|
||||||
|
|
||||||
|
if self.is_mumu_over_version_356:
|
||||||
|
if self.orientation == 1:
|
||||||
|
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def droidcast_wait_startup(self):
|
def droidcast_wait_startup(self):
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ class Uiautomator2(Connection):
|
|||||||
return hierarchy
|
return hierarchy
|
||||||
|
|
||||||
@retry
|
@retry
|
||||||
def resolution_uiautomator2(self) -> t.Tuple[int, int]:
|
def resolution_uiautomator2(self, cal_rotation=True) -> t.Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
Faster u2.window_size(), cause that calls `dumpsys display` twice.
|
Faster u2.window_size(), cause that calls `dumpsys display` twice.
|
||||||
|
|
||||||
@@ -251,9 +251,10 @@ class Uiautomator2(Connection):
|
|||||||
"""
|
"""
|
||||||
info = self.u2.http.get('/info').json()
|
info = self.u2.http.get('/info').json()
|
||||||
w, h = info['display']['width'], info['display']['height']
|
w, h = info['display']['width'], info['display']['height']
|
||||||
rotation = self.get_orientation()
|
if cal_rotation:
|
||||||
if (w > h) != (rotation % 2 == 1):
|
rotation = self.get_orientation()
|
||||||
w, h = h, w
|
if (w > h) != (rotation % 2 == 1):
|
||||||
|
w, h = h, w
|
||||||
return w, h
|
return w, h
|
||||||
|
|
||||||
def resolution_check_uiautomator2(self):
|
def resolution_check_uiautomator2(self):
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ class Screenshot(Adb, WSA, DroidCast, AScreenCap, Scrcpy):
|
|||||||
|
|
||||||
return self.image
|
return self.image
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_cached_image(self):
|
||||||
|
return hasattr(self, 'image') and self.image is not None
|
||||||
|
|
||||||
def _handle_orientated_image(self, image):
|
def _handle_orientated_image(self, image):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
Reference in New Issue
Block a user