├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── dbnavigator.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── key.jks ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wepon │ │ └── switchicondemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── wepon │ │ │ └── switchicondemo │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── aaaaa.png │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── wepon │ └── switchicondemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 26 | 27 | 28 | 29 | 39 | 40 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 70 | 71 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /.idea/dbnavigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 遇到的坑 2 | 这里我把做这个功能中遇到的一些问题写在前面,是为了大家能先了解有什么问题存在,遇到这些问题的时候就不慌了,这里我把应用图标和名称先统一使用icon代替进行说明。 3 | 4 | 1、动态替换icon,只能替换内置的icon,无法从服务器端获取来更新icon; 5 | 6 | 2、动态替换icon以后,应用内更新的时候必须要切换到原始icon),否则可能导致更新安装失败(AS上表现为adb运行会失败),或者升级后应用图标出现多个甚至应用图标都不显示的情况(这些问题都可以通过下面我推荐的开发规则解决掉,所以这是一个坑点,不是肯定会发生的问题,只不过大多数人会遇到。); 7 | 8 | 3、Android系统动态替换app icon会有延迟,在不同的手机系统上刷新icon的时间不一样,大概在10秒左右,在这个时间内点击icon会提示应用未安装(提示可能会有差别,目前我的小米就不会提示任何信息,点了没有反应); 9 | 10 | 4、更换icon的代码运行后一会应用就闪退了,或者导致显示中的Dialog和PopupWindow报错崩溃(这个问题和第二个问题有很大的相关性,按我下面给出的规则实行的话是可以解决的。 11 | 12 | update: 2019/02/25 13 | 14 | 5、在android9.0系统上使用了修改应用图标功能后,在最近任务栏里面不显示我们的app。关于这个问题在最后的开发规则里面也会给出解决方案。 15 | 16 | # 多入口配置 17 | 多入口配置,字面意思就是应用程序的多个入口配置,在AndroidManifest.xml中有一个叫activity-alias的标签,这个标签从字面上看就能理解是activity别名的意思,这里我给出一个示例作下相应的说明。 18 | 19 | activity-alias例子说明: 20 | ``` 21 | 28 | // LAUNCHER 启动入口 29 | 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | 37 | ## 显示多个启动入口 38 | 然后这里我先做一个多个启动入口全部显示的app示例,这里需要写的代码都在清单文件中,代码如下: 39 | ``` 40 | 41 | 43 | 44 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ``` 94 | 运行后的效果如下: 95 | 96 | ![](https://user-gold-cdn.xitu.io/2019/1/10/16836d51c1b77260?w=435&h=219&f=jpeg&s=18713) 97 | 可以看到桌面上显示了三个图标,进入的都是MainActivity这个页面,图标我用的自动生成的,就懒的去找图标了,效果上能看出来就行。 98 | 99 | 当然了,实际项目中我们只会显示一个图标,这里我们只需要把"别名1"和"别名2"的android:enabled="true"改为"false"就行了,这样就只显示一个图标了,就不放效果图了。 100 | 101 | ## 代码控制切换不同的应用图标显示 102 | 马上春节了,我们产品说到哪个时间点我们的应用图标就要换成春节用的图标了,当然,前面说了这些图标要先在应用写好,不是通过服务器动态拿的,而是应用内已经写好的。那这个时候我们就需要通过代码进行应用图标的动态切换了,这里我给出Demo里面布局如图: 103 | 104 | ![](https://user-gold-cdn.xitu.io/2019/1/10/16836dab820c7fe1?w=436&h=404&f=jpeg&s=22412) 105 | 106 | 这里三个按钮点击后切换到相应的应用图标和名称,"原ACTIVITY"代表只显示MainActivity这个原来的启动入口,"ALIAS_1"代表别名1,以此类推。 107 | 108 | 这三个按钮点击对应的代码如下: 109 | ``` 110 | /** 111 | * 设置Activity为启动入口 112 | * @param view 113 | */ 114 | public void setActivity(View view) { 115 | PackageManager packageManager = getPackageManager(); 116 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 117 | ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 118 | .DONT_KILL_APP); 119 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 120 | ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 121 | .DONT_KILL_APP); 122 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 123 | ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager 124 | .DONT_KILL_APP); 125 | } 126 | 127 | /** 128 | * 设置别名1为启动入口 129 | * @param view 130 | */ 131 | public void setAlias1(View view) { 132 | PackageManager packageManager = getPackageManager(); 133 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 134 | ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 135 | PackageManager.DONT_KILL_APP); 136 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 137 | ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 138 | .DONT_KILL_APP); 139 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 140 | ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 141 | .DONT_KILL_APP); 142 | } 143 | /** 144 | * 设置别名2为启动入口 145 | * @param view 146 | */ 147 | public void setAlias2(View view) { 148 | PackageManager packageManager = getPackageManager(); 149 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 150 | ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 151 | PackageManager.DONT_KILL_APP); 152 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 153 | ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager 154 | .DONT_KILL_APP); 155 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 156 | ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 157 | .DONT_KILL_APP); 158 | } 159 | 160 | ``` 161 | 162 | 163 | !!!这里要注意一个点,就是ComponentName里面的路径一定要写全了,如果在报错日志看到类似找不到这个路径的日志的话,那十有八九就是这个问题了。 164 | 165 | 切换的代码其实很少,大家看了基本上也都明白了,这里就不做过多解释了。这里我基于隐藏所以别名的情况下,也就是只显示原来的一个APP图标的情况,点一下"ALIAS_1"这个按钮,也就是将图标切换到"别名1",最终效果如下: 166 | 167 | ![](https://user-gold-cdn.xitu.io/2019/1/10/16836e2b8c1e44fd?w=440&h=192&f=jpeg&s=15913) 168 | 169 | 可以看到只显示这一个入口了,但是如果大家在点了"ALIAS_1"之后,马上就返回到主页看盯着这个app的图标,我们会发现在它在大概10s内是没有变化的,在大概10s后才更新成我们切换的那个图标,还有,在它没更新成功的时候如果我们点这个原来的图标,一般会吐司一条“未安装”之类的信息(华为是未安装),这里我的小米是点了没有反应,要等大概10s秒后更新成功了才能点这个图标进入应用。所以,通过代码我们"已经做到了"图标的切换,但是!!! 170 | 171 | 那是不是这样就完了呢??显然不是的,问题还挺多的,我一一道来。 172 | 173 | 不知道大家在点了切换的按钮后有没有一直停在app里面,没有的话我们尝试点完后在app里面不要回到桌面,如果停在app里面的话,我们会在大概10s,也就是更新成功的时候,应用就会发生闪退了,也就是坑4这个问题。这个问题我做了很多测试,总结了一下原因和规避的方法,原因是我们在代码里面设置了我们原来的真实的那个MainActiviy的enable为false,代码如下: 174 | ``` 175 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 176 | ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 177 | .DONT_KILL_APP); 178 | ``` 179 | 只要代码设置了真实的那个Activity的enable为false,也就是代码对应的PackageManager.COMPONENT_ENABLED_STATE_DISABLED,那就会导致我们的应用闪退,那是不是我们不设置这个就好了呢?那我们不设置这个的话怎么隐藏真实的MainActivity的图标呢?这个解决方法后面我会提出来。 180 | 181 | 但是,你以为只有这个问题吗?其实还有坑,只是这个坑不容易发现,这个时候我们回到我们当前的情况,也就是当前我们已经切换到"别名1"了,桌面上也只有这个图标了,我们也能点击这个图标正常使用我们的应用,这些都没有问题,我们以为都是正常的了。但是,这个时候,如果我们通过adb,使用Android Studio运行项目的时候,会提示launch app失败,失败的信息如下: 182 | ``` 183 | 184 | 01/10 16:48:54: Launching app 185 | $ adb shell am start -n "com.wepon.switchicondemo/com.wepon.switchicondemo.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER 186 | Error while executing: am start -n "com.wepon.switchicondemo/com.wepon.switchicondemo.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER 187 | Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.wepon.switchicondemo/.MainActivity } 188 | Error type 3 189 | Error: Activity class {com.wepon.switchicondemo/com.wepon.switchicondemo.MainActivity} does not exist. 190 | 191 | Error while Launching activity 192 | 193 | ``` 194 | 195 | 同样导致的问题还有一个,就是我们代码动态切换了app图标之后,应用升级,也就是更新应用的时候,会导致安装失败,或者是安装完成后出现多个图标甚至是没有图标出现在桌面上了!!这些问题是要遇到运行,或者升级包的时候才会发现的,但是那时候发现就晚了,所以这是一个比较大的坑,这里对应的坑就是我在前面提到的坑2这个点。 196 | 197 | 这里还有一种情况也会导致坑2的发生,例如,我们Demo现在是一个MainActivity和两个别名,如果我们在下一个版本把这两个别名删除了,或者删除了我们当前安装包正在显示的别名,那么安装的新版本可能就不会有应用图标显示了,那就会导致我们应用安装成功了,但是却没有入口! 198 | 199 | 类似的问题还有一些,主要都是在应用升级后发生,而且不管是导致安装失败、安装后没有图标或者安装后产生多个图标,这些现象都是非常严重的,但是这些问题我们都是可以避免的,这里我总结了一些规则,按这些规则进行操作的话是不会产生以上这些问题的,当然,如果还有其他问题的话欢迎交流,因为我们的app也在做这个功能。 200 | 201 | # 动态修改图标的开发规则,防坑专用 202 | 203 | 1、Activity的android:enabled属性,一定不要在代码里面去设置enabled这个值,否则会在切换图标的过程导致应用闪退,目前测试了小米、华为和官方模拟器都有在这个问题。 204 | 205 | 2、清单文件中设置Activity的android:enabled="false”,这个在之后的版本就固定这个值,如果设置为true了,则有可能在应用升级后出现多个图标; 206 | 207 | 3、然后为我们的应用设置一个默认的Activity-alias用来显示图标(也是唯一一个显示的,一般我们也只需要显示一个图标),也是用来代替第一点设置Activity的android:enabled="false”可能导致的桌面上没有应用图标的问题; 208 | 209 | 4、Activity-alias的android:enabled="true"的默认显示的项尽可能不要中途进行变动,如果确实需要使用新的默认值,则使用代码进行动态变换; 210 | 211 | 5、Activity-alias的android:enabled="true"的不要设置为多个,否则会出现多个图标,如果试图通过代码进行隐藏其中的一个或者几个,可能会出现图标消失的情况,这个第2点已经有提过了; 212 | 213 | 6、后面新的版本如果要加新的Activity-alias,那么都要设置android:enabled=“false”,这个清单文件中的值要设置成false,然后再通过代码动态变换; 214 | 215 | 7、后面新的版本的Activity-alias必须包含上一个版本的所有Activity-alias,主要是防止覆盖安装后应用图标消失的情况; 216 | 217 | update:2019年1月14日下午5:09 新发现需要注意的问题 218 | 219 | 8、设置enabled为false的Activity无法在代码中通过显式intent打开,会报错。例如:我在应用里面推送服务推送了一条指定打开页面SplashActivity的通知消息,而这个SplashActivity刚好设置了enabled为false的话,是打不开的,会有错误日志如下,其它同理(所以在项目里我将启动入口的Activity单独写出来了,除了作为启动入口用,就没有别的地方再用到这个Activity了。): 220 | 221 | ![](https://user-gold-cdn.xitu.io/2019/1/14/1684ba4c10c1eae1?w=2826&h=506&f=jpeg&s=268865) 222 | 223 | update:2019年2月25日 新发现需要注意的问题-------------- 224 | 225 | 9、这个问题是关于一开始说的第5个点,在9.0系统的最近任务栏不不显示我们的应用了,如果遇到这个问题,可以尝试设置一个闪屏activity,启动模式设置为SingleInstance,通过这个设置的闪屏activity来启动我们的应用就可以了。或者设置我们的主页activity为SingleInstance启动模式也是可以的,关键是看大家的项目需求,设置不一样从后台回到应用显示的页面也就不一样。这里的关键就是我们设置了enabled为false的activity要和其他的activity不在一个activity栈里面就行了(我暂时没明白这块的原理,也是猜想加代码实践后解决的)。 226 | 227 | 228 | 229 | 以上就是我在做这个功能的过程中总结出来的规则,目前没有发现在其它的问题,有别的问题的朋友欢迎留言讨论,还有,按照这些规则做的话,覆盖安装后的应用图标也会是你上一次通过代码动态修改成功的图标,因为手机的Launcher会有记录,也就是我们通过代码会修改这个在Launcher中的记录。 230 | 231 | 对了,我们在清单文件中配置的Activity和Activity-alias的icon和label信息在新的版本中都是可以换的,这些跟代码无关了,也就是跟我们平常换下app图标名称是一样的操作,希望大家不要误解了这里 -_-!!!。 232 | 233 | 234 | # 最后 235 | 236 | 最后,可能有的同学会想,我现在的应用入口就是默认的一个Activity,默认的enable也是true,也没有配置任何的Activity-alias,而我在上面说的规则中都是建议清单文件中的Activity的android:enabled="false”,那有人可能就会想我的新版本设置成false会不会导致我的图标入口不见了呢?那么我告诉你,如果按照我上面说的规则对你的新版本(可以动态切换图标的版本)进行设置的话,是不会有以上情况产生的,这里我给一个针对这种情况进行升级的版本的清单文件的示例: 237 | ``` 238 | 239 | 241 | 242 | 248 | 249 | 250 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | ``` 292 | 293 | # 简单示例Demo 294 | 295 | 这里放一个简单的示例demo仅供参考 296 | 297 | [https://github.com/ywp0919/SwitchIconDemo](https://github.com/ywp0919/SwitchIconDemo) 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.wepon.switchicondemo" 7 | minSdkVersion 19 8 | targetSdkVersion 28 9 | versionCode 2 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | } 29 | -------------------------------------------------------------------------------- /app/key.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ywp0919/SwitchIconDemo/fe4884e0e70197c97228e969fb3d729ab6c1dfa5/app/key.jks -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/wepon/switchicondemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wepon.switchicondemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wepon.switchicondemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/wepon/switchicondemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wepon.switchicondemo; 2 | 3 | import android.content.ComponentName; 4 | import android.content.pm.PackageManager; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Toast; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_main); 16 | } 17 | 18 | 19 | /** 20 | * 设置Activity为启动入口 21 | * @param view 22 | */ 23 | public void setActivity(View view) { 24 | Toast.makeText(this, "直接设置Activity是会有很多问题的,不要这么做哦!", Toast.LENGTH_SHORT).show(); 25 | 26 | // 这种方式不要用。 27 | // PackageManager packageManager = getPackageManager(); 28 | // packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 29 | // ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 30 | // .DONT_KILL_APP); 31 | // packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 32 | // ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 33 | // .DONT_KILL_APP); 34 | // packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 35 | // ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager 36 | // .DONT_KILL_APP); 37 | } 38 | 39 | /** 40 | * 设置默认的别名为启动入口 41 | * @param view 42 | */ 43 | public void setDefaultAlias(View view) { 44 | Toast.makeText(this, "已切换到默认别名,大概10秒后生效", Toast.LENGTH_SHORT).show(); 45 | 46 | PackageManager packageManager = getPackageManager(); 47 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 48 | ".DefaultAlias"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 49 | PackageManager.DONT_KILL_APP); 50 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 51 | ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 52 | PackageManager.DONT_KILL_APP); 53 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 54 | ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 55 | .DONT_KILL_APP); 56 | 57 | } 58 | 59 | /** 60 | * 设置别名1为启动入口 61 | * @param view 62 | */ 63 | public void setAlias1(View view) { 64 | 65 | Toast.makeText(this, "已切换到别名1,大概10秒后生效", Toast.LENGTH_SHORT).show(); 66 | 67 | PackageManager packageManager = getPackageManager(); 68 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 69 | ".DefaultAlias"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 70 | PackageManager.DONT_KILL_APP); 71 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 72 | ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 73 | PackageManager.DONT_KILL_APP); 74 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 75 | ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 76 | .DONT_KILL_APP); 77 | 78 | 79 | // 不要操作Activity 80 | // packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 81 | // ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 82 | // .DONT_KILL_APP); 83 | } 84 | /** 85 | * 设置别名2为启动入口 86 | * @param view 87 | */ 88 | public void setAlias2(View view) { 89 | 90 | Toast.makeText(this, "已切换到别名2,大概10秒后生效", Toast.LENGTH_SHORT).show(); 91 | 92 | PackageManager packageManager = getPackageManager(); 93 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 94 | ".DefaultAlias"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 95 | PackageManager.DONT_KILL_APP); 96 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 97 | ".NewActivity1"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 98 | PackageManager.DONT_KILL_APP); 99 | packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 100 | ".NewActivity2"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager 101 | .DONT_KILL_APP); 102 | 103 | 104 | // 不要操作Activity 105 | // packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() + 106 | // ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager 107 | // .DONT_KILL_APP); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 |