使用 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()
,传递与清单中相同的权限。
我有一个通过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()
,传递与清单中相同的权限。