android相机、相册相关(android13 适配)13又又又又又又搞事,让第三方难以生存,踩坑

前几天公司开发的新项目要上线,结果......

由于项目中有用户更换头像功能,应用市场经过检测android13无法更换头像,也没有崩溃也没有反应,所以App就被无情退回,相当无奈,那就查问题,手头有没有android13测试机,怎么办,怎么办,怎么办...那就想办法,还好vivo有云测试,也是真机那种,关键还免费,帮了大忙。

    开始不是太了解android13究竟更新了什么,一顿查找资料.......过了几天其实还是很懵逼,网上资料很少介绍,那就自己一点一点一点试呗,中国有句古话(只要功夫深铁杵磨成针)让我听从古训,坚持磨针。就开始将之前第三方选择照片框架去掉自己就用原生试。果然经过很多弯路终于解决问题,一下给大家分享。

android13 之前读写文件权限是这样写的
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE">

android13 之后读写文件权限它是这样写的

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:maxSdkVersion="32"/>

还得加上(重点)

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

将读取本地图片、视频、音频权限细分

开始我真的没有怎么明白怎么回事,用法上也有错误、我判断版本进行申请不同的权限,结果就是实现不了,一直有错,有一顿查看资料,一顿试,最终上天被我感动赐我一缕灵光,上代码

XXPermissions.with(activity)
        // 不适配分区存储应该这样写
        //.permission(Permission.MANAGE_EXTERNAL_STORAGE)android 13 废弃AndroidManifest.xml也要去掉
        // 适配分区存储应该这样写
        .permission(Permission.CAMERA)//android 13使用一下权限
        .permission(Permission.READ_MEDIA_IMAGES)//读取照片
        .permission(Permission.READ_MEDIA_VIDEO)//读取视频
        .permission(Permission.READ_MEDIA_AUDIO)//读取音频(按需申请)
        .interceptor(new PermissionInterceptor())
        .request(new OnPermissionCallback() {

            @Override
            public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
                if (allGranted) {
                    if ("1".equals(type)) {//这是自己写的工具类下面给你们贴出源码
                        PhotoUtils.getPicFromCamera(activity, resultCode);
                    } else {
                        PhotoUtils.getPicFromAlbm(activity, resultCode);
                    }
                }

            }

            @Override
            public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
                OnPermissionCallback.super.onDenied(permissions, doNotAskAgain);
                if (doNotAskAgain) {
                    ToastUtil.show("权限被拒绝!!!");
                }
            }
        });

就是上面的代码,你说可气不可气,直接申请新权限即可,当你申请新权限时,也就直接默认同一之前的权限了,也可以理解为新权限覆盖了老权限不用苦逼的判断版本什么的了。XXPermissions权限框架自己网上查看

权限申请完既可以调用相机相册功能了,但是android11后也更改了不少东西,android13对文件管理又做了调整,很多文件你都无法读取写入,本以为经过上面一顿操作可以了,在这里又卡住了,继续研究呗,经过九九八十一难也给解决了。代码

 /**
     * 从相机获取图片
     *
     * @param activity   上下文
     * @param resultCode 请求吗
     */
    public static void getPicFromCamera(Activity activity, int resultCode) {
        //用于保存调用相机拍照后所生成的文件
        tempFile = new File(activity.getExternalFilesDir(DIRECTORY_DCIM), "/wy_head.jpg");
        //跳转到调用系统相机
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        //判断版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {  
 //如果在Android7.0以上,使用FileProvider获取Uri xxx.client.xxx.fileProvider 红色是自己的//包名,也可以自己定义,反正要和AndroidManifest.xml里面定义的一致就行 intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(activity, file_provider, tempFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);

        } else {    //否则使用Uri.fromFile(file)方法获取Uri
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
        }
        activity.startActivityForResult(intent, resultCode);
    }

    /**
     * 从相册获取图片
     *
     * @param activity   上下文
     * @param resultCode 请求吗
     */
    public static void getPicFromAlbm(Activity activity, int resultCode) {
        Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
        photoPickerIntent.setType("image/*");
        activity.startActivityForResult(photoPickerIntent, resultCode);
    }

这就是我自己慢慢弄出来的,也许大神们也有弄但是我试过一直弄不好自己就开始一边看大神博客一边自己想办法,得以解决。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        Bitmap bitmap;
        File imageFile = new File(getExternalFilesDir(DIRECTORY_DCIM), "/wy_img.jpg");
        switch (requestCode) {
            case CAMERA_REQUEST_CODE:
                //用相机返回的照片去调用剪裁也需要对Uri进行处理
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    bitmap = PhotoUtils.getBitmapFromUri(LaunchComplaintActivity.this, PhotoUtils.getUri(LaunchComplaintActivity.this));
                } else {
                    bitmap = PhotoUtils.getBitmapFromUri(LaunchComplaintActivity.this, Uri.fromFile(PhotoUtils.tempFile));
                }
                PhotoUtils.qualityCompress(bitmap, imageFile);//压缩图片
                UPlLoad(imageFile);
                break;
            case ALBUM_REQUEST_CODE:
                bitmap = PhotoUtils.getBitmapFromUri(LaunchComplaintActivity.this, data.getData());
                PhotoUtils.qualityCompress(bitmap, imageFile);//压缩图片
                UPlLoad(imageFile);
                break;
            default:
                break;
        }
    }
}

基本代码就这样了,一些工具类源码给大家分享到下面

package sol.client.com.utils;

import static android.os.Environment.DIRECTORY_DCIM;
import static android.os.Environment.DIRECTORY_DOWNLOADS;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;

import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;

import com.base_libs.utils.ToastUtil;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import sol.client.com.permission.PermissionInterceptor;

public class PhotoUtils {


    public static File tempFile;
    /**
     * 这个要和 AndroidManifest provider一致
     * 他的值和 AndroidManifest 一致即可没有标准
     **/
    private static final String file_provider = "sol.client.com.fileProvider";

    /**
     * 拍照相册权限 适配android 13
     *
     * @param activity
     * @param type       1、相机请求 2、相册请求(可自己定义)
     * @param resultCode
     */
    public static void requirePermission(Activity activity, String type, int resultCode) {

        XXPermissions.with(activity)
                // 不适配分区存储应该这样写
                //.permission(Permission.MANAGE_EXTERNAL_STORAGE)android 13 废弃AndroidManifest.xml也要去掉
                // 适配分区存储应该这样写
                .permission(Permission.CAMERA)//android 13使用一下权限
                .permission(Permission.READ_MEDIA_IMAGES)
                .permission(Permission.READ_MEDIA_VIDEO)
                .permission(Permission.READ_MEDIA_AUDIO)
                .interceptor(new PermissionInterceptor())
                .request(new OnPermissionCallback() {

                    @Override
                    public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
                        if (allGranted) {
                            if ("1".equals(type)) {
                                PhotoUtils.getPicFromCamera(activity, resultCode);
                            } else {
                                PhotoUtils.getPicFromAlbm(activity, resultCode);
                            }
                        }

                    }

                    @Override
                    public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
                        OnPermissionCallback.super.onDenied(permissions, doNotAskAgain);
                        if (doNotAskAgain) {
                            ToastUtil.show("权限被拒绝!!!");
                        }
                    }
                });
    }

    /**
     * 从相机获取图片
     *
     * @param activity   上下文
     * @param resultCode 请求吗
     */
    public static void getPicFromCamera(Activity activity, int resultCode) {
        //用于保存调用相机拍照后所生成的文件
        tempFile = new File(activity.getExternalFilesDir(DIRECTORY_DCIM), "/wy_head.jpg");
        //跳转到调用系统相机
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        //判断版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {   //如果在Android7.0以上,使用FileProvider获取Uri
//            intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(activity, file_provider, tempFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);

        } else {    //否则使用Uri.fromFile(file)方法获取Uri
            intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
        }
        activity.startActivityForResult(intent, resultCode);
    }

    /**
     * 从相册获取图片
     *
     * @param activity   上下文
     * @param resultCode 请求吗
     */
    public static void getPicFromAlbm(Activity activity, int resultCode) {
        Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
        photoPickerIntent.setType("image/*");
        activity.startActivityForResult(photoPickerIntent, resultCode);
    }

    /**
     * 保存切图到本地 DOWNLOADS-WYXY文件夹
     *
     * @param activity
     * @param name
     * @param bmp
     * @return
     */
    public static String saveImage(Activity activity, String name, Bitmap bmp) {
        File appDir = new File(activity.getExternalFilesDir(DIRECTORY_DOWNLOADS), "WYXY");
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        String fileName = name + ".jpg";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.flush();
            fos.close();
            return file.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static Uri getUri(Activity activity) {
        return FileProvider.getUriForFile(activity, file_provider, PhotoUtils.tempFile);
    }

    //Uri转化为Bitmap
    public static Bitmap getBitmapFromUri(Activity activity, Uri uri) {
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(activity.getContentResolver().openInputStream(uri));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    public static String getPathToUri(Activity activity, Uri uri) {
        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = activity.managedQuery(uri, proj, null, null, null);
        int actual_image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        //游标跳到首位,防止越界
        cursor.moveToFirst();
        return cursor.getString(actual_image_column_index);
    }

    public File saveFile(Bitmap bm, String fileName) {//将Bitmap类型的图片转化成file类型,便于上传到服务器
        String path = Environment.getExternalStorageDirectory() + "/wy";
        File dirFile = new File(path);
        if (!dirFile.exists()) {
            dirFile.mkdir();
        }
        File myCaptureFile = new File(path + fileName);
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
            bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);
            bos.flush();
            bos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return myCaptureFile;

    }

    /**
     * 质量压缩
     * 设置bitmap options属性,降低图片的质量,像素不会减少
     * 第一个参数为需要压缩的bitmap图片对象,第二个参数为压缩后图片保存的位置
     * 设置options 属性0-100,来实现压缩(因为png是无损压缩,所以该属性对png是无效的)
     *
     * @param bmp
     * @param file 保存到的位置
     */
    public static void qualityCompress(Bitmap bmp, File file) {
        // 0-100 100为不压缩
        int quality = 70;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 把压缩后的数据存放到baos中
        bmp.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(outputStream.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path
            name="camera_photos"
            path="" />
    </paths>
</resources>

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            Bitmap bitmap;
            File imageFile = new File(getExternalFilesDir(DIRECTORY_DCIM), "/wy_img.jpg");
            switch (requestCode) {
                case CAMERA_REQUEST_CODE:
                    //用相机返回的照片去调用剪裁也需要对Uri进行处理
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        bitmap = PhotoUtils.getBitmapFromUri(LaunchComplaintActivity.this, PhotoUtils.getUri(LaunchComplaintActivity.this));
                    } else {
                        bitmap = PhotoUtils.getBitmapFromUri(LaunchComplaintActivity.this, Uri.fromFile(PhotoUtils.tempFile));
                    }
                    PhotoUtils.qualityCompress(bitmap, imageFile);//压缩图片
                    UPlLoad(imageFile);//自己写上传代码逻辑
                    break;
                case ALBUM_REQUEST_CODE:
                    bitmap = PhotoUtils.getBitmapFromUri(LaunchComplaintActivity.this, data.getData());
                    PhotoUtils.qualityCompress(bitmap, imageFile);//压缩图片
                    UPlLoad(imageFile);//自己写上传代码逻辑
                    break;
                default:
                    break;
            }
        }
    }

注:

扫描二维码关注公众号,回复: 15211162 查看本文章
PhotoUtils.requirePermission(LaunchComplaintActivity.this, "1", CAMERA_REQUEST_CODE);

这是点击按钮后调用,根据type区分拍照还是相册选取,CAMERA_REQUEST_CODE这是自己定义的code方便onActivityResult返回是接收你的图片

希望能帮助需要帮助的人,有问题可以私聊

猜你喜欢

转载自blog.csdn.net/qiankun_zyk/article/details/129797081
今日推荐