联系人无法分享至Gmail

项目中遇到Gmail升级后,联系人无法分享至Gmail,但是Google的原生联系人是能正常分享。

先给出方案以及遇到的问题,最后给出分析过程。

修改方案

import android.content.Context;
import android.support.v4.content.FileProvider;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.util.Date;
import java.text.SimpleDateFormat;

    private File getVCardFilePath(Context context, String name) {
        final String fileExtension = ".vcf";
        final String export_file_prefix = "vcards_";
        if (name == null) {
            final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
            final String currentDateString = dateFormat.format(new Date()).toString();
            final String localFilename = export_file_prefix + currentDateString + fileExtension;
            return new File(context.getCacheDir(), localFilename);
        } else {
            return new File(context.getCacheDir(), name + fileExtension);;
        }
    }

    // the uri must be a vcard uri created by Contacts
    public Uri getVCardFilePathByUri(Context context, Uri uri) {
        if (uri == null) {
            return uri;
        }
        File file = getVCardFilePath(context);
        try {
            InputStream in = null;
            OutputStream out = null;
            try {
                in = context.getContentResolver().openInputStream(uri);
                out = new FileOutputStream(file);
                byte[] buf = new byte[8096];
                int size = 0;
                while ((size = in.read(buf)) != -1) {
                    out.write(buf, 0, size);
                }
            } finally {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "exception getVCardFilePathByUri ", e);
        }

        return FileProvider.getUriForFile(context, getString(
                R.string.contacts_file_provider_authority), file);
    }

AndroidManifest.xml

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="@string/contacts_file_provider_authority"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>
res/xml/file_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Offer access to files under Context.getCacheDir() -->
    <cache-path name="my_cache" />
</paths>

res/values/donottranslate_config.xml

<string name="contacts_file_provider_authority">com.android.contacts.files</string>

思路就是本地创建vcf临时文件,然后传递新的uri。分享的代码

    private void shareContact() {
        final String lookupKey = mContactData.getLookupKey();
        final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
        Uri contentUri = getVCardFilePathByUri(shareUri, mContactData.getDisplayName());
        final Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType(Contacts.CONTENT_VCARD_TYPE);
        intent.putExtra(Intent.EXTRA_STREAM, contentUri);

        // Launch chooser to share contact via
        final CharSequence chooseTitle = getResources().getQuantityString(
                R.plurals.title_share_via, /* quantity */ 1);
        final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);

        try {
            mHasIntentLaunched = true;
            ImplicitIntentsUtil.startActivityOutsideApp(this, chooseIntent);
        } catch (final ActivityNotFoundException ex) {
            Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
        }
    }

遇到的问题

FileOutputStream与openFileOutput()的两种写文件方式的区别

1. openFileOutput()

在使用文件对数据进行存储时,Activity提供了openFileOutput()方法可以用于把数据输出到文件中。

    public void getVCardFileNameByUri(Uri uri) {
        if (uri == null) {
            return new String();
        }
        //final String filename = createVCardFileName();
        File file = getVCardFilePath(name);
        try {
            InputStream in = null;
            OutputStream out = null;
            try {
                in = mContext.getContentResolver().openInputStream(uri);
                //out = mContext.openFileOutput(filename, Context.MODE_PRIVATE);
                out = new FileOutputStream(file);
                byte[] buf = new byte[8096];
                int size = 0;
                while ((size = in.read(buf)) != -1) {
                    out.write(buf, 0, size);
                }
            } finally {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "exception getVCardFileNameByUri ", e);
        }
    }
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符"/" ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/com.android.contacts/files/test.vcf ,
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE    =  0
Context.MODE_APPEND    =  32768
Context.MODE_WORLD_READABLE =  1
Context.MODE_WORLD_WRITEABLE =  2
Context.MODE_PRIVATE:为默认操作模式

但是最新sdk(android N之后),除了MODE_PRIVATE模式,其他模式已禁止使用使用会抛出SecurityException异常。

Activity还提供了getCacheDir()和getFilesDir()方法:
getCacheDir()方法用于获取/data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录

2.FileOutputStream

File saveFile = new File(getCacheDir(), "a.txt");
FileOutputStream out = new FileOutputStream(saveFile);
out.write("test".getBytes());
out.close();

该方案的副作用,你以为cache会自动清理吗?笑话!!
cache里的文件还是需要手动清理。这里有个方案清理cache,后续可以考虑加上

    private final long A_DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
    private final String EXPORT_FILE_PREFIX = "vcards_";
    /**
     * Delete the files (that are untouched for more than 1 day) in the cache directory.
     * We cannot rely on VCardService to delete export files because it will delete export files
     * right after finishing writing so no files could be shared. Therefore, our approach to
     * deleting export files is:
     * 1. put export files in cache directory so that Android may delete them;
     * 2. manually delete the files that are older than 1 day when service is connected.
     */
    private void clearExportFiles() {
        for (File file : getCacheDir().listFiles()) {
            final long ageInMillis = System.currentTimeMillis() - file.lastModified();
            if (file.getName().startsWith(EXPORT_FILE_PREFIX) && ageInMillis > A_DAY_IN_MILLIS) {
                file.delete();
            }
        }
    }

分析过程

log定位到

Fail:
Gmail   : Gmail:Error adding attachment
04-27 15:05:03.404750   914 12032 I ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.android.contacts/contacts/as_vcard/347ic77d8d10d1af44f} (has extras)} from uid 10019

PASS:
ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.google.android.contacts.files/my_cache/Admin1.vcf} (has extras)} from uid 10093
ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.android.contacts.files/my_cache/Yy.vcf} (has extras)} from uid 10013

ActivityManager: START u0 {act=android.intent.action.SEND typ=text/x-vcard flg=0xb080001 cmp=com.google.android.gm/.ComposeActivityGmailExternal clip={text/x-vcard U:content://com.google.android.contacts.files/my_cache/vcards_20180428_122715.vcf} (has extras)} from uid 10093

这样就很明显了,Gmail更新后,应该没有去本地保存vcf文件,需要Intent发送时,传入的uri对应就需要是分享联系人的路径。


百度面试失败总结:上来就问你用过哪些开源框架,MDZZ,用了别人的框架就说明你经验多了?自己解决问题就不行吗!唉,算了还是去看开源框架吧。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值