You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

683 lines
24 KiB

//
// VideoTabPopView.m
// tongxin
//
// Created by ecell on 2023/5/5.
// Copyright © 2023 xTT. All rights reserved.
//
#import "VideoTabPopView.h"
#import "User.h"
#import <JLAuthorizationManager.h>
#import "AgoraConfigModel.h"
#import "JuphoonModel.h"
#import "BairuiModel.h"
#import "TihuModel.h"
#import "VideoUserModel.h"
#import "CallViewController.h"
#import "JCManager.h"
#import "BRManager.h"
#import "TihuManager.h"
#import <UIButton+WebCache.h>
@interface VideoTabPopView ()
@property (nonatomic ,strong) NSMutableArray *btnsArr;
@property (nonatomic ,strong) NSMutableArray *labelsArr;
@property (nonatomic ,weak) UIView *bgView;
/**
设备 支持的视频类型 0 不支持 1 声网 2 环信 4 菊风 5 佰锐
*/
@property (assign, nonatomic) int video_type;
/**
声网配置model
*/
@property (strong, nonatomic) AgoraConfigModel *agoraModel;
/**
菊风配置model
*/
@property (strong, nonatomic) JuphoonModel *jhModel;
/**
佰锐配置model
*/
@property (strong, nonatomic) BairuiModel *brModel;
/**
鹈鹕配置model
*/
@property (strong, nonatomic) TihuModel *tihuModel;
/**
选中的家庭成员数组
*/
@property (strong, nonatomic) NSMutableArray<VideoUserModel*> *checked_video_list;
/**
未选中的家庭成员数组
*/
@property (strong, nonatomic) NSMutableArray<VideoUserModel*> *unchecked_video_list;
@property (assign, nonatomic) int callTimes;
@property (assign, nonatomic) BOOL isCancelTimer;
@property (strong, nonatomic) NSString *dwUserStr;
@end
@implementation VideoTabPopView
+ (instancetype)sharedManager{
static dispatch_once_t onceToken;
static VideoTabPopView *instance;
dispatch_once(&onceToken, ^{
instance = [[VideoTabPopView alloc] init];
});
return instance;
}
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.frame = [UIScreen mainScreen].bounds;
self.backgroundColor = RGBA(0, 0, 0, 0.4);
self.hidden = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGuestures:)];
[self addGestureRecognizer:tapGesture];
[self subView];
}
return self;
}
- (void)subView
{
WEAKSELF
UIView *bgView = [UICommon ui_view:CGRectMake(0, ScreenHeight, ScreenWidth, 368) backgroundColor:KKWhiteColorColor cornerRadius:0 borderWidth:0 borderColor:KKWhiteColorColor];
self.bgView = bgView;
[UICommon ui_uiViewFillet:bgView.bounds Viewdirection:PYUIdirectionTop toView:bgView sizeMake:17];
[self addSubview:bgView];
UIImageView *topImg = [UICommon ui_imageView:CGRectZero fileName:@"video_popView_topbg"];
[bgView addSubview:topImg];
[topImg mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(bgView);
make.top.equalTo(bgView);
make.height.mas_equalTo(Adapted(topImg.image.size.height));
}];
UIButton *phoneBtn = [UICommon ui_buttonSimple:CGRectZero font:FontADA_(14) normalColor:RGB(20, 20, 20) normalText:@"拨打电话" click:^(id x) {
if (self.deviceModel.phone == 0) {
LGAlertView *alertView = [[LGAlertView alloc] initWithTitle:@"未设置设备号码,是否设置?" message:nil style:LGAlertViewStyleAlert buttonTitles:@[@"设置"] cancelButtonTitle:@"取消" destructiveButtonTitle:nil actionHandler:^(LGAlertView *alertView, NSString *title, NSUInteger index) {
[weakSelf dismiss];
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UINavigationController *deviceVC = [sb instantiateViewControllerWithIdentifier:@"EditDeviceViewController"];
deviceVC.hidesBottomBarWhenPushed = YES;
[[UICommon currentVC] setValue:@(NO) forKey:@"isAdd"];
[[UICommon currentVC].navigationController pushViewController:deviceVC animated:YES];
} cancelHandler:nil destructiveHandler:nil];
[alertView showAnimated:YES completionHandler:nil];
}else{
NSString * string = [[NSMutableString alloc] initWithFormat:@"tel:%@",self.deviceModel.phone];
if(self.deviceModel.phone.length == 11){
string = [NSString stringWithFormat:@"tel:+86-%@", self.deviceModel.phone];
}
if(CurrentSystemVersion.doubleValue > 10){
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:string] options:@{} completionHandler:nil];
}else{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:string]];
}
}
}];
phoneBtn.size = CGSizeMake(88, 120);
[phoneBtn setImage:ImageName_(@"popView_callPhone_icon") forState:0];
[bgView addSubview:phoneBtn];
[phoneBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(bgView.mas_centerX).inset(49);
make.bottom.equalTo(bgView.mas_bottom).inset(80);
make.size.mas_equalTo(CGSizeMake(88, 120));
}];
UIButton *videoBtn = [UICommon ui_buttonSimple:CGRectZero font:FontADA_(14) normalColor:RGB(20, 20, 20) normalText:@"视频通话" click:^(id x) {
[weakSelf monitorDevice];
}];
videoBtn.size = CGSizeMake(88, 120);
[videoBtn setImage:ImageName_(@"popView_callVideo_icon") forState:0];
[bgView addSubview:videoBtn];
[videoBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(bgView.mas_centerX).offset(49);
make.top.equalTo(phoneBtn.mas_top);
make.size.mas_equalTo(CGSizeMake(88, 120));
}];
[UICommon setTitleAndImage:videoBtn :1.1];
[UICommon setTitleAndImage:phoneBtn :1.1];
}
-(void)viewWillAppears
{
NSString * valueStr = [UserDefaults valueForKey:VideoTimeAndIDAndWHRecordKey];
NSArray *strArr = [valueStr componentsSeparatedByString:@"&"];
if(valueStr && strArr.count == 5){
//上传视频时间
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
int durationTime = [strArr[0] intValue];
int wait_duration = [strArr[4] intValue];
if(durationTime >= wait_duration){
durationTime = durationTime - wait_duration;
}else{
durationTime = 0;
}
[dic setValue:@(durationTime) forKey:@"duration"];//减去等待时间
[dic setValue:strArr[1] forKey:@"video_id"];
[dic setValue:@([strArr[2] intValue]) forKey:@"distinguishability_width"];
[dic setValue:@([strArr[3] intValue]) forKey:@"distinguishability_heiger"];
[dic setValue:@(wait_duration) forKey:@"wait_duration"];
[cUser postVideoCallTimeParame:[dic copy] deviceModel:self.deviceModel success:^(id responseObject) {
xLog(@"上传视频时间成功");
//MARK: 上传完视频时间后删除存储的 Key
[UserDefaults removeObjectForKey:VideoTimeAndIDAndWHRecordKey];
//[self loadData];
} failure:^{
xLog(@"上传视频时间失败");
}];
}else{
//[self loadData];
}
}
-(void)loadData
{
self.video_type = cUser.cDevice.support_video.intValue;
WEAKSELF
if(_video_type == 0)
return;
//MARK: 获取设备uuid成员列表
[cUser.cDevice getVideoUserListSuccess:^(id _Nonnull responseObject) {
[weakSelf.checked_video_list removeAllObjects];
if([responseObject[@"checked_video_list"] isKindOfClass:[NSArray class]]){
for (NSDictionary *item in responseObject[@"checked_video_list"]) {
VideoUserModel *model = [VideoUserModel new];
[model mj_setKeyValues:item];
[weakSelf.checked_video_list addObject:model];
}
}
[weakSelf.unchecked_video_list removeAllObjects];
if([responseObject[@"unchecked_video_list"] isKindOfClass:[NSArray class]]){
for (NSDictionary *item in responseObject[@"unchecked_video_list"]) {
VideoUserModel *model = [VideoUserModel new];
[model mj_setKeyValues:item];
[weakSelf.unchecked_video_list addObject:model];
}
}
} failure:^{
}];
}
- (void)subBtns
{
for (UIButton *btn in self.btnsArr) {
[btn removeFromSuperview];
}
for (UILabel *label in self.labelsArr) {
[label removeFromSuperview];
}
self.btnsArr = [NSMutableArray new];
self.labelsArr = [NSMutableArray new];
CGFloat beginX = ScreenWidth/2- (cUser.myDevices.count*48+(20*cUser.myDevices.count-1))/2;
for (NSInteger i = 0; i < cUser.myDevices.count; i++)
{
Device *dev = cUser.myDevices[i];
UIButton *deviceBtn = [UICommon ui_buttonSimple:CGRectZero font:FontADA_(0) normalColor:KKGrey102 normalText:@"" click:^(UIButton *btn) {
for (UIButton *butt in self.btnsArr)
{
butt.layer.borderWidth = butt.tag == btn.tag ? 4 : 0;
if (btn.tag == butt.tag)
{
self.deviceModel = cUser.myDevices[btn.tag];
butt.frame = CGRectMake(butt.x, 28, 64, 64);
}
else
butt.frame = CGRectMake(butt.x, 36, 48, 48);
butt.layer.borderColor = KKWhiteColorColor.CGColor;
butt.layer.cornerRadius = butt.size.height/2;
butt.layer.masksToBounds = YES;
}
for (UILabel *label in self.labelsArr)
{
label.font = label.tag == btn.tag ? FontBold_(14) : Font_(14);
}
}];
[deviceBtn sd_setImageWithURL:[NSURL URLWithString:dev.avator] forState:0
placeholderImage:[myHelper getImageWithName:@"默认用户头像"]];
deviceBtn.frame = CGRectMake(beginX+i*(48+20), 36, 48, 48);
deviceBtn.tag = i;
[self.bgView addSubview:deviceBtn];
UILabel *nameLabel = [UICommon ui_label:CGRectZero lines:1 align:NSTextAlignmentCenter font:Font_(14) textColor:KKTextBlackColor51 text:dev.name Radius:0];
nameLabel.tag = i;
[self.bgView addSubview:nameLabel];
[nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(deviceBtn);
make.top.equalTo(self.bgView).offset(92);
}];
if([dev.imei isEqualToString:cUser.cDevice.imei])
{
self.deviceModel = cUser.cDevice;
nameLabel.font = FontBold_(14);
deviceBtn.layer.borderWidth = 4;
deviceBtn.layer.borderColor = KKWhiteColorColor.CGColor;
deviceBtn.frame = CGRectMake(beginX+i*(48+20), 28, 64, 64);
}
deviceBtn.layer.cornerRadius = deviceBtn.size.height/2;
deviceBtn.layer.masksToBounds = YES;
[self.btnsArr addObject:deviceBtn];
[self.labelsArr addObject:nameLabel];
}
}
- (void)showView
{
[UICommon currentVC].tabBarController.tabBar.hidden = YES;
//[self loadData];
[self subBtns];
self.hidden = NO;
[UIView animateWithDuration:0.25f
delay:0.0f
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect frame = self.bgView.frame;
frame.origin.y = frame.origin.y - frame.size.height;
self.bgView.frame = frame;
}completion:^(BOOL finished) {
// self.backgroundColor = RGBA(0, 0, 0, 0.2);
}];
}
- (void)dismiss
{
[UIView animateWithDuration:0.25f
delay:0.0f
options:UIViewAnimationOptionCurveEaseIn
animations:^{
CGRect frame = self.bgView.frame;
frame.origin.y = frame.origin.y + frame.size.height;
self.bgView.frame = frame;
}completion:^(BOOL finished) {
[UICommon currentVC].tabBarController.tabBar.hidden = NO;
self.hidden = YES;
}];
}
- (void)handleGuestures:(UITapGestureRecognizer *)sender
{
CGPoint point = [sender locationInView:self.bgView];
if(![self.bgView.layer containsPoint:point])
{
[self dismiss];
return;
}
}
- (void)monitorDevice
{
WEAKSELF
JLAuthorizationManager *jlManager = [JLAuthorizationManager defaultManager];
[jlManager JL_requestAuthorizationWithAuthorizationType:JLAuthorizationTypeAudio
authorizedHandler:^{
[weakSelf selectCallTypeFunction:0];
// [weakSelf setDwUserStr:@"video"];
}
unAuthorizedHandler:^{
LGAlertView *alertView = [[LGAlertView alloc] initWithTitle:[NSString stringWithFormat:@"请在iPhone的\"设置-隐私-麦克风\"选项中,允许%@访问你的麦克风",APPName] message:nil style:LGAlertViewStyleAlert buttonTitles:@[@"确定"] cancelButtonTitle:@"" destructiveButtonTitle:nil actionHandler:^(LGAlertView *alertView, NSString *title, NSUInteger index) {
// [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
} cancelHandler:^(LGAlertView *alertView) {
} destructiveHandler:nil];
[alertView showAnimated:YES completionHandler:nil];
}];
[jlManager JL_requestAuthorizationWithAuthorizationType:JLAuthorizationTypeVideo
authorizedHandler:nil
unAuthorizedHandler:^{
LGAlertView *alertView = [[LGAlertView alloc] initWithTitle:[NSString stringWithFormat:@"请在iPhone的\"设置-隐私-相机\"选项中,允许%@访问你的相机",APPName] message:nil style:LGAlertViewStyleAlert buttonTitles:@[@"确定"] cancelButtonTitle:@"" destructiveButtonTitle:nil actionHandler:^(LGAlertView *alertView, NSString *title, NSUInteger index) {
} cancelHandler:^(LGAlertView *alertView) {
} destructiveHandler:nil];
[alertView showAnimated:YES completionHandler:nil];
}];
}
/// 选择拨打的是什么类型 0: 视频 1:语音
/// @param type 0: 视频 1:语音
- (void)selectCallTypeFunction:(int)type
{
[self dismiss];
WEAKSELF
[cUser getVideoAccoutCallType:type deviceModel:self.deviceModel Success:^(id responseObject) {
if([responseObject[@"deviceVideoType"] isEqualToString:@"SW"]){
//声网
weakSelf.agoraModel = [AgoraConfigModel mj_objectWithKeyValues:responseObject];
weakSelf.agoraModel.call_type = type;
//调起 SW
[weakSelf SWJoinChannel];
} else if ([responseObject[@"deviceVideoType"] isEqualToString:@"BR"]) {
weakSelf.brModel = [BairuiModel mj_objectWithKeyValues:responseObject];
weakSelf.brModel.call_type = type;
[weakSelf BR_LoginAndCallVideo];
} else if ([responseObject[@"deviceVideoType"] isEqualToString:@"TH"]) {
weakSelf.tihuModel = [TihuModel mj_objectWithKeyValues:responseObject];
weakSelf.tihuModel.call_type = type;
[weakSelf TH_LoginAndCallVideo];
}
else
{
//菊风 (默认)
weakSelf.jhModel = [JuphoonModel mj_objectWithKeyValues:responseObject];
//[weakSelf.jhModel setValuesForKeysWithDictionary:responseObject];
weakSelf.jhModel.call_type = type;
//调起 Juphoon
[weakSelf JH_LoginAndCallVideo];
}
[UICommon currentVC].tabBarController.tabBar.hidden = YES;
} failure:^{
[UICommon currentVC].tabBarController.tabBar.hidden = YES;
}];
}
/// 声网
- (void)SWJoinChannel
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
CallViewController *callVC = [sb instantiateViewControllerWithIdentifier:@"CallViewController"];
callVC.appid = self.agoraModel.appid;
// callVC.token = self.agoraModel.token;
callVC.video_id = self.agoraModel.video_id;
callVC.device = self.deviceModel;
callVC.channelID = self.agoraModel.channelID;
callVC.uid = self.agoraModel.app_uid;
callVC.device_uid = self.agoraModel.device_uid;
callVC.videoSize = CGSizeMake(self.agoraModel.distinguishability_width, self.agoraModel.distinguishability_heiger) ; //AgoraVideoDimension640x360
callVC.limit_time = self.agoraModel.limit_time;
callVC.wait_time = self.agoraModel.wait_time;
callVC.modalPresentationStyle = UIModalPresentationFullScreen;
[[UICommon currentVC] presentViewController:callVC animated:NO completion:nil];
}
#pragma mark --- Juphoon 拨打视频通话 ---
-(void)JH_LoginAndCallVideo
{
//需要去注册
if(![JCManager shared].client)
{
[[JCManager shared] initializeWithAPPKey:self.jhModel.appkey];
[[JCManager shared].client setServerAddress:@"http:cn.router.justalkcloud.com:8080"];
[[JCManager shared].client login:self.jhModel.username password:@"test" loginParam:nil];
}
else
{
[[JCManager shared] _stopCallTimeoutTimer];
[[JCManager shared] _stopVideoLimitTimeoutTimer];
}
[JCManager shared].gMianVC = [UICommon currentVC];
[JCManager shared].model = self.jhModel;
[[JCManager shared] callVideoWithUserName];
//调起页面
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
JuPhoonCallViewController *callVC = [sb instantiateViewControllerWithIdentifier:@"JuPhoonCallViewController"];
[JCManager shared].callViewController = callVC;
callVC.video_id = self.jhModel.video_id;
callVC.distinguishability_width = self.jhModel.distinguishability_width;
callVC.distinguishability_heiger = self.jhModel.distinguishability_heiger;
xLog(@"JF通话的限制时长:%d", self.jhModel.limit_time);
callVC.device = self.deviceModel;
callVC.isReceiveCall = NO; //主叫
callVC.statusLabel.text = @"正在发起通话请求...";
if (self.jhModel.call_type == 0) {
[callVC openLocationCarmeraViewWithFullSScreen:YES];
}
//清除原本定时器配置
// [[JCManager shared] _stopCallTimeoutTimer];
// [[JCManager shared] _stopVideoLimitTimeoutTimer];
callVC.modalPresentationStyle = UIModalPresentationFullScreen;
[[UICommon currentVC].navigationController presentViewController:callVC animated:YES completion:nil];
}
#pragma mark --- Bairui 拨打视频通话 ---
- (void)BR_LoginAndCallVideo {
if (![[BRManager shared] isUserLogin]) {
[[BRManager shared] login:cUser.bairui_id nick: cUser.name];
}
[BRManager shared].brModel = self.brModel;
[BRManager shared].isReceiveCall = NO;
[BRManager shared].video_id = self.brModel.video_id;
[BRManager shared].device = self.deviceModel;
[BRManager shared].callType = self.brModel.call_type;
WEAKSELF
[[BRManager shared] showCallDialog:cUser userId:[_brModel.device_username intValue] callback:^{
[weakSelf cancelTimer];
}];
_callTimes = 0;
BOOL online = [[BRManager shared] queryRemoteUserOnline:[_brModel.device_username intValue]];
if (online) {
[AnyChatPlatform VideoCallControl:BRAC_VIDEOCALL_EVENT_REQUEST :[_brModel.device_username intValue] :0 :0 :0 :_dwUserStr];
NSLog(@"callType=========1=====> %@", _dwUserStr);
} else {
[self performSelector:@selector(reCall) withObject:nil afterDelay:3];
}
}
- (void)cancelTimer {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(reCall) object:nil];
}
- (void)reCall {
_callTimes++;
if (_callTimes > 3) {
[AnyChatPlatform VideoCallControl:BRAC_VIDEOCALL_EVENT_REQUEST :[_brModel.device_username intValue] :0 :0 :0 :_dwUserStr];
NSLog(@"callType=========3=====> %@", _dwUserStr);
return;
}
BOOL online = [[BRManager shared] queryRemoteUserOnline:[_brModel.device_username intValue]];
if (online) {
[AnyChatPlatform VideoCallControl:BRAC_VIDEOCALL_EVENT_REQUEST :[_brModel.device_username intValue] :0 :0 :0 :_dwUserStr];
NSLog(@"callType=========2=====> %@", _dwUserStr);
} else {
[self performSelector:@selector(reCall) withObject:nil afterDelay:3];
}
NSLog(@"查询用户在线次数:%d", _callTimes);
}
#pragma mark --- Tihu 拨打视频通话 ---
- (void)TH_LoginAndCallVideo {
// [MoonChatPlatform startCalling:@"321" phoneType:MoonChatVideo callBlock:^(CallStatus status) {
// NSLog(@"==========CallStatus=%zd", status);
// }];
//
// return;
if (![[TihuManager shared] isUserLogin]) {
[[TihuManager shared] login:cUser.openid pwd:@"123456"];
}
[TihuManager shared].thModel = self.tihuModel;
[TihuManager shared].isReceiveCall = NO;
[TihuManager shared].video_id = self.tihuModel.video_id;
[TihuManager shared].device = self.deviceModel;
[TihuManager shared].callType = self.tihuModel.call_type;
WEAKSELF
[[TihuManager shared] showCallDialog:self.deviceModel userId:_tihuModel.device_username callback:^{
[weakSelf cancelTimer2];
}];
_callTimes = 0;
_isCancelTimer = NO;
[[TihuManager shared] queryRemoteUserOnline:self.deviceModel.imei callback:^(BOOL online) {
if (online) {
[MoonChatPlatform startCalling:self.deviceModel.imei phoneType:MoonChatVideo callBlock:^(CallStatus status) {
NSLog(@"========CallStatus=%zd", status);
}];
} else {
if (!_isCancelTimer) {
[weakSelf performSelector:@selector(reCall2) withObject:nil afterDelay:3];
}
}
NSLog(@"查询用户在线:%d", online);
}];
}
- (void)cancelTimer2 {
_isCancelTimer = YES;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(reCall2) object:nil];
}
- (void)reCall2 {
_callTimes++;
if (_callTimes > 3) {
[MoonChatPlatform startCalling:self.deviceModel.imei phoneType:MoonChatVideo callBlock:^(CallStatus status) {
NSLog(@"=========CallStatus=%zd", status);
}];
return;
}
WEAKSELF
[[TihuManager shared] queryRemoteUserOnline:self.deviceModel.imei callback:^(BOOL online) {
if (online) {
[MoonChatPlatform startCalling:self.deviceModel.imei phoneType:MoonChatVideo callBlock:^(CallStatus status) {
NSLog(@"========CallStatus=%zd", status);
}];
} else {
if (!_isCancelTimer) {
[weakSelf performSelector:@selector(reCall2) withObject:nil afterDelay:3];
}
}
NSLog(@"查询用户在线:%d", online);
}];
NSLog(@"查询用户在线次数:%d", _callTimes);
}
-(AgoraConfigModel *)agoraModel{
if(!_agoraModel){
_agoraModel = [AgoraConfigModel new];
}
return _agoraModel;
}
-(JuphoonModel *)jhModel{
if(!_jhModel){
_jhModel = [JuphoonModel new];
}
return _jhModel;
}
-(BairuiModel *)brModel{
if(!_brModel){
_brModel = [BairuiModel new];
}
return _brModel;
}
-(TihuModel *)tihuModel{
if(!_tihuModel){
_tihuModel = [TihuModel new];
}
return _tihuModel;
}
-(NSMutableArray *)checked_video_list{
if(!_checked_video_list){
_checked_video_list = [NSMutableArray array];
}
return _checked_video_list;
}
-(NSMutableArray *)unchecked_video_list{
if(!_unchecked_video_list){
_unchecked_video_list = [NSMutableArray array];
}
return _unchecked_video_list;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end