如何自定义Intent.createChooser的显示结果
Intent是android核心的概念之一,Intent为android系统提供了真正的开放。android的姿态是开放了,但却没有做到位。
拿“发邮件”这一功能来说,为了使用Intent机制来发送邮件,调用方需要知道Intent的规则,如action、uri、MIMEtype和category。但是在哪里能找到这些规则呢?官方文档里没有,还好能求助google,然后就发现StackOverflow有无数的人提过或者困惑于这个问题。从google搜索结果里没有看到导向官方文档的链接,由此大胆推测,官方文档确实没有相关的说明。
再者,从编写mail应用程序的角度来说,他们也迷茫-到底要支持那些Intent呢?我的Intent-filter到底要怎么写?难道是android team的geek精神作祟,一定要广大开发者分析gmail的apk才甘心?忍不住吐槽啊,这直接导致了用户体验不统一,程序猿编程不统一。
以上感受均来自我的一个非常合理的需求 - 发送多个附件的邮件
开始,我是这么做的
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{emailTo});
emailIntent.putExtra(Intent.EXTRA_CC, new String[]{emailCC});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, emailText);
ArrayList<Uri> uris = new ArrayList<Uri>();
for (String file : filePaths) {
File fileIn = new File(file);
Uri u = Uri.fromFile(fileIn);
uris.add(u);
}
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
context.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
结果除了我要的mail app,各种打酱油的Bluetooth、Evernote等等都出现在了选择列表中,选择太多对用户不是好事儿。这不是我想要的
为了限定所列出的选择全部都是mail application,我指定了Uri
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
Uri uri = Uri.parse("mailto:"+emailTo+"?subject="+subject+"&body="+emailText);
emailIntent.setData(uri);
for (String file : filePaths) {
File fileIn = new File(file);
Uri u = Uri.fromFile(fileIn);
uris.add(u);
}
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
context.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
此时chooser界面显示的只有mail application了,但选择gmail时,程序崩溃;选择系统默认的邮件程序同样崩溃;只有K9 mail能够正常运行。查看了android源代码中mail application的源代码,发现在Uri的schema是mailto时,确实不支持附件;而K9源代码却处理了这种情况。
于是决定不
使用由Intent.createChooser启动的界面(其实就是ChooserActivity.java),而是自定义chooser界面。做了才发现,如果尽可能保持与ChooserActivity风格一致,工作量非常之大。最终放弃,去看看Intent还有没有什么可以挖掘的。
无心插柳柳成荫,发现Intent有个EXTRA_INITIAL_INTENTS,文档中这样描述
A Parcelable[] of Intent or LabeledIntent objects as set with
putExtra(String, Parcelable[]) of additional activities
to place a the front of the list of choices,
when shown to the user with a ACTION_CHOOSER.
再看看ChooserActivity和ResolverActivity的源代码,由EXTRA_INITIAL_INTENTS指定的Intent会“有条件地”显示在选择界面上。什么条件呢?那就是一定要存在能够处理Intent.createIntent函数第一个参数Intent的Activity。于是代码如下
Intent emailIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{emailTo});
emailIntent.putExtra(Intent.EXTRA_CC, new String[]{emailCC});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, emailText);
ArrayList<Uri> uris = new ArrayList<Uri>();
for (String file : filePaths) {
File fileIn = new File(file);
Uri u = Uri.fromFile(fileIn);
uris.add(u);
}
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); PackageManager pm = context.getPackageManager();
Intent sendMultipleIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
List<ResolveInfo> aList = pm.queryIntentActivities(sendMultipleIntent, PackageManager.MATCH_DEFAULT_ONLY);
Intent mailIntent = new Intent(Intent.ACTION_SEND);
mailIntent.setData(Uri.fromParts("mailto", "", null));
List<ResolveInfo> bList = pm.queryIntentActivities(mailIntent, PackageManager.MATCH_DEFAULT_ONLY);
//TODO find out all target ResolbeInfo using aList and bList
List<ResolveInfo> targets = ...;
ArrayList<Intent> targetIntents = new ArrayList<Intent>();
for (ResolveInfo info : targets) {
ActivityInfo ai = info.activityInfo;
Intent intent = new Intent(emailIntent);
intent.setPackageName(ai.packageName);
intent.setClass(ai.packageName, ai.name);
targetIntents.add(intent);
}
Intent chooser = Intent.createChooser(targetIntents.remove(0), "Send mail...");
chooser.putParcelableArrayListExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents);
context.startActivity(chooser);
最终展现给用户的选择包括,符合createChooser第一个参数的应用(其实是指定了packageName和className的Intent),以及由EXTRA_INITIAL_INTENTS指定的应用。
注意,这里没有使用Intent.setComponent来明确指定要启动的Activity,而是通过setPackageName和setClass来指定。这是因为,在createChooser处理过程中,第一个参数intent指定的component会在ResolverActivity中强制被设置为null。这样的结果是什么呢?当component被设置为null后,targetIntents.remove(0)其实就是emailIntent,那么createChooser会显示所有满足emailIntent的应用,然后在加上由EXTRA_INITIAL_INTENTS指定的应用。你会发现选择界面中出现了重复的应用。
如何自定义Intent.createChooser的显示结果的更多相关文章
- android Intent.createChooser 应用选择
在微博案例: 1.public void onClickShare(View view) { 2. 3. Intent intent=new Intent(Intent.ACTION_SEND); 4 ...
- 自定义支持多行显示的RadioGroup
自定义支持多行显示的RadioGroup 原生的RadioGroup继承自LinearLayout,即只能支持一横排或者一竖排的排列显示RadioButton 现在改写RadioGroup,使它支持多 ...
- [翻译] MCProgressView 使用自定义图片做进度显示
MCProgressView 使用自定义图片做进度显示 https://github.com/Baglan/MCProgressView Progress bar view with custom i ...
- 写了一个迷你toast提示插件,支持自定义提示文字和显示时间
写了一个迷你toast提示插件,支持自定义提示文字和显示时间,不想用其他第三方的ui插件,又想要toast等小效果来完善交互的同学可以试试, 代码中还贡献了一段css能力检测js工具函数,做项目的时候 ...
- Android 开发之:Intent.createChooser() 妙用
大家对该功能第一印象就是ApiDemo 里面的 其只有区区几行代码 提取为: Intent intent = new Intent(Intent.ACTION_GET_CONTENT); inten ...
- iOS tabbar 自定义小红点 消息显示,定制边框、颜色、高宽
一般我们需要显示消息数,会利用到系统提供的api UIApplication.sharedApplication().applicationIconBadgeNumber = 10 但如果我们不想显示 ...
- 通过代码自定义cell 新浪微博页面显示
通过代码自定义cell(cell的高度不一致)(如果高度一致的cell 用xib实现) 1.新建一个集成自UItableVIewCell的类 2.重写initWithStle :方法 - (insta ...
- 第二篇、Swift_自定义 tabbar 的 badgeValue显示样式
在实际的开发中,我们常常需要根据实际的需求,去改变bageValue的显示样式,默认是红色的背景,白色的字体颜色 使用方式: class BKTabBarController: UITabBarCon ...
- WPF 中,动态创建Button,并使Button得样式按照自定义的Resource样式显示
第一步:自定义一个Button的样式 1.新建一个xaml文件,在其中自定义好自己的Resources 这个Resource 的根节点是 <ResourceDictionary xmlns=&q ...
随机推荐
- 整理mysql的一些常用用法
在php项目中,使用mysql的一些常用的语句,今天有空系统整体一下.有些整理自网络,如有错误,请指正,谢谢.... #显示数据库和显示数据表show databases;use databaseNa ...
- The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. 异常
异常信息如下: The Struts dispatcher cannot be found. This is usually caused by using Struts tags without t ...
- Repeater隔行变色,两个方式
<table> <tr> <td>用户编号</td> </tr> <asp:Repeater ID="rptUser&quo ...
- 如果有需要确解MD5的,可以尝试这个网站。
http://www.hashkiller.co.uk/md5-decrypter.aspx
- Python简明教程---学习笔记
字符双引号括起,数字不括: 分隔符为逗号(,),不能为空格 变量定义时即赋值 采用utf-8编码:#-*-coding:utf-8-*-或者#coding:utf-8 字符串定义:单/双引号括起 %符 ...
- WPS Office手机版调用接口代码指导帖之一(Android)
经常会有一些喜欢开发鼓捣的童鞋问我们,WPS Office手机版是否提供调用接口,希望在android中使用一个调用命令,直接调用WPS手机版来打开指定的DOC文件,而不用弹出一个程序可选列表(如果用 ...
- aix5.1 5.2 5.3 6.1 7.1运维技术总结
++++++++++++++++++++++++++++ + Ruiy 20014-10 zz + +Technology Area; + Tel:150 55198367 + QQ:5160 591 ...
- ubuntu 设置网卡为混杂模式 以及网络配置命令
1. ifconfig eth0 promisc 设置eth0为混杂模式. ifconfig eth0 -promisc 取消它的混杂模式 botnet@botnet-virtual-machine: ...
- c++适配器模式
你想使用一个已经存在的类,而它的接口不符合你的需求. 创建一个类需要和其他类协同完成任务,需要一个适配器将其他类的方法都转接到适配器当中 什么是适配器模式:有一个目标客户类想适用已经存在类的接口,但是 ...
- C# 如何利用反射来加载程序集,并调用程序集中有关类的方法【转】
假设在C盘根目录下有个Dog的Dll程序集文件,该程序集文件中包含类Dog 该类中有个狗叫几声的方法,如何通过反射来加载这个C:\Dog.dll,并且调用Dog类里面的Sound方法呢: public ...