融云自定义拍照插件

引子


发送自定义消息时 ConverstaionActivity(自定义的会话详情页)不显示的问题

一般官方文档会让你在布局汇总使用 来实现会话详情页,但当收不到消息的时候,你本能的想用fragment去刷新页面,实际上不需要

<fragment
        android:id="@+id/conversation"
        android:name="io.rong.imkit.fragment.ConversationFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title_bar" />

这个问题很隐晦,是由于发送消息时调用了RongIMClient.getInstance().sendMessage 而不是 RongIM.getInstance().sendMessage ,的原因,RongIMClient是IMLib的类,不更新UI, RongIM是IMKit的类

集成拍照的插件

融云自带的图片发送已经集成了拍照,但是是隐藏在第二级菜单中的,如下图在这里插入图片描述
如果要实现单独添加一个组件来拍照获取图片,按官方文档,需要处理文件的上传,进度等等,实现 下面这个方法的逻辑

RongIM.getInstance().sendImageMessage(Conversation.ConversationType.PRIVATE, "9517", imgMsg, null, null, new RongIMClient.SendImageMessageCallback() {

        @Override
        public void onAttached(Message message) {
                //保存数据库成功
        }

        @Override
        public void onError(Message message, RongIMClient.ErrorCode code) {
                //发送失败
        }

        @Override
        public void onSuccess(Message message) {
                //发送成功
        }

        @Override
        public void onProgress(Message message, int progress) {
                //发送进度
        }
});

我的想法是,既然官方已经实现了,就复用官方的代码吧,查看官方的ImagePlugin的实现逻辑,

public void onClick(Fragment currentFragment, RongExtension extension) {
        this.conversationType = extension.getConversationType();
        this.targetId = extension.getTargetId();
        String[] permissions = new String[]{"android.permission.READ_EXTERNAL_STORAGE", "android.permission.CAMERA"};
        if (PermissionCheckUtil.checkPermissions(currentFragment.getContext(), permissions)) {
            Intent intent = new Intent(currentFragment.getActivity(), PictureSelectorActivity.class);
            extension.startActivityForPluginResult(intent, 23, this);
        } else {
            extension.requestPermissionForPluginResult(permissions, 255, this);
        }

    }

是打开了PictureSelectorActivity,跟进去看,一堆多选图片的逻辑,最后发送给了PicturePreviewActivity 在 中的

data.putExtra("sendOrigin", PicturePreviewActivity.this.mUseOrigin.getChecked());
                data.putExtra("android.intent.extra.RETURN_RESULT", list);
                PicturePreviewActivity.this.setResult(1, data);
                PicturePreviewActivity.this.finish();` 

中实现了数据传送,并在PictureSelectorActivity的onActivityResult 中通过

if (resultCode == 1) {
                this.setResult(-1, data);
                this.finish();
            }

设置了数据,明显这个数据又交给了下一个地方去实现,具体在哪呢,一堆源码跟踪后,发现是调用了 RongExtension.class 中的onActivityPluginResult

public void onActivityPluginResult(int requestCode, int resultCode, Intent data) {
        int position = (requestCode >> 8) - 1;
        int reqCode = requestCode & 255;
        IPluginModule pluginModule = this.mPluginAdapter.getPluginModule(position);
        if (pluginModule != null) {
            if (this.mExtensionClickListener != null && resultCode == -1) {
                if (pluginModule instanceof ImagePlugin) {
                    boolean sendOrigin = data.getBooleanExtra("sendOrigin", false);
                    ArrayList<Uri> list = data.getParcelableArrayListExtra("android.intent.extra.RETURN_RESULT");
                    this.mExtensionClickListener.onImageResult(list, sendOrigin);
                } else if (pluginModule instanceof DefaultLocationPlugin || pluginModule instanceof CombineLocationPlugin) {
                    double lat = data.getDoubleExtra("lat", 0.0D);
                    double lng = data.getDoubleExtra("lng", 0.0D);
                    String poi = data.getStringExtra("poi");
                    String thumb = data.getStringExtra("thumb");
                    this.mExtensionClickListener.onLocationResult(lat, lng, poi, Uri.parse(thumb));
                }
            }

            pluginModule.onActivityResult(reqCode, resultCode, data);
        }

    }

其中 this.mExtensionClickListener.onImageResult(list, sendOrigin); 就是实现逻辑,但他判断了是否是ImagePlugin的子类,看到这里,大概逻辑了解了

首先 实现插件类的基本动作

public class TakePhotoPlugin   implements IPluginModule , IPluginRequestPermissionResultCallback

在onClick方法中模仿融云 PictureSelectorActivity requestCamera()的逻辑

@Override
    public void onClick(Fragment currentFragment, RongExtension extension) {
        this.extension = extension;
        this.conversationType = extension.getConversationType();
        this.targetId = extension.getTargetId();
        String[] permissions = new String[]{"android.permission.READ_EXTERNAL_STORAGE", "android.permission.CAMERA"};
        if (PermissionCheckUtil.checkPermissions(currentFragment.getContext(), permissions)) {
            requestCamera();
        } else {
            extension.requestPermissionForPluginResult(permissions, 255, this);
        }

    }
/**
     * 打开相机
     */
    protected void requestCamera() {
        Context ct = extension.getContext();
        File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        if (!path.exists()) {
            path.mkdirs();
        }

        String name = System.currentTimeMillis() + ".jpg";
        File file = new File(path, name);
        mTakePictureUri = Uri.fromFile(file);
        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        List<ResolveInfo> resInfoList = ct.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        if (resInfoList.size() <= 0) {
            Toast.makeText(ct, ct.getResources().getString(io.rong.imkit.R.string.rc_voip_cpu_error), Toast.LENGTH_SHORT).show();
        } else {
            Uri uri;
            try {
                uri = FileProvider.getUriForFile(ct, ct.getPackageName() + ct.getString(io.rong.imkit.R.string.rc_authorities_fileprovider), file);
            } catch (Exception var10) {
                RLog.e(TAG, "requestCamera", var10);
                throw new RuntimeException("Please check IMKit Manifest FileProvider config. Please refer to http://support.rongcloud.cn/kb/NzA1");
            }

            Iterator var7 = resInfoList.iterator();

            while(var7.hasNext()) {
                ResolveInfo resolveInfo = (ResolveInfo)var7.next();
                String packageName = resolveInfo.activityInfo.packageName;
                ct.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                ct.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }

            intent.putExtra("output", uri);
            extension.startActivityForPluginResult(intent, 23, this);
        }
    }

融云在图片交给PicturePreviewActivity中的方法是通过PictureSelectorActivity中一个实现Parcelable接口的类PictureSelectorActivity.PicItem来传递的,这个类是外部无法访问,那么只有反射了

try {
                    //反射设置uri ,调用融云自己的 PicturePreviewActivity类的话,他用到了PictureSelectorActivity中的 PicItem
                    PictureSelectorActivity.PicItemHolder.itemList = new ArrayList();
                    PictureSelectorActivity.PicItem item = new PictureSelectorActivity.PicItem();
                    Field uri = PictureSelectorActivity.PicItem.class.getDeclaredField("uri");
                    uri.setAccessible(true);
                    uri.set(item, this.mTakePictureUri.getPath());
                    PictureSelectorActivity.PicItemHolder.itemList.add(item);
                    PictureSelectorActivity.PicItemHolder.itemSelectedList = null;
                    Intent intent = new Intent(extension.getContext(), PicturePreviewActivity.class);
                    extension.startActivityForPluginResult(intent, 0, this);
                } catch (Exception e) {
                    e.printStackTrace();
                }

(最近升级了融云,发现官方把PictureSelectorActivity.PicItemHolder 设置为非public了) 那么需要反射静态内部类并设置其值 ,如下

try {
                    //反射获取设置静态内部类
                    // 获取类
                    Class mediaItemClazz = Class.forName("io.rong.imkit.plugin.image.PictureSelectorActivity$MediaItem");
                    // 获取属性
                    Field uriField = mediaItemClazz.getDeclaredField("uri");
                    // 设置访问权限
                    uriField.setAccessible(true);
                    Field mediaTypeField = mediaItemClazz.getDeclaredField("mediaType");
                    mediaTypeField.setAccessible(true);
                    // 实例化对象
                    Object item = mediaItemClazz.newInstance();
                    // 设置属性
                    uriField.set(item, this.mTakePictureUri.getPath());
                    mediaTypeField.set(item, 1);

                    Class holderClazz = Class.forName("io.rong.imkit.plugin.image.PictureSelectorActivity$PicItemHolder");
                    // 获取静态属性
                    Field itemListField = holderClazz.getDeclaredField("itemList");
                    itemListField.setAccessible(true);
                    Field itemSelectedListField = holderClazz.getDeclaredField("itemSelectedList");
                    itemSelectedListField.setAccessible(true);
                    ArrayList arrayList = new ArrayList();
                    arrayList.add(item);
                    // 给静态属性赋值
                    itemListField.set(null, arrayList);
                    itemSelectedListField.set(null, null);


                    Intent intent = new Intent(extension.getContext(), PicturePreviewActivity.class);
                    extension.startActivityForPluginResult(intent, 0, this);
                } catch (Exception e) {
                    e.printStackTrace();
                }

传递完数据,最后实现插入,发送图片的是 RongExtension 下的onActivityPluginResult 中的方法,关键是mExtensionClickListener 也是内部的,也无法调用,又只能反射了

try {
                //动用反射实现RongExtension中的监听功能的手动调用
                Field field = RongExtension.class.getDeclaredField("mExtensionClickListener");
                field.setAccessible(true);
                IExtensionClickListener listener = (IExtensionClickListener) field.get(extension);
                if(listener != null && data != null) {
                    boolean sendOrigin = data.getBooleanExtra("sendOrigin", false);
                    ArrayList<Uri> list = data.getParcelableArrayListExtra("android.intent.extra.RETURN_RESULT");
                    if(list != null)
                        listener.onImageResult(list , sendOrigin);
                }
            } catch(Exception e) {
                e.printStackTrace();
            }

基本到这里,所有逻辑就完成了。
下面贴出完整代码

public class TakePhotoPlugin /* extends ImagePlugin*/  implements IPluginModule , IPluginRequestPermissionResultCallback{
    ConversationType conversationType;
    String targetId;
    Uri mTakePictureUri;

    RongExtension extension;
    private String TAG = TakePhotoPlugin.class.getSimpleName();

    public TakePhotoPlugin() {
    }

    @Override
    public Drawable obtainDrawable(Context context) {
        return ContextCompat.getDrawable(context, drawable.selector_rc_ext_plugin_takephoto);
    }

    @Override
    public String obtainTitle(Context context) {
        return context.getString(R.string.takephoto);
    }

    @Override
    public void onClick(Fragment currentFragment, RongExtension extension) {
        this.extension = extension;
        this.conversationType = extension.getConversationType();
        this.targetId = extension.getTargetId();
        String[] permissions = new String[]{"android.permission.READ_EXTERNAL_STORAGE", "android.permission.CAMERA"};
        if (PermissionCheckUtil.checkPermissions(currentFragment.getContext(), permissions)) {
//            Intent intent = new Intent(currentFragment.getActivity(), RongIMTakePhotoActivity.class);
//            extension.startActivityForPluginResult(intent, 23, this);
            requestCamera();
        } else {
            extension.requestPermissionForPluginResult(permissions, 255, this);
        }

    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        //resultCode == 0 没有拍照,直接返回了
        if(requestCode == 23 && resultCode == 0) {
            return;
        }
        //resultCode == 0 拍照了,有返回值
        if(requestCode == 23 && resultCode != 0) {
            if (this.mTakePictureUri != null) {

                try {
                    //反射设置uri ,调用融云自己的 PicturePreviewActivity类的话,他用到了PictureSelectorActivity中的 PicItem
                    PictureSelectorActivity.PicItemHolder.itemList = new ArrayList();
                    PictureSelectorActivity.PicItem item = new PictureSelectorActivity.PicItem();
                    Field uri = PictureSelectorActivity.PicItem.class.getDeclaredField("uri");
                    uri.setAccessible(true);
                    uri.set(item, this.mTakePictureUri.getPath());
                    PictureSelectorActivity.PicItemHolder.itemList.add(item);
                    PictureSelectorActivity.PicItemHolder.itemSelectedList = null;
                    Intent intent = new Intent(extension.getContext(), PicturePreviewActivity.class);
                    extension.startActivityForPluginResult(intent, 0, this);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        //resultcode == 1 是 PicturePreviewActivity 中 132行设置。
        if (resultCode == 1) {
            try {
                //动用反射实现RongExtension中的监听功能的手动调用
                Field field = RongExtension.class.getDeclaredField("mExtensionClickListener");
                field.setAccessible(true);
                IExtensionClickListener listener = (IExtensionClickListener) field.get(extension);
                if(listener != null && data != null) {
                    boolean sendOrigin = data.getBooleanExtra("sendOrigin", false);
                    ArrayList<Uri> list = data.getParcelableArrayListExtra("android.intent.extra.RETURN_RESULT");
                    if(list != null)
                        listener.onImageResult(list , sendOrigin);
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * 打开相机
     */
    protected void requestCamera() {
        Context ct = extension.getContext();
        File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        if (!path.exists()) {
            path.mkdirs();
        }

        String name = System.currentTimeMillis() + ".jpg";
        File file = new File(path, name);
        mTakePictureUri = Uri.fromFile(file);
        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        List<ResolveInfo> resInfoList = ct.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        if (resInfoList.size() <= 0) {
            Toast.makeText(ct, ct.getResources().getString(io.rong.imkit.R.string.rc_voip_cpu_error), Toast.LENGTH_SHORT).show();
        } else {
            Uri uri;
            try {
                uri = FileProvider.getUriForFile(ct, ct.getPackageName() + ct.getString(io.rong.imkit.R.string.rc_authorities_fileprovider), file);
            } catch (Exception var10) {
                RLog.e(TAG, "requestCamera", var10);
                throw new RuntimeException("Please check IMKit Manifest FileProvider config. Please refer to http://support.rongcloud.cn/kb/NzA1");
            }

            Iterator var7 = resInfoList.iterator();

            while(var7.hasNext()) {
                ResolveInfo resolveInfo = (ResolveInfo)var7.next();
                String packageName = resolveInfo.activityInfo.packageName;
                ct.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                ct.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }

            intent.putExtra("output", uri);
            extension.startActivityForPluginResult(intent, 23, this);
        }
    }


    /**
     * 权限请求回调
     * @param fragment
     * @param extension
     * @param requestCode
     * @param permissions
     * @param grantResults
     * @return
     */
    @Override
    public boolean onRequestPermissionResult(Fragment fragment, RongExtension extension, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (PermissionCheckUtil.checkPermissions(fragment.getActivity(), permissions)) {
//            Intent intent = new Intent(fragment.getActivity(), RongIMTakePhotoActivity.class);
//            extension.startActivityForPluginResult(intent, 23, this);
            requestCamera();
        } else {
            extension.showRequestPermissionFailedAlter(PermissionCheckUtil.getNotGrantedPermissionMsg(fragment.getActivity(), permissions, grantResults));
        }

        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值