// // FlutterPagPlugin.m // FlutterPagPlugin // // Created by 黎敬茂 on 2022/3/14. // Copyright © 2022 Tencent. All rights reserved. // #import "FlutterPagPlugin.h" #import "TGFlutterPagRender.h" #import "TGFlutterPagDownloadManager.h" /** FlutterPagPlugin,处理flutter MethodChannel约定的方法 */ #define PlayCallback @"PAGCallback" #define ArgumentEvent @"PAGEvent" #define ArgumentTextureId @"textureId" @interface FlutterPagPlugin() /// flutter引擎注册的textures对象 @property(nonatomic, weak) NSObject* textures; /// flutter引擎注册的registrar对象 @property(nonatomic, weak) NSObject* registrar; /// 保存textureId跟render对象的对应关系 @property (nonatomic, strong) NSMutableDictionary *renderMap; /// pag对象的缓存 @property (nonatomic, strong)NSCache *cache; /// 用于通信的channel @property (nonatomic, strong)FlutterMethodChannel* channel; @end @implementation FlutterPagPlugin + (void)registerWithRegistrar:(NSObject*)registrar { FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"flutter_pag_plugin" binaryMessenger:[registrar messenger]]; FlutterPagPlugin* instance = [[FlutterPagPlugin alloc] init]; instance.textures = registrar.textures; instance.registrar = registrar; instance.channel = channel; [registrar addMethodCallDelegate:instance channel:channel]; } - (void)getLayersUnderPoint:(id)arguments result:(FlutterResult _Nonnull)result { NSNumber* textureId = arguments[@"textureId"]; NSNumber* x = arguments[@"x"]; NSNumber* y = arguments[@"y"]; TGFlutterPagRender *render = [_renderMap objectForKey:textureId]; NSArray *names = [render getLayersUnderPoint:CGPointMake(x.doubleValue, y.doubleValue)]; result(names); } - (void)release:(id)arguments result:(FlutterResult _Nonnull)result { NSNumber* textureId = arguments[@"textureId"]; if(textureId == nil){ result(@""); return; } [self.textures unregisterTexture:textureId.intValue]; TGFlutterPagRender *render = [_renderMap objectForKey:textureId]; [render releaseRender]; [_renderMap removeObjectForKey:textureId]; result(@""); } - (void)setProgress:(id)arguments result:(FlutterResult _Nonnull)result { NSNumber* textureId = arguments[@"textureId"]; if(textureId == nil){ result(@""); return; } double progress = 0.0; if (arguments[@"progress"]) { progress = [arguments[@"progress"] doubleValue]; } TGFlutterPagRender *render = [_renderMap objectForKey:textureId]; [render setProgress:progress]; result(@""); } - (void)pause:(id)arguments result:(FlutterResult _Nonnull)result { NSNumber* textureId = arguments[@"textureId"]; if(textureId == nil){ result(@""); return; } TGFlutterPagRender *render = [_renderMap objectForKey:textureId]; [render pauseRender]; result(@""); } - (void)stop:(id)arguments result:(FlutterResult _Nonnull)result { NSNumber* textureId = arguments[@"textureId"]; if(textureId == nil){ result(@""); return; } TGFlutterPagRender *render = [_renderMap objectForKey:textureId]; [render stopRender]; result(@""); } - (void)start:(id)arguments result:(FlutterResult _Nonnull)result { NSNumber* textureId = arguments[@"textureId"]; if(textureId == nil){ result(@""); return; } TGFlutterPagRender *render = [_renderMap objectForKey:textureId]; [render startRender]; result(@""); } - (void)initPag:(id)arguments result:(FlutterResult _Nonnull)result { if (arguments == nil || (arguments[@"assetName"] == NSNull.null && arguments[@"url"] == NSNull.null && arguments[@"bytesData"] == NSNull.null)) { result(@-1); NSLog(@"showPag arguments is nil"); return; } double initProgress = 0.0; if (arguments[@"initProgress"]) { initProgress = [arguments[@"initProgress"] doubleValue]; } int repeatCount = -1; if(arguments[@"repeatCount"]){ repeatCount = [[arguments objectForKey:@"repeatCount"] intValue]; } BOOL autoPlay = NO; if(arguments[@"autoPlay"]){ autoPlay = [[arguments objectForKey:@"autoPlay"] boolValue]; } NSString* assetName = arguments[@"assetName"]; NSData *pagData = nil; if ([assetName isKindOfClass:NSString.class] && assetName.length > 0) { NSString *key = assetName; pagData = [self getCacheData:key]; if (!pagData) { NSString* package = arguments[@"package"]; NSString* resourcePath; if(package && [package isKindOfClass:NSString.class] && package.length > 0){ resourcePath = [self.registrar lookupKeyForAsset:assetName fromPackage:package]; }else{ resourcePath = [self.registrar lookupKeyForAsset:assetName]; } resourcePath = [[NSBundle mainBundle] pathForResource:resourcePath ofType:nil]; pagData = [NSData dataWithContentsOfFile:resourcePath]; [self setCacheData:key data:pagData]; } [self pagRenderWithPagData:pagData progress:initProgress repeatCount:repeatCount autoPlay:autoPlay result:result]; } NSString* url = arguments[@"url"]; if ([url isKindOfClass:NSString.class] && url.length > 0) { NSURLSessionDownloadTask *task; [task resume]; NSString *key = url; pagData = [self getCacheData:key]; if (!pagData) { __weak typeof(self) weak_self = self; [TGFlutterPagDownloadManager download:url completionHandler:^(NSData * _Nonnull data, NSError * _Nonnull error) { if (data) { [weak_self setCacheData:key data:pagData]; [weak_self pagRenderWithPagData:data progress:initProgress repeatCount:repeatCount autoPlay:autoPlay result:result]; }else{ result(@-1); } }]; }else{ [self pagRenderWithPagData:pagData progress:initProgress repeatCount:repeatCount autoPlay:autoPlay result:result]; } } id bytesData = arguments[@"bytesData"]; if(bytesData != nil && [bytesData isKindOfClass:FlutterStandardTypedData.class]){ FlutterStandardTypedData *typedData = bytesData; if(typedData.type == FlutterStandardDataTypeUInt8 && typedData.data != nil){ [self pagRenderWithPagData:typedData.data progress:initProgress repeatCount:repeatCount autoPlay:autoPlay result:result]; }else{ result(@-1); } } } - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { id arguments = call.arguments; if ([@"getPlatformVersion" isEqualToString:call.method]) { result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); } else if([@"initPag" isEqualToString:call.method]){ [self initPag:arguments result:result]; } else if([@"start" isEqualToString:call.method]){ [self start:arguments result:result]; } else if([@"stop" isEqualToString:call.method]){ [self stop:arguments result:result]; } else if([@"pause" isEqualToString:call.method]){ [self pause:arguments result:result]; } else if([@"setProgress" isEqual:call.method]){ [self setProgress:arguments result:result]; } else if([@"release" isEqualToString:call.method]){ [self release:arguments result:result]; } else if([@"getLayersUnderPoint" isEqualToString:call.method]){ [self getLayersUnderPoint:arguments result:result]; } else { result(FlutterMethodNotImplemented); } } -(void)pagRenderWithPagData:(NSData *)pagData progress:(double)progress repeatCount:(int)repeatCount autoPlay:(BOOL)autoPlay result:(FlutterResult)result{ __block int64_t textureId = -1; __weak typeof(self) weakSelf = self; TGFlutterPagRender *render = [[TGFlutterPagRender alloc] initWithPagData:pagData progress:progress frameUpdateCallback:^{ [weakSelf.textures textureFrameAvailable:textureId]; } eventCallback:^(NSString * event) { [weakSelf.channel invokeMethod:PlayCallback arguments:@{ArgumentTextureId:@(textureId), ArgumentEvent:event}]; }]; [render setRepeatCount:repeatCount]; textureId = [self.textures registerTexture:render]; if(_renderMap == nil){ _renderMap = [[NSMutableDictionary alloc] init]; } [_renderMap setObject:render forKey:@(textureId)]; result(@{@"textureId":@(textureId), @"width":@([render size].width), @"height":@([render size].height)}); if(autoPlay){ [render startRender]; } } -(NSData *)getCacheData:(NSString *)key{ return [self.cache objectForKey:key]; } -(void)setCacheData:(NSString *)key data:(NSData *)data{ if (data == nil || key == nil) { return; } [self.cache setObject:data forKey:key cost:data.length]; } -(NSCache *)cache{ if (!_cache) { _cache = [[NSCache alloc] init]; ///缓存64m _cache.totalCostLimit = 64*1024*1024; _cache.countLimit = 32; } return _cache; } @end