小米开发平台 刘海屏、水滴屏、挖孔屏 Android P/Q 适配

小米开发平台 刘海屏、水滴屏、挖孔屏 Android P/Q 适配

1. 背景

  • 小米 8 等刘海设备上市时运行的是 Android O 设备,但由于 Android O 没有标准接口,所以当时适配的规则和接口仅在 MIUI 系统生效。关于小米 Android O 的规则,详见https://dev.mi.com/console/doc/detail?pId=1293
  • 后来 Android P 中新增了刘海屏适配的API,为了与行业标准一致,MIUI 也决定在运行 Android P 的设备上完全采用 Android P 的接口。
  • 但由于 Android P 的接口定义得比较晚,导致 MIUI 接口无法与其完全兼容,开发者需要针对 Android P 的小米设备重新适配。

该文档将结合小米的情况给大家简要介绍 Android P 的刘海屏适配规则及 API,更详细的内容可以直接查看官方文档 https://developer.android.com/guide/topics/display-cutout/

2. 部分小米水滴屏/刘海屏/挖孔屏设备信息如下

机型modeldevice分辨率Notch高度Notch宽度DPI
小米8MI 8dipper1080*224889560440
小米8 SEMI 8 SEsirius1080*224485540440
小米8 透明探索版MI 8 Explorer Editionursa1080*224889560440
小米8 屏幕指纹版MI 8 UDequuleus1080*224889560440
小米8 青春版MI8Liteplatina1080*228082296440
小米POCO F1POCO F1beryllium1080*224686588440
红米6 ProRedmi 6 Prosakura1080*228089352440
红米Note 7Redmi Note 7lavender1080*234079116440
小米CC9 ProMi CC9 Protucana1080*2340​71146​​440
Redmi K30​​Redmi K30​phoenix​1080*2400​92179​440

注意事项:

  • 以上设备,由于MIUI调整了 DPI 值,因此DP值与像素值的转换关系是 1dp = 2.75 px ;
  • 用原生api DisplayCutout就可以直接获取​设备屏幕尺寸和异形的位置大小等信息,以上仅做参考,之后新机将不再罗列。​

3. 概念说明

为了方便讨论,我们明确下以下概念:

上述两种屏幕都可以统称为刘海屏,不过对于右侧较小的刘海,业界一般称为水滴屏或美人尖。为便于说明,后文提到的「刘海屏」「刘海区」都同时指代上图两种屏幕。

4. Android P/Q 刘海屏水滴屏挖孔屏的适配规则 

Android P 提供了 3 种显示模式供开发者选择,分别是:

  • 默认模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)
  • 刘海区绘制模式( LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
  • 刘海区不绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER)

如果开发者未作任何声明,则会按默认模式处理。以下将具体介绍这三种模式的表现。

4.1. 默认模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)

为了在不影响操作的情况下,尽可能利用刘海屏的显示区域,有以下表现:

非全屏(normal mode)全屏(fullscreen mode)
竖屏(portrait mode)使用耳朵区禁用耳朵区
横屏(landscape mode)禁用耳朵区禁用耳朵区

注:所谓全屏(fullscreen mode),是指隐藏状态栏(status bar),即通过 SYSTEM_UI_FLAG_FULLSCREEN 实现的效果。

默认模式的截图效果如下:

4.2. 刘海区绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)

如上所述,默认模式下某些场景会禁用耳朵区,那是因为这些场景下,系统无法判断开发者是否会把控件放置在耳朵区,所以只好默认禁用。如果开发者想要在那些场景下使用耳朵区,需要主动声明,即使用 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 来主动声明。

由于各个厂商的刘海或者凹口形状、位置不一, 开发者可以通过 WindowInsets.getDisplayCutout()  来获得 DisplayCutout object,里面包含了几个有用的方法:

开发者根据业务内容,自行判断是否需要根据不同的刘海形状做不同的布局调整。以小米8(刘海高度89px)为例,当开发者选用 SHORT_EDGES 模式时,以上接口会返回以下值:

竖屏横屏(刘海在左边)
getBoundingRects()(201, 0 – 879, 90)(0, 201 – 90, 879)
getSafeInsetLeft() 090
getSafeInsetTop()900
getSafeInsetRight() 00
getSafeInsetBottom()00

上述接口的返回值代表:

  • 小米8有一个刘海,竖屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (201, 0) 和 (879, 90) —— 左上角为 (0, 0) 原点;横屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (0, 201) 和 (90, 879) —— 左上角为 (0, 0) 原点。
  • 对于小米8,如果开发者需要将内容避开刘海区域,竖屏时就需要从顶部向下偏移 90 px,左、右和下无需要偏移。

又以红米Note 7(水滴屏设备) 为例,当开发者选用 SHORT_EDGES 模式时,以上接口会返回以下值:

竖屏横屏(水滴在左边)
getBoundingRects()(450, 0 – 630, 80)(0, 450 – 80, 630)
getSafeInsetLeft() 080
getSafeInsetTop()800
getSafeInsetRight() 00
getSafeInsetBottom()00

上述接口的返回值代表:红米Note 7 有一个刘海(水滴),竖屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (450, 0) 和 (630, 80) ;横屏时,这个刘海所在的矩形区域的左上角、右下角的坐标分别为 (0, 450) 和 (80, 630) 。

又以Redmi K30 (挖孔屏设备)为例子,当开发者选用 SHORT_EDGES 模式时,以上接口会返回以下值:

 竖屏横屏(摄像头在左边)
getBoundingRects()(844, 0 – 1080, 95)](0, 0 – 95, 236)
getSafeInsetLeft()095
getSafeInsetTop()950
getSafeInsetRight()00
getSafeInsetBottom()00

上述接口的返回值代表:

Redmi K30有刘海/水滴/挖孔,竖屏是,这个刘海在手机的左上角,右下角的坐标分别为 (844,0)和 (1080, 95);左上角为(0,0);横屏时,这个刘海对应的值为(0,0)和(95,236)。对于Redmi K30,如果开发者需要将内容避开挖孔区域,竖屏就需要从顶部向下偏移95px,左、右和下无需偏移。

4.3. 刘海区不绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER )

开发者选用这个模式后,意味着不绘制内容到耳朵区。如非必需,我们不建议采用这种模式,因为那样会浪费不少屏幕空间,用户体验不佳。

当开发者选用 NEVER 模式时, DisplayCutout object 的以下方法都会返回空值,因为 Google 认为既然开发者不使用耳朵区,就不需要关心刘海的大小了。

竖屏横屏(刘海在左边)
getBoundingRects()nullnull
getSafeInsetLeft() null null
 getSafeInsetTop()nullnull
getSafeInsetRight() nullnull
getSafeInsetBottom()nullnull

5. 其他注意事项

5.1. 避免写死状态栏的值

由于 Notch 设备的状态栏高度与正常机器不一样,因此在需要使用状态栏高度时,不建议写死一个值,而应该改为读取系统的值。

以下是获取当前设备状态栏高度的方法:

int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");

if (resourceId > 0) {

result = context.getResources().getDimensionPixelSize(resourceId);

}

5.2. 处理好同一页面,进入与退出全屏模式(fullscreen mode)的过渡

因为在默认模式 / LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 下,系统针对全屏与非全屏的页面,耳朵区的显示逻辑不一样。如果开发者没有处理好,容易出现页面可用区域跳变的问题。针对这种页面,我们建议开发者主动声明是否使用耳朵区,以避免跳变。

6. 常见问题

6.1. 如何测试

有两种方法:

  • 使用小米设备测试,如小米8系列(含标准版、探索版、屏幕指纹版),然后升级至 Android P 的 MIUI 版本,下载地址为:https://www.miui.com/download-345.html
  • 使用运行原生 Android 9 的设备,然后前往「开发者选项 – 模拟“刘海屏”」,选择任一刘海选项。 

若适配中遇到问题,可以发邮件给相关工程师张定昌 zhangdingchang@xiaomi.com、喻伟 yuwei@xiaomi.com 或工程组 miuishell@xiaomi.com。

6.2. 适配过小米 Android O 的刘海屏接口,在小米的 Android P 设备上是否需要重新适配

需要。如文章开头所说,Android P 的接口今年6月才公布,我们在接口设计上和他们有一些出入,所以没法兼容。开发者仍然需要再针对 Android P 做适配,但好消息是,各大手机厂商都支持 Android P 的接口,所以大家只要适配一次就可以了。

6.3. MIUI Android O 的老接口在 Android P/Q设备上是否生效?

P和Q大部份用的是原生Andoid的API,MIUI的接口保留了O里面的Application级别的控制接口

<meta-data
 android:name="notch.config"
 android:value="portrait|landscape"/>

app如果用这个meta-data声明了横竖屏都绘制到耳朵区,相当于每个页面都设成了LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式。这就需要app确认自己的所有页面会不会被遮挡,特别是横屏和全屏页面。如果想单独修改某个页面,可以单独修改该页面Window的layoutInDisplayCutoutMode属性

6.4. 原生 Android P 的规则和 MIUI Android O 的规则有什么区别

双方在默认模式下的表现是完全一致的,区别主要体现在:

  • Android P 能通过 DisplayCutout object 获取刘海 / Notch / Cutout 的具体信息,但 MIUI Android O 只能获取刘海的高宽信息。
  • Android P 不能控制仅竖屏(或横屏)使用耳朵区,但 MIUI Android O 可以分别配置横竖屏对耳朵区的使用策略。 

Android Q应用适配指南

一、如何体验升级android Q

1.1 vivo机器升级android Q指导

vivo NEX S, vivo NEX A,vivo X27已经提供有可升级到Android Q的开发者预览版本,开发者可以直接通过以下链接指导进行升级试用:

https://dev.vivo.com.cn/documentCenter/doc/210

1.2 vivo远程真机调试

vivo云测平台已经提供关于vivo NEX S, vivo NEX A,vivo X27 Android Q的远程真机服务,开发者可以直接通过以下链接进入远程真机进行试用体验:

https://vcl.vivo.com.cn/#/home/index

1.3 google原生机升级android Q

开发者持有Pixel系列的机器可以直接ota升级,或者下载镜像升级,具体见链接:

https://developer.android.google.cn/preview/download

二、Android Q兼容性变化

2.1 非SDK接口限制

Android 平台开始限制您的应用在 Android 9(API 级别 28)中使用非 SDK 接口,灰名单和白名单的非 SDK 接口已有更新。

适配建议: 

依赖非SDK接口的应用应尽快迁移到 SDK 替代方案,如部分非SDK接口无替代方案,则请求新的公共API。

2.2 重大隐私权变更

2.2.1 分区存储

以 Android Q 为目标平台的应用(以及选择接受这些变更的应用),Android Q 更改了应用对设备外部存储设备中的文件(例如存储在路径 /sdcard 下的文件)的访问方式,只有满足相关条件,应用才能访问其他应用创建的文件。

适配建议: 

如果应用以 Android Q 为目标平台,则在访问外部存储设备中的文件时会进入过滤视图,具有过滤视图的应用对其创建的文件始终拥有读/写权限,无论文件位于特定于此应用的目录以内还是以外。只有在满足以下两个条件时,您的应用才能访问其他应用创建的文件:

(1)您的应用已获得 READ_EXTERNAL_STORAGE 权限。

(2)这些文件位于以下其中一个明确定义的媒体集合中:

照片:存储在 MediaStore.Images 中。

视频:存储在 MediaStore.Video 中。

音乐文件:存储在 MediaStore.Audio 中。

2.2.2 设置位置信息

Android Q 引入了新的位置权限,区分应用前台和后台获取位置信息。如果您的应用在后台时请求访问设备位置信息,则会影响您的应用。如果用户只授权前台获取位置信息权限, 应用在返回后台后则不能再获取位置信息。

适配建议: 

使用新权限在后台访问位置信息,并在没有后台位置信息更新的情况下确保优雅降级。应用应向用户说明他们需要允许该应用始终都能访问设备位置信息,以确保正常运行,然后请求在后台访问位置信息。

2.2.3 后台应用启动

Android Q 对应用可启动 Activity 的时间施加了限制,只有在满足相关条件时才能启动 Activity。此项行为变更适用于在 Android Q 上运行的所有应用。

适配建议: 

检查activity是否符合启动的条件,后台应用都应创建通知以便向用户提供信息。

2.2.4 硬件标识符获取

从 Android Q 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE 特许权限才能访问设备的不可重置标识符(包含 IMEI 和序列号),会对应用依赖device id的功能产生影响。影响在 Android Q 上运行的所有应用,即使这些应用以 Android 9(API 级别 28)或更低版本为目标平台。

适配建议: 

请关注厂商近期发布的适配指导。

2.3 加固版本兼容

Android每个大版本升级,加固方案商都会对android大版本适配和兼容。依赖加固固件的应用需要及时更新最新的加固固件来适配Android新版本,避免加固导致应用无法正常使用或影响应用稳定性。

2.4 第三方SDK兼容

应用会依赖第三方的SDK Jar或Native so库等第三方SDK,Android大版本升级第三方SDK会存在不兼容的问题,需要推动以及更新依赖的第三方SDK适配android Q。

三、参考链接

(1)Android Q所有应用行为变更:https://developer.android.google.cn/preview/behavior-changes-all

(2)Android Q targetSDK 29应用行为变更:https://developer.android.google.cn/about/versions/10/behavior-changes-10

(3)Android Q 中的隐私权:https://developer.android.google.cn/preview/privacy

(4)google pixel系列镜像升级包:https://developer.android.google.cn/preview/download