有没有办法像日历应用程序一样动态更改应用程序图标?

44

我想创建一个Android应用程序,并希望能够动态自动更新应用程序图标,就像日历图标在用户的主屏幕上更新一样。

日历会根据每天显示当月日期的数字更改其图标。还有一个闹钟应用程序,它会根据当前时间更改其图标设置,换句话说,每分钟都会更改。

这不是一个小部件,而是一个真正的应用程序图标。因此它必须有一种方法来实现。我该如何在我的应用程序中做到这一点?


无论您正在查看应用小部件,还是您的主屏幕为您的日历应用程序提供了特殊的挂钩。通常情况下,应用程序无法更新其图标。“但我的不同”--那么请详细解释其他问题是什么以及为什么您的问题不同。 - CommonsWare
@CommonsWare 我刚刚更改了我的文本。正如您在问题中所看到的那样,这不是一个实际的小部件,而是应用程序图标。它会在主屏幕和菜单中发生变化。 - Lennon Spirlandelli
然后,无论您的主屏幕是什么,都有特殊的钩子用于您的日历应用程序和闹钟应用程序。一般来说,应用程序无法更新其图标。 - CommonsWare
@CommonsWare 你所说的 hooks 是什么意思? - Lennon Spirlandelli
7个回答

37

无论你的主屏幕是什么,都有专门针对你的日历应用和闹钟应用的特殊钩子。一般来说,应用程序不能更新它们的图标。

什么是钩子?

例如,三星可以在三星设备上预装三星日历应用。在这些同样的三星设备上,三星的主屏幕可以有特殊的规则来呈现三星日历应用的图标,规则包括显示月份的日期。这是因为三星编写了主屏幕。如果你安装第三方主屏幕,可能不会做同样的事情。毕竟,我可以在一个小时左右内编写一个主屏幕,并且我非常自信我不必为三星的日历应用做任何特殊处理。

没有什么可以阻止三星暴露某种API,以允许开发人员钩入三星的主屏幕,并通知它关于这种事情。无论三星是否打算让第三方使用该API,或者是某个人在黑客攻击三星如何为他们自己的应用程序做到这一点,我无法说。

(顺便说一句,在此引用三星作为一个可能的例子--我不知道他们是否真的有这种功能,如果有,在哪些设备上拥有)

我记得有人有一个 GitHub 项目,试图包装各种主屏幕的专有API。如果我没记错的话,一些支持的功能包括替换应用程序图标或添加徽章(例如未读消息计数)。但是:

  • 只有少数设备支持这些专有API

  • 通过反向工程应用程序发现的未记录和不受支持的API可能会发生变化,并且可能以意想不到的方式失效。

我非常确定Android SDK中没有支持动态应用程序图标的内容。我所知道的唯一方法是使用<activity-alias>,创建N个不同的“活动”,它们都指向相同的实现,但具有不同的图标。应用程序使用PackageManagersetComponentEnabledSetting(),禁用旧的启动器别名并启用不同的别名,以期望主屏幕能够识别并显示新图标。有些主屏幕可以成功,而其他的则只能在重新启动后才能发现变化。

反过来说,我可以编写一个自己的主屏幕。也许我想要提供一些方式,使应用程序可以随时更改其图标,即使没有标准可以遵循。也许我不想这样做。也许我根本不打算使用图标,因为我的主屏幕针对视觉受损者进行了优化,因此它使用文本转语音和硬件键输入。这是我的主屏幕实现,我想要怎么做就怎么做。


4
我不认为这就是答案。例如,在我的设备上,箭头启动器也可以使用日历图标。有更好的解决方案可用,它们展示出了使用不同方法的可能性(尽管都不是官方方法)。其中之一是:https://dev59.com/LnNA5IYBdhLWcg3wEZiT#19593601 - VipulKumar
1
@commonsware 这个功能在 Android Nougat 及更高版本的 Google 日历中存在。问题是“所有开发者都可以使用吗?” - JP Ventura
3
目前不行。即使谷歌在 Android P 中开放了这个功能,主屏幕的开发者也需要选择加入这个功能。目前的 API 中没有任何东西能强制主屏幕使用一个与其缓存不同的图标。 - CommonsWare

8
实际上,在Android O中,即使在应用程序抽屉中,时钟应用程序图标也会显示当前时间,而日历应用程序图标则会显示当前日期。据这篇文章所解释的那样:
Chris Lacy 是知名 Action Launcher 背后的开发者,他在 Android O 中自带的 Clock 应用程序的 APK 文件中发现了一些内容: manifest.xml 中提到了小时、分钟和秒作为带有默认值的层,而图标具有不同元素的不同图像。
这很自然地让他推断出,从 Android O 开始,时钟应用程序图标会动画显示当前时间。无论是将时钟放置在主屏幕上还是在应用程序抽屉中,都会发生这种情况。
Pixel launcher 似乎会根据时间和日期选择不同的图标图像层资源。

5
我是一名"启动器开发者",与动态图标钩子无关,只是按照安卓API提供的标准方式获取活动图标。我发现我的启动器确实在时钟应用图标中显示当前时间,在日历图标中显示当前日期。这个设备是Nubia X,运行着Android 8。操作系统应该对这两个应用进行了某些处理,并在不知情的情况下动态地更改它们的图标。这两个应用是由设备制造商开发的,作为第三方开发人员,你不应该以正常的方式执行相同的操作。

4
@commonsware的回答中增加更多的上下文信息。
我对Google如何在他们的日历和时钟应用程序中实现它进行了更深入的挖掘。
他们似乎在启动器代码中有特殊情况,如下所示:

https://cs.android.com/android/platform/superproject/+/master:packages/apps/Launcher3/src/com/android/launcher3/icons/IconProvider.java;l=54;drc=337c81f6646e8d8351604ee2e1dd1884bc7d0452

在这里: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Launcher3/src/com/android/launcher3/icons/IconProvider.java;l=74;drc=337c81f6646e8d8351604ee2e1dd1884bc7d0452 因此,目前唯一的解决方法是使用具有不同图标的多个活动别名,然后在这些别名之间切换。

http://blog.jakelee.co.uk/programmatically-changing-app-icon/


4

您可以在清单文件中定义多个活动别名,为每个图标设置不同的名称。

<activity-alias
        android:name="OneLauncherAlias"
        android:enabled="true"
        android:icon="@drawable/one"
        android:label="One"
        android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity-alias>

根据需要启用和禁用基于activity-alias的别名。

packageManager.setComponentEnabledSetting(ComponentName(this@MainActivity, com.misles.dynamiclaunchericon.OneLauncherAlias::class.java), PackageManager.COMPONENT_ENABLED_STATE_ENABLED,PackageManager.DONT_KILL_APP)
packageManager.setComponentEnabledSetting(ComponentName(this@MainActivity, com.misles.dynamiclaunchericon.TwoLauncherAlias::class.java), PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP)

具体详见此文章 https://medium.com/@simonmisles/dynamic-launcher-icon-and-name-for-android-e40bf0561715


3

我有同样的问题,但这里提供的答案并不能回答我的问题。
我使用的是原生安卓Ore 8.1操作系统的Google Pixel 2手机,并且有两个应用程序随时间而改变:日历时钟。在时钟应用程序的指针显示当前时间,在日历图标上的数字与今天的日期相对应。
因此考虑到这是默认启动器,可能有一些API可以做类似的事情。

例如,它可以是反映图标上行星位置的行星应用程序,或者显示已使用内存百分比的内存清理程序...


这些应用程序使用小部件来实现,因为它们需要不断更新。然而,如果只是为了特定的原因更改图标,可以使用<activity-alias>来实现。我在我的项目中使用过它。 - Lennon Spirlandelli
2
@LennonPetrick 小部件只出现在桌面上,但我也在谈论启动器。将小部件添加到桌面的过程与添加图标不同。因此,我可以百分之百确定这不是小部件。 - oleg.semen
@oleg.semen 是的,这很有道理。他们可能有不同的方法,因为如果您使用 activity-alias>,则在激活它时桌面上的快捷方式 tend to be removed。也许是因为日历和时钟应用程序来自设备所有者,例如三星之类的公司,我不知道。 - Lennon Spirlandelli

3
启动器通过从日历应用程序访问每天的图标来处理动态日历。例如,使用 Google 日历,启动器开发人员可以执行以下操作:
Resources res = activity.getPackageManager()
                            .getResourcesForApplication("com.google.android.calendar");
int resId = res.getIdentifier("logo_calendar_01_regular", "drawable"
                            , "com.google.android.calendar");
Drawable icon = res.getDrawable(resId);

这将访问月份第一天的日历图标。要访问任何其他日期,“logo_calendar_01_regular”将更改为当前日期。例如,第7天将是“logo_calendar_07_regular”。此外,Google日历有两种样式,“logo_calendar_01_regular”和“logo_calendar_01_round”。

另外,图标主题还包含每个日期的单独图标。


3
它只能让你访问图标。我想要动态地改变我的应用程序图标。 - Lennon Spirlandelli
1
正确的说,这似乎是不可能的,除非启动器执行逻辑。另一个选项是更新小部件。但当然,那并不完全是你所问的。 - Michael Kern

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接