1.在 Settings/System/Developer options/DRAWING 里面看到有 “Display cutout” 菜单
2.根据这个字符串找到
packages/apps/Settings/res/values/strings.xml

<string name="display_cutout_emulation">Display cutout</string>

3.然后根据 display_cutout_emulation 字符串找到在 packages/apps/Settings/res/xml/development_settings.xml 文件中有使用

<ListPreferenceandroid:key="display_cutout_emulation"android:title="@string/display_cutout_emulation"settings:keywords="@string/display_cutout_emulation_keywords" />

4.根据 display_cutout_emulation 这个 key 找到
packages/apps/Settings/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java

public class EmulateDisplayCutoutPreferenceController extends OverlayCategoryPreferenceController {private static final String KEY = "display_cutout_emulation";@VisibleForTestingEmulateDisplayCutoutPreferenceController(Context context, PackageManager packageManager,IOverlayManager overlayManager) {super(context, packageManager, overlayManager, DisplayCutout.EMULATION_OVERLAY_CATEGORY);}public EmulateDisplayCutoutPreferenceController(Context context) {this(context, context.getPackageManager(), IOverlayManager.Stub.asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)));}@Overridepublic String getPreferenceKey() {return KEY;}
}

其中有个重要的参数
DisplayCutout.EMULATION_OVERLAY_CATEGORY 这个参数是用来过滤和匹配 cutout_emulation 模拟apk的

找到 DisplayCutout 类如下:
frameworks/base/core/java/android/view/DisplayCutout.java

public static final String EMULATION_OVERLAY_CATEGORY = "com.android.internal.display_cutout_emulation";

用 OpenGrok 找到代码中有如下几个应用是有这个 category 的
frameworks/base/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml frameworks/base/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml frameworks/base/packages/overlays/DisplayCutoutEmulationCornerOverlay/AndroidManifest.xml frameworks/base/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml frameworks/base/packages/overlays/DisplayCutoutEmulationDoubleOverlay/AndroidManifest.xml

具体使用代码如下

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.internal.display.cutout.emulation.tall"android:versionCode="1"android:versionName="1.0"><overlay android:targetPackage="android"android:category="com.android.internal.display_cutout_emulation"android:priority="1"/><application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
</manifest>

代码中的过滤方法在 packages/apps/Settings/src/com/android/settings/development/OverlayCategoryPreferenceController.java 类中,具体代码如下:

private List<OverlayInfo> getOverlayInfos() {final List<OverlayInfo> filteredInfos = new ArrayList<>();try {List<OverlayInfo> overlayInfos = mOverlayManager.getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE, USER_SYSTEM);for (OverlayInfo overlayInfo : overlayInfos) {if (mCategory.equals(overlayInfo.category)) {filteredInfos.add(overlayInfo);}}} catch (RemoteException re) {throw re.rethrowFromSystemServer();}filteredInfos.sort(OVERLAY_INFO_COMPARATOR);return filteredInfos;
}

找到 DisplayCutoutEmulationTallOverlay 对应代码
首先是 Android.mk 文件,这个文件没什么特别的,只有就 LOCAL_RRO_THEME 这个变量
frameworks/base/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_RRO_THEME := DisplayCutoutEmulationCorner
LOCAL_PRODUCT_MODULE := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_PACKAGE_NAME := DisplayCutoutEmulationCornerOverlay
LOCAL_SDK_VERSION := current
include $(BUILD_RRO_PACKAGE)

这个变量的定义在 build/make/core/build_rro_package.mk 文件中,代码如下

#############################################################################
## Standard rules for installing runtime resouce overlay APKs.
##
## Set LOCAL_RRO_THEME to the theme name if the package should apply only to
## a particular theme as set by ro.boot.vendor.overlay.theme system property.
##
## If LOCAL_RRO_THEME is not set, the package will apply always, independent
## of themes.
##
#############################################################################LOCAL_IS_RUNTIME_RESOURCE_OVERLAY := trueifneq ($(strip $(LOCAL_SRC_FILES)),)$(error runtime resource overlay package should not contain sources)
endifpartition :=
ifeq ($(strip $(LOCAL_ODM_MODULE)),true)partition := $(TARGET_OUT_ODM)
else ifeq ($(strip $(LOCAL_VENDOR_MODULE)),true)partition := $(TARGET_OUT_VENDOR)
else ifeq ($(strip $(LOCAL_PRODUCT_SERVICES_MODULE)),true)partition := $(TARGET_OUT_PRODUCT_SERVICES)
elsepartition := $(TARGET_OUT_PRODUCT)
endififeq ($(LOCAL_RRO_THEME),)LOCAL_MODULE_PATH := $(partition)/overlay
elseLOCAL_MODULE_PATH := $(partition)/overlay/$(LOCAL_RRO_THEME)
endifpartition :=include $(BUILD_SYSTEM)/package.mk

另一个是 config.xml 文件,这个文件是定义刘海屏的参数的
frameworks/base/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><string translatable="false" name="config_mainBuiltInDisplayCutout">M 0,0L -48, 0L -44.3940446283, 36.0595537175C -43.5582133885, 44.4178661152 -39.6, 48.0 -31.2, 48.0L 31.2, 48.0C 39.6, 48.0 43.5582133885, 44.4178661152 44.3940446283, 36.0595537175L 48, 0Z@dp</string><string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string><bool name="config_fillMainBuiltInDisplayCutout">true</bool><dimen name="status_bar_height_portrait">48dp</dimen><dimen name="status_bar_height_landscape">28dp</dimen><dimen name="quick_qs_offset_height">48dp</dimen><dimen name="quick_qs_total_height">176dp</dimen>
</resources>

其中 config_mainBuiltInDisplayCutout 就是刘海屏的 svg 代码

5.但是 EmulateDisplayCutoutPreferenceController 文件中并没有相关的设置逻辑代码,那我猜想应该是在父类中处理的,于是找到 packages/apps/Settings/src/com/android/settings/development/OverlayCategoryPreferenceController.java

看到 onPreferenceChange 中是调用 setOverlay() 方法去设置水滴屏效果的

private boolean setOverlay(String packageName) {final String currentPackageName = getOverlayInfos().stream().filter(info -> info.isEnabled()).map(info -> info.packageName).findFirst().orElse(null);if (PACKAGE_DEVICE_DEFAULT.equals(packageName) && TextUtils.isEmpty(currentPackageName)|| TextUtils.equals(packageName, currentPackageName)) {// Already set.return true;}new AsyncTask<Void, Void, Boolean>() {@Overrideprotected Boolean doInBackground(Void... params) {try {if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) {return mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM);} else {return mOverlayManager.setEnabledExclusiveInCategory(packageName,USER_SYSTEM);}} catch (RemoteException re) {Log.w(TAG, "Error enabling overlay.", re);return false;}}@Overrideprotected void onPostExecute(Boolean success) {updateState(mPreference);if (!success) {Toast.makeText(mContext, R.string.overlay_toast_failed_to_apply, Toast.LENGTH_LONG).show();}}}.execute();return true; // Assume success; toast on failure.
}

其中最关键的方法如下

mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM);mOverlayManager = IOverlayManager.Stub.asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE))

找到对应的 IOverlayManager.aidl 文件如下
frameworks/base/core/java/android/content/om/IOverlayManager.aidl

找到对应的服务
frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java

找到里面的 setEnabledExclusiveInCategory()方法实现,发现最终是 mImpl.setEnabledExclusive() 方法的

@Override
public boolean setEnabledExclusiveInCategory(@Nullable String packageName, int userId)throws RemoteException {try {traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);enforceChangeOverlayPackagesPermission("setEnabledExclusiveInCategory");userId = handleIncomingUser(userId, "setEnabledExclusiveInCategory");if (packageName == null) {return false;}final long ident = Binder.clearCallingIdentity();try {synchronized (mLock) {return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,userId);}} finally {Binder.restoreCallingIdentity(ident);}} finally {traceEnd(TRACE_TAG_RRO);}
}

找到最终的实现类 OverlayManagerServiceImpl
frameworks/base/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java

看到 setEnabledExclusive() 方法如下

boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,final int userId) {if (DEBUG) {Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"+ " withinCategory=%s userId=%d", packageName, withinCategory, userId));}final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);if (overlayPackage == null) {return false;}try {final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);final String targetPackageName = oi.targetPackageName;List<OverlayInfo> allOverlays = getOverlayInfosForTarget(targetPackageName, userId);boolean modified = false;// Disable all other overlays.allOverlays.remove(oi);for (int i = 0; i < allOverlays.size(); i++) {final String disabledOverlayPackageName = allOverlays.get(i).packageName;final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(disabledOverlayPackageName, userId);if (disabledOverlayPackageInfo == null) {modified |= mSettings.remove(disabledOverlayPackageName, userId);continue;}if (disabledOverlayPackageInfo.isStaticOverlayPackage()) {// Don't touch static overlays.continue;}if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory,oi.category)) {// Don't touch overlays from other categories.continue;}// Disable the overlay.modified |= mSettings.setEnabled(disabledOverlayPackageName, userId, false);modified |= updateState(targetPackageName, disabledOverlayPackageName, userId, 0);}// Enable the selected overlay.modified |= mSettings.setEnabled(packageName, userId, true);modified |= updateState(targetPackageName, packageName, userId, 0);if (modified) {mListener.onOverlaysChanged(targetPackageName, userId);}return true;} catch (OverlayManagerSettings.BadKeyException e) {return false;}
}

从注释中可以看到会先去 Disable all other overlays 然后再 Enable the selected overlay,调用的是 mSettings.setEnabled() 方法

关键的方法是 mListener.onOverlaysChanged(targetPackageName, userId);
这个方法的具体实现在 OverlayManagerService.java 文件中,代码如下:

private final class OverlayChangeListenerimplements OverlayManagerServiceImpl.OverlayChangeListener {@Overridepublic void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {schedulePersistSettings();FgThread.getHandler().post(() -> {updateAssets(userId, targetPackageName);final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,Uri.fromParts("package", targetPackageName, null));intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);if (DEBUG) {Slog.d(TAG, "send broadcast " + intent);}try {ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,userId);} catch (RemoteException e) {// Intentionally left empty.}});}
}

关键方法

ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);

frameworks/base/packages/overlays/Android.mk 文件中看到 overlays 包下所有的 Overlay 都是在 frameworks-base-overlays 模块中的,如果要移除这些 overlay 模块只需要把 frameworks-base-overlays 模块移除即可

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE := frameworks-base-overlays
LOCAL_REQUIRED_MODULES := \AccentColorBlackOverlay \AccentColorCinnamonOverlay \AccentColorOceanOverlay \AccentColorOrchidOverlay \AccentColorSpaceOverlay \AccentColorGreenOverlay \AccentColorPurpleOverlay \DisplayCutoutEmulationCornerOverlay \DisplayCutoutEmulationDoubleOverlay \DisplayCutoutEmulationTallOverlay \FontNotoSerifSourceOverlay \IconPackCircularAndroidOverlay \IconPackCircularLauncherOverlay \IconPackCircularSettingsOverlay \IconPackCircularSystemUIOverlay \IconPackCircularThemePickerOverlay \IconPackFilledAndroidOverlay \IconPackFilledLauncherOverlay \IconPackFilledSettingsOverlay \IconPackFilledSystemUIOverlay \IconPackFilledThemePickerOverlay \IconPackRoundedAndroidOverlay \IconPackRoundedLauncherOverlay \IconPackRoundedSettingsOverlay \IconPackRoundedSystemUIOverlay \IconPackRoundedThemePickerUIOverlay \IconShapeRoundedRectOverlay \IconShapeSquareOverlay \IconShapeSquircleOverlay \IconShapeTeardropOverlay \NavigationBarMode3ButtonOverlay \NavigationBarMode2ButtonOverlay \NavigationBarModeGesturalOverlay \NavigationBarModeGesturalOverlayNarrowBack \NavigationBarModeGesturalOverlayWideBack \NavigationBarModeGesturalOverlayExtraWideBackinclude $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)LOCAL_MODULE := frameworks-base-overlays-debuginclude $(BUILD_PHONY_PACKAGE)
include $(call first-makefiles-under,$(LOCAL_PATH))

frameworks-base-overlays 这个模块的控制在 build/make/target/product/handheld_product.mk文件中

$(call inherit-product, $(SRC_TARGET_DIR)/product/media_product.mk)# /product packages
PRODUCT_PACKAGES += \Calendar \Camera2 \Contacts \DeskClock \Email \Gallery2 \LatinIME \Launcher3QuickStep \Music \OneTimeInitializer \Provision \QuickSearchBox \Settings \SettingsIntelligence \StorageManager \SystemUI \WallpaperCropper \frameworks-base-overlaysPRODUCT_PACKAGES_DEBUG += \frameworks-base-overlays-debug

以上是 RRO 刘海屏的相关代码流程,如果要用 SRO 只需要参考壁纸等的 overlay 方法即可,在我们的展锐项目上代码如下,关键是 config_mainBuiltInDisplayCutout 这个刘海屏 svg 图
device/sprd/pike2/xxx/yyy/frameworks/base/core/res/res/values/config.xml

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><string translatable="false" name="config_mainBuiltInDisplayCutout">M 0,0L -33, 0L 0, 22L 33, 0Z@dp</string><string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string><!-- Whether the display cutout region of the main built-in display should be forced toblack in software (to avoid aliasing or emulate a cutout that is not physically existent).--><bool name="config_fillMainBuiltInDisplayCutout">true</bool>
</resources>

然后把这个 overlay 路径添加到 device/sprd/pike2/xxx/sp7731e_1h10_native.mk 文件

DEVICE_PACKAGE_OVERLAYS += $(BOARDDIR)/overlay_SP652_AH6521_Mobicel

因为我们项目上的刘海屏是一个小水滴,所以我把 config_mainBuiltInDisplayCutout 这个 svg 用了一个近似三角形的路径进行替换

深度解析Android Q cutout刘海屏相关推荐

  1. android 关于google刘海屏的解决方案

    转载请标明出处: https://blog.csdn.net/DJY1992/article/details/80689308 本文出自:[奥特曼超人的博客] 推荐: android 兼容所有刘海屏的 ...

  2. android 兼容所有刘海屏的方案大全

    转载请标明出处: https://blog.csdn.net/DJY1992/article/details/80689632 本文出自:[奥特曼超人的博客] 推荐: - ios兼容iphonex刘海 ...

  3. Android P版本 刘海屏适配 与WindowInsets,DisplayCutout使用

    Android P版本的发布,引入了刘海屏的概念,像华为nova 3e,vivo X21等手机厂商也纷纷推出自己的刘海屏手机,我们先预览下HUAWEI nova 3e的机照 从这张图中我们可以看出,所 ...

  4. Android P(3)---Android P版本刘海屏适配指南

    Android P版本刘海屏适配指南 Android P预览版增加了很多亮点新特性,其中最接地气.最直观的改变当属适配了类似于华为P20的顶部凹槽屏幕设计这一项,俗称刘海屏. 在开发者模式中,Andr ...

  5. android Q HIDL(小屏显示)

    android Q HIDL(小屏显示) 手机设备上添加了一个小的lcd屏,需求是可以显示文字与图片.且可以在每个应用里面使用,过CTS,那么可供选择的实现方式也就没几种了. (a)过cts的话最好是 ...

  6. Android 8刘海屏特性,Android 8.0刘海屏适配方案 小米和华为根据官方api即可获取隐藏显示转态,oppo和Vivo没有公开API...

    /** * @return 判断Android O的刘海屏 */ private boolean hasNotchInScreen() { boolean ret_all = false; ret_h ...

  7. 【Android 屏幕适配】异形屏适配 ② ( 需要异形屏适配情况 | 需要异形屏适配的 Android 系统版本 | 刘海屏状态判定 | 异形屏适配调试 - 华为云调试 )

    文章目录 一.需要异形屏适配情况 1.需要异形屏适配的 Android 系统版本 8.0 2.正常有状态栏的界面竖屏不需要适配 3.正常有状态栏的界面横屏需要适配 4.刘海屏状态判定 二.异形屏适配调 ...

  8. android p x6,刘海屏新机诺基亚X6发布 售价1299元起

    网易手机讯,2018年5月16日消息,在"刘海屏"手机逐步普及的今天,诺基亚也于今天带来了全新的X系列首款新机--诺基亚X6,售价1299元起.值得注意的是,未来诺基亚X系列新品也 ...

  9. android 全面屏/刘海屏有效适配

    黑底问题 手机厂商追求高用户体验,屏幕宽高比越做越高.17:9 19:10 18:9 18.5:9所谓全面屏. 原来一般主流手机1920*1080分辨率 16:9高宽比.如果没有单独去配置属性,会导致 ...

最新文章

  1. python xlrd 的merged_cells 里面四个参数的含义
  2. (3)数据库的建立和数据表的操作
  3. win7更新错误0x800b0109_Win7系统Windows update更新出现错误代码800b0101怎么办
  4. 利用事件冒泡实现简单的网页计算器
  5. php编码和c语言,急求windows下用c语言开发PHP扩展时,在C语言里把字符串转成utf-8编码再打印的方法。...
  6. Android之版本检测和更新
  7. cd mysql 权限不够_.bash_profile权限不够_cdmysql权限不够
  8. 计算机网络原理201810自考,2018年10月自考04741计算机网络原理试卷及答案
  9. 苹果2019新款iPhone售价惊曝:咬牙仍坚持高价位?
  10. ccs是轮_CCS-船型标准.pdf
  11. 实现一个 webpack loader
  12. css之div内部靠右
  13. 计算化学对计算机知识的要求,计算化学软件对大学有机化学教学的应用
  14. 基于JAVA实现的图形化页面置换算法
  15. pdm系统是归档服务器吗,PDM系统的主要功能
  16. html文件引用.vue 文件的方式
  17. uniApp 使用uView遇到的小坑 LineProgress 线形进度条
  18. js判断两个字符串相等问题
  19. LittleVGL(LVGL)学习笔记——PC 模拟器的安装和使用(CodeBlocks)
  20. CentOS 7.0.1503

热门文章

  1. 心流:最优体验心理学 1
  2. matlab火箭模型,基于Matlab/Simulink的新型火箭建模与仿真平台搭建
  3. 《Vue.js技术内幕》读后感
  4. 淘宝商品详情APi接口(原数据APP、h5)
  5. Android Camera开发系列:设置对焦模式模式
  6. 随时随地掌上邮,飞邮Android版邮件客户端正式提供试用
  7. STL:容器共性机制、容器元素深拷贝和浅拷贝问题
  8. [Hadoop]Hadoop Archives
  9. 刘东明应邀赴台湾担任金手指网络奖终审评委
  10. 区块链电子合同,杜绝萝卜章风险