Launcher3 一键改变Icon Shape 原理浅析

Launcher3 一键改变Icon Shape 原理浅析

在Android O Launcher3 Google 团队增加了一个新特性,可以在设置里面更改 桌面Icon 形状,分别可以改为系统默认、方形、方圆形、圆形、泪珠形。
在Android P Launcher3 Google团队继续保持这一神奇特性,那么,看上去好高大上神奇的特性是怎样实现的呢?带着这个疑问,follow me》》》》》

下面我们基于Android P Launcher3 分析Launcher3 实现基本原理。

一.先看桌面设置中的菜单实现:

源码位置 Launcher3\src\com\android\launcher3\SettingsActivity.javaPreference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);if (iconShapeOverride != null) {if (IconShapeOverride.isSupported(getActivity())) {IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);} else {getPreferenceScreen().removePreference(iconShapeOverride);}}

可以看到isSupported方法是是否支持设置图标形状的判断条件。

public static boolean isSupported(Context context) {    if (!Utilities.ATLEAST_OREO) {return false;}// Only supported when developer settings is enabledif (Settings.Global.getInt(context.getContentResolver(),Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 1) {return false;}try {if (getSystemResField().get(null) != Resources.getSystem()) {// Our assumption that mSystem is the system resource is not true.return false;}} catch (Exception e) {// Ignore, not supportedreturn false;}return getConfigResId() != 0;
}

由源码 可以看出 满足几个条件才能看到设置选项

1.判断系统SDK 版本是否>=26
2.是否打开了开发者选项。如果开发者选项没打开,就看不到这个菜单。(至于为神马开发者模式才可以看到待追踪!!!可能让厂商在适配此特性吧)
3.大概意思就是获取不到mSystem,如果获取不到,说明当前系统存在问题。

二.菜单出现后,我们选择其中一种形状来设置:

<string-array translatable="false" name="icon_shape_override_paths_values"><item></item><item>M50,0L100,0 100,100 0,100 0,0z</item><item>M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z</item><item>M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0</item><item>M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z</item>
</string-array><string-array translatable="false" name="icon_shape_override_paths_names"><!-- Option to not change the icon shape on home screen. [CHAR LIMIT=50] --><item>@string/icon_shape_system_default</item><item>Square</item><item>Squircle</item><item>Circle</item><item>Teardrop</item>
</string-array>

发现每个Item对应一个path 矢量图的string值。

private static class PreferenceChangeHandler implements OnPreferenceChangeListener {    private final Context mContext;private PreferenceChangeHandler(Context context) {mContext = context;}@Overridepublic boolean onPreferenceChange(Preference preference, Object o) {String newValue = (String) o;if (!getAppliedValue(mContext).equals(newValue)) {// Value has changedProgressDialog.show(mContext,null /* title */,mContext.getString(R.string.icon_shape_override_progress),true /* indeterminate */,false /* cancelable */);new LooperExecuter(LauncherModel.getWorkerLooper()).execute(new OverrideApplyHandler(mContext, newValue));}return false;}
}
private static class OverrideApplyHandler implements Runnable {    private final Context mContext;private final String mValue;private OverrideApplyHandler(Context context, String value) {mContext = context;mValue = value;}@Overridepublic void run() {// Synchronously write the preference.prefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();// Clear the icon cache.LauncherAppState.getInstance(mContext).getIconCache().clear();// Wait for ittry {Thread.sleep(PROCESS_KILL_DELAY_MS);} catch (Exception e) {Log.e(TAG, "Error waiting", e);}// Schedule an alarm before we kill ourself.Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME).setPackage(mContext.getPackageName()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);PendingIntent pi = PendingIntent.getActivity(mContext, RESTART_REQUEST_CODE,homeIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);mContext.getSystemService(AlarmManager.class).setExact(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 50, pi);// Kill processandroid.os.Process.killProcess(android.os.Process.myPid());}
}

设置的时候执行上面代码,主要将设置的保存到本地,清除图标缓存,然后kill Launcher process 重启launcher。

三.怎样通过矢量图工作的:

源码位置 :Launcher3\src\com\android\launcher3\graphics\IconShapeOverride.java

IconShapeOverride.apply(getContext());private static int getConfigResId() {    return Resources.getSystem().getIdentifier("config_icon_mask", "string", "android");
}

public static void apply(Context context) {    if (!Utilities.isAtLeastO()) {return;}String path = getAppliedValue(context);if (TextUtils.isEmpty(path)) {return;}if (!isSupported(context)) {return;}// magictry {Resources override =new ResourcesOverride(Resources.getSystem(), getConfigResId(), path);getSystemResField().set(null, override);} catch (Exception e) {Log.e(TAG, "Unable to override icon shape", e);// revert value.prefs(context).edit().remove(KEY_PREFERENCE).apply();}
}

其中ResourcesOverride是继承了Resources,并且重写了getString方法。

private static class ResourcesOverride extends Resources {   private final int mOverrideId;private final String mOverrideValue;@SuppressWarnings("deprecated")public ResourcesOverride(Resources parent, int overrideId, String overrideValue) {super(parent.getAssets(), parent.getDisplayMetrics(), parent.getConfiguration());mOverrideId = overrideId;mOverrideValue = overrideValue;}@NonNull@Overridepublic String getString(int id) throws NotFoundException {if (id == mOverrideId) {return mOverrideValue;}return super.getString(id);}
}

在根据源码看下getSystemResField方法:

private static Field getSystemResField() throws Exception {
Field staticField = Resources.class.getDeclaredField("mSystem");
staticField.setAccessible(true);
return staticField;
}

这个方法是反射系统Resources中mSystem变量。

小结:

从Launcher 源代码可以看出大概的意思就是Launcher中将Resources 的mSystem设置成了ResourcesOverride对象,
也就是说Resources的getSystem方法获取的是我们重写的ResourcesOverride,当调用getString方法的时候,走的也是重写的方法。getString方法里面判断了如果string id 是config_icon_mask这个的时候,返回我们传入的mOverrideValue,这个mOverrideValue就是用户选择的图标形状值。

2.pmg.jpg

追踪下 AdaptiveIconDrawable的构造方法:

/**
* The one constructor to rule them all. This is called by all public
* constructors to set the state and initialize local properties.
*/

AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {mLayerState = createConstantState(state, res);if (sMask == null) {sMask = PathParser.createPathFromPathData(Resources.getSystem().getString(R.string.config_icon_mask));}mMask = PathParser.createPathFromPathData(Resources.getSystem().getString(R.string.config_icon_mask));mMaskMatrix = new Matrix();mCanvas = new Canvas();mTransparentRegion = new Region();
}

此方法的Resources.getSystem().getString(R.string.config_icon_mask),通过getString方法,如果id是config_icon_mask,则返回的是mOverrideValue,mOverrideValue就是上面5种里面的一种。

四.Launcher是如何获取应用图标的: 

public Drawable getFullResIcon(LauncherActivityInfo info) {
return mIconProvider.getIcon(info, mIconDpi);
}public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {return info.getIcon(iconDpi);
}

最终调用到LauncherActivityInfo的getIcon方法

/**
* Returns the icon for this activity, without any badging for the profile

 * @param density The preferred density of the icon, zero for default density. Use* density DPI values from {@link DisplayMetrics}.* @see #getBadgedIcon(int)* @see DisplayMetrics* @return The drawable associated with the activity.*/public Drawable getIcon(int density) {// TODO: Go through LauncherAppsServicefinal int iconRes = mActivityInfo.getIconResource();Drawable icon = null;// Get the preferred density icon from the app's resourcesif (density != 0 && iconRes != 0) {try {final Resources resources= mPm.getResourcesForApplication(mActivityInfo.applicationInfo);icon = resources.getDrawableForDensity(iconRes, density);} catch (NameNotFoundException | Resources.NotFoundException exc) {}}// Get the default density iconif (icon == null) {icon = mActivityInfo.loadIcon(mPm);}return icon;
}

通过以上步骤可以看出,Launcher获取应用图标的时候时候,如果该应用是支持AdaptiveIcon的话,返回的图标就是根据形状裁剪出来的AdaptiveIconDrawable,Launcher从系统拿到的图标已经是想要的形状图标了。
这就是在把launcher进程kill掉,重启 launcher 重新获取加载就是被裁减过的Icon形状了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/207579.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

离高薪测试你可能只差这个理解:python 内存管理机制

近期有小伙伴跟我反馈 &#xff0c;面试有遇到面试官问 python 内存管理机制相关的问题&#xff0c;因为之前没有特地的去了解过&#xff0c;所以不知道怎么回答。 所以今天就专门写了这篇 python 内存管理机制的文章&#xff0c;来给大家系统的梳理一下内存管理机制的知识点&…

1.查看表的基本结构,表的详细结构和修改表名

查看表的基本结构,表的详细结构和修改表名 1.查看数据表基本结构 有强迫症或健忘症的小伙伴们在建好数据库和表以后&#xff0c;通常会怀疑自己刚才是不是敲错了&#xff0c;怎么办&#xff1f;如果不是使用图形界面是不是就没法查看啦&#xff1f; 不存在的&#xff0c;这就…

大创项目推荐 医学大数据分析 - 心血管疾病分析

文章目录 1 前言1 课题背景2 数据处理3 数据可视化4 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于大数据的心血管疾病分析 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9…

给Flask加上百度翻译功能,这样可以用中文进行搜索了

上一篇博客&#xff1a;Flask之手搓bootstrap翻页-CSDN博客 里&#xff0c;对 OMDb API - The Open Movie Database 的搜索&#xff0c;只能使用英文&#xff0c;才能搜索出电影信息&#xff0c;如果使用中文&#xff0c;是搜索不到结果的。这里就需要使用翻译&#xff0c;把中…

多功能智能遥测终端机 5G/4G+北斗多信道 视频采集传输

计讯物联多功能智能遥测终端机&#xff0c;全网通5G/4G无线通信、弱信号地区北斗通信&#xff0c;多信道自动切换保障通信联通&#xff0c;丰富网络接口及行业应用接口&#xff0c;支持水利、环保、工业传感器、控制终端、智能终端接入&#xff0c;模拟量/数字量/信号量采集&am…

深入理解 Python 中的 eval 函数

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com eval 是 Python 中一个强大而灵活的函数&#xff0c;它允许将字符串作为代码执行。然而&#xff0c;由于其潜在的安全风险&#xff0c;使用时需要谨慎。本文将深入探讨 eval 函数的各个方面&#xff0c;包括基本…

delphi/python 实现小红书xhs用户作品列表和图片/视频无水印解析

技术学习&#xff0c;请勿用与非法用途&#xff01;&#xff01;&#xff01; 成品图用户作品列表接口 /api/sns/web/v1/user_posted?num30&cursor&user_id642bf0850000000011022c4e&image_scenes http Get方式&#xff0c;请求头需要带上x-s x-t签名验证笔记明细…

直流负载箱的技术发展趋势和创新有哪些?

直流负载箱广泛应用于电子、通信、航空航天等领域&#xff0c;随着科技的不断发展&#xff0c;直流负载箱也在不断创新和改进&#xff0c;直流负载箱在负载电流和电压的测量方面要求高精度和高稳定性。未来的发展趋势是提高负载箱的测量精度和稳定性&#xff0c;以满足更高要求…

uniapp 使用 $emit和$on——$on中无法为data中的变量赋值

问题在于this的指向&#xff0c; 解决办法是使用变量保存$on&#xff0c;其次再为data中的值赋值 以下是具体代码&#xff1a; 1、html代码&#xff1a; <view class"form_picker" click"selePositionFun()"><view class""><inp…

Git

第1章 Git 概述 Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git 易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于 Subversi…

系统设计之数据库

为您的项目选择正确的数据库是一项复杂的任务。许多数据库选项都适合不同的用例&#xff0c;很快就会导致决策疲劳。 我们希望这份备忘单提供高级指导&#xff0c;以找到符合您项目需求的正确服务并避免潜在的陷阱。 注意&#xff1a;Google 关于其数据库用例的文档有限。尽管…

软件测试卷王的自述,我难道真的很卷?

前言 前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作都还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff…

北邮22级信通院数电:Verilog-FPGA(12)第十二周实验(2)彩虹呼吸灯(bug已解决 更新至3.0)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.代码部分 1.1一些更新和讲解 1.2改正后的…

解密HubSpot CMS Hub:构建引人入胜的企业网站!

在数字化时代&#xff0c;网站是企业与客户互动的重要窗口。为了在竞争激烈的市场中脱颖而出&#xff0c;企业需要一个现代化、用户友好且高度可定制的网站。而HubSpot CMS Hub作为一款领先的内容管理系统&#xff0c;为企业提供了独特的优势&#xff0c;让网站建设变得更加轻松…

Private Set Intersection from Pseudorandom CorrelationGenerators 最快PSI!导览解读

目录 一、概述 二、相关介绍 三、性能对比 四、技术细节 1.KKRT 2.Pseudorandom Correlation Generators 3.A New sVOLE-Based BaRK-OPRF 4.BaRK-OPRF 五、总结 参考文献 一、概述 这篇文章的主要脉络和核心思想是探讨如何利用伪随机相关生成器&#xff08;PCG&#…

【AI】以大厂PaaS为例,看人工智能技术方案服务能力的方向(2/2)

目录 三、解决方案 3.1 人脸身份验证 3.2 图像审核&#xff08;暴恐、色情等&#xff09; 3.3 人脸会场签到 3.4 机器人视觉 3.5 视频审核 3.6 电商图文详情生成 3.7 智能客服 接上回&#xff1a; 【AI】以大厂PaaS为例&#xff0c;看人工智能技术方案服务能力的方向&…

麒麟系统进入救援模式或者是crtl D界面排查方法

如出现以下图片的情况可能需要修复磁盘&#xff1a; V10GFB-desktop&#xff1a; 开机后发现一致卡在此界面&#xff1a; 按esc键后有以下报错信息说明在/etc/fstab里面编写的外挂磁盘的命令有问题 解决方法如下&#xff1a;进入单用户模式对/etc/fstab进行修改&#xff1a; …

智能优化算法应用:基于侏儒猫鼬算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于侏儒猫鼬算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于侏儒猫鼬算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.侏儒猫鼬算法4.实验参数设定5.算法结果6.参考…

天津大数据培训机构品牌 数据分析师的发展方向

大数据专业还是有一定难度的&#xff0c;毕竟大数据开发技术所包含的编程技术知识是比较杂且多的如果是计算机专业的学生或者自身有一定基础的人学&#xff0c;相对来说会比较容易&#xff0c;但对于零基础小伙伴学习来说&#xff0c;想要学习大数据&#xff0c;难度还是很高的…

3D Web可视化平台助力Aras开发PLM系统:提供数据访问、可视化和发布功能

HOOPS中文网慧都科技是HOOPS全套产品中国地区指定授权经销商&#xff0c;提供3D软件开发工具HOOPS售卖、试用、中文试用指导服务、中文技术支持。http://techsoft3d.evget.com/ Aras是一个面向数字化工业应用的开放性平台&#xff0c;帮助世界领先的复杂互联产品制造商转变其产…