一款简单好用的Android副屏模拟器

SecondaryScreen源码

背景

最近几年新能源电车大火,华为等不少厂商推出了副驾屏,不少车机应用开始支持副驾屏,用以实现主副屏联动等场景。

初尝试

熟悉Android Studio的同学可能会知道,利用Android Studio创建完虚拟设备之后可以在Display设置项里通过“Add secondary display”新增第二块显示屏,此显示屏具体信息如下:

mBaseDisplayInfo=DisplayInfo{"Emulator 2D Display", displayId 2, displayGroupId 0, FLAG_PRESENTATION, FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS, FLAG_TRUSTED, real 1080 x 1920, largest app 1080 x 1920, smallest app 1080 x 1920, appVsyncOff 0, presDeadline 16666666, mode 260.0, defaultMode 2, modes [{id=2, width=1080, height=1920, fps=60.0, alternativeRefreshRates=[], supportedHdrTypes=[]}], hdrCapabilities null, userDisabledHdrTypes [], minimalPostProcessingSupported false, rotation 0, state ON, committedState UNKNOWN, type VIRTUAL, uniqueId "virtual:com.android.emulator.multidisplay:1234562", app 1080 x 1920, density 320 (320.0 x 320.0) dpi, layerStack 2, colorMode 0, supportedColorModes [0], deviceProductInfo null, owner com.android.emulator.multidisplay (uid 1000), removeMode 0, refreshRateOverride 0.0, brightnessMinimum 0.0, brightnessMaximum 0.0, brightnessDefault 0.0, installOrientation ROTATION_0, layoutLimitedRefreshRate null, hdrSdrRatio not_available, thermalRefreshRateThrottling {}, thermalBrightnessThrottlingDataId default}

此方式创建的虚拟屏依赖于电脑环境,即搭建的Android系统体系结构依赖于电脑本身的体系结构,且比较吃电脑资源。而大部分电脑屏幕是非触摸屏,只可以用鼠标进行操作,鼠标只能模拟单指,无法模拟出双指、多指操组。

再尝试

熟悉开发者模式的同学可能会知道,Android手机开发者模式中的“绘图-模拟辅助显示设备“选项,打开此选项后,主屏会有一个副屏叠加窗,此叠加窗可以改变位置和大小,然而无法相应触摸事件,此显示屏具体信息如下:

mOverrideDisplayInfo=DisplayInfo{"叠加视图 #1, displayId 2", uniqueId "****", app 1920 x 1080, real 1920 x 1080, largest app 1920 x 1920, smallest app 1080 x 1080, mode 3, defaultMode 3, modes [{id=3, width=1920, height=1080, fps=60.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities null, rotation 0, density 320 (320.0 x 320.0) dpi, layerStack 2, appVsyncOff 0, presDeadline 32333332, type OVERLAY, state ON, FLAG_PRESENTATION, removeMode 0}

我们可以通过scrcpy把副屏映射到电脑上进行相关操作。
除到开发者模式打开副屏外,还通过shell或Android API创建副屏:

shell命令创建一块副屏
$settings put global overlay_display_devices "WxH/DENSITY"
shell命令创建多块副屏(最多6块)
$settings put global overlay_display_devices "WxH/DENSITY,secure;WxH/DENSITY,secure"
shell命令删除副屏
$settings put global overlay_display_devices "null"
$settings put global overlay_display_devices ""
$settings delete global overlay_display_devices

部分手机无法弹出软键盘,可通过打开Android手机开发者模式中的“绘图-强制桌面模式“解决,或者使用电脑键盘代替,shell或Android API也可打开、关闭强制桌面模式。

查看强制桌面模式
$settings get global force_desktop_mode_on_external_displays
打开强制桌面模式
$settings put global force_desktop_mode_on_external_displays 1
关闭强制桌面模式
$shell settings put global force_desktop_mode_on_external_displays 0

从Android10开始Android才引入事件注入机制,因此scrcpy仅能控制Android10及以上Android设备的触摸事件。而大部分电脑屏幕是非触摸屏,只可以用鼠标进行操作,鼠标只能模拟单指,无法模拟出双指、多指操组,且系统Overlay方式始终在主屏上有一个遮盖的叠加窗,这个叠加窗无法操作内部画布,更无法隐藏。

终尝试

在Android设备上创建一个悬浮窗,因悬浮窗是自己创建,故悬浮窗可以隐藏或最小化,利用悬浮窗的SurfaceTexture创建一个VirtualDisplay,想办法将副屏的secondActivity追加到新创建的VirtualDisplay,将悬浮窗上的触摸事件注入给Android系统,Android系统就会相应副屏的触摸事件,即触摸事件传给内部画布,内部画布可以相应单指、双指、多指等操作。
因App无Android系统级权限,所以OverlayWindow App创建VirtualDisplay无法在另一个App中使用,此时会报权限错误。然可通过shell命令提权,使用am start命令启动secondActivity,并通过--display参数指定到副屏上,这样就能让secondActivity运行在OverlayWindow App创建的副屏上。同时业务App需要过滤掉OverlayWindow App创建的VirtualDisplay(如过滤VirtualDisplay的名称),否则业务App无法正常运行。然后通过scrcpy方式验证业务App正常启动secondActivity的逻辑,这样就能覆盖OverlayWindow App无法验证的部分(shell命令启动secondActivity)。因此scrcpy+OverlayWindow App搭配才能覆盖所有业务逻辑。

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
    VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(
              "OverlayWindowVirtualDisplay",
              width, 
              height,
              mDensityDpi,
              new Surface(surfaceTexture), 
              DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | 
              DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION | 
              DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
              null,
              null);
}
mOverrideDisplayInfo=DisplayInfo{"OverlayWindowVirtualDisplay, displayId 16", uniqueId "virtual:com.floatwindow.demo,10102,OverlayWindowVirtualDisplay,0", app 2392 x 1440, real 2392 x 1440, largest app 2392 x 2392, smallest app 1440 x 1440, mode 17, defaultMode 17, modes [{id=17, width=2392, height=1440, fps=60.0}], colorMode 0, supportedColorModes [0], hdrCapabilities null, rotation 0, density 560 (560.0 x 560.0) dpi, layerStack 16, appVsyncOff 0, presDeadline 16666666, type VIRTUAL, state ON, owner com.floatwindow.demo (uid 10102), FLAG_PRESENTATION, removeMode 0}

再次尝试

之前一直局限于将virtualdisplay关联到一个可视化的surface上,偶然间探索发现可以将virtualdisplay关联到一个非可视化的surface上,即通过反射方式创建一个virtualdisplay,此virtualdisplay的surface是一个ImageReader创建的surface,因此主屏上无任何副屏相关的渲染内容。
Android 10 ~ 12设备上创建virtualdisplay后,APP可以识别到virtualdisplay,但是在通过指定displayId拉起activity时会报权限错误。 可以通过am start命令拉起activity,此时模拟器就无法覆盖到APP正常拉起activity的逻辑。
Android 13及以上设备上创建virtualdisplay后,APP可以识别到virtualdisplay,在通过指定displayId拉起activity时可正常拉起activity,无任何权限问题,因此极力推荐使用Android 13及以上设备。
电脑充当副屏:主屏上无任何副屏的渲染内容,副屏对主屏零污染。
Android设备悬浮窗充当副屏:副屏可以实现双指、多指触摸事件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容