引子
发送自定义消息时 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;
}
}