# 自定义事件

为了让用户在使用七鱼 SDK 时拥有更大的灵活性,我们将持续添加更多的事件自定义响应接口。

# URL链接点击响应

如果用户或者客服发送的文本消息中带有 URL 链接,SDK 将会解析出该链接。用户点击这个链接后,SDK 默认会打开系统的浏览器,并访问这个 URL。同时 SDK 提供了一个配置选项,允许应用自己处理这个点击事件,用于在应用的内置浏览器中打开链接,以及做钓鱼网址过滤等等场景。

设置自定义的链接点击响应需要在初始化 SDK 时,为 YSFOptionsonMessageItemClickListener 赋值:

// 初始化代码
// YSFOptions options = new YSFOptions();
OnMessageItemClickListener messageItemClickListener = new OnMessageItemClickListener() {
    // 响应 url 点击事件
    public void onURLClicked(Context context, String url) {
        // 打开内置浏览器等动作
    }
}
options.onMessageItemClickListener = messageItemClickListener;

// ... 其他初始化代码
// Unicorn.init(this, "你的appid", options, new UILImageLoader());

# 咨询界面退出事件监听(只适用于 Activity 接入方式)

SDK 在 V5.4.0 版本开放了咨询界面返回事件的监听,当用户从咨询界面退出的时候会回调相关方法通知 APP ,具体请查看如下代码:

//当我们在设置 YSFOption 的时候,设置方式如下
YSFOptions ysfOptions = new YSFOptions();
	options.sdkEvents = new SDKEvents();
	options.sdkEvents.eventProcessFactory = new EventProcessFactory() {
    	@Override
    	public UnicornEventBase eventOf(int eventType) {
        	if (eventType == 3) {
           	 return new DemoLeaveActivityEvent();
        	}
       		return null;
    	}
};

//DemoLeaveActivityEvent 实现方式
public class DemoLeaveActivityEvent implements UnicornEventBase<String> {

    @Override
    public void onEvent(String s, final Context context, EventCallback<String> callback) {
        AlertDialog dialog = new AlertDialog.Builder(context)
                .setMessage("是否退出会话界面?")
                .setPositiveButton("是", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (context instanceof Activity) {
                            ((Activity) context).finish();
                        }
                    }
                })
                .setNegativeButton("否", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                }).create();
        dialog.show();
    }
}

从上面的代码中我们可以看到,我们首先在设置 YSFOption 的时候,监听 SDKEvent 中的 eventType = 3 的回调,当用户离开咨询界面的时候 SDK 会回调 eventOf 方法且 eventType = 3。所以我们在 eventType = 3 的时候返回了 DemoLeaveActivityEvent ,然后 SDK 会调用 DemoLeaveActivityEvent 中的 onEvent 方法,这样我们就可以在 onEvent 方法中去处理用户离开咨询界面的逻辑,上面的代码中可以看到,我们在用户离开咨询界面的时候进行了弹框。

# SDK 申请权限事件监听

在 V5.15.0 版本中,SDK 开放了申请权限的事件,当 SDK 申请相关权限的时候,会通过 App 并把申请权限的场景和申请权限的列表给到 App。 在 V7.11.0版本中,SDK开放了拒绝权限的回调,当用户拒绝相应权限的时候,会回调给APP自己处理。代码示例如下:

// 1. 设置 SDKEvent 并处理 eventType = 5 的事件
YSFOptions ysfOptions = new YSFOptions();
	options.sdkEvents = new SDKEvents();
	options.sdkEvents.eventProcessFactory = new EventProcessFactory() {
    	@Override
    	public UnicornEventBase eventOf(int eventType) {
        	if (eventType == 5) {
           	 return new DemoRequestPermissionEvent();
        	}
       		return null;
    	}
};

//2. 在 DemoRequestPermissionEvent 对事件进行处理
public class DemoRequestPermissionEvent implements UnicornEventBase<RequestPermissionEventEntry> {
    private Map<String, String> h5MessageHandlerMap = new HashMap<>();

    private Context mApplicationContext;

    public DemoRequestPermissionEvent(Context context) {
        mApplicationContext = context;
        h5MessageHandlerMap.put("android.permission.RECORD_AUDIO", "麦克风");
        h5MessageHandlerMap.put("android.permission.CAMERA", "相机");
        h5MessageHandlerMap.put("android.permission.READ_EXTERNAL_STORAGE", "存储");
        h5MessageHandlerMap.put("android.permission.WRITE_EXTERNAL_STORAGE", "存储");
        h5MessageHandlerMap.put("android.permission.READ_MEDIA_AUDIO", "多媒体文件");
        h5MessageHandlerMap.put("android.permission.READ_MEDIA_IMAGES", "多媒体文件");
        h5MessageHandlerMap.put("android.permission.READ_MEDIA_VIDEO", "多媒体文件");
        h5MessageHandlerMap.put("android.permission.POST_NOTIFICATIONS", "通知栏权限");
    }

    private String transToPermissionStr(List<String> permissionList) {
        if (permissionList == null || permissionList.size() == 0) {
            return "";
        }
        HashSet<String> set = new HashSet<>();
        for (int i = 0; i < permissionList.size(); i++) {
            if (!TextUtils.isEmpty(h5MessageHandlerMap.get(permissionList.get(i)))) {
                set.add(h5MessageHandlerMap.get(permissionList.get(i)));
            }
        }
        if (set.isEmpty()) {
            return "";
        }
        StringBuilder str = new StringBuilder();
        for (String temp : set) {
            str.append(temp);
            str.append("、");
        }
        if (str.length() > 0) {
            str.deleteCharAt(str.length() - 1);
        }
        return str.toString();
    }

    /**
     * 该方法为点击相应的权限场景,用户可以通过RequestPermissionEventEntry.getPermissionList()拿到相应的权限,根据
     * 自己APP的权限规则,作自己的处理.
     *
     * 比如判断客户之前点击的是拒绝权限还是不再询问,可以使用 AppCompatActivity.shouldShowRequestPermissionRationale()
     * 方法等各种情况都是在这个回调中进行自己的处理.
     *
     * 各种情况都处理完了,可以告诉SDK,是要申请权限还是拒绝,调用SDK相应的方法.
     * callback.onProcessEventSuccess(requestPermissionEventEntry):用户同意授予权限
     * callback.onInterceptEvent():用户不授予权限,SDK自己处理不授予权限的提醒;或者用户自己处理不授予权限的提醒,就不要调用这个方法了
     *
     * @param requestPermissionEventEntry 获取权限相关的类
     * @param context  当前界面的 context 对象,使用之前请判断是否为 null
     * @param callback sdk 的回调对象  注意:如果该事件 sdk 不需要回调的时候,这个对象会为 null,所以当使用的时候需要做一下非null判断
     */
    @Override
    public void onEvent(RequestPermissionEventEntry requestPermissionEventEntry, Context context, EventCallback<RequestPermissionEventEntry> callback) {

        //申请权限的场景
        //从本地选择媒体文件(视频和图片):0
        //拍摄视频场景:1
        //保存图片到本地:2
        //保存视频到本地:3
        //选择本地视频:4
        //选择本地文件:5
        //选择本地图片:6
        //拍照:7
        //录音:8
        //视频客服:9
        //通知栏权限:10
        int type = requestPermissionEventEntry.getScenesType();
        if (type == 10) {
            Toast.makeText(mApplicationContext, "适配Android13,没有通知栏权限,需要给通知栏权限", Toast.LENGTH_SHORT).show();
            return;
        }
        String permissionName = transToPermissionStr(requestPermissionEventEntry.getPermissionList());
        AlertDialog dialog = new AlertDialog.Builder(context).setMessage("为保证您" + type + "功能的正常使用," + "需要使用您的:" + (TextUtils.isEmpty(permissionName) ? "相关" : permissionName) + "权限,\n" + "拒绝或取消不影响使用其他服务")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //如果想用户授予权限,需要调用 onProcessEventSuccess 告诉 SDK 处理成功
                        callback.onProcessEventSuccess(requestPermissionEventEntry);
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //用户不授予权限,告诉 SDK 用户没有授予调用onInterceptEvent,SDK自己处理不授予权限的提醒
                        //或者用户自己处理不授予权限的提醒,就不要调用这个方法了
                        callback.onInterceptEvent();
                    }
                }).create();
        dialog.show();
    }


    /**
     * 当相关权限被拒绝后,客户自己的处理
     *
     * @return 返回值默认为false,若返回值为true,则客户自己处理
     */
    @Override
    public boolean onDenyEvent(Context context, RequestPermissionEventEntry requestPermissionEventEntry) {
        String permissionName = transToPermissionStr(requestPermissionEventEntry.getPermissionList());
        AlertDialog dialog = new AlertDialog.Builder(context).setMessage("您没有:" + (TextUtils.isEmpty(permissionName) ? "相关" : permissionName) + "权限,\n" + "是否进行其他设置")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //用户自定义拒绝后的权限处理
                        Toast.makeText(context, "我自己处理没有的权限情况", Toast.LENGTH_SHORT).show();
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(context, "我什么也没有做", Toast.LENGTH_SHORT).show();
                    }
                }).create();
        dialog.show();
        return true;
    }
}