package cn.tee3.avd;
/**
 * Created by bob.liu on 2015/8/24.
 */
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nullable;

import tee3.webrtc.ScaleScreenCapturer;
import tee3.webrtc.ScreenCapturerAndroid;
import tee3.webrtc.VideoCapturer;

/**
 * 房间中屏幕窗口共享模块接口.
 *
 * 房间屏幕窗口共享模块定义房间中屏幕窗口共享相关功能接口。
 */
public class MScreen  extends Module {
    private static final String TAG = "MScreen";
    private ScreenResolution resolution;
    private int width, height, fps;

    private ScreenWindow screenWindowFullScreen;
    private ScreenWindow screenWindowScaleScreen;

    public enum ScreenResolution {  /* 共享屏幕分辨率 */
        SCREEN_FULL(-1, -1),
        SCREEN_1080P(1920, 1080),
        SCREEN_720P(1280, 720),
        SCREEN_540P(960, 540),
        SCREEN_360P(640, 360),
        SCREEN_CUSTOMIZE(1920, 1080);

        private int width;
        private int height;

        ScreenResolution(int height, int width) {
            this.height = height;
            this.width = width;
        }

        public int getWidth() {
            return width;
        }

        public int getHeight() {
            return height;
        }

        public void setSize(int height, int width) {
            if (width <= 0 || width > height) {
                return;
            }
            this.width = width;
            this.height = height;
        }
    }

    public enum ScreenType {  /* 共享屏幕分辨率 */
        SCREEN_FULL("_androidFullScreenId"),
        SCREEN_SCALED("_businessVideoFictitious");

        private String type;

        ScreenType(String type) {
            this.type = type;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

    /**
     * 屏幕窗口设备信息结构.
     */
    public static class ScreenWindow extends VideoDevice {
        protected ScreenWindow(String id, String name, int level, String description, DeviceStatus status, int deviceQualities, int publishedQualities)  {
            super(id, name, level, description, status, deviceQualities, publishedQualities);
        }
        protected ScreenWindow(String id, String name, int level, String description, DeviceStatus status, int deviceQualities, MVideo.CameraCapability cap, VideoOptions.VideoCodec codec)  {
            super(id, name, level, description, status, deviceQualities, cap, codec);
        }
        @Override
        public String toString() {
            return "Screen:" + super.toString();
        }
    }
    /**
     * 房间中屏幕窗口共享模块回调接口.
     *
     * 屏幕窗口共享模块回调接口定义了房间中屏幕窗口共享相关的通知和异步操作返回。
     */
    public static interface Listener {
        /// 通知
        /** 屏幕窗口状态更改通知
         *
         * @param[in] status 屏幕窗口状态。
         * @param[in] fromId 屏幕窗口Id，唯一标示一个屏幕或者一个应用窗口。
         * @note 当屏幕窗口状态更改后，房间内所有用户接收到此通知。
         */
        public void onScreenStatusNotify(Device.DeviceStatus status, String fromId);
        /** 屏幕窗口数据更改通知
         *
         * @param[in] level 屏幕窗口整形数据。
         * @param[in] description 屏幕窗口字符串数据。
         * @param[in] fromId 屏幕窗口Id，唯一标示一个屏幕窗口。
         * @note 当屏幕窗口数据更改后，房间内所有用户接收到此通知, notify when publishLocalScreen(Camera), updateScreenData。
         */
        public void onScreenDataNotify(int level, String description, String fromId);
        /** 屏幕窗口视频发布通知
         *
         * @param[in] screen 屏幕窗口信息，屏幕信息中level和description为应用层数据，应用层可以使用这些字段保存次屏幕窗口相关的应用逻辑数据，方便标示、订阅视频。
         *
         * @note 当屏幕窗口视频发布时，房间内所有用户接收到此通知；因通知的重要程度，将从onScreenStatusNotify中分离出此状态。
         * @sa publishScreen
         */
        public void onPublishScreenNotify(ScreenWindow screen);
        /** 屏幕窗口视频取消发布通知
         *
         * @param[in] screen 屏幕窗口信息。
         *
         * @note 当屏幕窗口视频取消发布时，房间内所有用户接收到此通知；因通知的重要程度，将从onScreenStatusNotify中分离出此状态。
         * @sa unpublishScreen
         */
        public void onUnpublishScreenNotify(ScreenWindow screen);
        /// 异步返回
        /** 本用户订阅屏幕窗口异步返回
         *
         * @param[in] result 错误代码。
         * @param[in] fromId 屏幕窗口Id，唯一标示一个屏幕或者一个应用窗口。
         *
         * @sa subscrible
         */
        public void onSubscribeResult(int result, String fromId);
        /** 本用户取消订阅屏幕窗口异步返回
         *
         * @param[in] result 错误代码。
         * @param[in] fromId 屏幕窗口Id，唯一标示一个屏幕或者一个应用窗口。
         *
         * @sa unsubscrible
         */
        public void onUnsubscribeResult(int result, String fromId);
    }
    ///
    private long nativeListener = 0;
    @Nullable private Listener listener4cb = null;
    @Nullable private Handler listenerHandler = null;
    @Nullable private SDKListener listener4native = null;
    ///
    /** 获取房间内屏幕窗口共享模块接口
     *
     * @param[in] room 房间对象指针。
     * @return 返回屏幕窗口共享模块接口指针。
     */
    @Nullable public static MScreen getScreen(Room room) {
        return (MScreen)room.getModule(Type.screen);
    }
    protected MScreen(Room room) {
        super(room, Type.screen, room.nativegetMScreen());
        Tlog.d(TAG, "MScreen, nativeScreen:" + this.nativeobj);
        initNativeListener();
    }
    @Override
    protected void dispose() {
        if (0 != this.nativeListener) {
            nativeFreeListener(this.nativeListener);
        }

        this.nativeListener = 0;
        this.nativeobj = 0;
    }
    //
    /** 设置房间屏幕窗口共享模块回调接口
     *
     * @param[in] listener 远程屏幕窗口共享模块回调接口指针。
     * @return 返回错误代码。
     * @sa Listener
     */
    public boolean setListener(Listener listener) {
        Tlog.d(TAG, "setListener, listener:" + listener + ",listener4native=" + this.listener4native + ",listenerHandler=" + this.listenerHandler);
        this.listener4cb = listener;
        if (null != listener) {
            initNativeListener();
            Tlog.d(TAG, "setListener, Create nativelistener:" + nativeListener);
        }
        return true;
    }

    public void initNativeListener(){
        if (0 != this.nativeListener) {
            return;
        }
        initHandler();
        this.nativeListener = nativeCreateListener(listener4native);
        Tlog.d(TAG, "initNativeListener, Create nativelistener:" + nativeListener);
    }
    /** 订阅共屏屏幕窗口视频
     *
     * @param[in] deviceId 屏幕窗口Id，唯一标示一路共享的屏幕窗口视频。
     * @return 返回错误代码。
     * @note 订阅操作为异步操作，操作结果在 onSubscribeResult 中返回。
     * @sa onSubscribeResult
     */
    public int subscribe(String deviceId) {
        Tlog.d(TAG, "MScreen, subscribe DeviceId:" + deviceId);
        return nativesubscribe(deviceId);
    }
    /** 取消订阅共屏屏幕窗口视频
     *
     * @param[in] deviceId 屏幕窗口Id，唯一标示一路共享的屏幕窗口视频。
     * @return 返回错误代码。
     * @note 取消订阅操作为异步操作，操作结果在 onUnsubscribeResult 中返回。
     * @sa onUnsubscribeResult
     */
    public int unsubscribe(String deviceId) {
        Tlog.d(TAG, "MScreen, unsubscribe deviceId:" +deviceId);
        return nativeunsubscribe(deviceId);
    }
    /** 获取已共享屏幕窗口信息（包含自己发布的屏幕）
     *
     * @return 返回屏幕窗口集合存放列表。
     */
    public List<ScreenWindow> getPublishedScreens() {
        List<ScreenWindow> items = nativegetPublishedScreens();
        return items == null ? new ArrayList<>() : items;
    }
    /** 获取已订阅屏幕窗口信息
     *
     * @return 返回屏幕窗口集合存放列表。
     */
    public List<ScreenWindow> getSubscribedScreens() {
        List<ScreenWindow> items =  nativegetSubscribedScreens();
        Tlog.v(TAG, "getSubscribeScreens, count:"+ (null == items ? 0 : items.size()));
        return items == null ? new ArrayList<>() : items;
    }
    /** 判断屏幕是否已经发布到房间中
     */
    public boolean isScreenSubscribed(String deviceId) {
        return nativeisScreenSubscribed(deviceId);
    }
    /// render screen
    /** 关联共享的屏幕窗口视频数据和显示对象，在render中显示视频
     *
     * @param[in] deviceId 屏幕窗口Id，唯一标示一路共享的屏幕窗口视频。
     * @param[in] render 显示对象接口指针。
     *
     * @note 视频数据可以在多个显示对象上显示，而一个显示对象某一时刻只能显示一路视频。
     *		在屏幕窗口视频发布到房间中，或者远端屏幕窗口视频订阅成功后，调用此接口完成视频与显示对象的对接，从而在显示对象中显示视频。
     * @return 返回错误代码。
     * @sa VideoRenderer
     */
    public int attachRender(String deviceId, VideoRenderer render) {
        if (null == render) {
            Tlog.e(TAG, "attachRender, invalid render");
            return ErrorCode.Err_Invalid_Arg;
        }
        Tlog.d(TAG, "MScreen, attachRender deviceId:" + deviceId + ",render:" + render.hashCode());
        render.setVideoId(deviceId);
        render.viewRenderer.enableMeasure(true);
        render.viewRenderer.getEglRenderer().invalidDrawFrame(false);
        render.viewRenderer.clearImage();
        getRoom().getLocalCameraSwith().addRender(render);
        return nativeattachRender(deviceId, render.nativeRender());
    }
    /** 解除屏幕窗口视频的所有显示对象关联
     *
     * @param[in] deviceId 屏幕窗口Id，唯一标示一路共享的屏幕窗口视频。
     * @return 返回错误代码。
     */
    public int detachRender(String deviceId) {
        Tlog.d(TAG, "MScreen, detachRender deviceId:" +deviceId);
        if (getRoom() == null || getRoom().getLocalCameraSwith() == null) {
            return  ErrorCode.Err_Not_Found;
        }
        VideoRenderer render = getRoom().getLocalCameraSwith().removeRender(deviceId);
        if (null == render) {
            return ErrorCode.Err_Not_Found;
        }
        return nativedetachRender2(render.nativeRender());
    }
    /** 解除某一显示对象与屏幕窗口视频的关联
     *
     * @param[in] render 显示对象接口指针。
     * @sa VideoRenderer
     */
    public int detachRender(VideoRenderer render) {
        if (null == render) {
            Tlog.e(TAG, "detachRender, invalid render");
            return ErrorCode.Err_Invalid_Arg;
        }
        Tlog.d(TAG, "MScreen, detachRender render:" + render.hashCode());
        render.setVideoId(null);
        render.viewRenderer.enableMeasure(false);
        render.viewRenderer.getEglRenderer().invalidDrawFrame(true);
        render.viewRenderer.clearImage();
        getRoom().getLocalCameraSwith().removeRender(render);
        return nativedetachRender2(render.nativeRender());
    }

    private boolean calculateSize(ScreenResolution resolution) {
        DisplayMetrics displayMetrics = getDisplayMetrics();
        boolean isPortrait = displayMetrics.widthPixels <= displayMetrics.heightPixels;
        if (resolution == ScreenResolution.SCREEN_FULL) {
            width = displayMetrics.widthPixels;
            height = displayMetrics.heightPixels;
        } else {
            width = isPortrait ? resolution.getWidth() : resolution.getHeight();
            height = isPortrait ? resolution.getHeight() : resolution.getWidth();
        }
        return isPortrait;
    }

    //安卓 发布屏幕共享
    MVideo.LocalVideoSource localsource;
    public int publishedScreens(ScreenResolution resolution, int fps, boolean isPublish){
        if (resolution == null) {
            return ErrorCode.Err_Failure;
        }
        if (null == localsource) {
            localsource = new MVideo.LocalVideoSource(false);
        }

        this.fps = fps;
        this.resolution = resolution;

        calculateSize(resolution);

        MVideo.CameraCapability capability = new MVideo.CameraCapability(width, height, this.fps);

        if (!localsource.initScreen(width, height, fps)) {
            Tlog.w(TAG, "publishScreen, screen capturer and source create failed.");
            return ErrorCode.Err_Failure;
        }

        String codecType = AVDEngine.instance().getOption(AVDEngine.Option.eo_video_codec_priority);
        VideoOptions.VideoCodec videoCodec = "h264".equals(codecType) ? VideoOptions.VideoCodec.codec_h264 : VideoOptions.VideoCodec.codec_vp8;
        screenWindowFullScreen = new ScreenWindow(getUserManager().getSelfUserId() + "_androidFullScreenId","androidFullScreen",0,
                "", Device.DeviceStatus.ready,0,capability, videoCodec);

        int ret = 0;
        if (isPublish) {
            ret = nativePublishedScreen2(screenWindowFullScreen, capability, localsource.nativeSource());
            if (ErrorCode.AVD_OK != ret) {
                Tlog.d(TAG, "publishScreen, out: ret=" + ret);
                return ret;
            }
        }
        localsource.startVideoSource();
        Tlog.d(TAG, "publishScreen, out: ret=" + ret);
        return ret;
    }

    MVideo.LocalVideoSource localScaleSource;
    public int publishScaleScreen(ScaleScreenCapturer.ScaleZone scaleZone){
        DisplayMetrics displayMetrics = getDisplayMetrics();
        int screenWidth = displayMetrics.widthPixels;
        int screenHeight = displayMetrics.heightPixels;
        if (scaleZone.getWidth() == 0 || scaleZone.getHeight() == 0 ||
            scaleZone.getStartX() < 0 || scaleZone.getStartX() > screenWidth ||
            scaleZone.getStartY() < 0 || scaleZone.getStartY() > screenHeight ||
            scaleZone.getStartX() + scaleZone.getWidth() > screenWidth ||
            scaleZone.getStartY() + scaleZone.getHeight() > screenHeight) {
            Tlog.w(TAG, "publishScreen, scaleZone error!");
            return ErrorCode.Err_Failure;
        }

        float scaleWidth = (float) width / screenWidth;
        float scaleHeight = (float) height / screenHeight;

        int startX = (int)(scaleZone.getStartX() * scaleWidth);
        startX = startX % 8 == 0 ? startX : startX - startX % 8;
        if (startX < 0) {
            startX = 0;
        }
        int startY = (int)(scaleZone.getStartY() * scaleHeight);
        startY = startY % 4 == 0 ? startY : startY - startY % 4;
        if (startY < 0) {
            startY = 0;
        }

        int width = (int)(scaleZone.getWidth() * scaleWidth);
        width = width % 8 == 0 ? width : width - width % 8;
        if (width < 0) {
            width = 0;
        }
        int height = (int)(scaleZone.getHeight() * scaleHeight);
        height = height % 4 == 0 ? height : height - height % 4;
        if (height < 0) {
            height = 0;
        }

        ScaleScreenCapturer.ScaleZone scaleZoneAdjust = new ScaleScreenCapturer.ScaleZone(startX, startY, width, height);

        ScaleScreenCapturer.setScaleZone(scaleZoneAdjust);
        if (null == localScaleSource) {
            localScaleSource = new MVideo.LocalVideoSource(false);
        }
        if (!localScaleSource.initScaleScreen(scaleZoneAdjust)) {
            Tlog.w(TAG, "publishScreen, screen capturer and source create failed.");
            return ErrorCode.Err_Failure;
        }
        MVideo.CameraCapability capability = new MVideo.CameraCapability(scaleZone.getWidth(), scaleZone.getHeight(), 5);
        String codecType = AVDEngine.instance().getOption(AVDEngine.Option.eo_video_codec_priority);
        VideoOptions.VideoCodec videoCodec = "h264".equals(codecType) ? VideoOptions.VideoCodec.codec_h264 : VideoOptions.VideoCodec.codec_vp8;
        screenWindowScaleScreen = new ScreenWindow(getUserManager().getSelfUserId() + "_businessVideoFictitious", "businessVideoFictitious", 0,
                "", Device.DeviceStatus.ready, 0, capability, videoCodec);

        int ret = nativePublishedScreen2(screenWindowScaleScreen, capability, localScaleSource.nativeSource());
        if (ErrorCode.AVD_OK == ret) {
            localScaleSource.startVideoSource();
        }

        Tlog.d(TAG, "publishScreen, out: ret=" + ret);
        return ret;
    }

    public String getScreenId(ScreenType screenType) {
        List<ScreenWindow> screens = getPublishedScreens();
        for (MScreen.ScreenWindow screenWindow : screens) {
            if (isSelfDevice(screenWindow.getId()) && screenWindow.getId().contains(screenType.getType())) {
                return screenWindow.getId();
            }
        }
        return null;
    }

    public boolean isPublishedScreen(ScreenType screenType) {
        List<ScreenWindow> screens = getPublishedScreens();
        for (MScreen.ScreenWindow screenWindow : screens) {
            if (isSelfDevice(screenWindow.getId()) && screenWindow.getId().contains(screenType.getType())) {
                return true;
            }
        }
        return false;
    }

    //安卓取消发布屏幕共享
    public int unpublishedScreens(){
        Tlog.d(TAG, " unpublishedScreens");
        int ret = nativeUnPublishedScreen();
        Tlog.v(TAG, "unpublishedScreens, native out");
        if (localsource != null) {
            localsource.stopVideoSource();
        }

        if (localScaleSource != null) {
            localScaleSource.stopVideoSource();
        }
        Tlog.v(TAG, "unpublishedScreens, out");
        return ret;
    }

    //安卓取消发布屏幕共享
    public int unpublishScreen(ScreenType screenType){
        switch (screenType) {
            case SCREEN_FULL:
                if (localsource != null) {
                    localsource.stopVideoSource();
                }
                break;
            case SCREEN_SCALED:
                if (localScaleSource != null) {
                    localScaleSource.stopVideoSource();
                }
                break;
        }

        Tlog.d(TAG, " unpublishedScreens");
        int ret = nativeUnPublishedScreen();
        Tlog.v(TAG, "unpublishedScreens, native out");
        return ret;
    }

    public boolean hasScreenPublished() {
        List<ScreenWindow> screens = getPublishedScreens();
        if (screens.size() == 0) {
            return false;
        }
        Room room = getRoom();
        MVideo mVideo = MVideo.getVideo(room);
        MUserManager userManager = getUserManager();
        String selfUserId = "";
        if (userManager != null) {
            selfUserId = userManager.getSelfUserId();
        }
        for (ScreenWindow screen : screens) {
            if (selfUserId.equals(mVideo.getOwnerId(screen.getId()))) {
                return true;
            }
        }
        return false;
    }

    @TargetApi(17)
    private DisplayMetrics getDisplayMetrics() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager =
                (WindowManager) AVDEngine.instance().getContext().getSystemService(Context.WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
        return displayMetrics;
    }

    /** 摄像头数据更改
     *
     * @param[in] deviceId 摄像头Id，唯一标示一个摄像头。
     * @param[in] level 摄像头整形数据。
     * @param[in] description 摄像头字符串数据。
     * @sa onCameraDataNotify
     */
    public int updateScreenData(String deviceId, int level, String description) {
        Tlog.d(TAG, " updateScreenData: deviceId = " + deviceId + ", level = " + level + ", description = " + description);
        return nativeupdateScreenData(deviceId, level, description);
    }

    /**
     * 暂停桌面数据采集
     */
    public void pauseCapture() {
        Room room = getRoom();
        MVideo mVideo = MVideo.getVideo(room);
        VideoCapturer screenCapturer = mVideo.getCaptureByType(ScreenCapturerAndroid.class);
        if (screenCapturer != null) {
            Log.w(TAG, "pauseCapture()");
            ((ScreenCapturerAndroid)screenCapturer).pauseCapture();
        }
    }

    /**
     * 重启桌面数据采集
     */
    public void resumeCapture() {
        Room room = getRoom();
        MVideo mVideo = MVideo.getVideo(room);
        VideoCapturer screenCapturer = mVideo.getCaptureByType(ScreenCapturerAndroid.class);
        if (screenCapturer != null) {
            Log.w(TAG, "resumeCapture()");
            ((ScreenCapturerAndroid)screenCapturer).resumeCapture();
        }
    }

    ///
    private native long nativeCreateListener(Listener listener);
    private native void nativeFreeListener(long nativeListener);
    private native int nativesubscribe(String deviceId);
    private native int nativeunsubscribe(String deviceId);
    //
    private native List<ScreenWindow> nativegetPublishedScreens();
    private native List<ScreenWindow> nativegetSubscribedScreens();
    private native boolean nativeisScreenSubscribed(String deviceId);
    // render video
    private native int nativeattachRender(String deviceId, long nativeRender);
    private native int nativedetachRender1(String deviceId);
    private native int nativedetachRender2(long nativeRender);

    //publis unpublish
    private native int nativePublishedScreen(ScreenWindow screenWindow, long nativeSource);
    public native int nativePublishedScreen2(ScreenWindow screenWindow, MVideo.CameraCapability capability, long nativeSource);
    public native int nativeUnPublishedScreen();
    private native int nativeupdateScreenData(String deviceId, int level, String description);

    ///
    private static final int msg_onScreenStatusNotify = 1;
    private static final int msg_onScreenDataNotify = 2;
    private static final int msg_onPublishScreenNotify = 3;
    private static final int msg_onUnpublishScreenNotify = 5;
    private static final int msg_onSubscribeResult = 6;
    private static final int msg_onUnsubscribeResult = 7;
    private boolean initHandler() {
        if (null == listener4native) {
            listener4native = new SDKListener();
        }
        if (null != listenerHandler) {
            return true;
        }
        AVDEngine.runOnLoopThreadSync(new Runnable() {
            @Override
            public void run() {
                initHandler_main();
            }
        });
        return true;
    }
    private boolean initHandler_main() {
        this.listenerHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                Tlog.v(TAG, "handleMessage, msg:" + msg.toString());
                if (null == listener4cb) {
                    super.handleMessage(msg);
                    return;
                }
                switch (msg.what) {
                    case msg_onScreenStatusNotify:
                        listener4cb.onScreenStatusNotify(Device.DeviceStatus.values()[msg.arg1], (String) msg.obj);
                        break;
                    case msg_onScreenDataNotify:
                        listener4cb.onScreenDataNotify(
                                msg.getData().getInt("level"), msg.getData().getString("description"), msg.getData().getString("fromId"));
                        break;
                    case msg_onPublishScreenNotify:
                        listener4cb.onPublishScreenNotify((ScreenWindow) msg.obj);
                        break;
                    case msg_onUnpublishScreenNotify: {
                        ScreenWindow cam = (ScreenWindow) msg.obj;
                        if (null == cam) {
                            break;
                        }
                        Room room = getRoom();
                        MUserManager userManager = MUserManager.getUserManager(room);
                        MVideo mVideo = MVideo.getVideo(room);
                        if (userManager == null || mVideo == null) {
                            return;
                        }
                        String ownerId = mVideo.getOwnerId(cam.getId());
                        if (!ownerId.equals(userManager.getSelfUserId())) {
                            detachRender(cam.getId());
                            if (null != listener4cb) {
                                listener4cb.onUnpublishScreenNotify(cam);
                            }
                        }
                    }
                        break;
                    case msg_onSubscribeResult:
                        listener4cb.onSubscribeResult(msg.arg1, (String) msg.obj);
                        break;
                    case msg_onUnsubscribeResult:
                        listener4cb.onUnsubscribeResult(msg.arg1, (String) msg.obj);
                        break;
                }
                super.handleMessage(msg);
            }
        };
        return true;
    }
    private class SDKListener implements Listener {
        public SDKListener() {
        }
        @Override
        public void onScreenStatusNotify(Device.DeviceStatus status, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onScreenStatusNotify, status.ordinal(), 0, fromId);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onScreenDataNotify(int level, String description, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = obtainMessage(msg_onScreenDataNotify, level, description, fromId);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onPublishScreenNotify(ScreenWindow from) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onPublishScreenNotify, from);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onUnpublishScreenNotify(ScreenWindow from) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler,msg_onUnpublishScreenNotify, from);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onSubscribeResult(int result, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler, msg_onSubscribeResult, result, 0, fromId);
            listenerHandler.sendMessage(msg);
        }
        @Override
        public void onUnsubscribeResult(int result, String fromId) {
            if (null == listenerHandler) {
                return ;
            }
            Message msg = Message.obtain(listenerHandler, msg_onUnsubscribeResult, result, 0, fromId);
            listenerHandler.sendMessage(msg);
        }
    }

    private Message obtainMessage(int what, int level, String description, String fromId) {
        Message msg = Message.obtain(listenerHandler);
        msg.what = what;
        Bundle data = new Bundle();
        data.putInt("level", level);
        data.putString("description", description);
        data.putString("fromId", fromId);
        msg.setData(data);
        return msg;
    }
}
