Warning, /network/kdeconnect-ios/KDE Connect/KDE Connect/ObjC Backend/Device.m is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: 2014 YANG Qiao <yangqiao0505@me.com> 0003 * 2020 Weixuan Xiao <veyx.shaw@gmail.com> 0004 * 2021 Lucas Wang <lucas.wang@tuta.io> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 // Original header below: 0010 //Copyright 29/4/14 YANG Qiao yangqiao0505@me.com 0011 //kdeconnect is distributed under two licenses. 0012 // 0013 //* The Mozilla Public License (MPL) v2.0 0014 // 0015 //or 0016 // 0017 //* The General Public License (GPL) v2.1 0018 // 0019 //---------------------------------------------------------------------- 0020 // 0021 //Software distributed under these licenses is distributed on an "AS 0022 //IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 0023 //implied. See the License for the specific language governing rights 0024 //and limitations under the License. 0025 //kdeconnect is distributed under both the GPL and the MPL. The MPL 0026 //notice, reproduced below, covers the use of either of the licenses. 0027 // 0028 //--------------------------------------------------------------------- 0029 0030 #import "Device.h" 0031 //#import "BackgroundService.h" 0032 #import "KDE_Connect-Swift.h" 0033 @import os.log; 0034 static const NSTimeInterval kPairingTimeout = 30.0; 0035 0036 @implementation Device { 0037 NSMutableDictionary<NetworkPackageType, id<Plugin>> *_plugins; 0038 NSMutableDictionary<NetworkPackageType, NSNumber *> *_pluginsEnableStatus; 0039 os_log_t logger; 0040 } 0041 0042 @synthesize _id; 0043 @synthesize _name; 0044 @synthesize _pairStatus; 0045 @synthesize _protocolVersion; 0046 @synthesize _type; 0047 @synthesize deviceDelegate; 0048 @synthesize _links; 0049 - (void)setPlugins:(NSDictionary<NetworkPackageType, NSNumber *> *)plugins 0050 { 0051 _plugins = [[NSMutableDictionary alloc] initWithDictionary:plugins]; 0052 } 0053 @synthesize _failedPlugins; 0054 @synthesize _incomingCapabilities; 0055 @synthesize _outgoingCapabilities; 0056 //@synthesize _testDevice; 0057 0058 - (void)setPluginsEnableStatus:(NSDictionary<NetworkPackageType, NSNumber *> *)pluginsEnableStatus 0059 { 0060 _pluginsEnableStatus = [[NSMutableDictionary alloc] initWithDictionary:pluginsEnableStatus]; 0061 } 0062 @synthesize _SHA256HashFormatted; 0063 0064 // TODO: plugins should be saving their own preferences 0065 // Plugin-specific persistent data are stored in the Device object. Plugin objects contain runtime 0066 // data only and are therefore NOT stored persistently 0067 // Remote Input 0068 @synthesize _cursorSensitivity; 0069 // Presenter 0070 @synthesize _pointerSensitivity; 0071 0072 - (instancetype)initWithID:(NSString *)deviceID 0073 type:(DeviceType)deviceType 0074 name:(NSString *)deviceName 0075 incomingCapabilities:(NSArray<NSString *> *)incomingCapabilities 0076 outgoingCapabilities:(NSArray<NSString *> *)outgoingCapabilities 0077 protocolVersion:(NSInteger)protocolVersion 0078 deviceDelegate:(id<DeviceDelegate>)deviceDelegate 0079 { 0080 if (self = [super init]) { 0081 logger = os_log_create([NSString kdeConnectOSLogSubsystem].UTF8String, 0082 NSStringFromClass([self class]).UTF8String); 0083 _id = deviceID; 0084 _type = deviceType; 0085 _name = deviceName; 0086 _incomingCapabilities = incomingCapabilities; 0087 _outgoingCapabilities = outgoingCapabilities; 0088 _links = [NSMutableArray arrayWithCapacity:1]; 0089 _plugins = [NSMutableDictionary dictionaryWithCapacity:1]; 0090 _failedPlugins = [NSMutableArray arrayWithCapacity:1]; 0091 _protocolVersion = protocolVersion; 0092 _pluginsEnableStatus = [NSMutableDictionary dictionary]; 0093 self.deviceDelegate = deviceDelegate; 0094 _cursorSensitivity = 3.0; 0095 _hapticStyle = 0; 0096 _pointerSensitivity = 3.0; 0097 [self reloadPlugins]; 0098 } 0099 return self; 0100 } 0101 0102 - (instancetype)initWithNetworkPackage:(NetworkPackage *)np 0103 link:(BaseLink*)link 0104 delegate:(id<DeviceDelegate>)deviceDelegate { 0105 if (self = [self initWithID:[np objectForKey:@"deviceId"] 0106 type:[Device Str2DeviceType:[np objectForKey:@"deviceType"]] 0107 name:[np objectForKey:@"deviceName"] 0108 incomingCapabilities:[np objectForKey:@"incomingCapabilities"] 0109 outgoingCapabilities:[np objectForKey:@"outgoingCapabilities"] 0110 protocolVersion:[np integerForKey:@"protocolVersion"] 0111 deviceDelegate:deviceDelegate]) { 0112 [self addLink:link]; 0113 } 0114 return self; 0115 } 0116 0117 - (os_log_type_t)debugLogLevel { 0118 if ([SelfDeviceData shared].isDebuggingDiscovery) { 0119 return OS_LOG_TYPE_INFO; 0120 } 0121 return OS_LOG_TYPE_DEBUG; 0122 } 0123 0124 - (NSInteger) compareProtocolVersion 0125 { 0126 return 0; 0127 } 0128 0129 #pragma mark Link-related Functions 0130 0131 - (void)updateInfoWithNetworkPackage:(NetworkPackage*)np { 0132 if (_protocolVersion!=[np integerForKey:@"protocolVersion"]) { 0133 os_log_with_type(logger, self.debugLogLevel, "using different protocol version"); 0134 } 0135 _id=[np objectForKey:@"deviceId"]; 0136 _name=[np objectForKey:@"deviceName"]; 0137 _type=[Device Str2DeviceType:[np objectForKey:@"deviceType"]]; 0138 _incomingCapabilities=[np objectForKey:@"incomingCapabilities"]; 0139 _outgoingCapabilities=[np objectForKey:@"outgoingCapabilities"]; 0140 } 0141 0142 - (void)addLink:(BaseLink*)Link { 0143 os_log_with_type(logger, OS_LOG_TYPE_INFO, "add link to %{mask.hash}@",_id); 0144 NSUInteger count; 0145 @synchronized (_links) { 0146 [_links addObject:Link]; 0147 //[self saveSetting]; 0148 [Link setLinkDelegate:self]; 0149 count = [_links count]; 0150 } 0151 if (count == 1) { 0152 os_log_with_type(logger, self.debugLogLevel, "one link available"); 0153 if (deviceDelegate) { 0154 [deviceDelegate onDeviceReachableStatusChanged:self]; 0155 } 0156 // If a remembered device is online with its first link, ask for its battery status 0157 [self updateBatteryStatus]; 0158 } 0159 } 0160 0161 // FIXME: This ain't it, doesn't get called when connection is cut (e.g wifi off) from the remote device 0162 - (void) onLinkDestroyed:(BaseLink *)link 0163 { 0164 os_log_with_type(logger, self.debugLogLevel, "device on link destroyed"); 0165 NSUInteger count; 0166 @synchronized (_links) { 0167 [_links removeObject:link]; 0168 count = [_links count]; 0169 } 0170 os_log_with_type(logger, self.debugLogLevel, "remove link ; %lu remaining", count); 0171 if (count == 0) { 0172 os_log_with_type(logger, self.debugLogLevel, "no available link"); 0173 if (deviceDelegate) { 0174 [deviceDelegate onDeviceReachableStatusChanged:self]; 0175 // No, we don't want to remove the plugins because IF the device is coming back online later, we want to still have to ready 0176 //[_plugins removeAllObjects]; 0177 //[_failedPlugins removeAllObjects]; 0178 } 0179 } 0180 if (deviceDelegate) { 0181 [deviceDelegate onLinkDestroyed:link]; 0182 } 0183 } 0184 0185 - (BOOL) sendPackage:(NetworkPackage *)np tag:(long)tag 0186 { 0187 os_log_with_type(logger, self.debugLogLevel, "device send package"); 0188 @synchronized (_links) { 0189 for (BaseLink *link in _links) { 0190 if ([link sendPackage:np tag:tag]) { 0191 return true; 0192 } 0193 } 0194 } 0195 return false; 0196 } 0197 0198 - (void)onPackage:(NetworkPackage *)np sentWithPackageTag:(long)tag { 0199 os_log_with_type(logger, self.debugLogLevel, "device on send success"); 0200 if (tag==PACKAGE_TAG_PAIR) { 0201 if (_pairStatus==RequestedByPeer) { 0202 [self setAsPaired]; 0203 } 0204 } else if (tag == PACKAGE_TAG_PAYLOAD){ 0205 os_log_with_type(logger, self.debugLogLevel, "Last payload sent successfully, sending next one"); 0206 for (id<Plugin> plugin in [_plugins allValues]) { 0207 if ([plugin respondsToSelector:@selector(onPackage:sentWithPackageTag:)]) { 0208 [plugin onPackage:np sentWithPackageTag:tag]; 0209 } 0210 } 0211 } 0212 } 0213 0214 - (void)onPackage:(NetworkPackage *)np sendWithPackageTag:(long)tag 0215 failedWithError:(NSError *)error { 0216 switch (tag) { 0217 case PACKAGE_TAG_PAYLOAD: 0218 for (id<Plugin> plugin in [_plugins allValues]) { 0219 if ([plugin respondsToSelector:@selector(onPackage:sendWithPackageTag:failedWithError:)]) { 0220 [plugin onPackage:np sendWithPackageTag:tag 0221 failedWithError:error]; 0222 } 0223 } 0224 break; 0225 default: 0226 break; 0227 } 0228 } 0229 0230 - (void)onSendingPayload:(KDEFileTransferItem *)payload { 0231 for (id<Plugin> plugin in [_plugins allValues]) { 0232 if ([plugin respondsToSelector:@selector(onSendingPayload:)]) { 0233 [plugin onSendingPayload:payload]; 0234 } 0235 } 0236 } 0237 0238 - (void)willReceivePayload:(KDEFileTransferItem *)payload 0239 totalNumOfFilesToReceive:(long)numberOfFiles { 0240 for (id<Plugin> plugin in [_plugins allValues]) { 0241 if ([plugin respondsToSelector:@selector(willReceivePayload:totalNumOfFilesToReceive:)]) { 0242 [plugin willReceivePayload:payload totalNumOfFilesToReceive:numberOfFiles]; 0243 } 0244 } 0245 } 0246 0247 - (void)onReceivingPayload:(KDEFileTransferItem *)payload { 0248 for (id<Plugin> plugin in [_plugins allValues]) { 0249 if ([plugin respondsToSelector:@selector(onReceivingPayload:)]) { 0250 [plugin onReceivingPayload:payload]; 0251 } 0252 } 0253 } 0254 0255 - (void)onReceivingPayload:(KDEFileTransferItem *)payload 0256 failedWithError:(NSError *)error { 0257 for (id<Plugin> plugin in [_plugins allValues]) { 0258 if ([plugin respondsToSelector:@selector(onReceivingPayload:failedWithError:)]) { 0259 [plugin onReceivingPayload:payload failedWithError:error]; 0260 } 0261 } 0262 } 0263 0264 - (void)onPackageReceived:(NetworkPackage *)np { 0265 os_log_with_type(logger, self.debugLogLevel, "device on package received"); 0266 if ([np.type isEqualToString:NetworkPackageTypePair]) { 0267 os_log_with_type(logger, self.debugLogLevel, "Pair package received"); 0268 BOOL wantsPair=[np boolForKey:@"pair"]; 0269 if (wantsPair==[self isPaired]) { 0270 os_log_with_type(logger, self.debugLogLevel, "already done, paired:%d",wantsPair); 0271 if (_pairStatus==Requested) { 0272 os_log_with_type(logger, self.debugLogLevel, "canceled by other peer"); 0273 dispatch_async(dispatch_get_main_queue(), ^{ 0274 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(requestPairingTimeout:) object:nil]; 0275 }); 0276 _pairStatus=NotPaired; 0277 if (deviceDelegate) { 0278 [deviceDelegate onDevicePairRejected:self]; 0279 } 0280 } else if (wantsPair) { 0281 [self acceptPairing]; 0282 } 0283 return; 0284 } 0285 if (wantsPair) { 0286 os_log_with_type(logger, self.debugLogLevel, "pair request"); 0287 if ((_pairStatus)==Requested) { 0288 dispatch_async(dispatch_get_main_queue(), ^{ 0289 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(requestPairingTimeout:) object:nil]; 0290 }); 0291 [self setAsPaired]; 0292 } 0293 else{ 0294 _pairStatus=RequestedByPeer; 0295 if (deviceDelegate) { 0296 [deviceDelegate onDevicePairRequest:self]; 0297 } 0298 } 0299 } else { 0300 //NSLog(@"unpair request"); 0301 if (_pairStatus==Requested) { 0302 //NSLog(@"canceled by other peer"); 0303 _pairStatus=NotPaired; 0304 dispatch_async(dispatch_get_main_queue(), ^{ 0305 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(requestPairingTimeout:) object:nil]; 0306 }); 0307 } else if (_pairStatus==Paired) { 0308 // okay to call unpair directly because other is reachable 0309 [self unpair]; 0310 if (deviceDelegate) { 0311 [deviceDelegate onDeviceUnpaired:self]; 0312 } 0313 } 0314 } 0315 } else if ([self isPaired]) { 0316 // TODO: Instead of looping through all the Obj-C plugins here, calls Plugin handling function elsewhere in Swift 0317 os_log_with_type(logger, OS_LOG_TYPE_INFO, "received a plugin package: %{public}@", np.type); 0318 for (id<Plugin> plugin in [_plugins allValues]) { 0319 [plugin onDevicePackageReceivedWithNp:np]; 0320 } 0321 //[PluginsService goThroughHostPluginsForReceivingWithNp:np]; 0322 } else { 0323 // old iOS implementations send battery request while the devices are unpaired 0324 os_log_with_type(logger, OS_LOG_TYPE_DEFAULT, 0325 "not paired, ignore package of %{public}@, unpair the device", 0326 np.type); 0327 // remembered devices should have became paired, okay to call unpair. 0328 [self unpair]; 0329 } 0330 } 0331 0332 - (BOOL) isReachable 0333 { 0334 // synchronize not very helpful 0335 return [_links count] != 0; 0336 } 0337 0338 - (void) loadSetting 0339 { 0340 } 0341 0342 - (void) saveSetting 0343 { 0344 } 0345 0346 #pragma mark Pairing-related Functions 0347 - (BOOL) isPaired 0348 { 0349 return _pairStatus==Paired; // || _testDevice 0350 } 0351 0352 - (BOOL) isPaireRequested 0353 { 0354 return _pairStatus==Requested; 0355 } 0356 0357 - (void) setAsPaired 0358 { 0359 _pairStatus=Paired; 0360 //NSLog(@"paired with %@",_name); 0361 [self saveSetting]; 0362 // Request and update battery status for a newly paired device 0363 [self updateBatteryStatus]; 0364 if (deviceDelegate) { 0365 [deviceDelegate onDevicePairSuccess:self]; 0366 } 0367 } 0368 0369 - (void) requestPairing 0370 { 0371 if (![self isReachable]) { 0372 os_log_with_type(logger, OS_LOG_TYPE_ERROR, "device failed:not reachable"); 0373 return; 0374 } 0375 if (_pairStatus==Paired) { 0376 os_log_with_type(logger, OS_LOG_TYPE_DEFAULT, "device failed:already paired"); 0377 return; 0378 } 0379 if (_pairStatus==Requested) { 0380 os_log_with_type(logger, OS_LOG_TYPE_DEFAULT, "device failed:already requested"); 0381 return; 0382 } 0383 if (_pairStatus==RequestedByPeer) { 0384 os_log_with_type(logger, self.debugLogLevel, "device accept pair request"); 0385 } 0386 else{ 0387 os_log_with_type(logger, self.debugLogLevel, "device request pairing"); 0388 _pairStatus=Requested; 0389 dispatch_async(dispatch_get_main_queue(), ^{ 0390 [self performSelector:@selector(requestPairingTimeout:) withObject:nil afterDelay:kPairingTimeout]; 0391 }); 0392 } 0393 NetworkPackage* np=[NetworkPackage createPairPackage]; 0394 [self sendPackage:np tag:PACKAGE_TAG_PAIR]; 0395 } 0396 0397 - (void)requestPairingTimeout:(id)sender { 0398 os_log_with_type(logger, OS_LOG_TYPE_ERROR, "device request pairing timeout"); 0399 if (_pairStatus==Requested) { 0400 _pairStatus=NotPaired; 0401 os_log_with_type(logger, self.debugLogLevel, "pairing timeout"); 0402 if (deviceDelegate) { 0403 [deviceDelegate onDevicePairTimeout:self]; 0404 } 0405 // okay to call unpair directly because only invocation of this method 0406 // is scheduled when we initiate a pairing (and cancelled otherwise) 0407 // thus can't be a remembered or paired device. 0408 [self unpair]; 0409 } 0410 } 0411 0412 /// Change the status of the Device to unpaired WITHOUT sending out an unpair packet. 0413 - (void)setAsUnpaired { 0414 _pairStatus=NotPaired; 0415 } 0416 0417 - (void)unpair { 0418 os_log_with_type(logger, self.debugLogLevel, "device unpair"); 0419 _pairStatus=NotPaired; 0420 0421 NetworkPackage* np=[[NetworkPackage alloc] initWithType:NetworkPackageTypePair]; 0422 [np setBool:false forKey:@"pair"]; 0423 [self sendPackage:np tag:PACKAGE_TAG_UNPAIR]; 0424 } 0425 0426 - (void) acceptPairing 0427 { 0428 os_log_with_type(logger, self.debugLogLevel, "device accepted pair request"); 0429 NetworkPackage* np=[NetworkPackage createPairPackage]; 0430 [self sendPackage:np tag:PACKAGE_TAG_PAIR]; 0431 } 0432 0433 #pragma mark Plugins-related Functions 0434 0435 - (void)updateBatteryStatus { 0436 if ((_pluginsEnableStatus[NetworkPackageTypeBatteryRequest] != nil) 0437 && (_pluginsEnableStatus[NetworkPackageTypeBatteryRequest])) { 0438 id<Plugin> plugin = [_plugins objectForKey:NetworkPackageTypeBatteryRequest]; 0439 if ([plugin respondsToSelector:@selector(sendBatteryStatusRequest)]) { 0440 [plugin performSelector:@selector(sendBatteryStatusRequest)]; 0441 } 0442 // For backward compatibility reasons, we should update the other device 0443 if ([plugin respondsToSelector:@selector(sendBatteryStatusOut)]) { 0444 [plugin performSelector:@selector(sendBatteryStatusOut)]; 0445 } 0446 } 0447 } 0448 0449 - (void) reloadPlugins 0450 { 0451 // if (![self isReachable]) { 0452 // return; 0453 // } 0454 0455 os_log_with_type(logger, self.debugLogLevel, "device reload plugins"); 0456 [_plugins removeAllObjects]; 0457 [_failedPlugins removeAllObjects]; 0458 [_pluginsEnableStatus removeAllObjects]; 0459 0460 for (NSString* pluginID in _incomingCapabilities) { 0461 if ([pluginID isEqualToString:NetworkPackageTypePing]) { 0462 [_plugins setObject:[[Ping alloc] initWithControlDevice:self] forKey:NetworkPackageTypePing]; 0463 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypePing]; 0464 0465 } else if ([pluginID isEqualToString:NetworkPackageTypeShare]) { 0466 [_plugins setObject:[[Share alloc] initWithControlDevice:self] forKey:NetworkPackageTypeShare]; 0467 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypeShare]; 0468 0469 } else if ([pluginID isEqualToString:NetworkPackageTypeFindMyPhoneRequest]) { 0470 [_plugins setObject:[[FindMyPhone alloc] initWithControlDevice:self] forKey:NetworkPackageTypeFindMyPhoneRequest]; 0471 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypeFindMyPhoneRequest]; 0472 0473 } else if ([pluginID isEqualToString:NetworkPackageTypeBatteryRequest]) { 0474 [_plugins setObject:[[Battery alloc] initWithControlDevice:self] forKey:NetworkPackageTypeBatteryRequest]; 0475 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypeBatteryRequest]; 0476 0477 } else if ([pluginID isEqualToString:NetworkPackageTypeClipboard]) { 0478 [_plugins setObject:[[Clipboard alloc] initWithControlDevice:self] forKey:NetworkPackageTypeClipboard]; 0479 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypeClipboard]; 0480 0481 } else if ([pluginID isEqualToString:NetworkPackageTypeMousePadRequest]) { 0482 [_plugins setObject:[[RemoteInput alloc] initWithControlDevice:self] forKey:NetworkPackageTypeMousePadRequest]; 0483 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypeMousePadRequest]; 0484 0485 } else if ([pluginID isEqualToString:NetworkPackageTypePresenter]) { 0486 [_plugins setObject:[[Presenter alloc] initWithControlDevice:self] forKey:NetworkPackageTypePresenter]; 0487 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypePresenter]; 0488 } 0489 } 0490 0491 // for the capabilities that are ONLY in the outgoing section of KDE Connect iOS 0492 for (NSString* pluginID in _outgoingCapabilities) { 0493 if ([pluginID isEqualToString:NetworkPackageTypeRunCommand]) { 0494 [_plugins setObject:[[RunCommand alloc] initWithControlDevice:self] forKey:NetworkPackageTypeRunCommand]; 0495 [_pluginsEnableStatus setValue:@TRUE forKey:NetworkPackageTypeRunCommand]; 0496 0497 } 0498 } 0499 0500 0501 // //NSLog(@"device reload plugins"); 0502 // [_failedPlugins removeAllObjects]; 0503 // PluginFactory* pluginFactory=[PluginFactory sharedInstance]; 0504 // NSArray* pluginNames=[pluginFactory getAvailablePlugins]; 0505 // NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES]; 0506 // pluginNames=[pluginNames sortedArrayUsingDescriptors:[NSArray arrayWithObject:sd]]; 0507 // SettingsStore* _devSettings=[[SettingsStore alloc] initWithPath:_id]; 0508 // for (NSString* pluginName in pluginNames) { 0509 // if ([_devSettings objectForKey:pluginName]!=nil && ![_devSettings boolForKey:pluginName]) { 0510 // [[_plugins objectForKey:pluginName] stop]; 0511 // [_plugins removeObjectForKey:pluginName]; 0512 // [_failedPlugins addObject:pluginName]; 0513 // continue; 0514 // } 0515 // [_plugins removeObjectForKey:pluginName]; 0516 // Plugin* plugin=[pluginFactory instantiatePluginForDevice:self pluginName:pluginName]; 0517 // if (plugin) 0518 // [_plugins setValue:plugin forKey:pluginName]; 0519 // else 0520 // [_failedPlugins addObject:pluginName]; 0521 // } 0522 } 0523 0524 //- (NSArray*) getPluginViews:(UIViewController*)vc 0525 //{ 0526 // NSMutableArray* views=[NSMutableArray arrayWithCapacity:1]; 0527 // for (Plugin* plugin in [_plugins allValues]) { 0528 // UIView* view=[plugin getView:vc]; 0529 // if (view) { 0530 // [views addObject:view]; 0531 // } 0532 // } 0533 // return views; 0534 //} 0535 0536 #pragma mark enum tools 0537 + (NSString*)DeviceType2Str:(DeviceType)type 0538 { 0539 switch (type) { 0540 case DeviceTypeDesktop: 0541 return @"desktop"; 0542 case DeviceTypeLaptop: 0543 return @"laptop"; 0544 case DeviceTypePhone: 0545 return @"phone"; 0546 case DeviceTypeTablet: 0547 return @"tablet"; 0548 case DeviceTypeTv: 0549 return @"tv"; 0550 case DeviceTypeUnknown: 0551 return @"unknown"; 0552 } 0553 } 0554 + (DeviceType)Str2DeviceType:(NSString*)str 0555 { 0556 if ([str isEqualToString:@"desktop"]) { 0557 return DeviceTypeDesktop; 0558 } 0559 if ([str isEqualToString:@"laptop"]) { 0560 return DeviceTypeLaptop; 0561 } 0562 if ([str isEqualToString:@"phone"]) { 0563 return DeviceTypePhone; 0564 } 0565 if ([str isEqualToString:@"tablet"]) { 0566 return DeviceTypeTablet; 0567 } 0568 if ([str isEqualToString:@"tv"]) { 0569 return DeviceTypeTv; 0570 } 0571 return DeviceTypeUnknown; 0572 } 0573 0574 #pragma mark En/Decoding Methods 0575 - (void)encodeWithCoder:(nonnull NSCoder *)coder { 0576 [coder encodeObject:_id forKey:@"_id"]; 0577 [coder encodeObject:_name forKey:@"_name"]; 0578 [coder encodeInteger:_type forKey:@"_type"]; 0579 [coder encodeInteger:_protocolVersion forKey:@"_protocolVersion"]; 0580 [coder encodeInteger:_pairStatus forKey:@"_pairStatus"]; 0581 [coder encodeObject:_incomingCapabilities forKey:@"_incomingCapabilities"]; 0582 [coder encodeObject:_outgoingCapabilities forKey:@"_outgoingCapabilities"]; 0583 [coder encodeObject:_pluginsEnableStatus forKey:@"_pluginsEnableStatus"]; 0584 [coder encodeFloat:_cursorSensitivity forKey:@"_cursorSensitivity"]; 0585 [coder encodeInteger:_hapticStyle forKey:@"_hapticStyle"]; 0586 [coder encodeFloat:_pointerSensitivity forKey:@"_pointerSensitivity"]; 0587 [coder encodeObject:_SHA256HashFormatted forKey:@"_SHA256HashFormatted"]; 0588 } 0589 0590 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder { 0591 if (self = [super init]) { 0592 _id = [coder decodeObjectForKey:@"_id"]; 0593 _name = [coder decodeObjectForKey:@"_name"]; 0594 _type = [coder decodeIntegerForKey:@"_type"]; 0595 _protocolVersion = [coder decodeIntegerForKey:@"_protocolVersion"]; 0596 _pairStatus = [coder decodeIntegerForKey:@"_pairStatus"]; 0597 _incomingCapabilities = [coder decodeArrayOfObjectsOfClass:[NSString class] forKey:@"_incomingCapabilities"]; 0598 _outgoingCapabilities = [coder decodeArrayOfObjectsOfClass:[NSString class] forKey:@"_outgoingCapabilities"]; 0599 _pluginsEnableStatus = (NSMutableDictionary*)[(NSDictionary*)[coder decodeDictionaryWithKeysOfClass:[NSString class] objectsOfClass:[NSNumber class] forKey:@"_pluginsEnableStatus"] mutableCopy]; 0600 _cursorSensitivity = [coder decodeFloatForKey:@"_cursorSensitivity"]; 0601 _hapticStyle = [coder decodeIntegerForKey:@"_hapticStyle"]; 0602 _pointerSensitivity = [coder decodeFloatForKey:@"_pointerSensitivity"]; 0603 _SHA256HashFormatted = [coder decodeObjectForKey:@"_SHA256HashFormatted"]; 0604 0605 // To be set later in backgroundServices 0606 deviceDelegate = nil; 0607 0608 // To be populated later 0609 _plugins = [NSMutableDictionary dictionary]; 0610 _failedPlugins = [NSMutableArray array]; 0611 _links = [NSMutableArray array]; 0612 [self reloadPlugins]; 0613 } 0614 return self; 0615 } 0616 0617 + (BOOL)supportsSecureCoding { 0618 return YES; 0619 } 0620 0621 @end