使用 GMail 应用附加 apk 文件时权限被拒绝 android

Permisssion denied while attaching apk file with GMail app android

我有一个通过share Intent发送apk文件的需求,我也顺利实现了。但问题仅在通过 GMail 发送 apk 时出现,我在尝试附加时收到 permission denied。我真的不知道 GMail 发生了什么。请帮助我完成您的答案和解决方案。任何小提示和技巧对我也很有用。提前致谢

我正在使用以下代码通过共享意图发送 APK:

 public static void appSend(ArrayList<File> files,Context context){
        Intent shareIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
        shareIntent.setType("application/vnd.android.package-archive");
        shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
        ArrayList<Uri> uriFiles = new ArrayList<Uri> ();
        for(File file: files) {
            uriFiles.add (Uri.fromFile (file));
        }
        shareIntent.putParcelableArrayListExtra (Intent.EXTRA_STREAM, uriFiles);

        try {
            context.startActivity (Intent.createChooser (shareIntent, "Share via"));
        } catch (android.content.ActivityNotFoundException ex) {
            Toast.makeText (context, "There are no share applications installed.", Toast.LENGTH_SHORT).show();
        }
    }

Androidmanifest.xml:

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sample.share"
    android:versionCode="2"
    android:versionName="1.1">

    <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE"/>

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name="com.sample.share.SplashActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name="com.sample.share.LandingScreenActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar.FullScreen"/>

        <activity
            android:name="com.sample.share.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/ShareToolbar"/>
    </application>

</manifest>

适配器class获取设备中存在的应用程序列表
ShowAppsAdapter.java

public class ShowAppsAdapter extends RecyclerView.Adapter<ShowAppsAdapter.ViewHolder> implements Filterable{
    private Context context;
    private static List pkgAppsList;
    private List pkgAppsListOriginal;
    private int mLayout;
    private ArrayList<File> mFileList;
    static OnItemClickListener mItemClickListener;

    private ItemFilter filter = new ItemFilter();
    private HashMap<String,Boolean> itemChecked = new HashMap<String,Boolean>();
    private Typeface tf;

    public ShowAppsAdapter(Context context, List pkgAppsList, int layout, Typeface tf) {
        this.context = context;
        this.pkgAppsList = pkgAppsList;
        this.pkgAppsListOriginal = pkgAppsList;
        this.mLayout = layout;
        this.mFileList = new ArrayList<File> ();
        this.tf = tf;

        for (int i = 0; i < this.getItemCount (); i++) {
            itemChecked.put ((String) ((ResolveInfo) pkgAppsList.get (i)).loadLabel (context.getPackageManager ()), false); // initializes all items value with false
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        View v = LayoutInflater.from(viewGroup.getContext()).inflate(mLayout, viewGroup, false);


        return new ViewHolder(v);
    }




    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {

        final ResolveInfo info = (ResolveInfo) pkgAppsList.get(position);
        final File file = new File(info.activityInfo.applicationInfo.publicSourceDir);
        String appSize= (Utils.getFileSize(context, info.activityInfo.applicationInfo.sourceDir));

        holder.txtAppName.setText(info.loadLabel(context.getPackageManager()));


        holder.txtAppSize.setText(/*info.activityInfo.packageName*/""+appSize);

        holder.txtAppName.setTypeface(tf);
        holder.txtAppSize.setTypeface(tf);

        holder.txtAppIcon.setImageDrawable(info.loadIcon(context.getPackageManager()));

        holder.chkTick.setChecked(itemChecked.get(info.loadLabel (context.getPackageManager ())));
        holder.chkTick.setOnClickListener (new View.OnClickListener () {
            @Override
            public void onClick (View view) {

                toggleSelected((String) info.loadLabel (context.getPackageManager ()), holder.chkTick.isChecked(),  file);

            }
        });
    }


    private void toggleSelected (String name, boolean b, File file) {
        itemChecked.put (name,b);
        if(b){
            addAppList(file);
        }else{
            removeAppList(file);
        }
    }


    private void addAppList (File file) {

        mFileList.add (file);
    }

    private void removeAppList (File file) {

        mFileList.remove (file);
    }

    public ArrayList<File> getAppList(){


        return mFileList;
    }

    @Override
    public int getItemCount() {
        return pkgAppsList == null ? 0 : pkgAppsList.size();
    }

    @Override
    public Filter getFilter () {
        return filter;
    }




    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        TextView txtAppName;
        TextView txtAppSize;
        ImageView txtAppIcon;
        CheckBox chkTick;

        public ViewHolder(View itemView) {
            super(itemView);
            txtAppName = (TextView)itemView.findViewById(R.id.text_app_name);
            txtAppSize = (TextView)itemView.findViewById(R.id.txt_app_size);
            txtAppIcon = (ImageView)itemView.findViewById(R.id.img_app_icon);
            chkTick = (CheckBox)itemView.findViewById (R.id.chk_tick);
            itemView.setOnClickListener(this);

        }



        @Override
        public void onClick(View v) {
            mItemClickListener.onItemClick(v, getAdapterPosition (),((ResolveInfo)pkgAppsList.get (getAdapterPosition ()))); //OnItemClickListener mItemClickListener;
        }

    }


    public interface OnItemClickListener {
        public void onItemClick (View view, int position, ResolveInfo o);
    }

    public void SetOnItemClickListener(final OnItemClickListener mItemClickListener) {
        this.mItemClickListener = mItemClickListener;
    }
    @Override
    public long getItemId(int i) {
        return i;
    }


    private class ItemFilter extends Filter{
        @Override
        protected FilterResults performFiltering (CharSequence charSequence) {
            String searchString = charSequence.toString ().toLowerCase ();
            FilterResults results = new FilterResults();
            final List list = pkgAppsListOriginal;

            int count = list.size();
            final List nlist = new ArrayList<ResolveInfo>(count);

            String filterableString ;

            for (int i = 0; i < count; i++) {
                final ResolveInfo info = (ResolveInfo) pkgAppsListOriginal.get(i);
                filterableString = (String) info.loadLabel (context.getPackageManager ());
                if (filterableString.toLowerCase().contains(searchString)) {
                    nlist.add(pkgAppsListOriginal.get (i));
                }
            }

            results.values = nlist;
            results.count = nlist.size();

            return results;
        }

        @Override
        protected void publishResults (CharSequence charSequence, FilterResults filterResults) {
            pkgAppsList = (List) filterResults.values;
            notifyDataSetChanged ();
        }
    }
}

在应用程序之间共享文件时,您应该使用 FileProvider as per the setting up file sharing training,因为这样可以确保接收应用程序无需任何权限即可读取文件。从 Android 6.0 开始,Gmail 不请求存储权限,这意味着它无法读取您提供的任何文件 Uri.fromFile()

您将在清单中声明 FileProvider

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.myapp.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
</provider>

一个 XML 文件(在本例中称为 filepaths.xml 以匹配上面的清单声明),它通过 FileProvider:

确定哪些目录可用
<paths>
    <files-path path="images/" name="myimages" />
</paths>

然后,代替使用 Uri.fromFile(file),您使用 FileProvider.getUriForFile(),传递与清单中相同的权限。